From 7a23386de4c0a868b0bc43af70d5579ba116aabe Mon Sep 17 00:00:00 2001 From: MyPrototypeWhat Date: Thu, 4 Sep 2025 17:21:50 +0800 Subject: [PATCH] feat: enhance AI core with Claude code integration and new provider support - Added ClaudeCodeService for managing Claude code interactions via HTTP. - Updated IPC channels to include new provider functionalities, enabling communication with the Claude code service. - Enhanced electron configuration to support new AI core paths and dependencies. - Updated package.json to include new dependencies for AI SDK and express. - Refactored tsconfig to include paths for the new AI core modules, improving module resolution. This update improves the integration of AI capabilities and enhances the overall functionality of the application. --- electron.vite.config.ts | 5 +- package.json | 11 +- packages/shared/IpcChannel.ts | 1 + src/main/index.ts | 18 ++ src/main/ipc.ts | 6 + src/main/services/ClaudeCodeService.ts | 158 ++++++++++++++++++ src/preload/index.ts | 3 + .../src/aiCore/chunk/AiSdkToChunkAdapter.ts | 71 ++++++-- src/renderer/src/aiCore/index_new.ts | 78 +++++++++ src/renderer/src/aiCore/provider/factory.ts | 11 ++ .../src/aiCore/provider/providerConfig.ts | 1 - tsconfig.node.json | 8 +- yarn.lock | 157 ++++++++++++++++- 13 files changed, 511 insertions(+), 17 deletions(-) create mode 100644 src/main/services/ClaudeCodeService.ts diff --git a/electron.vite.config.ts b/electron.vite.config.ts index dff0a94a37..f522cb5cf1 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -23,7 +23,10 @@ export default defineConfig({ '@shared': resolve('packages/shared'), '@logger': resolve('src/main/services/LoggerService'), '@mcp-trace/trace-core': resolve('packages/mcp-trace/trace-core'), - '@mcp-trace/trace-node': resolve('packages/mcp-trace/trace-node') + '@mcp-trace/trace-node': resolve('packages/mcp-trace/trace-node'), + '@cherrystudio/ai-core/provider': resolve('packages/aiCore/src/core/providers'), + '@cherrystudio/ai-core/built-in/plugins': resolve('packages/aiCore/src/core/plugins/built-in'), + '@cherrystudio/ai-core': resolve('packages/aiCore/src') } }, build: { diff --git a/package.json b/package.json index c84bc483d4..282d9b00dd 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,8 @@ "@libsql/win32-x64-msvc": "^0.4.7", "@napi-rs/system-ocr": "patch:@napi-rs/system-ocr@npm%3A1.0.2#~/.yarn/patches/@napi-rs-system-ocr-npm-1.0.2-59e7a78e8b.patch", "@strongtz/win32-arm64-msvc": "^0.4.7", + "ai-sdk-provider-claude-code": "^1.1.3", + "express": "^5.1.0", "graceful-fs": "^4.2.11", "jsdom": "26.1.0", "node-stream-zip": "^1.15.0", @@ -169,6 +171,7 @@ "@truto/turndown-plugin-gfm": "^1.0.2", "@tryfabric/martian": "^1.2.4", "@types/cli-progress": "^3", + "@types/express": "^5.0.3", "@types/fs-extra": "^11", "@types/he": "^1", "@types/lodash": "^4.17.5", @@ -335,7 +338,13 @@ "pkce-challenge@npm:^4.1.0": "patch:pkce-challenge@npm%3A4.1.0#~/.yarn/patches/pkce-challenge-npm-4.1.0-fbc51695a3.patch", "undici": "6.21.2", "vite": "npm:rolldown-vite@latest", - "tesseract.js@npm:*": "patch:tesseract.js@npm%3A6.0.1#~/.yarn/patches/tesseract.js-npm-6.0.1-2562a7e46d.patch" + "tesseract.js@npm:*": "patch:tesseract.js@npm%3A6.0.1#~/.yarn/patches/tesseract.js-npm-6.0.1-2562a7e46d.patch", + "@img/sharp-darwin-arm64": "0.34.3", + "@img/sharp-darwin-x64": "0.34.3", + "@img/sharp-linux-arm": "0.34.3", + "@img/sharp-linux-arm64": "0.34.3", + "@img/sharp-linux-x64": "0.34.3", + "@img/sharp-win32-x64": "0.34.3" }, "packageManager": "yarn@4.9.1", "lint-staged": { diff --git a/packages/shared/IpcChannel.ts b/packages/shared/IpcChannel.ts index c5b7c6ec4e..cbe30007be 100644 --- a/packages/shared/IpcChannel.ts +++ b/packages/shared/IpcChannel.ts @@ -250,6 +250,7 @@ export enum IpcChannel { // Provider Provider_AddKey = 'provider:add-key', + Provider_GetClaudeCodePort = 'provider:get-claude-code-port', //Selection Assistant Selection_TextSelected = 'selection:text-selected', diff --git a/src/main/index.ts b/src/main/index.ts index e35f7cebfe..f4c12fc326 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -13,6 +13,7 @@ import installExtension, { REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS } from 'electro import { isDev, isLinux, isWin } from './constant' import { registerIpc } from './ipc' +import { claudeCodeService } from './services/ClaudeCodeService' import { configManager } from './services/ConfigManager' import mcpService from './services/MCPService' import { nodeTraceService } from './services/NodeTraceService' @@ -119,6 +120,14 @@ if (!app.requestSingleInstanceLock()) { nodeTraceService.init() + // Start Claude-code HTTP service + try { + await claudeCodeService.start() + logger.info('Claude-code HTTP service started successfully') + } catch (error) { + logger.error('Failed to start Claude-code HTTP service:', error as Error) + } + app.on('activate', function () { const mainWindow = windowService.getMainWindow() if (!mainWindow || mainWindow.isDestroyed()) { @@ -193,6 +202,15 @@ if (!app.requestSingleInstanceLock()) { } catch (error) { logger.warn('Error cleaning up MCP service:', error as Error) } + + // Stop Claude-code HTTP service + try { + await claudeCodeService.stop() + logger.info('Claude-code HTTP service stopped') + } catch (error) { + logger.warn('Error stopping Claude-code HTTP service:', error as Error) + } + // finish the logger logger.finish() }) diff --git a/src/main/ipc.ts b/src/main/ipc.ts index 8b22fee49c..0917b7498e 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -11,6 +11,7 @@ import { SpanEntity, TokenUsage } from '@mcp-trace/trace-core' import { MIN_WINDOW_HEIGHT, MIN_WINDOW_WIDTH, UpgradeChannel } from '@shared/config/constant' import { IpcChannel } from '@shared/IpcChannel' import { FileMetadata, Provider, Shortcut, ThemeMode } from '@types' +import { claudeCodeService } from './services/ClaudeCodeService' import { BrowserWindow, dialog, ipcMain, ProxyConfig, session, shell, systemPreferences, webContents } from 'electron' import { Notification } from 'src/renderer/src/types/notification' @@ -755,4 +756,9 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) { // CherryIN ipcMain.handle(IpcChannel.Cherryin_GetSignature, (_, params) => generateSignature(params)) + + // Provider + ipcMain.handle(IpcChannel.Provider_GetClaudeCodePort, () => { + return claudeCodeService.getPort() + }) } diff --git a/src/main/services/ClaudeCodeService.ts b/src/main/services/ClaudeCodeService.ts new file mode 100644 index 0000000000..d9d741fc5c --- /dev/null +++ b/src/main/services/ClaudeCodeService.ts @@ -0,0 +1,158 @@ +import { createExecutor } from '@cherrystudio/ai-core' +import { loggerService } from '@logger' +import { createClaudeCode } from 'ai-sdk-provider-claude-code' +import express, { Request, Response } from 'express' +import { Server } from 'http' + +const logger = loggerService.withContext('ClaudeCodeService') + +export class ClaudeCodeService { + private app: express.Application + private server: Server | null = null + private port: number = 0 + private claudeCodeProvider: any = null + + constructor() { + this.app = express() + this.setupMiddleware() + this.setupRoutes() + } + + private setupMiddleware() { + this.app.use(express.json()) + this.app.use(express.text()) + } + + private setupRoutes() { + // Health check endpoint + this.app.get('/health', (_req: Request, res: Response) => { + res.json({ status: 'ok', timestamp: new Date().toISOString() }) + }) + + // Initialize claude-code provider + this.app.post('/init', async (req: Request, res: Response) => { + try { + const config = req.body + logger.info('Initializing claude-code provider with config', config) + + this.claudeCodeProvider = createClaudeCode(config) + + res.json({ + success: true, + message: 'Claude-code provider initialized successfully' + }) + } catch (error) { + logger.error('Failed to initialize claude-code provider', error as Error) + res.status(500).json({ + success: false, + error: (error as Error).message + }) + } + }) + + // Stream text completion endpoint + this.app.post('/completions', async (req: Request, res: Response): Promise => { + try { + if (!this.claudeCodeProvider) { + res.status(400).json({ + success: false, + error: 'Claude-code provider not initialized. Call /init first.' + }) + return + } + + const { modelId, params, options } = req.body + logger.info('Processing completions request', { modelId, hasParams: !!params }) + + // 创建执行器 + const executor = createExecutor('claude-code', options || {}, []) + const model = this.claudeCodeProvider.languageModel('opus') + + // 执行流式文本生成 + const result = await executor.streamText({ + ...params, + model, + abortSignal: new AbortController().signal + }) + console.log('result', result) + // 使用 AI SDK 提供的便捷函数处理流式响应 + result.pipeUIMessageStreamToResponse(res) + + logger.info('Completions request completed successfully') + } catch (error) { + logger.error('Error in completions endpoint', error as Error) + if (!res.headersSent) { + res.status(500).json({ + success: false, + error: (error as Error).message + }) + } + } + }) + } + + public async start(): Promise { + return new Promise((resolve, reject) => { + // 尝试使用固定端口,如果失败则使用系统分配端口 + const preferredPort = 23456 + + this.server = this.app.listen(preferredPort, 'localhost', () => { + if (this.server?.address()) { + this.port = (this.server.address() as any)?.port || 0 + logger.info(`Claude-code HTTP service started on port ${this.port}`) + resolve(this.port) + } else { + reject(new Error('Failed to start server')) + } + }) + + this.server.on('error', (error: any) => { + if (error.code === 'EADDRINUSE') { + logger.warn(`Port ${preferredPort} is in use, trying with dynamic port`) + // 如果固定端口被占用,使用动态端口 + this.server = this.app.listen(0, 'localhost', () => { + if (this.server?.address()) { + this.port = (this.server.address() as any)?.port || 0 + logger.info(`Claude-code HTTP service started on dynamic port ${this.port}`) + resolve(this.port) + } else { + reject(new Error('Failed to start server')) + } + }) + + this.server.on('error', (dynamicError) => { + logger.error('Server error on dynamic port', dynamicError) + reject(dynamicError) + }) + } else { + logger.error('Server error', error) + reject(error) + } + }) + }) + } + + public async stop(): Promise { + return new Promise((resolve) => { + if (this.server) { + this.server.close(() => { + logger.info('Claude-code HTTP service stopped') + resolve() + }) + } else { + resolve() + } + }) + } + + public getPort(): number { + return this.port + } + + public isRunning(): boolean { + return this.server !== null && this.server.listening + } +} + +// 单例实例 +export const claudeCodeService = new ClaudeCodeService() diff --git a/src/preload/index.ts b/src/preload/index.ts index 2244264753..fa77d76a2c 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -437,6 +437,9 @@ const api = { cherryin: { generateSignature: (params: { method: string; path: string; query: string; body: Record }) => ipcRenderer.invoke(IpcChannel.Cherryin_GetSignature, params) + }, + provider: { + getClaudeCodePort: () => ipcRenderer.invoke(IpcChannel.Provider_GetClaudeCodePort) } } diff --git a/src/renderer/src/aiCore/chunk/AiSdkToChunkAdapter.ts b/src/renderer/src/aiCore/chunk/AiSdkToChunkAdapter.ts index 183e469cdd..81343805cd 100644 --- a/src/renderer/src/aiCore/chunk/AiSdkToChunkAdapter.ts +++ b/src/renderer/src/aiCore/chunk/AiSdkToChunkAdapter.ts @@ -53,6 +53,54 @@ export class AiSdkToChunkAdapter { return await aiSdkResult.text } + /** + * 直接处理单个 chunk 数据 + * @param chunk AI SDK 的 chunk 数据 + */ + async processChunk(response: ReadableStream>): Promise { + const reader = response.getReader() + const final = { + text: '', + reasoningContent: '', + webSearchResults: [], + reasoningId: '' + } + try { + let buffer = '' + const decoder = new TextDecoder() + while (true) { + const { done, value } = await reader.read() + if (done) break + + const chunk = decoder.decode(value, { stream: true }) + buffer += chunk + + // 按行处理 SSE 数据 + const lines = buffer.split('\n') + buffer = lines.pop() || '' // 保留最后一行(可能不完整) + + for (const line of lines) { + if (line.startsWith('data: ')) { + const dataStr = line.slice(6) // 移除 "data: " 前缀 + + if (dataStr === '[DONE]') { + break + } + try { + const data = JSON.parse(dataStr) + this.convertAndEmitChunk(data, final) + } catch (parseError) { + // 忽略无法解析的数据 + // logger.debug('Failed to parse streamed data:', parseError as Error, line) + } + } + } + } + } finally { + reader.releaseLock() + } + } + /** * 读取 fullStream 并转换为 Cherry Studio chunks * @param fullStream AI SDK 的 fullStream (ReadableStream) @@ -90,6 +138,7 @@ export class AiSdkToChunkAdapter { final: { text: string; reasoningContent: string; webSearchResults: any[]; reasoningId: string } ) { logger.info(`AI SDK chunk type: ${chunk.type}`, chunk) + console.log('final', final) switch (chunk.type) { // === 文本相关事件 === case 'text-start': @@ -99,7 +148,7 @@ export class AiSdkToChunkAdapter { break case 'text-delta': if (this.accumulate) { - final.text += chunk.text || '' + final.text += chunk.delta || '' } else { final.text = chunk.text || '' } @@ -232,13 +281,13 @@ export class AiSdkToChunkAdapter { text: final.text || '', reasoning_content: final.reasoningContent || '', usage: { - completion_tokens: chunk.totalUsage.outputTokens || 0, - prompt_tokens: chunk.totalUsage.inputTokens || 0, - total_tokens: chunk.totalUsage.totalTokens || 0 + completion_tokens: chunk?.totalUsage?.outputTokens || 0, + prompt_tokens: chunk?.totalUsage?.inputTokens || 0, + total_tokens: chunk?.totalUsage?.totalTokens || 0 }, - metrics: chunk.totalUsage + metrics: chunk?.totalUsage ? { - completion_tokens: chunk.totalUsage.outputTokens || 0, + completion_tokens: chunk?.totalUsage?.outputTokens || 0, time_completion_millsec: 0 } : undefined @@ -250,13 +299,13 @@ export class AiSdkToChunkAdapter { text: final.text || '', reasoning_content: final.reasoningContent || '', usage: { - completion_tokens: chunk.totalUsage.outputTokens || 0, - prompt_tokens: chunk.totalUsage.inputTokens || 0, - total_tokens: chunk.totalUsage.totalTokens || 0 + completion_tokens: chunk?.totalUsage?.outputTokens || 0, + prompt_tokens: chunk?.totalUsage?.inputTokens || 0, + total_tokens: chunk?.totalUsage?.totalTokens || 0 }, - metrics: chunk.totalUsage + metrics: chunk?.totalUsage ? { - completion_tokens: chunk.totalUsage.outputTokens || 0, + completion_tokens: chunk?.totalUsage?.outputTokens || 0, time_completion_millsec: 0 } : undefined diff --git a/src/renderer/src/aiCore/index_new.ts b/src/renderer/src/aiCore/index_new.ts index 9991dffd1f..6e2b047466 100644 --- a/src/renderer/src/aiCore/index_new.ts +++ b/src/renderer/src/aiCore/index_new.ts @@ -90,6 +90,11 @@ export default class ModernAiProvider { // 准备特殊配置 await prepareSpecialProviderConfig(this.actualProvider, this.config) + // 特殊处理 claude-code provider,通过本地 HTTP 服务器 + // if (this.config.providerId === 'claude-code') { + return await this._completionsViaHttpService(modelId, params, config) + // } + // 提前创建本地 provider 实例 if (!this.localProvider) { this.localProvider = await createAiSdkProvider(this.config) @@ -246,6 +251,79 @@ export default class ModernAiProvider { } } + /** + * 通过本地 HTTP 服务器处理 claude-code completions + */ + private async _completionsViaHttpService( + modelId: string, + params: StreamTextParams, + config: ModernAiProviderConfig + ): Promise { + logger.info('Starting claude-code completions via HTTP service', { + modelId, + providerId: this.config!.providerId, + topicId: config.topicId, + hasOnChunk: !!config.onChunk + }) + + try { + // 初始化 claude-code provider + const initResponse = await fetch('http://localhost:' + (await this.getClaudeCodePort()) + '/init', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(this.config!.options) + }) + + if (!initResponse.ok) { + throw new Error(`Failed to initialize claude-code provider: ${initResponse.statusText}`) + } + + // 发送 completions 请求 + const completionsResponse = await fetch('http://localhost:' + (await this.getClaudeCodePort()) + '/completions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + modelId, + params, + options: this.config!.options + }) + }) + + if (!completionsResponse.ok) { + throw new Error(`Failed to get completions: ${completionsResponse.statusText}`) + } + + let finalText = '' + + if (config.onChunk && completionsResponse.body) { + // 创建 adapter 来处理 chunk 数据 + const accumulate = this.model!.supported_text_delta !== false + const adapter = new AiSdkToChunkAdapter(config.onChunk, config.mcpTools, accumulate) + await adapter.processChunk(completionsResponse.body) + } else { + finalText = await completionsResponse.text() + } + + return { + getText: () => finalText + } + } catch (error) { + logger.error('Error in claude-code HTTP service completions', error as Error) + throw error + } + } + + /** + * 获取 Claude-code HTTP 服务端口 + */ + private async getClaudeCodePort(): Promise { + return await window.api.provider.getClaudeCodePort() + } + /** * 使用现代化AI SDK的completions实现 */ diff --git a/src/renderer/src/aiCore/provider/factory.ts b/src/renderer/src/aiCore/provider/factory.ts index 617758753e..9233aca339 100644 --- a/src/renderer/src/aiCore/provider/factory.ts +++ b/src/renderer/src/aiCore/provider/factory.ts @@ -76,6 +76,17 @@ export function getAiSdkProviderId(provider: Provider): ProviderId | 'openai-com export async function createAiSdkProvider(config) { let localProvider: Awaited | null = null try { + // 特殊处理 claude-code provider,通过 IPC 在主线程中创建 + // if (config.providerId === 'claude-code') { + localProvider = await window.api.provider.createClaudeCode() + logger.debug('Claude-code provider created via IPC', { + providerId: config.providerId, + hasOptions: !!config.options + }) + console.log('localProvider', localProvider) + return localProvider + // } + if (config.providerId === 'openai' && config.options?.mode === 'chat') { config.providerId = `${config.providerId}-chat` } else if (config.providerId === 'azure' && config.options?.mode === 'responses') { diff --git a/src/renderer/src/aiCore/provider/providerConfig.ts b/src/renderer/src/aiCore/provider/providerConfig.ts index 63d1b6ed54..aaab471a07 100644 --- a/src/renderer/src/aiCore/provider/providerConfig.ts +++ b/src/renderer/src/aiCore/provider/providerConfig.ts @@ -92,7 +92,6 @@ function formatProviderApiHost(provider: Provider): Provider { */ export function getActualProvider(model: Model): Provider { const baseProvider = getProviderByModel(model) - // 按顺序处理各种转换 let actualProvider = cloneDeep(baseProvider) actualProvider = handleSpecialProviders(model, actualProvider) diff --git a/tsconfig.node.json b/tsconfig.node.json index 9159069b38..62864e158d 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -7,6 +7,7 @@ "src/main/env.d.ts", "src/renderer/src/types/*", "packages/shared/**/*", + "packages/aiCore/**/*", "scripts", "packages/mcp-trace/**/*", "src/renderer/src/services/traceApi.ts" ], @@ -19,12 +20,17 @@ "vitest/globals" ], "baseUrl": ".", + "moduleResolution": "bundler", "paths": { "@logger": ["src/main/services/LoggerService"], "@main/*": ["src/main/*"], "@types": ["src/renderer/src/types/index.ts"], "@shared/*": ["packages/shared/*"], - "@mcp-trace/*": ["packages/mcp-trace/*"] + "@mcp-trace/*": ["packages/mcp-trace/*"], + "@cherrystudio/ai-core/provider": ["packages/aiCore/src/core/providers/index.ts"], + "@cherrystudio/ai-core/built-in/plugins": ["packages/aiCore/src/core/plugins/built-in/index.ts"], + "@cherrystudio/ai-core/*": ["packages/aiCore/src/*"], + "@cherrystudio/ai-core": ["packages/aiCore/src/index.ts"], }, "experimentalDecorators": true, "emitDecoratorMetadata": true, diff --git a/yarn.lock b/yarn.lock index 04ea80ce6c..89dc1c5e89 100644 --- a/yarn.lock +++ b/yarn.lock @@ -451,6 +451,35 @@ __metadata: languageName: node linkType: hard +"@anthropic-ai/claude-code@npm:1.0.94": + version: 1.0.94 + resolution: "@anthropic-ai/claude-code@npm:1.0.94" + dependencies: + "@img/sharp-darwin-arm64": "npm:^0.33.5" + "@img/sharp-darwin-x64": "npm:^0.33.5" + "@img/sharp-linux-arm": "npm:^0.33.5" + "@img/sharp-linux-arm64": "npm:^0.33.5" + "@img/sharp-linux-x64": "npm:^0.33.5" + "@img/sharp-win32-x64": "npm:^0.33.5" + dependenciesMeta: + "@img/sharp-darwin-arm64": + optional: true + "@img/sharp-darwin-x64": + optional: true + "@img/sharp-linux-arm": + optional: true + "@img/sharp-linux-arm64": + optional: true + "@img/sharp-linux-x64": + optional: true + "@img/sharp-win32-x64": + optional: true + bin: + claude: cli.js + checksum: 10c0/d97966f07f43f8c247a10d0a55766c018a10bd3bddb0fc5c68f75b5c9a54324175e8f1f5c4a3a3469ff9673e2150defdbb9f9b8da6b52dbbe62f8f9fec4d5afb + languageName: node + linkType: hard + "@anthropic-ai/sdk@npm:>=0.50.3 <1": version: 0.56.0 resolution: "@anthropic-ai/sdk@npm:0.56.0" @@ -3252,7 +3281,7 @@ __metadata: languageName: node linkType: hard -"@emnapi/runtime@npm:^1.4.3, @emnapi/runtime@npm:^1.4.4, @emnapi/runtime@npm:^1.4.5": +"@emnapi/runtime@npm:^1.4.3, @emnapi/runtime@npm:^1.4.5": version: 1.4.5 resolution: "@emnapi/runtime@npm:1.4.5" dependencies: @@ -3261,6 +3290,15 @@ __metadata: languageName: node linkType: hard +"@emnapi/runtime@npm:^1.4.4": + version: 1.5.0 + resolution: "@emnapi/runtime@npm:1.5.0" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/a85c9fc4e3af49cbe41e5437e5be2551392a931910cd0a5b5d3572532786927810c9cc1db11b232ec8f9657b33d4e6f7c4f985f1a052917d7cd703b5b2a20faa + languageName: node + linkType: hard + "@emnapi/wasi-threads@npm:1.0.4": version: 1.0.4 resolution: "@emnapi/wasi-threads@npm:1.0.4" @@ -8143,6 +8181,16 @@ __metadata: languageName: node linkType: hard +"@types/body-parser@npm:*": + version: 1.19.6 + resolution: "@types/body-parser@npm:1.19.6" + dependencies: + "@types/connect": "npm:*" + "@types/node": "npm:*" + checksum: 10c0/542da05c924dce58ee23f50a8b981fee36921850c82222e384931fda3e106f750f7880c47be665217d72dbe445129049db6eb1f44e7a06b09d62af8f3cca8ea7 + languageName: node + linkType: hard + "@types/cacheable-request@npm:^6.0.1": version: 6.0.3 resolution: "@types/cacheable-request@npm:6.0.3" @@ -8173,6 +8221,15 @@ __metadata: languageName: node linkType: hard +"@types/connect@npm:*": + version: 3.4.38 + resolution: "@types/connect@npm:3.4.38" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/2e1cdba2c410f25649e77856505cd60223250fa12dff7a503e492208dbfdd25f62859918f28aba95315251fd1f5e1ffbfca1e25e73037189ab85dd3f8d0a148c + languageName: node + linkType: hard + "@types/d3-array@npm:*": version: 3.2.1 resolution: "@types/d3-array@npm:3.2.1" @@ -8477,6 +8534,29 @@ __metadata: languageName: node linkType: hard +"@types/express-serve-static-core@npm:^5.0.0": + version: 5.0.7 + resolution: "@types/express-serve-static-core@npm:5.0.7" + dependencies: + "@types/node": "npm:*" + "@types/qs": "npm:*" + "@types/range-parser": "npm:*" + "@types/send": "npm:*" + checksum: 10c0/28666f6a0743b8678be920a6eed075bc8afc96fc7d8ef59c3c049bd6b51533da3b24daf3b437d061e053fba1475e4f3175cb4972f5e8db41608e817997526430 + languageName: node + linkType: hard + +"@types/express@npm:^5.0.3": + version: 5.0.3 + resolution: "@types/express@npm:5.0.3" + dependencies: + "@types/body-parser": "npm:*" + "@types/express-serve-static-core": "npm:^5.0.0" + "@types/serve-static": "npm:*" + checksum: 10c0/f0fbc8daa7f40070b103cf4d020ff1dd08503477d866d1134b87c0390bba71d5d7949cb8b4e719a81ccba89294d8e1573414e6dcbb5bb1d097a7b820928ebdef + languageName: node + linkType: hard + "@types/fs-extra@npm:9.0.13, @types/fs-extra@npm:^9.0.11": version: 9.0.13 resolution: "@types/fs-extra@npm:9.0.13" @@ -8526,6 +8606,13 @@ __metadata: languageName: node linkType: hard +"@types/http-errors@npm:*": + version: 2.0.5 + resolution: "@types/http-errors@npm:2.0.5" + checksum: 10c0/00f8140fbc504f47356512bd88e1910c2f07e04233d99c88c854b3600ce0523c8cd0ba7d1897667243282eb44c59abb9245959e2428b9de004f93937f52f7c15 + languageName: node + linkType: hard + "@types/json-schema@npm:^7.0.15": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15" @@ -8621,6 +8708,13 @@ __metadata: languageName: node linkType: hard +"@types/mime@npm:^1": + version: 1.3.5 + resolution: "@types/mime@npm:1.3.5" + checksum: 10c0/c2ee31cd9b993804df33a694d5aa3fa536511a49f2e06eeab0b484fef59b4483777dbb9e42a4198a0809ffbf698081fdbca1e5c2218b82b91603dfab10a10fbc + languageName: node + linkType: hard + "@types/ms@npm:*": version: 2.1.0 resolution: "@types/ms@npm:2.1.0" @@ -8682,6 +8776,20 @@ __metadata: languageName: node linkType: hard +"@types/qs@npm:*": + version: 6.14.0 + resolution: "@types/qs@npm:6.14.0" + checksum: 10c0/5b3036df6e507483869cdb3858201b2e0b64b4793dc4974f188caa5b5732f2333ab9db45c08157975054d3b070788b35088b4bc60257ae263885016ee2131310 + languageName: node + linkType: hard + +"@types/range-parser@npm:*": + version: 1.2.7 + resolution: "@types/range-parser@npm:1.2.7" + checksum: 10c0/361bb3e964ec5133fa40644a0b942279ed5df1949f21321d77de79f48b728d39253e5ce0408c9c17e4e0fd95ca7899da36841686393b9f7a1e209916e9381a3c + languageName: node + linkType: hard + "@types/react-dom@npm:^19.0.4": version: 19.1.2 resolution: "@types/react-dom@npm:19.1.2" @@ -8734,6 +8842,27 @@ __metadata: languageName: node linkType: hard +"@types/send@npm:*": + version: 0.17.5 + resolution: "@types/send@npm:0.17.5" + dependencies: + "@types/mime": "npm:^1" + "@types/node": "npm:*" + checksum: 10c0/a86c9b89bb0976ff58c1cdd56360ea98528f4dbb18a5c2287bb8af04815513a576a42b4e0e1e7c4d14f7d6ea54733f6ef935ebff8c65e86d9c222881a71e1f15 + languageName: node + linkType: hard + +"@types/serve-static@npm:*": + version: 1.15.8 + resolution: "@types/serve-static@npm:1.15.8" + dependencies: + "@types/http-errors": "npm:*" + "@types/node": "npm:*" + "@types/send": "npm:*" + checksum: 10c0/8ad86a25b87da5276cb1008c43c74667ff7583904d46d5fcaf0355887869d859d453d7dc4f890788ae04705c23720e9b6b6f3215e2d1d2a4278bbd090a9268dd + languageName: node + linkType: hard + "@types/stylis@npm:4.2.5": version: 4.2.5 resolution: "@types/stylis@npm:4.2.5" @@ -9771,6 +9900,7 @@ __metadata: "@truto/turndown-plugin-gfm": "npm:^1.0.2" "@tryfabric/martian": "npm:^1.2.4" "@types/cli-progress": "npm:^3" + "@types/express": "npm:^5.0.3" "@types/fs-extra": "npm:^11" "@types/he": "npm:^1" "@types/lodash": "npm:^4.17.5" @@ -9797,6 +9927,7 @@ __metadata: "@viz-js/viz": "npm:^3.14.0" "@xyflow/react": "npm:^12.4.4" ai: "npm:^5.0.29" + ai-sdk-provider-claude-code: "npm:^1.1.3" antd: "patch:antd@npm%3A5.27.0#~/.yarn/patches/antd-npm-5.27.0-aa91c36546.patch" archiver: "npm:^7.0.1" async-mutex: "npm:^0.5.0" @@ -9830,6 +9961,7 @@ __metadata: eslint-plugin-react-hooks: "npm:^5.2.0" eslint-plugin-simple-import-sort: "npm:^12.1.1" eslint-plugin-unused-imports: "npm:^4.1.4" + express: "npm:^5.1.0" fast-diff: "npm:^1.3.0" fast-xml-parser: "npm:^5.2.0" fetch-socks: "npm:1.3.2" @@ -10022,6 +10154,20 @@ __metadata: languageName: node linkType: hard +"ai-sdk-provider-claude-code@npm:^1.1.3": + version: 1.1.3 + resolution: "ai-sdk-provider-claude-code@npm:1.1.3" + dependencies: + "@ai-sdk/provider": "npm:2.0.0" + "@ai-sdk/provider-utils": "npm:3.0.3" + "@anthropic-ai/claude-code": "npm:1.0.94" + jsonc-parser: "npm:^3.3.1" + peerDependencies: + zod: ^3.0.0 || ^4.0.0 + checksum: 10c0/e503414d19f89ebc638dea791b49a1a4c322088c2f7446b0122b2c557e6e8001f00e9cdd7fbe126870082aaff2f9c25199e07a42d3f594fa7303dfd9547986ca + languageName: node + linkType: hard + "ai@npm:^5.0.29": version: 5.0.29 resolution: "ai@npm:5.0.29" @@ -13910,7 +14056,7 @@ __metadata: languageName: node linkType: hard -"express@npm:^5.0.1": +"express@npm:^5.0.1, express@npm:^5.1.0": version: 5.1.0 resolution: "express@npm:5.1.0" dependencies: @@ -16170,6 +16316,13 @@ __metadata: languageName: node linkType: hard +"jsonc-parser@npm:^3.3.1": + version: 3.3.1 + resolution: "jsonc-parser@npm:3.3.1" + checksum: 10c0/269c3ae0a0e4f907a914bf334306c384aabb9929bd8c99f909275ebd5c2d3bc70b9bcd119ad794f339dec9f24b6a4ee9cd5a8ab2e6435e730ad4075388fc2ab6 + languageName: node + linkType: hard + "jsonfile@npm:^4.0.0": version: 4.0.0 resolution: "jsonfile@npm:4.0.0"