add mcp for web (#6411)
* add mcp for web * update /jan/v1 endpoint to /v1 * update mise and makefile * update yarn lock * use mcp oauth properly
This commit is contained in:
parent
72128117a9
commit
b5b6e1dc19
2
Makefile
2
Makefile
@ -40,9 +40,11 @@ install-web-app: config-yarn
|
||||
yarn install
|
||||
|
||||
dev-web-app: install-web-app
|
||||
yarn build:core
|
||||
yarn dev:web-app
|
||||
|
||||
build-web-app: install-web-app
|
||||
yarn build:core
|
||||
yarn build:web-app
|
||||
|
||||
serve-web-app:
|
||||
|
||||
@ -7,6 +7,7 @@ export enum ExtensionTypeEnum {
|
||||
Inference = 'inference',
|
||||
Model = 'model',
|
||||
SystemMonitoring = 'systemMonitoring',
|
||||
MCP = 'mcp',
|
||||
HuggingFace = 'huggingFace',
|
||||
Engine = 'engine',
|
||||
Hardware = 'hardware',
|
||||
|
||||
@ -14,6 +14,11 @@ export { InferenceExtension } from './inference'
|
||||
*/
|
||||
export { AssistantExtension } from './assistant'
|
||||
|
||||
/**
|
||||
* MCP extension for managing tools and server communication.
|
||||
*/
|
||||
export { MCPExtension } from './mcp'
|
||||
|
||||
/**
|
||||
* Base AI Engines.
|
||||
*/
|
||||
|
||||
99
core/src/browser/extensions/mcp.test.ts
Normal file
99
core/src/browser/extensions/mcp.test.ts
Normal file
@ -0,0 +1,99 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest'
|
||||
import { MCPExtension } from './mcp'
|
||||
import { ExtensionTypeEnum } from '../extension'
|
||||
import { MCPTool, MCPToolCallResult } from '../../types'
|
||||
|
||||
class TestMCPExtension extends MCPExtension {
|
||||
constructor() {
|
||||
super('test://mcp', 'test-mcp')
|
||||
}
|
||||
|
||||
async getTools(): Promise<MCPTool[]> {
|
||||
return [
|
||||
{
|
||||
name: 'test_tool',
|
||||
description: 'A test tool',
|
||||
inputSchema: { type: 'object' },
|
||||
server: 'test-server'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
async callTool(toolName: string, args: Record<string, unknown>): Promise<MCPToolCallResult> {
|
||||
return {
|
||||
error: '',
|
||||
content: [{ type: 'text', text: `Called ${toolName} with ${JSON.stringify(args)}` }]
|
||||
}
|
||||
}
|
||||
|
||||
async getConnectedServers(): Promise<string[]> {
|
||||
return ['test-server']
|
||||
}
|
||||
|
||||
async refreshTools(): Promise<void> {
|
||||
// Mock implementation
|
||||
}
|
||||
|
||||
async isHealthy(): Promise<boolean> {
|
||||
return true
|
||||
}
|
||||
|
||||
async onLoad(): Promise<void> {
|
||||
// Mock implementation
|
||||
}
|
||||
|
||||
onUnload(): void {
|
||||
// Mock implementation
|
||||
}
|
||||
}
|
||||
|
||||
describe('MCPExtension', () => {
|
||||
let mcpExtension: TestMCPExtension
|
||||
|
||||
beforeEach(() => {
|
||||
mcpExtension = new TestMCPExtension()
|
||||
})
|
||||
|
||||
describe('type', () => {
|
||||
it('should return MCP extension type', () => {
|
||||
expect(mcpExtension.type()).toBe(ExtensionTypeEnum.MCP)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getTools', () => {
|
||||
it('should return array of MCP tools', async () => {
|
||||
const tools = await mcpExtension.getTools()
|
||||
expect(tools).toHaveLength(1)
|
||||
expect(tools[0]).toEqual({
|
||||
name: 'test_tool',
|
||||
description: 'A test tool',
|
||||
inputSchema: { type: 'object' },
|
||||
server: 'test-server'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('callTool', () => {
|
||||
it('should call tool and return result', async () => {
|
||||
const result = await mcpExtension.callTool('test_tool', { param: 'value' })
|
||||
expect(result).toEqual({
|
||||
error: '',
|
||||
content: [{ type: 'text', text: 'Called test_tool with {"param":"value"}' }]
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('getConnectedServers', () => {
|
||||
it('should return list of connected servers', async () => {
|
||||
const servers = await mcpExtension.getConnectedServers()
|
||||
expect(servers).toEqual(['test-server'])
|
||||
})
|
||||
})
|
||||
|
||||
describe('isHealthy', () => {
|
||||
it('should return health status', async () => {
|
||||
const healthy = await mcpExtension.isHealthy()
|
||||
expect(healthy).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
21
core/src/browser/extensions/mcp.ts
Normal file
21
core/src/browser/extensions/mcp.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { MCPInterface, MCPTool, MCPToolCallResult } from '../../types'
|
||||
import { BaseExtension, ExtensionTypeEnum } from '../extension'
|
||||
|
||||
/**
|
||||
* MCP (Model Context Protocol) extension for managing tools and server communication.
|
||||
* @extends BaseExtension
|
||||
*/
|
||||
export abstract class MCPExtension extends BaseExtension implements MCPInterface {
|
||||
/**
|
||||
* MCP extension type.
|
||||
*/
|
||||
type(): ExtensionTypeEnum | undefined {
|
||||
return ExtensionTypeEnum.MCP
|
||||
}
|
||||
|
||||
abstract getTools(): Promise<MCPTool[]>
|
||||
abstract callTool(toolName: string, args: Record<string, unknown>): Promise<MCPToolCallResult>
|
||||
abstract getConnectedServers(): Promise<string[]>
|
||||
abstract refreshTools(): Promise<void>
|
||||
abstract isHealthy(): Promise<boolean>
|
||||
}
|
||||
@ -10,3 +10,4 @@ export * from './api'
|
||||
export * from './setting'
|
||||
export * from './engine'
|
||||
export * from './hardware'
|
||||
export * from './mcp'
|
||||
|
||||
2
core/src/types/mcp/index.ts
Normal file
2
core/src/types/mcp/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './mcpEntity'
|
||||
export * from './mcpInterface'
|
||||
24
core/src/types/mcp/mcpEntity.ts
Normal file
24
core/src/types/mcp/mcpEntity.ts
Normal file
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* MCP (Model Context Protocol) entities
|
||||
*/
|
||||
|
||||
export interface MCPTool {
|
||||
name: string
|
||||
description: string
|
||||
inputSchema: Record<string, unknown>
|
||||
server: string
|
||||
}
|
||||
|
||||
export interface MCPToolCallResult {
|
||||
error: string
|
||||
content: Array<{
|
||||
type?: string
|
||||
text: string
|
||||
}>
|
||||
}
|
||||
|
||||
export interface MCPServerInfo {
|
||||
name: string
|
||||
connected: boolean
|
||||
tools?: MCPTool[]
|
||||
}
|
||||
32
core/src/types/mcp/mcpInterface.ts
Normal file
32
core/src/types/mcp/mcpInterface.ts
Normal file
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* MCP (Model Context Protocol) interface
|
||||
*/
|
||||
|
||||
import { MCPTool, MCPToolCallResult } from './mcpEntity'
|
||||
|
||||
export interface MCPInterface {
|
||||
/**
|
||||
* Get all available MCP tools
|
||||
*/
|
||||
getTools(): Promise<MCPTool[]>
|
||||
|
||||
/**
|
||||
* Call a specific MCP tool
|
||||
*/
|
||||
callTool(toolName: string, args: Record<string, unknown>): Promise<MCPToolCallResult>
|
||||
|
||||
/**
|
||||
* Get list of connected MCP servers
|
||||
*/
|
||||
getConnectedServers(): Promise<string[]>
|
||||
|
||||
/**
|
||||
* Refresh the list of available tools
|
||||
*/
|
||||
refreshTools(): Promise<void>
|
||||
|
||||
/**
|
||||
* Check if MCP service is healthy
|
||||
*/
|
||||
isHealthy(): Promise<boolean>
|
||||
}
|
||||
@ -30,5 +30,8 @@
|
||||
"peerDependencies": {
|
||||
"@janhq/core": "*",
|
||||
"zustand": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.17.5"
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ import type { WebExtensionRegistry } from './types'
|
||||
export { default as AssistantExtensionWeb } from './assistant-web'
|
||||
export { default as ConversationalExtensionWeb } from './conversational-web'
|
||||
export { default as JanProviderWeb } from './jan-provider-web'
|
||||
export { default as MCPExtensionWeb } from './mcp-web'
|
||||
|
||||
// Re-export types
|
||||
export type {
|
||||
@ -17,7 +18,8 @@ export type {
|
||||
WebExtensionLoader,
|
||||
AssistantWebModule,
|
||||
ConversationalWebModule,
|
||||
JanProviderWebModule
|
||||
JanProviderWebModule,
|
||||
MCPWebModule
|
||||
} from './types'
|
||||
|
||||
// Extension registry for dynamic loading
|
||||
@ -25,4 +27,5 @@ export const WEB_EXTENSIONS: WebExtensionRegistry = {
|
||||
'assistant-web': () => import('./assistant-web'),
|
||||
'conversational-web': () => import('./conversational-web'),
|
||||
'jan-provider-web': () => import('./jan-provider-web'),
|
||||
'mcp-web': () => import('./mcp-web'),
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
* Handles API requests to Jan backend for models and chat completions
|
||||
*/
|
||||
|
||||
import { JanAuthService } from './auth'
|
||||
import { JanAuthService } from '../shared/auth'
|
||||
import { JanModel, janProviderStore } from './store'
|
||||
|
||||
// JAN_API_BASE is defined in vite.config.ts
|
||||
@ -18,6 +18,7 @@ export interface JanChatMessage {
|
||||
content: string
|
||||
reasoning?: string
|
||||
reasoning_content?: string
|
||||
tool_calls?: any[]
|
||||
}
|
||||
|
||||
export interface JanChatCompletionRequest {
|
||||
@ -30,6 +31,8 @@ export interface JanChatCompletionRequest {
|
||||
presence_penalty?: number
|
||||
stream?: boolean
|
||||
stop?: string | string[]
|
||||
tools?: any[]
|
||||
tool_choice?: any
|
||||
}
|
||||
|
||||
export interface JanChatCompletionChoice {
|
||||
@ -63,6 +66,7 @@ export interface JanChatCompletionChunk {
|
||||
content?: string
|
||||
reasoning?: string
|
||||
reasoning_content?: string
|
||||
tool_calls?: any[]
|
||||
}
|
||||
finish_reason: string | null
|
||||
}>
|
||||
@ -83,40 +87,12 @@ export class JanApiClient {
|
||||
return JanApiClient.instance
|
||||
}
|
||||
|
||||
private async makeAuthenticatedRequest<T>(
|
||||
url: string,
|
||||
options: RequestInit = {}
|
||||
): Promise<T> {
|
||||
try {
|
||||
const authHeader = await this.authService.getAuthHeader()
|
||||
|
||||
const response = await fetch(url, {
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...authHeader,
|
||||
...options.headers,
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text()
|
||||
throw new Error(`API request failed: ${response.status} ${response.statusText} - ${errorText}`)
|
||||
}
|
||||
|
||||
return response.json()
|
||||
} catch (error) {
|
||||
console.error('API request failed:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async getModels(): Promise<JanModel[]> {
|
||||
try {
|
||||
janProviderStore.setLoadingModels(true)
|
||||
janProviderStore.clearError()
|
||||
|
||||
const response = await this.makeAuthenticatedRequest<JanModelsResponse>(
|
||||
const response = await this.authService.makeAuthenticatedRequest<JanModelsResponse>(
|
||||
`${JAN_API_BASE}/models`
|
||||
)
|
||||
|
||||
@ -138,7 +114,7 @@ export class JanApiClient {
|
||||
try {
|
||||
janProviderStore.clearError()
|
||||
|
||||
return await this.makeAuthenticatedRequest<JanChatCompletionResponse>(
|
||||
return await this.authService.makeAuthenticatedRequest<JanChatCompletionResponse>(
|
||||
`${JAN_API_BASE}/chat/completions`,
|
||||
{
|
||||
method: 'POST',
|
||||
|
||||
@ -62,6 +62,7 @@ export default class JanProviderWeb extends AIEngine {
|
||||
path: undefined, // Remote model, no local path
|
||||
owned_by: model.owned_by,
|
||||
object: model.object,
|
||||
capabilities: ['tools'], // Jan models support both tools via MCP
|
||||
}))
|
||||
} catch (error) {
|
||||
console.error('Failed to list Jan models:', error)
|
||||
@ -150,6 +151,8 @@ export default class JanProviderWeb extends AIEngine {
|
||||
presence_penalty: opts.presence_penalty ?? undefined,
|
||||
stream: opts.stream ?? false,
|
||||
stop: opts.stop ?? undefined,
|
||||
tools: opts.tools ?? undefined,
|
||||
tool_choice: opts.tool_choice ?? undefined,
|
||||
}
|
||||
|
||||
if (opts.stream) {
|
||||
@ -176,6 +179,7 @@ export default class JanProviderWeb extends AIEngine {
|
||||
content: choice.message.content,
|
||||
reasoning: choice.message.reasoning,
|
||||
reasoning_content: choice.message.reasoning_content,
|
||||
tool_calls: choice.message.tool_calls,
|
||||
},
|
||||
finish_reason: (choice.finish_reason || 'stop') as 'stop' | 'length' | 'tool_calls' | 'content_filter' | 'function_call',
|
||||
})),
|
||||
@ -233,6 +237,7 @@ export default class JanProviderWeb extends AIEngine {
|
||||
content: choice.delta.content,
|
||||
reasoning: choice.delta.reasoning,
|
||||
reasoning_content: choice.delta.reasoning_content,
|
||||
tool_calls: choice.delta.tool_calls,
|
||||
},
|
||||
finish_reason: choice.finish_reason,
|
||||
})),
|
||||
@ -300,8 +305,9 @@ export default class JanProviderWeb extends AIEngine {
|
||||
return Array.from(this.activeSessions.values()).map(session => session.model_id)
|
||||
}
|
||||
|
||||
async isToolSupported(): Promise<boolean> {
|
||||
// Tools are not yet supported
|
||||
return false
|
||||
async isToolSupported(modelId: string): Promise<boolean> {
|
||||
// Jan models support tool calls via MCP
|
||||
console.log(`Checking tool support for Jan model ${modelId}: supported`);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
237
extensions-web/src/mcp-web/index.ts
Normal file
237
extensions-web/src/mcp-web/index.ts
Normal file
@ -0,0 +1,237 @@
|
||||
/**
|
||||
* MCP Web Extension
|
||||
* Provides Model Context Protocol functionality for web platform
|
||||
* Uses official MCP TypeScript SDK with proper session handling
|
||||
*/
|
||||
|
||||
import { MCPExtension, MCPTool, MCPToolCallResult } from '@janhq/core'
|
||||
import { JanAuthService } from '../shared/auth'
|
||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
|
||||
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
|
||||
import { JanMCPOAuthProvider } from './oauth-provider'
|
||||
|
||||
// JAN_API_BASE is defined in vite.config.ts (defaults to 'https://api-dev.jan.ai/jan/v1')
|
||||
declare const JAN_API_BASE: string
|
||||
|
||||
export default class MCPExtensionWeb extends MCPExtension {
|
||||
private mcpEndpoint = '/mcp'
|
||||
private tools: MCPTool[] = []
|
||||
private initialized = false
|
||||
private authService: JanAuthService
|
||||
private mcpClient: Client | null = null
|
||||
private oauthProvider: JanMCPOAuthProvider
|
||||
|
||||
constructor(
|
||||
url: string,
|
||||
name: string,
|
||||
productName?: string,
|
||||
active?: boolean,
|
||||
description?: string,
|
||||
version?: string
|
||||
) {
|
||||
super(url, name, productName, active, description, version)
|
||||
this.authService = JanAuthService.getInstance()
|
||||
this.oauthProvider = new JanMCPOAuthProvider(this.authService)
|
||||
}
|
||||
|
||||
async onLoad(): Promise<void> {
|
||||
try {
|
||||
// Initialize authentication first
|
||||
await this.authService.initialize()
|
||||
// Initialize MCP client with OAuth
|
||||
await this.initializeMCPClient()
|
||||
// Then fetch tools
|
||||
await this.initializeTools()
|
||||
} catch (error) {
|
||||
console.warn('Failed to initialize MCP extension:', error)
|
||||
this.tools = []
|
||||
}
|
||||
}
|
||||
|
||||
async onUnload(): Promise<void> {
|
||||
this.tools = []
|
||||
this.initialized = false
|
||||
|
||||
// Close MCP client
|
||||
if (this.mcpClient) {
|
||||
try {
|
||||
await this.mcpClient.close()
|
||||
} catch (error) {
|
||||
console.warn('Error closing MCP client:', error)
|
||||
}
|
||||
this.mcpClient = null
|
||||
}
|
||||
}
|
||||
|
||||
private async initializeMCPClient(): Promise<void> {
|
||||
try {
|
||||
// Close existing client if any
|
||||
if (this.mcpClient) {
|
||||
try {
|
||||
await this.mcpClient.close()
|
||||
} catch (error) {
|
||||
// Ignore close errors
|
||||
}
|
||||
this.mcpClient = null
|
||||
}
|
||||
|
||||
// Create transport with OAuth provider (handles token refresh automatically)
|
||||
const transport = new StreamableHTTPClientTransport(
|
||||
new URL(`${JAN_API_BASE}${this.mcpEndpoint}`),
|
||||
{
|
||||
authProvider: this.oauthProvider
|
||||
// No sessionId needed - server will generate one automatically
|
||||
}
|
||||
)
|
||||
|
||||
// Create MCP client
|
||||
this.mcpClient = new Client(
|
||||
{
|
||||
name: 'jan-web-client',
|
||||
version: '1.0.0'
|
||||
},
|
||||
{
|
||||
capabilities: {
|
||||
tools: {},
|
||||
resources: {},
|
||||
prompts: {},
|
||||
logging: {}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// Connect to MCP server (OAuth provider handles auth automatically)
|
||||
await this.mcpClient.connect(transport)
|
||||
|
||||
console.log('MCP client connected successfully, session ID:', transport.sessionId)
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize MCP client:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
private async initializeTools(): Promise<void> {
|
||||
if (this.initialized || !this.mcpClient) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// Use MCP SDK to list tools
|
||||
const result = await this.mcpClient.listTools()
|
||||
console.log('MCP tools/list response:', result)
|
||||
|
||||
if (result.tools && Array.isArray(result.tools)) {
|
||||
this.tools = result.tools.map((tool) => ({
|
||||
name: tool.name,
|
||||
description: tool.description || '',
|
||||
inputSchema: (tool.inputSchema || {}) as Record<string, unknown>,
|
||||
server: 'Jan MCP Server'
|
||||
}))
|
||||
} else {
|
||||
console.warn('No tools found in MCP server response')
|
||||
this.tools = []
|
||||
}
|
||||
|
||||
this.initialized = true
|
||||
console.log(`Initialized MCP extension with ${this.tools.length} tools:`, this.tools.map(t => t.name))
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch MCP tools:', error)
|
||||
this.tools = []
|
||||
this.initialized = false
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async getTools(): Promise<MCPTool[]> {
|
||||
if (!this.initialized) {
|
||||
await this.initializeTools()
|
||||
}
|
||||
return this.tools
|
||||
}
|
||||
|
||||
async callTool(toolName: string, args: Record<string, unknown>): Promise<MCPToolCallResult> {
|
||||
if (!this.mcpClient) {
|
||||
return {
|
||||
error: 'MCP client not initialized',
|
||||
content: [{ type: 'text', text: 'MCP client not initialized' }]
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Use MCP SDK to call tool (OAuth provider handles auth automatically)
|
||||
const result = await this.mcpClient.callTool({
|
||||
name: toolName,
|
||||
arguments: args
|
||||
})
|
||||
|
||||
console.log(`MCP tool call result for ${toolName}:`, result)
|
||||
|
||||
// Handle tool call result
|
||||
if (result.isError) {
|
||||
const errorText = Array.isArray(result.content) && result.content.length > 0
|
||||
? (result.content[0].type === 'text' ? (result.content[0] as any).text : 'Tool call failed')
|
||||
: 'Tool call failed'
|
||||
|
||||
return {
|
||||
error: errorText,
|
||||
content: [{ type: 'text', text: errorText }]
|
||||
}
|
||||
}
|
||||
|
||||
// Convert MCP content to Jan's format
|
||||
const content = Array.isArray(result.content)
|
||||
? result.content.map(item => {
|
||||
if (item.type === 'text') {
|
||||
return { type: 'text' as const, text: (item as any).text }
|
||||
} else {
|
||||
// For non-text types, convert to text representation
|
||||
return { type: 'text' as const, text: JSON.stringify(item) }
|
||||
}
|
||||
})
|
||||
: [{ type: 'text' as const, text: 'No content returned' }]
|
||||
|
||||
return {
|
||||
error: '',
|
||||
content
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||
console.error(`Failed to call MCP tool ${toolName}:`, error)
|
||||
|
||||
return {
|
||||
error: errorMessage,
|
||||
content: [{ type: 'text', text: errorMessage }]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async isHealthy(): Promise<boolean> {
|
||||
if (!this.mcpClient) {
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
// Try to list tools as health check (OAuth provider handles auth)
|
||||
await this.mcpClient.listTools()
|
||||
return true
|
||||
} catch (error) {
|
||||
console.warn('MCP health check failed:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async getConnectedServers(): Promise<string[]> {
|
||||
// Return servers based on MCP client connection status
|
||||
return this.mcpClient && this.initialized ? ['Jan MCP Server'] : []
|
||||
}
|
||||
|
||||
async refreshTools(): Promise<void> {
|
||||
this.initialized = false
|
||||
try {
|
||||
await this.initializeTools()
|
||||
} catch (error) {
|
||||
console.error('Failed to refresh tools:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
60
extensions-web/src/mcp-web/oauth-provider.ts
Normal file
60
extensions-web/src/mcp-web/oauth-provider.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js'
|
||||
import { OAuthTokens, OAuthClientInformation, OAuthClientMetadata } from '@modelcontextprotocol/sdk/shared/auth.js'
|
||||
import { JanAuthService } from '../shared/auth'
|
||||
|
||||
/**
|
||||
* MCP OAuth provider that integrates with Jan Auth Service
|
||||
* Just provides tokens, no storage or validation needed
|
||||
*/
|
||||
export class JanMCPOAuthProvider implements OAuthClientProvider {
|
||||
private authService: JanAuthService
|
||||
|
||||
constructor(authService: JanAuthService) {
|
||||
this.authService = authService
|
||||
}
|
||||
|
||||
get redirectUrl(): string {
|
||||
return ''
|
||||
}
|
||||
|
||||
get clientMetadata(): OAuthClientMetadata {
|
||||
return {
|
||||
redirect_uris: [] // Not used, but required by interface
|
||||
}
|
||||
}
|
||||
|
||||
async clientInformation(): Promise<OAuthClientInformation | undefined> {
|
||||
return undefined
|
||||
}
|
||||
|
||||
async tokens(): Promise<OAuthTokens | undefined> {
|
||||
try {
|
||||
const accessToken = await this.authService.getValidAccessToken()
|
||||
if (accessToken) {
|
||||
return {
|
||||
access_token: accessToken,
|
||||
token_type: 'Bearer'
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to get tokens from auth service:', error)
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
async saveTokens(): Promise<void> {
|
||||
// No-op: Jan auth service handles token storage
|
||||
}
|
||||
|
||||
redirectToAuthorization(): void {
|
||||
// No-op: Not handling authorization flow
|
||||
}
|
||||
|
||||
async saveCodeVerifier(): Promise<void> {
|
||||
// No-op: Not handling authorization flow
|
||||
}
|
||||
|
||||
async codeVerifier(): Promise<string> {
|
||||
throw new Error('Code verifier not supported')
|
||||
}
|
||||
}
|
||||
12
extensions-web/src/mcp-web/types.ts
Normal file
12
extensions-web/src/mcp-web/types.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/**
|
||||
* MCP Web Extension Types
|
||||
*/
|
||||
|
||||
export interface MCPApiResponse {
|
||||
content?: Array<{
|
||||
type?: string
|
||||
text?: string
|
||||
}>
|
||||
result?: string | object
|
||||
}
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
/**
|
||||
* Jan Provider Authentication Service
|
||||
* Shared Authentication Service
|
||||
* Handles guest login and token refresh for Jan API
|
||||
*/
|
||||
|
||||
// JAN_API_BASE is defined in vite.config.ts
|
||||
declare const JAN_API_BASE: string
|
||||
|
||||
export interface AuthTokens {
|
||||
access_token: string
|
||||
expires_in: number
|
||||
@ -13,7 +16,6 @@ export interface AuthResponse {
|
||||
expires_in: number
|
||||
}
|
||||
|
||||
// JAN_API_BASE is defined in vite.config.ts
|
||||
const AUTH_STORAGE_KEY = 'jan_auth_tokens'
|
||||
const TOKEN_EXPIRY_BUFFER = 60 * 1000 // 1 minute buffer before actual expiry
|
||||
|
||||
@ -184,6 +186,34 @@ export class JanAuthService {
|
||||
}
|
||||
}
|
||||
|
||||
async makeAuthenticatedRequest<T>(
|
||||
url: string,
|
||||
options: RequestInit = {}
|
||||
): Promise<T> {
|
||||
try {
|
||||
const authHeader = await this.getAuthHeader()
|
||||
|
||||
const response = await fetch(url, {
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...authHeader,
|
||||
...options.headers,
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text()
|
||||
throw new Error(`API request failed: ${response.status} ${response.statusText} - ${errorText}`)
|
||||
}
|
||||
|
||||
return response.json()
|
||||
} catch (error) {
|
||||
console.error('API request failed:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
logout(): void {
|
||||
this.clearTokens()
|
||||
}
|
||||
3
extensions-web/src/shared/index.ts
Normal file
3
extensions-web/src/shared/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export { getSharedDB } from './db'
|
||||
export { JanAuthService } from './auth'
|
||||
export type { AuthTokens, AuthResponse } from './auth'
|
||||
@ -2,7 +2,7 @@
|
||||
* Web Extension Types
|
||||
*/
|
||||
|
||||
import type { AssistantExtension, ConversationalExtension, BaseExtension, AIEngine } from '@janhq/core'
|
||||
import type { AssistantExtension, ConversationalExtension, BaseExtension, AIEngine, MCPExtension } from '@janhq/core'
|
||||
|
||||
type ExtensionConstructorParams = ConstructorParameters<typeof BaseExtension>
|
||||
|
||||
@ -18,12 +18,17 @@ export interface JanProviderWebModule {
|
||||
default: new (...args: ExtensionConstructorParams) => AIEngine
|
||||
}
|
||||
|
||||
export type WebExtensionModule = AssistantWebModule | ConversationalWebModule | JanProviderWebModule
|
||||
export interface MCPWebModule {
|
||||
default: new (...args: ExtensionConstructorParams) => MCPExtension
|
||||
}
|
||||
|
||||
export type WebExtensionModule = AssistantWebModule | ConversationalWebModule | JanProviderWebModule | MCPWebModule
|
||||
|
||||
export interface WebExtensionRegistry {
|
||||
'assistant-web': () => Promise<AssistantWebModule>
|
||||
'conversational-web': () => Promise<ConversationalWebModule>
|
||||
'jan-provider-web': () => Promise<JanProviderWebModule>
|
||||
'mcp-web': () => Promise<MCPWebModule>
|
||||
}
|
||||
|
||||
export type WebExtensionName = keyof WebExtensionRegistry
|
||||
|
||||
@ -14,6 +14,6 @@ export default defineConfig({
|
||||
emptyOutDir: false // Don't clean the output directory
|
||||
},
|
||||
define: {
|
||||
JAN_API_BASE: JSON.stringify(process.env.JAN_API_BASE || 'https://api-dev.jan.ai/jan/v1'),
|
||||
JAN_API_BASE: JSON.stringify(process.env.JAN_API_BASE || 'https://api-dev.jan.ai/v1'),
|
||||
}
|
||||
})
|
||||
@ -517,41 +517,41 @@ __metadata:
|
||||
|
||||
"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fassistant-extension%40workspace%3Aassistant-extension":
|
||||
version: 0.1.10
|
||||
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=9fbce2&locator=%40janhq%2Fassistant-extension%40workspace%3Aassistant-extension"
|
||||
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=c5357d&locator=%40janhq%2Fassistant-extension%40workspace%3Aassistant-extension"
|
||||
dependencies:
|
||||
rxjs: "npm:^7.8.1"
|
||||
ulidx: "npm:^2.3.0"
|
||||
checksum: 10c0/e59c11201e0e95ea4ed567e3a34921c7fd2901cf32d07d13bdc8254992b6a4b51de7c1e3be633a3c2f558fe533065fa69455b065230dac87f31cb9b86649e932
|
||||
checksum: 10c0/95e2ec1f1213d604730f5c9c381c80840402b00a9649039d1a9754ee3efa13e224e4ca39ea094aab5751f3f2ace1860c7640769e66b191b8c56998fd5f2ba5b9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fconversational-extension%40workspace%3Aconversational-extension":
|
||||
version: 0.1.10
|
||||
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=9fbce2&locator=%40janhq%2Fconversational-extension%40workspace%3Aconversational-extension"
|
||||
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=c5357d&locator=%40janhq%2Fconversational-extension%40workspace%3Aconversational-extension"
|
||||
dependencies:
|
||||
rxjs: "npm:^7.8.1"
|
||||
ulidx: "npm:^2.3.0"
|
||||
checksum: 10c0/e59c11201e0e95ea4ed567e3a34921c7fd2901cf32d07d13bdc8254992b6a4b51de7c1e3be633a3c2f558fe533065fa69455b065230dac87f31cb9b86649e932
|
||||
checksum: 10c0/95e2ec1f1213d604730f5c9c381c80840402b00a9649039d1a9754ee3efa13e224e4ca39ea094aab5751f3f2ace1860c7640769e66b191b8c56998fd5f2ba5b9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fdownload-extension%40workspace%3Adownload-extension":
|
||||
version: 0.1.10
|
||||
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=9fbce2&locator=%40janhq%2Fdownload-extension%40workspace%3Adownload-extension"
|
||||
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=c5357d&locator=%40janhq%2Fdownload-extension%40workspace%3Adownload-extension"
|
||||
dependencies:
|
||||
rxjs: "npm:^7.8.1"
|
||||
ulidx: "npm:^2.3.0"
|
||||
checksum: 10c0/e59c11201e0e95ea4ed567e3a34921c7fd2901cf32d07d13bdc8254992b6a4b51de7c1e3be633a3c2f558fe533065fa69455b065230dac87f31cb9b86649e932
|
||||
checksum: 10c0/95e2ec1f1213d604730f5c9c381c80840402b00a9649039d1a9754ee3efa13e224e4ca39ea094aab5751f3f2ace1860c7640769e66b191b8c56998fd5f2ba5b9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fllamacpp-extension%40workspace%3Allamacpp-extension":
|
||||
version: 0.1.10
|
||||
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=9fbce2&locator=%40janhq%2Fllamacpp-extension%40workspace%3Allamacpp-extension"
|
||||
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=c5357d&locator=%40janhq%2Fllamacpp-extension%40workspace%3Allamacpp-extension"
|
||||
dependencies:
|
||||
rxjs: "npm:^7.8.1"
|
||||
ulidx: "npm:^2.3.0"
|
||||
checksum: 10c0/e59c11201e0e95ea4ed567e3a34921c7fd2901cf32d07d13bdc8254992b6a4b51de7c1e3be633a3c2f558fe533065fa69455b065230dac87f31cb9b86649e932
|
||||
checksum: 10c0/95e2ec1f1213d604730f5c9c381c80840402b00a9649039d1a9754ee3efa13e224e4ca39ea094aab5751f3f2ace1860c7640769e66b191b8c56998fd5f2ba5b9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
@ -82,12 +82,12 @@ run = [
|
||||
|
||||
[tasks.dev-web-app]
|
||||
description = "Start web application development server (matches Makefile)"
|
||||
depends = ["install"]
|
||||
depends = ["build-core"]
|
||||
run = "yarn dev:web-app"
|
||||
|
||||
[tasks.build-web-app]
|
||||
description = "Build web application (matches Makefile)"
|
||||
depends = ["install"]
|
||||
depends = ["build-core"]
|
||||
run = "yarn build:web-app"
|
||||
|
||||
[tasks.serve-web-app]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -21,7 +21,7 @@ export const PlatformFeatures: Record<PlatformFeature, boolean> = {
|
||||
[PlatformFeature.LOCAL_INFERENCE]: isPlatformTauri(),
|
||||
|
||||
// MCP (Model Context Protocol) servers
|
||||
[PlatformFeature.MCP_SERVERS]: isPlatformTauri(),
|
||||
[PlatformFeature.MCP_SERVERS]: true,
|
||||
|
||||
// Local API server
|
||||
[PlatformFeature.LOCAL_API_SERVER]: isPlatformTauri(),
|
||||
|
||||
@ -23,6 +23,8 @@ import { useTranslation } from '@/i18n/react-i18next-compat'
|
||||
import { useAppState } from '@/hooks/useAppState'
|
||||
import { PlatformGuard } from '@/lib/platform/PlatformGuard'
|
||||
import { PlatformFeature } from '@/lib/platform'
|
||||
import { isPlatformTauri } from '@/lib/platform/utils'
|
||||
import { MCPTool } from '@janhq/core'
|
||||
|
||||
// Function to mask sensitive values
|
||||
const maskSensitiveValue = (value: string) => {
|
||||
@ -91,12 +93,118 @@ export const Route = createFileRoute(route.settings.mcp_servers as any)({
|
||||
function MCPServers() {
|
||||
return (
|
||||
<PlatformGuard feature={PlatformFeature.MCP_SERVERS}>
|
||||
<MCPServersContent />
|
||||
{isPlatformTauri() ? <MCPServersDesktop /> : <MCPServersWeb />}
|
||||
</PlatformGuard>
|
||||
)
|
||||
}
|
||||
|
||||
function MCPServersContent() {
|
||||
// Web version of MCP servers - simpler UI without server management
|
||||
function MCPServersWeb() {
|
||||
const { t } = useTranslation()
|
||||
const serviceHub = useServiceHub()
|
||||
const { allowAllMCPPermissions, setAllowAllMCPPermissions } = useToolApproval()
|
||||
|
||||
const [webMcpTools, setWebMcpTools] = useState<MCPTool[]>([])
|
||||
const [webMcpServers, setWebMcpServers] = useState<string[]>([])
|
||||
const [webMcpLoading, setWebMcpLoading] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
async function loadWebMcpData() {
|
||||
setWebMcpLoading(true)
|
||||
try {
|
||||
const [tools, servers] = await Promise.all([
|
||||
serviceHub.mcp().getTools(),
|
||||
serviceHub.mcp().getConnectedServers(),
|
||||
])
|
||||
setWebMcpTools(tools)
|
||||
setWebMcpServers(servers)
|
||||
} catch (error) {
|
||||
console.error('Failed to load web MCP data:', error)
|
||||
setWebMcpTools([])
|
||||
setWebMcpServers([])
|
||||
} finally {
|
||||
setWebMcpLoading(false)
|
||||
}
|
||||
}
|
||||
loadWebMcpData()
|
||||
}, [serviceHub])
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<HeaderPage>
|
||||
<h1 className="font-medium">{t('common:settings')}</h1>
|
||||
</HeaderPage>
|
||||
<div className="flex h-full w-full">
|
||||
<SettingsMenu />
|
||||
<div className="p-4 w-full h-[calc(100%-32px)] overflow-y-auto">
|
||||
<div className="flex flex-col justify-between gap-4 gap-y-3 w-full">
|
||||
<Card
|
||||
header={
|
||||
<div className="flex flex-col mb-4">
|
||||
<h1 className="text-main-view-fg font-medium text-base">
|
||||
{t('mcp-servers:title')}
|
||||
</h1>
|
||||
<p className="text-sm text-main-view-fg/70 mt-1">
|
||||
MCP tools are automatically available in your chat sessions
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<CardItem
|
||||
title={t('mcp-servers:allowPermissions')}
|
||||
description={t('mcp-servers:allowPermissionsDesc')}
|
||||
actions={
|
||||
<div className="flex-shrink-0 ml-4">
|
||||
<Switch
|
||||
checked={allowAllMCPPermissions}
|
||||
onCheckedChange={setAllowAllMCPPermissions}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardItem
|
||||
title="MCP Service Status"
|
||||
description={
|
||||
webMcpLoading
|
||||
? "Loading MCP service status..."
|
||||
: webMcpServers.length > 0
|
||||
? `Connected to ${webMcpServers.join(', ')}. ${webMcpTools.length} tools available.`
|
||||
: "MCP service not connected"
|
||||
}
|
||||
descriptionOutside={
|
||||
webMcpTools.length > 0 && !webMcpLoading && (
|
||||
<div className="mt-2">
|
||||
<h4 className="text-sm font-medium text-main-view-fg/80 mb-2">Available Tools:</h4>
|
||||
<div className="grid grid-cols-1 gap-2">
|
||||
{webMcpTools.map((tool) => (
|
||||
<div key={tool.name} className="flex items-start gap-2 p-2 bg-main-view-fg/5 rounded">
|
||||
<div className="flex-1">
|
||||
<div className="font-medium text-sm">{tool.name}</div>
|
||||
<div className="text-xs text-main-view-fg/70">{tool.description}</div>
|
||||
{tool.server && (
|
||||
<div className="text-xs text-main-view-fg/50 mt-1">Server: {tool.server}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Desktop version of MCP servers - full server management capabilities
|
||||
function MCPServersDesktop() {
|
||||
const { t } = useTranslation()
|
||||
const serviceHub = useServiceHub()
|
||||
const {
|
||||
|
||||
@ -157,6 +157,7 @@ class PlatformServiceHub implements ServiceHub {
|
||||
windowModule,
|
||||
deepLinkModule,
|
||||
providersModule,
|
||||
mcpModule,
|
||||
] = await Promise.all([
|
||||
import('./theme/web'),
|
||||
import('./app/web'),
|
||||
@ -167,6 +168,7 @@ class PlatformServiceHub implements ServiceHub {
|
||||
import('./window/web'),
|
||||
import('./deeplink/web'),
|
||||
import('./providers/web'),
|
||||
import('./mcp/web'),
|
||||
])
|
||||
|
||||
this.themeService = new themeModule.WebThemeService()
|
||||
@ -178,6 +180,7 @@ class PlatformServiceHub implements ServiceHub {
|
||||
this.windowService = new windowModule.WebWindowService()
|
||||
this.deepLinkService = new deepLinkModule.WebDeepLinkService()
|
||||
this.providersService = new providersModule.WebProvidersService()
|
||||
this.mcpService = new mcpModule.WebMCPService()
|
||||
}
|
||||
|
||||
this.initialized = true
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
* Default MCP Service - Generic implementation with minimal returns
|
||||
*/
|
||||
|
||||
import { MCPTool } from '@/types/completion'
|
||||
import { MCPTool, MCPToolCallResult } from '@janhq/core'
|
||||
import type { MCPServerConfig } from '@/hooks/useMCPServers'
|
||||
import type { MCPService, MCPConfig, ToolCallResult, ToolCallWithCancellationResult } from './types'
|
||||
import type { MCPService, MCPConfig, ToolCallWithCancellationResult } from './types'
|
||||
|
||||
export class DefaultMCPService implements MCPService {
|
||||
async updateMCPConfig(configs: string): Promise<void> {
|
||||
@ -28,7 +28,7 @@ export class DefaultMCPService implements MCPService {
|
||||
return []
|
||||
}
|
||||
|
||||
async callTool(args: { toolName: string; arguments: object }): Promise<ToolCallResult> {
|
||||
async callTool(args: { toolName: string; arguments: object }): Promise<MCPToolCallResult> {
|
||||
console.log('callTool called with args:', args)
|
||||
return {
|
||||
error: '',
|
||||
|
||||
@ -2,20 +2,15 @@
|
||||
* MCP Service Types
|
||||
*/
|
||||
|
||||
import { MCPTool } from '@/types/completion'
|
||||
import { MCPTool, MCPToolCallResult } from '@janhq/core'
|
||||
import type { MCPServerConfig, MCPServers } from '@/hooks/useMCPServers'
|
||||
|
||||
export interface MCPConfig {
|
||||
mcpServers?: MCPServers
|
||||
}
|
||||
|
||||
export interface ToolCallResult {
|
||||
error: string
|
||||
content: { text: string }[]
|
||||
}
|
||||
|
||||
export interface ToolCallWithCancellationResult {
|
||||
promise: Promise<ToolCallResult>
|
||||
promise: Promise<MCPToolCallResult>
|
||||
cancel: () => Promise<void>
|
||||
token: string
|
||||
}
|
||||
@ -26,7 +21,7 @@ export interface MCPService {
|
||||
getMCPConfig(): Promise<MCPConfig>
|
||||
getTools(): Promise<MCPTool[]>
|
||||
getConnectedServers(): Promise<string[]>
|
||||
callTool(args: { toolName: string; arguments: object }): Promise<ToolCallResult>
|
||||
callTool(args: { toolName: string; arguments: object }): Promise<MCPToolCallResult>
|
||||
callToolWithCancellation(args: {
|
||||
toolName: string
|
||||
arguments: object
|
||||
|
||||
279
web-app/src/services/mcp/web.ts
Normal file
279
web-app/src/services/mcp/web.ts
Normal file
@ -0,0 +1,279 @@
|
||||
/**
|
||||
* Web MCP Service - Implementation for web platform
|
||||
* Uses the MCP extension through ExtensionManager
|
||||
* Follows OpenAI function calling standards
|
||||
*/
|
||||
|
||||
import type { MCPServerConfig } from '@/hooks/useMCPServers'
|
||||
import type { MCPService, MCPConfig, ToolCallWithCancellationResult } from './types'
|
||||
import { ExtensionManager } from '@/lib/extension'
|
||||
import { ExtensionTypeEnum, MCPExtension, MCPTool, MCPToolCallResult } from '@janhq/core'
|
||||
|
||||
export class WebMCPService implements MCPService {
|
||||
private abortControllers: Map<string, AbortController> = new Map()
|
||||
private extensionCache: MCPExtension | null = null
|
||||
private cacheTimestamp = 0
|
||||
private readonly CACHE_TTL = 5000 // 5 seconds
|
||||
|
||||
private getMCPExtension(): MCPExtension | null {
|
||||
const now = Date.now()
|
||||
if (this.extensionCache && (now - this.cacheTimestamp) < this.CACHE_TTL) {
|
||||
return this.extensionCache
|
||||
}
|
||||
|
||||
this.extensionCache = ExtensionManager.getInstance().get<MCPExtension>(
|
||||
ExtensionTypeEnum.MCP
|
||||
) || null
|
||||
|
||||
this.cacheTimestamp = now
|
||||
return this.extensionCache
|
||||
}
|
||||
|
||||
private invalidateCache(): void {
|
||||
this.extensionCache = null
|
||||
this.cacheTimestamp = 0
|
||||
}
|
||||
|
||||
async updateMCPConfig(configs: string): Promise<void> {
|
||||
if (!configs || typeof configs !== 'string') {
|
||||
throw new Error('Invalid MCP configuration provided')
|
||||
}
|
||||
// For web platform, configuration is handled by the remote API server
|
||||
// Invalidate cache to ensure fresh extension retrieval
|
||||
this.invalidateCache()
|
||||
}
|
||||
|
||||
async restartMCPServers(): Promise<void> {
|
||||
// For web platform, servers are managed remotely
|
||||
// This triggers a refresh of available tools
|
||||
this.invalidateCache()
|
||||
const extension = this.getMCPExtension()
|
||||
if (extension) {
|
||||
try {
|
||||
await extension.refreshTools()
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to restart MCP servers: ${error instanceof Error ? error.message : String(error)}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getMCPConfig(): Promise<MCPConfig> {
|
||||
// Return empty config since web platform doesn't manage local MCP servers
|
||||
return {}
|
||||
}
|
||||
|
||||
async getTools(): Promise<MCPTool[]> {
|
||||
const extension = this.getMCPExtension()
|
||||
if (!extension) {
|
||||
return []
|
||||
}
|
||||
|
||||
try {
|
||||
return await extension.getTools()
|
||||
} catch (error) {
|
||||
console.error('Failed to get MCP tools:', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
async getConnectedServers(): Promise<string[]> {
|
||||
const extension = this.getMCPExtension()
|
||||
if (!extension) {
|
||||
return []
|
||||
}
|
||||
|
||||
try {
|
||||
return await extension.getConnectedServers()
|
||||
} catch (error) {
|
||||
console.error('Failed to get connected servers:', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
async callTool(args: { toolName: string; arguments: object }): Promise<MCPToolCallResult> {
|
||||
// Validate input parameters
|
||||
if (!args.toolName || typeof args.toolName !== 'string') {
|
||||
return {
|
||||
error: 'Invalid tool name provided',
|
||||
content: [{ type: 'text', text: 'Tool name must be a non-empty string' }]
|
||||
}
|
||||
}
|
||||
|
||||
const extension = this.getMCPExtension()
|
||||
if (!extension) {
|
||||
return {
|
||||
error: 'MCP extension not available',
|
||||
content: [{ type: 'text', text: 'MCP service is not available' }]
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await extension.callTool(args.toolName, args.arguments as Record<string, unknown>)
|
||||
|
||||
// Ensure OpenAI-compliant response format
|
||||
if (!result.content || !Array.isArray(result.content)) {
|
||||
return {
|
||||
error: 'Invalid tool response format',
|
||||
content: [{ type: 'text', text: 'Tool returned invalid response format' }]
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
error: result.error || '',
|
||||
content: result.content.map(item => ({
|
||||
type: item.type || 'text',
|
||||
text: item.text || JSON.stringify(item)
|
||||
}))
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||
return {
|
||||
error: errorMessage,
|
||||
content: [{ type: 'text', text: `Tool execution failed: ${errorMessage}` }]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
callToolWithCancellation(args: {
|
||||
toolName: string
|
||||
arguments: object
|
||||
cancellationToken?: string
|
||||
}): ToolCallWithCancellationResult {
|
||||
// Validate input parameters
|
||||
if (!args.toolName || typeof args.toolName !== 'string') {
|
||||
const errorResult: MCPToolCallResult = {
|
||||
error: 'Invalid tool name provided',
|
||||
content: [{ type: 'text', text: 'Tool name must be a non-empty string' }]
|
||||
}
|
||||
return {
|
||||
promise: Promise.resolve(errorResult),
|
||||
cancel: async () => {}, // No-op for failed validation
|
||||
token: 'invalid'
|
||||
}
|
||||
}
|
||||
|
||||
const token = args.cancellationToken || this.generateCancellationToken()
|
||||
const abortController = new AbortController()
|
||||
this.abortControllers.set(token, abortController)
|
||||
|
||||
const promise = this.callToolWithAbort(args, abortController.signal)
|
||||
.finally(() => {
|
||||
this.abortControllers.delete(token)
|
||||
})
|
||||
|
||||
return {
|
||||
promise,
|
||||
cancel: async () => {
|
||||
const controller = this.abortControllers.get(token)
|
||||
if (controller && !controller.signal.aborted) {
|
||||
controller.abort()
|
||||
}
|
||||
this.abortControllers.delete(token)
|
||||
},
|
||||
token
|
||||
}
|
||||
}
|
||||
|
||||
private async callToolWithAbort(
|
||||
args: { toolName: string; arguments: object },
|
||||
signal: AbortSignal
|
||||
): Promise<MCPToolCallResult> {
|
||||
// Check if already aborted
|
||||
if (signal.aborted) {
|
||||
return {
|
||||
error: 'Tool call was cancelled',
|
||||
content: [{ type: 'text', text: 'Tool call was cancelled by user' }]
|
||||
}
|
||||
}
|
||||
|
||||
const extension = this.getMCPExtension()
|
||||
if (!extension) {
|
||||
return {
|
||||
error: 'MCP extension not available',
|
||||
content: [{ type: 'text', text: 'MCP service is not available' }]
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const abortHandler = () => {
|
||||
resolve({
|
||||
error: 'Tool call was cancelled',
|
||||
content: [{ type: 'text', text: 'Tool call was cancelled by user' }]
|
||||
})
|
||||
}
|
||||
signal.addEventListener('abort', abortHandler, { once: true })
|
||||
|
||||
extension.callTool(args.toolName, args.arguments as Record<string, unknown>)
|
||||
.then(result => {
|
||||
if (!signal.aborted) {
|
||||
if (!result.content || !Array.isArray(result.content)) {
|
||||
resolve({
|
||||
error: 'Invalid tool response format',
|
||||
content: [{ type: 'text', text: 'Tool returned invalid response format' }]
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
resolve({
|
||||
error: result.error || '',
|
||||
content: result.content.map(item => ({
|
||||
type: item.type || 'text',
|
||||
text: item.text || JSON.stringify(item)
|
||||
}))
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
if (!signal.aborted) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||
resolve({
|
||||
error: errorMessage,
|
||||
content: [{ type: 'text', text: `Tool execution failed: ${errorMessage}` }]
|
||||
})
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
signal.removeEventListener('abort', abortHandler)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async cancelToolCall(cancellationToken: string): Promise<void> {
|
||||
const controller = this.abortControllers.get(cancellationToken)
|
||||
if (controller) {
|
||||
controller.abort()
|
||||
this.abortControllers.delete(cancellationToken)
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async activateMCPServer(name: string, _config: MCPServerConfig): Promise<void> {
|
||||
// For web platform, server activation is handled remotely
|
||||
this.invalidateCache()
|
||||
const extension = this.getMCPExtension()
|
||||
if (extension) {
|
||||
try {
|
||||
await extension.refreshTools()
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to activate MCP server ${name}: ${error instanceof Error ? error.message : String(error)}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async deactivateMCPServer(name: string): Promise<void> {
|
||||
// For web platform, server deactivation is handled remotely
|
||||
this.invalidateCache()
|
||||
const extension = this.getMCPExtension()
|
||||
if (extension) {
|
||||
try {
|
||||
await extension.refreshTools()
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to deactivate MCP server ${name}: ${error instanceof Error ? error.message : String(error)}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private generateCancellationToken(): string {
|
||||
return `mcp_cancel_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`
|
||||
}
|
||||
}
|
||||
471
yarn.lock
471
yarn.lock
@ -3464,6 +3464,7 @@ __metadata:
|
||||
resolution: "@jan/extensions-web@workspace:extensions-web"
|
||||
dependencies:
|
||||
"@janhq/core": "workspace:*"
|
||||
"@modelcontextprotocol/sdk": "npm:^1.17.5"
|
||||
typescript: "npm:^5.3.3"
|
||||
vite: "npm:^5.0.0"
|
||||
vitest: "npm:^2.0.0"
|
||||
@ -3590,6 +3591,7 @@ __metadata:
|
||||
react-textarea-autosize: "npm:^8.5.9"
|
||||
rehype-katex: "npm:^7.0.1"
|
||||
rehype-raw: "npm:^7.0.0"
|
||||
remark-breaks: "npm:^4.0.0"
|
||||
remark-emoji: "npm:^5.0.1"
|
||||
remark-gfm: "npm:^4.0.1"
|
||||
remark-math: "npm:^6.0.0"
|
||||
@ -3721,6 +3723,26 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@modelcontextprotocol/sdk@npm:^1.17.5":
|
||||
version: 1.17.5
|
||||
resolution: "@modelcontextprotocol/sdk@npm:1.17.5"
|
||||
dependencies:
|
||||
ajv: "npm:^6.12.6"
|
||||
content-type: "npm:^1.0.5"
|
||||
cors: "npm:^2.8.5"
|
||||
cross-spawn: "npm:^7.0.5"
|
||||
eventsource: "npm:^3.0.2"
|
||||
eventsource-parser: "npm:^3.0.0"
|
||||
express: "npm:^5.0.1"
|
||||
express-rate-limit: "npm:^7.5.0"
|
||||
pkce-challenge: "npm:^5.0.0"
|
||||
raw-body: "npm:^3.0.0"
|
||||
zod: "npm:^3.23.8"
|
||||
zod-to-json-schema: "npm:^3.24.1"
|
||||
checksum: 10c0/182b92b5e7c07da428fd23c6de22021c4f9a91f799c02a8ef15def07e4f9361d0fc22303548658fec2a700623535fd44a9dc4d010fb5d803a8f80e3c6c64a45e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@napi-rs/wasm-runtime@npm:^0.2.4":
|
||||
version: 0.2.7
|
||||
resolution: "@napi-rs/wasm-runtime@npm:0.2.7"
|
||||
@ -8356,6 +8378,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"accepts@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "accepts@npm:2.0.0"
|
||||
dependencies:
|
||||
mime-types: "npm:^3.0.0"
|
||||
negotiator: "npm:^1.0.0"
|
||||
checksum: 10c0/98374742097e140891546076215f90c32644feacf652db48412329de4c2a529178a81aa500fbb13dd3e6cbf6e68d829037b123ac037fc9a08bcec4b87b358eef
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"acorn-jsx@npm:^5.3.2":
|
||||
version: 5.3.2
|
||||
resolution: "acorn-jsx@npm:5.3.2"
|
||||
@ -8430,7 +8462,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ajv@npm:^6.12.3, ajv@npm:^6.12.4":
|
||||
"ajv@npm:^6.12.3, ajv@npm:^6.12.4, ajv@npm:^6.12.6":
|
||||
version: 6.12.6
|
||||
resolution: "ajv@npm:6.12.6"
|
||||
dependencies:
|
||||
@ -8967,6 +8999,23 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"body-parser@npm:^2.2.0":
|
||||
version: 2.2.0
|
||||
resolution: "body-parser@npm:2.2.0"
|
||||
dependencies:
|
||||
bytes: "npm:^3.1.2"
|
||||
content-type: "npm:^1.0.5"
|
||||
debug: "npm:^4.4.0"
|
||||
http-errors: "npm:^2.0.0"
|
||||
iconv-lite: "npm:^0.6.3"
|
||||
on-finished: "npm:^2.4.1"
|
||||
qs: "npm:^6.14.0"
|
||||
raw-body: "npm:^3.0.0"
|
||||
type-is: "npm:^2.0.0"
|
||||
checksum: 10c0/a9ded39e71ac9668e2211afa72e82ff86cc5ef94de1250b7d1ba9cc299e4150408aaa5f1e8b03dd4578472a3ce6d1caa2a23b27a6c18e526e48b4595174c116c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bowser@npm:^2.11.0":
|
||||
version: 2.11.0
|
||||
resolution: "bowser@npm:2.11.0"
|
||||
@ -9221,7 +9270,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bytes@npm:3.1.2":
|
||||
"bytes@npm:3.1.2, bytes@npm:^3.1.2":
|
||||
version: 3.1.2
|
||||
resolution: "bytes@npm:3.1.2"
|
||||
checksum: 10c0/76d1c43cbd602794ad8ad2ae94095cddeb1de78c5dddaa7005c51af10b0176c69971a6d88e805a90c2b6550d76636e43c40d8427a808b8645ede885de4a0358e
|
||||
@ -9798,6 +9847,22 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"content-disposition@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "content-disposition@npm:1.0.0"
|
||||
dependencies:
|
||||
safe-buffer: "npm:5.2.1"
|
||||
checksum: 10c0/c7b1ba0cea2829da0352ebc1b7f14787c73884bc707c8bc2271d9e3bf447b372270d09f5d3980dc5037c749ceef56b9a13fccd0b0001c87c3f12579967e4dd27
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"content-type@npm:^1.0.5":
|
||||
version: 1.0.5
|
||||
resolution: "content-type@npm:1.0.5"
|
||||
checksum: 10c0/b76ebed15c000aee4678c3707e0860cb6abd4e680a598c0a26e17f0bfae723ec9cc2802f0ff1bc6e4d80603719010431d2231018373d4dde10f9ccff9dadf5af
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"convert-source-map@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "convert-source-map@npm:2.0.0"
|
||||
@ -9805,6 +9870,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cookie-signature@npm:^1.2.1":
|
||||
version: 1.2.2
|
||||
resolution: "cookie-signature@npm:1.2.2"
|
||||
checksum: 10c0/54e05df1a293b3ce81589b27dddc445f462f6fa6812147c033350cd3561a42bc14481674e05ed14c7bd0ce1e8bb3dc0e40851bad75415733711294ddce0b7bc6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cookie@npm:^0.7.1":
|
||||
version: 0.7.2
|
||||
resolution: "cookie@npm:0.7.2"
|
||||
checksum: 10c0/9596e8ccdbf1a3a88ae02cf5ee80c1c50959423e1022e4e60b91dd87c622af1da309253d8abdb258fb5e3eacb4f08e579dc58b4897b8087574eee0fd35dfa5d2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"copy-descriptor@npm:^0.1.0":
|
||||
version: 0.1.1
|
||||
resolution: "copy-descriptor@npm:0.1.1"
|
||||
@ -9849,6 +9928,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cors@npm:^2.8.5":
|
||||
version: 2.8.5
|
||||
resolution: "cors@npm:2.8.5"
|
||||
dependencies:
|
||||
object-assign: "npm:^4"
|
||||
vary: "npm:^1"
|
||||
checksum: 10c0/373702b7999409922da80de4a61938aabba6929aea5b6fd9096fefb9e8342f626c0ebd7507b0e8b0b311380744cc985f27edebc0a26e0ddb784b54e1085de761
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cpx@npm:^1.5.0":
|
||||
version: 1.5.0
|
||||
resolution: "cpx@npm:1.5.0"
|
||||
@ -9938,7 +10027,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.6":
|
||||
"cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.5, cross-spawn@npm:^7.0.6":
|
||||
version: 7.0.6
|
||||
resolution: "cross-spawn@npm:7.0.6"
|
||||
dependencies:
|
||||
@ -10089,7 +10178,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"debug@npm:^4.3.7, debug@npm:^4.4.1":
|
||||
"debug@npm:^4.3.5, debug@npm:^4.3.7, debug@npm:^4.4.0, debug@npm:^4.4.1":
|
||||
version: 4.4.1
|
||||
resolution: "debug@npm:4.4.1"
|
||||
dependencies:
|
||||
@ -10225,6 +10314,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"depd@npm:2.0.0, depd@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "depd@npm:2.0.0"
|
||||
checksum: 10c0/58bd06ec20e19529b06f7ad07ddab60e504d9e0faca4bd23079fac2d279c3594334d736508dc350e06e510aba5e22e4594483b3a6562ce7c17dd797f4cc4ad2c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dequal@npm:^2.0.0, dequal@npm:^2.0.3":
|
||||
version: 2.0.3
|
||||
resolution: "dequal@npm:2.0.3"
|
||||
@ -10376,6 +10472,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ee-first@npm:1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "ee-first@npm:1.1.1"
|
||||
checksum: 10c0/b5bb125ee93161bc16bfe6e56c6b04de5ad2aa44234d8f644813cc95d861a6910903132b05093706de2b706599367c4130eb6d170f6b46895686b95f87d017b7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ejs@npm:^3.1.6":
|
||||
version: 3.1.10
|
||||
resolution: "ejs@npm:3.1.10"
|
||||
@ -10455,6 +10558,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"encodeurl@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "encodeurl@npm:2.0.0"
|
||||
checksum: 10c0/5d317306acb13e6590e28e27924c754163946a2480de11865c991a3a7eed4315cd3fba378b543ca145829569eefe9b899f3d84bb09870f675ae60bc924b01ceb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"encoding@npm:^0.1.13":
|
||||
version: 0.1.13
|
||||
resolution: "encoding@npm:0.1.13"
|
||||
@ -10790,6 +10900,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"escape-html@npm:^1.0.3":
|
||||
version: 1.0.3
|
||||
resolution: "escape-html@npm:1.0.3"
|
||||
checksum: 10c0/524c739d776b36c3d29fa08a22e03e8824e3b2fd57500e5e44ecf3cc4707c34c60f9ca0781c0e33d191f2991161504c295e98f68c78fe7baa6e57081ec6ac0a3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"escape-string-regexp@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "escape-string-regexp@npm:4.0.0"
|
||||
@ -11048,6 +11165,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"etag@npm:^1.8.1":
|
||||
version: 1.8.1
|
||||
resolution: "etag@npm:1.8.1"
|
||||
checksum: 10c0/12be11ef62fb9817314d790089a0a49fae4e1b50594135dcb8076312b7d7e470884b5100d249b28c18581b7fd52f8b485689ffae22a11ed9ec17377a33a08f84
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"event-target-shim@npm:^5.0.0":
|
||||
version: 5.0.1
|
||||
resolution: "event-target-shim@npm:5.0.1"
|
||||
@ -11062,6 +11186,22 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eventsource-parser@npm:^3.0.0, eventsource-parser@npm:^3.0.1":
|
||||
version: 3.0.6
|
||||
resolution: "eventsource-parser@npm:3.0.6"
|
||||
checksum: 10c0/70b8ccec7dac767ef2eca43f355e0979e70415701691382a042a2df8d6a68da6c2fca35363669821f3da876d29c02abe9b232964637c1b6635c940df05ada78a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eventsource@npm:^3.0.2":
|
||||
version: 3.0.7
|
||||
resolution: "eventsource@npm:3.0.7"
|
||||
dependencies:
|
||||
eventsource-parser: "npm:^3.0.1"
|
||||
checksum: 10c0/c48a73c38f300e33e9f11375d4ee969f25cbb0519608a12378a38068055ae8b55b6e0e8a49c3f91c784068434efe1d9f01eb49b6315b04b0da9157879ce2f67d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"evp_bytestokey@npm:^1.0.0, evp_bytestokey@npm:^1.0.3":
|
||||
version: 1.0.3
|
||||
resolution: "evp_bytestokey@npm:1.0.3"
|
||||
@ -11144,6 +11284,50 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"express-rate-limit@npm:^7.5.0":
|
||||
version: 7.5.1
|
||||
resolution: "express-rate-limit@npm:7.5.1"
|
||||
peerDependencies:
|
||||
express: ">= 4.11"
|
||||
checksum: 10c0/b07de84d700a2c07c4bf2f040e7558ed5a1f660f03ed5f30bf8ff7b51e98ba7a85215640e70fc48cbbb9151066ea51239d9a1b41febc9b84d98c7915b0186161
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"express@npm:^5.0.1":
|
||||
version: 5.1.0
|
||||
resolution: "express@npm:5.1.0"
|
||||
dependencies:
|
||||
accepts: "npm:^2.0.0"
|
||||
body-parser: "npm:^2.2.0"
|
||||
content-disposition: "npm:^1.0.0"
|
||||
content-type: "npm:^1.0.5"
|
||||
cookie: "npm:^0.7.1"
|
||||
cookie-signature: "npm:^1.2.1"
|
||||
debug: "npm:^4.4.0"
|
||||
encodeurl: "npm:^2.0.0"
|
||||
escape-html: "npm:^1.0.3"
|
||||
etag: "npm:^1.8.1"
|
||||
finalhandler: "npm:^2.1.0"
|
||||
fresh: "npm:^2.0.0"
|
||||
http-errors: "npm:^2.0.0"
|
||||
merge-descriptors: "npm:^2.0.0"
|
||||
mime-types: "npm:^3.0.0"
|
||||
on-finished: "npm:^2.4.1"
|
||||
once: "npm:^1.4.0"
|
||||
parseurl: "npm:^1.3.3"
|
||||
proxy-addr: "npm:^2.0.7"
|
||||
qs: "npm:^6.14.0"
|
||||
range-parser: "npm:^1.2.1"
|
||||
router: "npm:^2.2.0"
|
||||
send: "npm:^1.1.0"
|
||||
serve-static: "npm:^2.2.0"
|
||||
statuses: "npm:^2.0.1"
|
||||
type-is: "npm:^2.0.1"
|
||||
vary: "npm:^1.1.2"
|
||||
checksum: 10c0/80ce7c53c5f56887d759b94c3f2283e2e51066c98d4b72a4cc1338e832b77f1e54f30d0239cc10815a0f849bdb753e6a284d2fa48d4ab56faf9c501f55d751d6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"extend-shallow@npm:^2.0.1":
|
||||
version: 2.0.1
|
||||
resolution: "extend-shallow@npm:2.0.1"
|
||||
@ -11425,6 +11609,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"finalhandler@npm:^2.1.0":
|
||||
version: 2.1.0
|
||||
resolution: "finalhandler@npm:2.1.0"
|
||||
dependencies:
|
||||
debug: "npm:^4.4.0"
|
||||
encodeurl: "npm:^2.0.0"
|
||||
escape-html: "npm:^1.0.3"
|
||||
on-finished: "npm:^2.4.1"
|
||||
parseurl: "npm:^1.3.3"
|
||||
statuses: "npm:^2.0.1"
|
||||
checksum: 10c0/da0bbca6d03873472ee890564eb2183f4ed377f25f3628a0fc9d16dac40bed7b150a0d82ebb77356e4c6d97d2796ad2dba22948b951dddee2c8768b0d1b9fb1f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"find-index@npm:^0.1.1":
|
||||
version: 0.1.1
|
||||
resolution: "find-index@npm:0.1.1"
|
||||
@ -11591,6 +11789,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"forwarded@npm:0.2.0":
|
||||
version: 0.2.0
|
||||
resolution: "forwarded@npm:0.2.0"
|
||||
checksum: 10c0/9b67c3fac86acdbc9ae47ba1ddd5f2f81526fa4c8226863ede5600a3f7c7416ef451f6f1e240a3cc32d0fd79fcfe6beb08fd0da454f360032bde70bf80afbb33
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fragment-cache@npm:^0.2.1":
|
||||
version: 0.2.1
|
||||
resolution: "fragment-cache@npm:0.2.1"
|
||||
@ -11644,6 +11849,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fresh@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "fresh@npm:2.0.0"
|
||||
checksum: 10c0/0557548194cb9a809a435bf92bcfbc20c89e8b5eb38861b73ced36750437251e39a111fc3a18b98531be9dd91fe1411e4969f229dc579ec0251ce6c5d4900bbc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fs-extra@npm:^11.2.0":
|
||||
version: 11.3.0
|
||||
resolution: "fs-extra@npm:11.3.0"
|
||||
@ -12593,6 +12805,19 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"http-errors@npm:2.0.0, http-errors@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "http-errors@npm:2.0.0"
|
||||
dependencies:
|
||||
depd: "npm:2.0.0"
|
||||
inherits: "npm:2.0.4"
|
||||
setprototypeof: "npm:1.2.0"
|
||||
statuses: "npm:2.0.1"
|
||||
toidentifier: "npm:1.0.1"
|
||||
checksum: 10c0/fc6f2715fe188d091274b5ffc8b3657bd85c63e969daa68ccb77afb05b071a4b62841acb7a21e417b5539014dff2ebf9550f0b14a9ff126f2734a7c1387f8e19
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"http-proxy-agent@npm:^7.0.0, http-proxy-agent@npm:^7.0.2":
|
||||
version: 7.0.2
|
||||
resolution: "http-proxy-agent@npm:7.0.2"
|
||||
@ -12670,7 +12895,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2":
|
||||
"iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2, iconv-lite@npm:^0.6.3":
|
||||
version: 0.6.3
|
||||
resolution: "iconv-lite@npm:0.6.3"
|
||||
dependencies:
|
||||
@ -12679,6 +12904,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"iconv-lite@npm:0.7.0":
|
||||
version: 0.7.0
|
||||
resolution: "iconv-lite@npm:0.7.0"
|
||||
dependencies:
|
||||
safer-buffer: "npm:>= 2.1.2 < 3.0.0"
|
||||
checksum: 10c0/2382400469071c55b6746c531eed5fa4d033e5db6690b7331fb2a5f59a30d7a9782932e92253db26df33c1cf46fa200a3fbe524a2a7c62037c762283f188ec2f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"idb@npm:^7.0.1":
|
||||
version: 7.1.1
|
||||
resolution: "idb@npm:7.1.1"
|
||||
@ -12752,7 +12986,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"inherits@npm:2, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.3, inherits@npm:~2.0.4":
|
||||
"inherits@npm:2, inherits@npm:2.0.4, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.3, inherits@npm:~2.0.4":
|
||||
version: 2.0.4
|
||||
resolution: "inherits@npm:2.0.4"
|
||||
checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2
|
||||
@ -12808,6 +13042,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ipaddr.js@npm:1.9.1":
|
||||
version: 1.9.1
|
||||
resolution: "ipaddr.js@npm:1.9.1"
|
||||
checksum: 10c0/0486e775047971d3fdb5fb4f063829bac45af299ae0b82dcf3afa2145338e08290563a2a70f34b732d795ecc8311902e541a8530eeb30d75860a78ff4e94ce2a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-accessor-descriptor@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "is-accessor-descriptor@npm:1.0.1"
|
||||
@ -13276,6 +13517,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-promise@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "is-promise@npm:4.0.0"
|
||||
checksum: 10c0/ebd5c672d73db781ab33ccb155fb9969d6028e37414d609b115cc534654c91ccd061821d5b987eefaa97cf4c62f0b909bb2f04db88306de26e91bfe8ddc01503
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-regex@npm:^1.2.1":
|
||||
version: 1.2.1
|
||||
resolution: "is-regex@npm:1.2.1"
|
||||
@ -13625,6 +13873,7 @@ __metadata:
|
||||
cpx: "npm:^1.5.0"
|
||||
cross-env: "npm:^7.0.3"
|
||||
husky: "npm:^9.1.5"
|
||||
jsdom: "npm:^26.1.0"
|
||||
rimraf: "npm:^3.0.2"
|
||||
run-script-os: "npm:^1.1.6"
|
||||
tar: "npm:^4.4.19"
|
||||
@ -14549,6 +14798,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mdast-util-newline-to-break@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "mdast-util-newline-to-break@npm:2.0.0"
|
||||
dependencies:
|
||||
"@types/mdast": "npm:^4.0.0"
|
||||
mdast-util-find-and-replace: "npm:^3.0.0"
|
||||
checksum: 10c0/756a5660b0a821e0d6d6a0b2d9b13ac32e41cc028c485a91bccf6300977e2557236c6cc93dbd55c68b785f1ed6eae69209a4ffe182533cd1cdfda369021bebd2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mdast-util-phrasing@npm:^4.0.0":
|
||||
version: 4.1.0
|
||||
resolution: "mdast-util-phrasing@npm:4.1.0"
|
||||
@ -14602,6 +14861,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"media-typer@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "media-typer@npm:1.1.0"
|
||||
checksum: 10c0/7b4baa40b25964bb90e2121ee489ec38642127e48d0cc2b6baa442688d3fde6262bfdca86d6bbf6ba708784afcac168c06840c71facac70e390f5f759ac121b9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"merge-descriptors@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "merge-descriptors@npm:2.0.0"
|
||||
checksum: 10c0/95389b7ced3f9b36fbdcf32eb946dc3dd1774c2fdf164609e55b18d03aa499b12bd3aae3a76c1c7185b96279e9803525550d3eb292b5224866060a288f335cb3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"merge-stream@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "merge-stream@npm:2.0.0"
|
||||
@ -15031,7 +15304,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mime-db@npm:>= 1.43.0 < 2":
|
||||
"mime-db@npm:>= 1.43.0 < 2, mime-db@npm:^1.54.0":
|
||||
version: 1.54.0
|
||||
resolution: "mime-db@npm:1.54.0"
|
||||
checksum: 10c0/8d907917bc2a90fa2df842cdf5dfeaf509adc15fe0531e07bb2f6ab15992416479015828d6a74200041c492e42cce3ebf78e5ce714388a0a538ea9c53eece284
|
||||
@ -15063,6 +15336,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mime-types@npm:^3.0.0, mime-types@npm:^3.0.1":
|
||||
version: 3.0.1
|
||||
resolution: "mime-types@npm:3.0.1"
|
||||
dependencies:
|
||||
mime-db: "npm:^1.54.0"
|
||||
checksum: 10c0/bd8c20d3694548089cf229016124f8f40e6a60bbb600161ae13e45f793a2d5bb40f96bbc61f275836696179c77c1d6bf4967b2a75e0a8ad40fe31f4ed5be4da5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mimic-fn@npm:^2.1.0":
|
||||
version: 2.1.0
|
||||
resolution: "mimic-fn@npm:2.1.0"
|
||||
@ -15803,7 +16085,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"object-assign@npm:^4.1.1":
|
||||
"object-assign@npm:^4, object-assign@npm:^4.1.1":
|
||||
version: 4.1.1
|
||||
resolution: "object-assign@npm:4.1.1"
|
||||
checksum: 10c0/1f4df9945120325d041ccf7b86f31e8bcc14e73d29171e37a7903050e96b81323784ec59f93f102ec635bcf6fa8034ba3ea0a8c7e69fa202b87ae3b6cec5a414
|
||||
@ -15887,6 +16169,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"on-finished@npm:^2.4.1":
|
||||
version: 2.4.1
|
||||
resolution: "on-finished@npm:2.4.1"
|
||||
dependencies:
|
||||
ee-first: "npm:1.1.1"
|
||||
checksum: 10c0/46fb11b9063782f2d9968863d9cbba33d77aa13c17f895f56129c274318b86500b22af3a160fe9995aa41317efcd22941b6eba747f718ced08d9a73afdb087b4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"on-headers@npm:~1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "on-headers@npm:1.1.0"
|
||||
@ -16171,6 +16462,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"parseurl@npm:^1.3.3":
|
||||
version: 1.3.3
|
||||
resolution: "parseurl@npm:1.3.3"
|
||||
checksum: 10c0/90dd4760d6f6174adb9f20cf0965ae12e23879b5f5464f38e92fce8073354341e4b3b76fa3d878351efe7d01e617121955284cfd002ab087fba1a0726ec0b4f5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pascalcase@npm:^0.1.1":
|
||||
version: 0.1.1
|
||||
resolution: "pascalcase@npm:0.1.1"
|
||||
@ -16247,6 +16545,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"path-to-regexp@npm:^8.0.0":
|
||||
version: 8.3.0
|
||||
resolution: "path-to-regexp@npm:8.3.0"
|
||||
checksum: 10c0/ee1544a73a3f294a97a4c663b0ce71bbf1621d732d80c9c9ed201b3e911a86cb628ebad691b9d40f40a3742fe22011e5a059d8eed2cf63ec2cb94f6fb4efe67c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pathe@npm:^1.1.2":
|
||||
version: 1.1.2
|
||||
resolution: "pathe@npm:1.1.2"
|
||||
@ -16324,6 +16629,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pkce-challenge@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "pkce-challenge@npm:5.0.0"
|
||||
checksum: 10c0/c6706d627fdbb6f22bf8cc5d60d96d6b6a7bb481399b336a3d3f4e9bfba3e167a2c32f8ec0b5e74be686a0ba3bcc9894865d4c2dd1b91cea4c05dba1f28602c3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pkg-dir@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "pkg-dir@npm:5.0.0"
|
||||
@ -16575,6 +16887,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"proxy-addr@npm:^2.0.7":
|
||||
version: 2.0.7
|
||||
resolution: "proxy-addr@npm:2.0.7"
|
||||
dependencies:
|
||||
forwarded: "npm:0.2.0"
|
||||
ipaddr.js: "npm:1.9.1"
|
||||
checksum: 10c0/c3eed999781a35f7fd935f398b6d8920b6fb00bbc14287bc6de78128ccc1a02c89b95b56742bf7cf0362cc333c61d138532049c7dedc7a328ef13343eff81210
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"psl@npm:^1.1.28":
|
||||
version: 1.15.0
|
||||
resolution: "psl@npm:1.15.0"
|
||||
@ -16621,7 +16943,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"qs@npm:^6.12.3":
|
||||
"qs@npm:^6.12.3, qs@npm:^6.14.0":
|
||||
version: 6.14.0
|
||||
resolution: "qs@npm:6.14.0"
|
||||
dependencies:
|
||||
@ -16688,6 +17010,25 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"range-parser@npm:^1.2.1":
|
||||
version: 1.2.1
|
||||
resolution: "range-parser@npm:1.2.1"
|
||||
checksum: 10c0/96c032ac2475c8027b7a4e9fe22dc0dfe0f6d90b85e496e0f016fbdb99d6d066de0112e680805075bd989905e2123b3b3d002765149294dce0c1f7f01fcc2ea0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"raw-body@npm:^3.0.0":
|
||||
version: 3.0.1
|
||||
resolution: "raw-body@npm:3.0.1"
|
||||
dependencies:
|
||||
bytes: "npm:3.1.2"
|
||||
http-errors: "npm:2.0.0"
|
||||
iconv-lite: "npm:0.7.0"
|
||||
unpipe: "npm:1.0.0"
|
||||
checksum: 10c0/892f4fbd21ecab7e2fed0f045f7af9e16df7e8050879639d4e482784a2f4640aaaa33d916a0e98013f23acb82e09c2e3c57f84ab97104449f728d22f65a7d79a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"rc@npm:^1.0.1, rc@npm:^1.1.6":
|
||||
version: 1.2.8
|
||||
resolution: "rc@npm:1.2.8"
|
||||
@ -17295,6 +17636,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"remark-breaks@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "remark-breaks@npm:4.0.0"
|
||||
dependencies:
|
||||
"@types/mdast": "npm:^4.0.0"
|
||||
mdast-util-newline-to-break: "npm:^2.0.0"
|
||||
unified: "npm:^11.0.0"
|
||||
checksum: 10c0/d7b319a7993b54c5d574e9255080c5de68cfa24f993873b0ee296af13f478521c41d4b7ae0fc14b4607ea70c8f6967e998ab7a467de13139141e66a1a34cb6be
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"remark-emoji@npm:^5.0.1":
|
||||
version: 5.0.1
|
||||
resolution: "remark-emoji@npm:5.0.1"
|
||||
@ -17863,6 +18215,19 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"router@npm:^2.2.0":
|
||||
version: 2.2.0
|
||||
resolution: "router@npm:2.2.0"
|
||||
dependencies:
|
||||
debug: "npm:^4.4.0"
|
||||
depd: "npm:^2.0.0"
|
||||
is-promise: "npm:^4.0.0"
|
||||
parseurl: "npm:^1.3.3"
|
||||
path-to-regexp: "npm:^8.0.0"
|
||||
checksum: 10c0/3279de7450c8eae2f6e095e9edacbdeec0abb5cb7249c6e719faa0db2dba43574b4fff5892d9220631c9abaff52dd3cad648cfea2aaace845e1a071915ac8867
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"rrweb-cssom@npm:^0.8.0":
|
||||
version: 0.8.0
|
||||
resolution: "rrweb-cssom@npm:0.8.0"
|
||||
@ -18019,6 +18384,25 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"send@npm:^1.1.0, send@npm:^1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "send@npm:1.2.0"
|
||||
dependencies:
|
||||
debug: "npm:^4.3.5"
|
||||
encodeurl: "npm:^2.0.0"
|
||||
escape-html: "npm:^1.0.3"
|
||||
etag: "npm:^1.8.1"
|
||||
fresh: "npm:^2.0.0"
|
||||
http-errors: "npm:^2.0.0"
|
||||
mime-types: "npm:^3.0.1"
|
||||
ms: "npm:^2.1.3"
|
||||
on-finished: "npm:^2.4.1"
|
||||
range-parser: "npm:^1.2.1"
|
||||
statuses: "npm:^2.0.1"
|
||||
checksum: 10c0/531bcfb5616948d3468d95a1fd0adaeb0c20818ba4a500f439b800ca2117971489e02074ce32796fd64a6772ea3e7235fe0583d8241dbd37a053dc3378eff9a5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"serialize-javascript@npm:^6.0.1":
|
||||
version: 6.0.2
|
||||
resolution: "serialize-javascript@npm:6.0.2"
|
||||
@ -18059,6 +18443,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"serve-static@npm:^2.2.0":
|
||||
version: 2.2.0
|
||||
resolution: "serve-static@npm:2.2.0"
|
||||
dependencies:
|
||||
encodeurl: "npm:^2.0.0"
|
||||
escape-html: "npm:^1.0.3"
|
||||
parseurl: "npm:^1.3.3"
|
||||
send: "npm:^1.2.0"
|
||||
checksum: 10c0/30e2ed1dbff1984836cfd0c65abf5d3f3f83bcd696c99d2d3c97edbd4e2a3ff4d3f87108a7d713640d290a7b6fe6c15ddcbc61165ab2eaad48ea8d3b52c7f913
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"serve@npm:^14.2.4":
|
||||
version: 14.2.5
|
||||
resolution: "serve@npm:14.2.5"
|
||||
@ -18136,6 +18532,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"setprototypeof@npm:1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "setprototypeof@npm:1.2.0"
|
||||
checksum: 10c0/68733173026766fa0d9ecaeb07f0483f4c2dc70ca376b3b7c40b7cda909f94b0918f6c5ad5ce27a9160bdfb475efaa9d5e705a11d8eaae18f9835d20976028bc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"sha.js@npm:^2.4.0, sha.js@npm:^2.4.11, sha.js@npm:^2.4.8":
|
||||
version: 2.4.11
|
||||
resolution: "sha.js@npm:2.4.11"
|
||||
@ -18574,6 +18977,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"statuses@npm:2.0.1":
|
||||
version: 2.0.1
|
||||
resolution: "statuses@npm:2.0.1"
|
||||
checksum: 10c0/34378b207a1620a24804ce8b5d230fea0c279f00b18a7209646d5d47e419d1cc23e7cbf33a25a1e51ac38973dc2ac2e1e9c647a8e481ef365f77668d72becfd0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"statuses@npm:^2.0.1":
|
||||
version: 2.0.2
|
||||
resolution: "statuses@npm:2.0.2"
|
||||
checksum: 10c0/a9947d98ad60d01f6b26727570f3bcceb6c8fa789da64fe6889908fe2e294d57503b14bf2b5af7605c2d36647259e856635cd4c49eab41667658ec9d0080ec3f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"std-env@npm:^3.8.0, std-env@npm:^3.9.0":
|
||||
version: 3.9.0
|
||||
resolution: "std-env@npm:3.9.0"
|
||||
@ -19154,6 +19571,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"toidentifier@npm:1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "toidentifier@npm:1.0.1"
|
||||
checksum: 10c0/93937279934bd66cc3270016dd8d0afec14fb7c94a05c72dc57321f8bd1fa97e5bea6d1f7c89e728d077ca31ea125b78320a616a6c6cd0e6b9cb94cb864381c1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"token.js@npm:token.js-fork@0.7.23":
|
||||
version: 0.7.23
|
||||
resolution: "token.js-fork@npm:0.7.23"
|
||||
@ -19391,6 +19815,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"type-is@npm:^2.0.0, type-is@npm:^2.0.1":
|
||||
version: 2.0.1
|
||||
resolution: "type-is@npm:2.0.1"
|
||||
dependencies:
|
||||
content-type: "npm:^1.0.5"
|
||||
media-typer: "npm:^1.1.0"
|
||||
mime-types: "npm:^3.0.0"
|
||||
checksum: 10c0/7f7ec0a060b16880bdad36824ab37c26019454b67d73e8a465ed5a3587440fbe158bc765f0da68344498235c877e7dbbb1600beccc94628ed05599d667951b99
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typed-array-buffer@npm:^1.0.3":
|
||||
version: 1.0.3
|
||||
resolution: "typed-array-buffer@npm:1.0.3"
|
||||
@ -19736,6 +20171,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"unpipe@npm:1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "unpipe@npm:1.0.0"
|
||||
checksum: 10c0/193400255bd48968e5c5383730344fbb4fa114cdedfab26e329e50dd2d81b134244bb8a72c6ac1b10ab0281a58b363d06405632c9d49ca9dfd5e90cbd7d0f32c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"unplugin@npm:^2.1.2":
|
||||
version: 2.3.2
|
||||
resolution: "unplugin@npm:2.3.2"
|
||||
@ -20004,7 +20446,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"vary@npm:~1.1.2":
|
||||
"vary@npm:^1, vary@npm:^1.1.2, vary@npm:~1.1.2":
|
||||
version: 1.1.2
|
||||
resolution: "vary@npm:1.1.2"
|
||||
checksum: 10c0/f15d588d79f3675135ba783c91a4083dcd290a2a5be9fcb6514220a1634e23df116847b1cc51f66bfb0644cf9353b2abb7815ae499bab06e46dd33c1a6bf1f4f
|
||||
@ -20941,6 +21383,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"zod-to-json-schema@npm:^3.24.1":
|
||||
version: 3.24.6
|
||||
resolution: "zod-to-json-schema@npm:3.24.6"
|
||||
peerDependencies:
|
||||
zod: ^3.24.1
|
||||
checksum: 10c0/b907ab6d057100bd25a37e5545bf5f0efa5902cd84d3c3ec05c2e51541431a47bd9bf1e5e151a244273409b45f5986d55b26e5d207f98abc5200702f733eb368
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"zod@npm:^3.23.8":
|
||||
version: 3.24.2
|
||||
resolution: "zod@npm:3.24.2"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user