coding-tool-x 3.8.4 → 3.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +86 -15
- package/dist/web/assets/AgentsDrawer-Db9wAyaZ.js +1 -0
- package/dist/web/assets/AgentsDrawer-Pw2k-ZAa.css +1 -0
- package/dist/web/assets/Analytics-C4gMzv7g.js +1 -0
- package/dist/web/assets/Analytics-CyHhR126.css +1 -0
- package/dist/web/assets/AssetPathField-Bd_nMFu3.js +1 -0
- package/dist/web/assets/ChatHistoryDrawer-B2qBQs9L.css +1 -0
- package/dist/web/assets/ChatHistoryDrawer-CwzJHyNP.js +1 -0
- package/dist/web/assets/CollapseItem-CTCfSiue.js +1 -0
- package/dist/web/assets/CommandsDrawer-BZJxlPvV.css +1 -0
- package/dist/web/assets/CommandsDrawer-vDghB1U9.js +1 -0
- package/dist/web/assets/ConfigExportDrawer-C-XDvlHc.js +1 -0
- package/dist/web/assets/ConfigExportDrawer-dwruRKWr.css +1 -0
- package/dist/web/assets/ConfigTemplates-CYY8XRED.js +1 -0
- package/dist/web/assets/ConfigTemplatesDrawer-BfDi8yO3.css +1 -0
- package/dist/web/assets/ConfigTemplatesDrawer-CbsJp76X.js +1 -0
- package/dist/web/assets/ConfigTemplatesPanel-CWyPtk67.css +1 -0
- package/dist/web/assets/ConfigTemplatesPanel-liMSTh7s.js +1 -0
- package/dist/web/assets/Divider-D28g1gwj.js +1 -0
- package/dist/web/assets/DrawerContent-ChXgZ1Xm.js +1 -0
- package/dist/web/assets/FavoritesDrawer-CG9eI9Ay.css +1 -0
- package/dist/web/assets/FavoritesDrawer-D6QSiYaL.js +1 -0
- package/dist/web/assets/GatewayConvertDrawer-CuNlf7AC.css +1 -0
- package/dist/web/assets/GatewayConvertDrawer-RvET6iNG.js +1 -0
- package/dist/web/assets/Home-CSuFuJ9Y.css +1 -0
- package/dist/web/assets/Home-P8eseZcf.js +1 -0
- package/dist/web/assets/MarkdownEditor-CYmLGYju.js +1 -0
- package/dist/web/assets/MarkdownEditor-qfVgSre_.css +1 -0
- package/dist/web/assets/McpDrawer-C0dnsZhQ.css +1 -0
- package/dist/web/assets/McpDrawer-Ch3bkKIq.js +1 -0
- package/dist/web/assets/McpFormDrawer-BRbDcawn.css +1 -0
- package/dist/web/assets/McpFormDrawer-BcTlgSpN.js +1 -0
- package/dist/web/assets/PluginManager-Dh2o0xkZ.js +1 -0
- package/dist/web/assets/PluginsDrawer-DM3QjprE.css +1 -0
- package/dist/web/assets/PluginsDrawer-deKQ5u3I.js +1 -0
- package/dist/web/assets/PluginsPanel-BofsgQgj.js +1 -0
- package/dist/web/assets/PluginsPanel-D6ZqITcR.css +1 -0
- package/dist/web/assets/ProjectList-677RLZ8O.css +1 -0
- package/dist/web/assets/ProjectList-DhSWJHTA.js +1 -0
- package/dist/web/assets/PromptsDrawer-BIgiYCEQ.css +1 -0
- package/dist/web/assets/PromptsDrawer-Cfb2FeeU.js +1 -0
- package/dist/web/assets/RecentSessionsDrawer-3eXamJhr.js +1 -0
- package/dist/web/assets/RecentSessionsDrawer-CKFsOYDM.css +1 -0
- package/dist/web/assets/SessionCard-CW0eHURI.js +1 -0
- package/dist/web/assets/SessionCard-aM2NHu6i.css +1 -0
- package/dist/web/assets/SessionList-BpYRz1cX.css +1 -0
- package/dist/web/assets/SessionList-DQfyDGWU.js +1 -0
- package/dist/web/assets/SettingsDrawer-Dw5NUgHC.css +1 -0
- package/dist/web/assets/SettingsDrawer-u_vwUuPS.js +1 -0
- package/dist/web/assets/SkillManager-C0GCT2Kb.css +1 -0
- package/dist/web/assets/SkillManager-DHMprJLA.js +1 -0
- package/dist/web/assets/SkillsDrawer-BCNme-aJ.css +1 -0
- package/dist/web/assets/SkillsDrawer-CkF3BpyC.js +1 -0
- package/dist/web/assets/SkillsPanel-B3Nm5j32.css +1 -0
- package/dist/web/assets/SkillsPanel-DWE4pFGx.js +1 -0
- package/dist/web/assets/SpeedTestDrawer-BgfG1Ked.js +1 -0
- package/dist/web/assets/SpeedTestDrawer-Dc0WToJ_.css +1 -0
- package/dist/web/assets/Upload-CNwaGOws.js +1 -0
- package/dist/web/assets/WorkspaceDrawer-Br5CeCzL.js +1 -0
- package/dist/web/assets/WorkspaceDrawer-CGnMu9p1.css +1 -0
- package/dist/web/assets/WorkspaceManager-B2O8-qIw.js +1 -0
- package/dist/web/assets/WorkspacePanel-5oDKit3c.js +1 -0
- package/dist/web/assets/WorkspacePanel-D0-zHTHP.css +1 -0
- package/dist/web/assets/_plugin-vue_export-helper-BeLZJZij.js +1 -0
- package/dist/web/assets/client-UDwXBP9i.js +1 -0
- package/dist/web/assets/clipboard-B46yyoz_.js +1 -0
- package/dist/web/assets/headers-cf-QapUq.js +1 -0
- package/dist/web/assets/icons-B2Vg-IOC.js +1 -0
- package/dist/web/assets/index-BMAUPq5K.js +2 -0
- package/dist/web/assets/index-Bi1MQHYX.css +1 -0
- package/dist/web/assets/light-Cp0t7v32.js +1 -0
- package/dist/web/assets/markdown-u082bbtj.js +1 -0
- package/dist/web/assets/platforms-CBkB45JP.js +1 -0
- package/dist/web/assets/platforms-FTpjxAT6.js +1 -0
- package/dist/web/assets/project-config-B2baPzYL.js +1 -0
- package/dist/web/assets/sessions-Cmy31slD.js +1 -0
- package/dist/web/assets/sessions-yC_oVVh8.js +1 -0
- package/dist/web/assets/skill-repo-input-B_fkGlUK.js +1 -0
- package/dist/web/assets/use-lock-html-scroll-DXIqmUW4.js +1 -0
- package/dist/web/assets/use-message-DoLM8C1C.js +1 -0
- package/dist/web/assets/useResponsiveDrawer-jbmqZuJs.js +1 -0
- package/dist/web/assets/vendors-D0c_0KLP.js +1 -0
- package/dist/web/assets/vue-vendor-BqZbbXHA.js +1 -0
- package/dist/web/assets/workspaces-mjyPVGS8.js +1 -0
- package/dist/web/assets/xml-CgY3RMlX.js +1 -0
- package/dist/web/index.html +12 -8
- package/docs/native-log-memory-2026-09-16.md +43 -0
- package/docs/superpowers/plans/2026-08-26-configuration-driven-cli-platform.md +1578 -0
- package/docs/superpowers/plans/2026-08-30-project-scoped-config-control.md +1260 -0
- package/docs/superpowers/plans/2026-09-01-omp-skill-refresh-boundary.md +213 -0
- package/docs/superpowers/plans/2026-09-01-realtime-logs-panel.md +125 -0
- package/docs/superpowers/plans/2026-09-02-config-driven-platform-boundary.md +86 -0
- package/docs/superpowers/plans/2026-09-02-platform-implementation-drivers.md +508 -0
- package/docs/superpowers/plans/2026-09-03-cli-platform-correctness-boundary.md +760 -0
- package/docs/superpowers/plans/2026-09-03-dependency-security-remediation.md +133 -0
- package/docs/superpowers/plans/2026-09-04-model-catalog-pricing-sync.md +1078 -0
- package/docs/superpowers/plans/2026-09-05-codex-project-scan-500.md +128 -0
- package/docs/superpowers/plans/2026-09-06-project-skill-path-fix.md +97 -0
- package/docs/superpowers/specs/2026-08-26-configuration-driven-cli-platform-design.md +408 -0
- package/docs/superpowers/specs/2026-08-30-project-scoped-config-control-design.md +467 -0
- package/docs/superpowers/specs/2026-09-01-claude-session-content-repair-design.md +111 -0
- package/docs/superpowers/specs/2026-09-01-omp-skill-refresh-boundary-design.md +83 -0
- package/docs/superpowers/specs/2026-09-01-realtime-logs-panel-design.md +27 -0
- package/docs/superpowers/specs/2026-09-02-config-driven-platform-boundary-design.md +64 -0
- package/docs/superpowers/specs/2026-09-02-platform-implementation-driver-boundary-design.md +123 -0
- package/docs/superpowers/specs/2026-09-03-cli-platform-correctness-boundary-design.md +249 -0
- package/docs/superpowers/specs/2026-09-03-dependency-security-remediation-design.md +87 -0
- package/docs/superpowers/specs/2026-09-04-model-catalog-pricing-sync-design.md +301 -0
- package/docs/superpowers/specs/2026-09-05-codex-project-scan-500-design.md +24 -0
- package/package.json +8 -4
- package/src/commands/channels.js +88 -191
- package/src/commands/cli-type.js +19 -11
- package/src/commands/daemon.js +104 -32
- package/src/commands/doctor.js +16 -9
- package/src/commands/export-config.js +23 -5
- package/src/commands/list.js +130 -52
- package/src/commands/logs.js +44 -27
- package/src/commands/platform-command-registry.js +78 -0
- package/src/commands/plugin.js +11 -5
- package/src/commands/port-config.js +34 -52
- package/src/commands/proxy-control.js +207 -145
- package/src/commands/proxy.js +2 -2
- package/src/commands/resume.js +87 -91
- package/src/commands/search.js +137 -67
- package/src/commands/stats.js +30 -22
- package/src/commands/switch.js +1 -1
- package/src/commands/toggle-proxy.js +94 -111
- package/src/commands/ui.js +67 -6
- package/src/commands/workspace.js +90 -8
- package/src/config/default.js +6 -53
- package/src/config/loader.js +61 -24
- package/src/config/model-metadata.js +49 -59
- package/src/config/model-metadata.json +4095 -99
- package/src/config/paths.js +181 -8
- package/src/index.js +134 -71
- package/src/platforms/access.js +196 -0
- package/src/{server/services → platforms}/agents-service.js +354 -197
- package/src/platforms/channel-auth-registry.js +165 -0
- package/src/platforms/channel-auth-service.js +107 -0
- package/src/{server/services → platforms}/commands-service.js +195 -115
- package/src/platforms/driver-registry.js +59 -0
- package/src/platforms/drivers/claude/api-operations.js +62 -0
- package/src/{server/services/channels.js → platforms/drivers/claude/channels-implementation.js} +131 -33
- package/src/platforms/drivers/claude/channels.js +39 -0
- package/src/{server/services/config-sync-service.js → platforms/drivers/claude/config-sync.js} +29 -5
- package/src/{server/services → platforms/drivers/claude}/health-check.js +4 -4
- package/src/platforms/drivers/claude/health.js +22 -0
- package/src/platforms/drivers/claude/mcp.js +9 -0
- package/src/{server/services/settings-manager.js → platforms/drivers/claude/native-config-implementation.js} +13 -4
- package/src/platforms/drivers/claude/native-config.js +46 -0
- package/src/platforms/drivers/claude/native-logs.js +72 -0
- package/src/{server/services/claude-openai-gateway.js → platforms/drivers/claude/openai-gateway.js} +8 -65
- package/src/platforms/drivers/claude/project-config.js +21 -0
- package/src/platforms/drivers/claude/projects.js +62 -0
- package/src/platforms/drivers/claude/prompts.js +9 -0
- package/src/{server/proxy-server.js → platforms/drivers/claude/proxy-implementation.js} +41 -278
- package/src/platforms/drivers/claude/proxy.js +16 -0
- package/src/platforms/drivers/claude/resource-sync.js +9 -0
- package/src/platforms/drivers/claude/session-history-adapter.js +313 -0
- package/src/{server/services/sessions.js → platforms/drivers/claude/sessions-implementation.js} +145 -35
- package/src/platforms/drivers/claude/sessions.js +76 -0
- package/src/{server/services/claude-statistics-service.js → platforms/drivers/claude/statistics-implementation.js} +2 -2
- package/src/platforms/drivers/claude/statistics.js +16 -0
- package/src/{server/services/claude-wire.js → platforms/drivers/claude/wire.js} +1 -1
- package/src/platforms/drivers/codex/api-operations.js +9 -0
- package/src/{server/services/codex-channels.js → platforms/drivers/codex/channels-implementation.js} +149 -14
- package/src/platforms/drivers/codex/channels.js +45 -0
- package/src/{server/services/codex-config.js → platforms/drivers/codex/config.js} +12 -2
- package/src/{server/services/codex-env-manager.js → platforms/drivers/codex/env-manager.js} +1 -1
- package/src/platforms/drivers/codex/mcp.js +9 -0
- package/src/{server/services/codex-settings-manager.js → platforms/drivers/codex/native-config-implementation.js} +15 -6
- package/src/platforms/drivers/codex/native-config.js +47 -0
- package/src/platforms/drivers/codex/native-logs.js +166 -0
- package/src/platforms/drivers/codex/project-config.js +21 -0
- package/src/platforms/drivers/codex/projects.js +26 -0
- package/src/platforms/drivers/codex/prompts.js +9 -0
- package/src/{server/codex-proxy-server.js → platforms/drivers/codex/proxy-implementation.js} +30 -233
- package/src/platforms/drivers/codex/proxy.js +16 -0
- package/src/platforms/drivers/codex/resource-sync.js +9 -0
- package/src/{server/services/session-history-adapters/codex.js → platforms/drivers/codex/session-history-adapter.js} +37 -23
- package/src/{server/services/codex-sessions.js → platforms/drivers/codex/sessions-implementation.js} +81 -75
- package/src/platforms/drivers/codex/sessions.js +36 -0
- package/src/{server/services/codex-statistics-service.js → platforms/drivers/codex/statistics-implementation.js} +2 -2
- package/src/platforms/drivers/codex/statistics.js +16 -0
- package/src/platforms/drivers/gemini/api-operations.js +9 -0
- package/src/{server/services/gemini-channels.js → platforms/drivers/gemini/channels-implementation.js} +202 -37
- package/src/platforms/drivers/gemini/channels.js +39 -0
- package/src/{server/services/gemini-config.js → platforms/drivers/gemini/config.js} +12 -2
- package/src/platforms/drivers/gemini/mcp.js +9 -0
- package/src/{server/services/gemini-settings-manager.js → platforms/drivers/gemini/native-config-implementation.js} +14 -5
- package/src/platforms/drivers/gemini/native-config.js +39 -0
- package/src/platforms/drivers/gemini/native-logs.js +180 -0
- package/src/platforms/drivers/gemini/project-config.js +21 -0
- package/src/platforms/drivers/gemini/projects.js +26 -0
- package/src/platforms/drivers/gemini/prompts.js +9 -0
- package/src/{server/gemini-proxy-server.js → platforms/drivers/gemini/proxy-implementation.js} +36 -267
- package/src/platforms/drivers/gemini/proxy.js +16 -0
- package/src/platforms/drivers/gemini/resource-sync.js +9 -0
- package/src/{server/services/session-history-adapters/gemini.js → platforms/drivers/gemini/session-history-adapter.js} +70 -18
- package/src/{server/services/gemini-sessions.js → platforms/drivers/gemini/sessions-implementation.js} +53 -37
- package/src/platforms/drivers/gemini/sessions.js +47 -0
- package/src/{server/services/gemini-statistics-service.js → platforms/drivers/gemini/statistics-implementation.js} +2 -2
- package/src/platforms/drivers/gemini/statistics.js +16 -0
- package/src/{server/services/gemini-wire.js → platforms/drivers/gemini/wire.js} +1 -1
- package/src/platforms/drivers/generic-filesystem.js +166 -0
- package/src/platforms/drivers/generic-jsonl.js +141 -0
- package/src/platforms/drivers/generic-mcp.js +44 -0
- package/src/platforms/drivers/generic-openai-compatible.js +45 -0
- package/src/platforms/drivers/generic-prompt.js +41 -0
- package/src/platforms/drivers/legacy.js +677 -0
- package/src/platforms/drivers/native-config-snapshot.js +125 -0
- package/src/platforms/drivers/native-log-utils.js +1292 -0
- package/src/platforms/drivers/notification-hooks.js +578 -0
- package/src/{server/api/omp-config.js → platforms/drivers/omp/api-operations.js} +52 -64
- package/src/{server/services/omp-auth-provider-worker.js → platforms/drivers/omp/auth-provider-worker.js} +1 -1
- package/src/{server/services/omp-auth-providers.js → platforms/drivers/omp/auth-providers.js} +1 -1
- package/src/{server/services/omp-channels.js → platforms/drivers/omp/channels-implementation.js} +166 -39
- package/src/platforms/drivers/omp/channels.js +58 -0
- package/src/{server/services/omp-config.js → platforms/drivers/omp/config.js} +53 -13
- package/src/{server/services/omp-gateway-routing.js → platforms/drivers/omp/gateway-routing.js} +14 -7
- package/src/{server/services/omp-gateway.js → platforms/drivers/omp/gateway.js} +4 -262
- package/src/platforms/drivers/omp/mcp.js +9 -0
- package/src/{server/services/omp-settings-manager.js → platforms/drivers/omp/native-config-implementation.js} +75 -10
- package/src/platforms/drivers/omp/native-config.js +46 -0
- package/src/platforms/drivers/omp/native-logs.js +52 -0
- package/src/{server/services/omp-native-plugin-adapter.js → platforms/drivers/omp/native-plugin-adapter.js} +91 -4
- package/src/platforms/drivers/omp/project-config.js +29 -0
- package/src/platforms/drivers/omp/projects.js +20 -0
- package/src/platforms/drivers/omp/prompts.js +9 -0
- package/src/{server/omp-proxy-server.js → platforms/drivers/omp/proxy-implementation.js} +6 -15
- package/src/platforms/drivers/omp/proxy.js +16 -0
- package/src/platforms/drivers/omp/resource-sync.js +9 -0
- package/src/{server/services/session-history-adapters/omp.js → platforms/drivers/omp/session-history-adapter.js} +42 -18
- package/src/{server/services/omp-sessions.js → platforms/drivers/omp/sessions-implementation.js} +130 -94
- package/src/platforms/drivers/omp/sessions.js +36 -0
- package/src/{server/services/omp-skill-discovery.js → platforms/drivers/omp/skill-discovery.js} +159 -32
- package/src/{server/services/omp-skill-settings-service.js → platforms/drivers/omp/skill-settings.js} +1 -1
- package/src/{server/services/omp-statistics-service.js → platforms/drivers/omp/statistics-implementation.js} +3 -3
- package/src/platforms/drivers/omp/statistics.js +16 -0
- package/src/platforms/drivers/opencode/api-operations.js +9 -0
- package/src/{server/services/opencode-channels.js → platforms/drivers/opencode/channels-implementation.js} +28 -9
- package/src/platforms/drivers/opencode/channels.js +40 -0
- package/src/platforms/drivers/opencode/conversion.js +41 -0
- package/src/{server/services/opencode-gateway-converter.js → platforms/drivers/opencode/gateway-converter.js} +1 -1
- package/src/platforms/drivers/opencode/mcp.js +9 -0
- package/src/{server/services/opencode-settings-manager.js → platforms/drivers/opencode/native-config-implementation.js} +17 -6
- package/src/platforms/drivers/opencode/native-config.js +26 -0
- package/src/platforms/drivers/opencode/native-logs.js +133 -0
- package/src/platforms/drivers/opencode/project-config.js +98 -0
- package/src/platforms/drivers/opencode/projects.js +20 -0
- package/src/platforms/drivers/opencode/prompts.js +9 -0
- package/src/{server/opencode-proxy-server.js → platforms/drivers/opencode/proxy-implementation.js} +27 -362
- package/src/platforms/drivers/opencode/proxy.js +16 -0
- package/src/platforms/drivers/opencode/resource-sync.js +9 -0
- package/src/{server/services/opencode-sessions.js → platforms/drivers/opencode/sessions-implementation.js} +211 -64
- package/src/platforms/drivers/opencode/sessions.js +48 -0
- package/src/{server/services/opencode-statistics-service.js → platforms/drivers/opencode/statistics-implementation.js} +2 -2
- package/src/platforms/drivers/opencode/statistics.js +16 -0
- package/src/platforms/drivers/secure-file-driver.js +272 -0
- package/src/platforms/drivers/unsupported.js +7 -0
- package/src/platforms/manifest-schema.js +398 -0
- package/src/platforms/manifests/claude.json +489 -0
- package/src/platforms/manifests/codex.json +448 -0
- package/src/platforms/manifests/gemini.json +447 -0
- package/src/platforms/manifests/omp.json +496 -0
- package/src/platforms/manifests/opencode.json +422 -0
- package/src/{server/services → platforms}/native-oauth-adapters.js +98 -418
- package/src/{server/services → platforms}/notification-hooks.js +190 -1023
- package/src/platforms/oauth-credentials-service.js +635 -0
- package/src/platforms/path-resolver.js +179 -0
- package/src/platforms/platform-path-context.js +305 -0
- package/src/platforms/registry.js +248 -0
- package/src/platforms/remote-notification-providers.js +678 -0
- package/src/platforms/runtime.js +259 -0
- package/src/{server/services/session-history-adapters/index.js → platforms/session-history-adapters.js} +8 -1
- package/src/plugins/plugin-loader.js +2 -2
- package/src/plugins/plugin-manager.js +2 -2
- package/src/plugins/registry.js +27 -10
- package/src/reset-config.js +21 -5
- package/src/server/api/agents.js +46 -72
- package/src/server/api/commands.js +16 -6
- package/src/server/api/config-registry.js +206 -59
- package/src/server/api/config-sync.js +3 -2
- package/src/server/api/config-templates.js +20 -4
- package/src/server/api/config.js +111 -102
- package/src/server/api/convert.js +54 -178
- package/src/server/api/dashboard.js +168 -102
- package/src/server/api/hooks.js +1 -1
- package/src/server/api/mcp.js +224 -84
- package/src/server/api/platform-api-config.js +102 -0
- package/src/server/api/platform-route-factory.js +215 -0
- package/src/server/api/platform-routes.js +26 -0
- package/src/server/api/platforms.js +110 -0
- package/src/server/api/plugins.js +57 -57
- package/src/server/api/project-config.js +168 -0
- package/src/server/api/prompts.js +58 -45
- package/src/server/api/settings.js +55 -8
- package/src/server/api/skills.js +262 -133
- package/src/server/api/ui-config.js +2 -2
- package/src/server/api/validation-errors.js +18 -4
- package/src/server/api/workspaces.js +47 -12
- package/src/server/index.js +164 -213
- package/src/server/platform-context.js +81 -0
- package/src/server/services/channel-balance.js +133 -24
- package/src/server/services/channel-health.js +21 -6
- package/src/server/services/channel-scheduler.js +29 -39
- package/src/server/services/channel-sync-utils.js +23 -13
- package/src/server/services/config-export-service.js +350 -319
- package/src/server/services/config-registry-service.js +280 -54
- package/src/server/services/config-sync-manager.js +172 -944
- package/src/server/services/config-templates-service.js +419 -353
- package/src/server/services/control-manifest-store.js +206 -0
- package/src/server/services/dashboard-snapshot-worker.js +656 -100
- package/src/server/services/effective-control-service.js +557 -0
- package/src/server/services/local-resource-index.js +314 -0
- package/src/server/services/mcp-service.js +607 -977
- package/src/server/services/model-detector.js +15 -10
- package/src/server/services/native-log-channel-resolver.js +214 -0
- package/src/server/services/native-log-observer.js +291 -0
- package/src/server/services/network-access.js +12 -2
- package/src/server/services/platform-catalog.js +82 -0
- package/src/server/services/platform-resolution.js +32 -29
- package/src/server/services/plugins-service.js +376 -122
- package/src/server/services/project-config-service.js +550 -0
- package/src/server/services/project-path-validation.js +31 -0
- package/src/server/services/prompts-service.js +267 -233
- package/src/server/services/proxy-runtime.js +7 -4
- package/src/server/services/remote-credential-cache.js +43 -0
- package/src/server/services/repo-scanner-base.js +182 -75
- package/src/server/services/request-logger.js +7 -4
- package/src/server/services/session-history-index.js +876 -214
- package/src/server/services/session-history-worker.js +228 -33
- package/src/server/services/session-snapshot-worker.js +124 -34
- package/src/server/services/session-snapshots.js +4 -2
- package/src/server/services/skill-artifact-store.js +355 -0
- package/src/server/services/skill-format-adapters.js +65 -0
- package/src/server/services/skill-projection-service.js +323 -0
- package/src/server/services/skill-refresh-task-service.js +275 -0
- package/src/server/services/skill-service.js +2020 -677
- package/src/server/services/snapshot-cache.js +30 -8
- package/src/server/services/speed-test.js +8 -8
- package/src/server/services/sqlite-connection.js +19 -3
- package/src/server/services/statistics-service.js +240 -57
- package/src/server/services/ui-config.js +107 -76
- package/src/server/services/usage-log-utils.js +98 -0
- package/src/server/services/web-build.js +1 -1
- package/src/server/services/workspace-service.js +59 -29
- package/src/server/utils/pricing.js +10 -19
- package/src/server/websocket-server.js +153 -28
- package/src/{server/services/base → shared}/base-channel-service.js +156 -28
- package/src/shared/capability-driver.js +104 -0
- package/src/shared/channel-auth-policy.js +27 -0
- package/src/shared/channel-driver.js +161 -0
- package/src/shared/driver-factories/api.js +180 -0
- package/src/shared/driver-factories/mcp.js +244 -0
- package/src/shared/driver-factories/projects.js +121 -0
- package/src/shared/driver-factories/prompts.js +58 -0
- package/src/shared/driver-factories/resource-sync.js +375 -0
- package/src/shared/driver-result.js +78 -0
- package/src/shared/mcp-format.js +189 -0
- package/src/shared/platforms.js +104 -167
- package/src/shared/project-config.js +512 -0
- package/src/shared/proxy-driver.js +57 -0
- package/src/{server/services/base → shared}/response-usage-parser.js +8 -4
- package/src/ui/menu.js +91 -67
- package/src/utils/port-helper.js +35 -19
- package/src/utils/session.js +1 -1
- package/dist/web/assets/Analytics-COEUXmd4.js +0 -25
- package/dist/web/assets/Analytics-RNn1BUbG.css +0 -1
- package/dist/web/assets/ConfigTemplates-DLLkS53H.js +0 -1
- package/dist/web/assets/Home-DCEyLzwX.js +0 -1
- package/dist/web/assets/Home-DYVy4cGZ.css +0 -1
- package/dist/web/assets/PluginManager-Oe1ZHoMO.js +0 -1
- package/dist/web/assets/ProjectList-CWjuAFof.js +0 -1
- package/dist/web/assets/ProjectList-VeMNXfgO.css +0 -1
- package/dist/web/assets/SessionList-C4chCPxK.css +0 -1
- package/dist/web/assets/SessionList-v3ehGg9U.js +0 -1
- package/dist/web/assets/SkillManager-BsX8Qpj0.css +0 -1
- package/dist/web/assets/SkillManager-DUF_qP5S.js +0 -1
- package/dist/web/assets/WorkspaceManager-CjAWBosO.js +0 -1
- package/dist/web/assets/icons-nGdSKQn2.js +0 -1
- package/dist/web/assets/index-D5hyHPj7.js +0 -2
- package/dist/web/assets/index-bJRG3RXH.css +0 -1
- package/dist/web/assets/markdown-BNJfEgCA.js +0 -1
- package/dist/web/assets/markdown-BfC0goYb.css +0 -10
- package/dist/web/assets/naive-ui-BALVpMgd.js +0 -1
- package/dist/web/assets/vendors-DQA4Hw9v.js +0 -7
- package/dist/web/assets/vue-vendor-B5RqjEjQ.js +0 -45
- package/src/config/model-pricing.js +0 -35
- package/src/server/api/channels.js +0 -439
- package/src/server/api/claude-hooks.js +0 -56
- package/src/server/api/claude-statistics.js +0 -57
- package/src/server/api/codex-channels.js +0 -491
- package/src/server/api/codex-projects.js +0 -117
- package/src/server/api/codex-proxy.js +0 -293
- package/src/server/api/codex-sessions.js +0 -652
- package/src/server/api/codex-statistics.js +0 -57
- package/src/server/api/gemini-channels.js +0 -392
- package/src/server/api/gemini-projects.js +0 -103
- package/src/server/api/gemini-proxy.js +0 -231
- package/src/server/api/gemini-sessions.js +0 -550
- package/src/server/api/gemini-statistics.js +0 -57
- package/src/server/api/health-check.js +0 -31
- package/src/server/api/oauth-credentials.js +0 -198
- package/src/server/api/omp-channels.js +0 -575
- package/src/server/api/omp-projects.js +0 -88
- package/src/server/api/omp-proxy.js +0 -125
- package/src/server/api/omp-sessions.js +0 -386
- package/src/server/api/omp-statistics.js +0 -40
- package/src/server/api/opencode-channels.js +0 -471
- package/src/server/api/opencode-projects.js +0 -110
- package/src/server/api/opencode-proxy.js +0 -354
- package/src/server/api/opencode-sessions.js +0 -466
- package/src/server/api/opencode-statistics.js +0 -57
- package/src/server/api/projects.js +0 -120
- package/src/server/api/proxy.js +0 -330
- package/src/server/api/sessions.js +0 -810
- package/src/server/services/oauth-credentials-service.js +0 -945
- package/src/server/services/omp-session-log-observer.js +0 -138
- package/src/server/services/opencode-gateway-adapters.js +0 -43
- package/src/server/services/project-snapshots.js +0 -59
- package/src/server/services/proxy-log-helper.js +0 -298
- package/src/server/services/session-cache.js +0 -127
- package/src/server/services/session-converter.js +0 -582
- package/src/server/services/session-history-adapters/claude.js +0 -235
- /package/src/{server/services/claude-token-recovery.js → platforms/drivers/claude/token-recovery.js} +0 -0
- /package/src/{server/services/codex-parser.js → platforms/drivers/codex/parser.js} +0 -0
- /package/src/{server/services/codex-speed-test-template.json → platforms/drivers/codex/speed-test-template.json} +0 -0
- /package/src/{server/services/codex-wire.js → platforms/drivers/codex/wire.js} +0 -0
- /package/src/{server/services/omp-auth-gateway-client.js → platforms/drivers/omp/auth-gateway-client.js} +0 -0
- /package/src/{server/services/opencode-normalization.js → platforms/drivers/opencode/normalization.js} +0 -0
- /package/src/{server/services → shared}/config-artifact-paths.js +0 -0
- /package/src/{server/services/base → shared}/proxy-utils.js +0 -0
package/src/{server/services/claude-openai-gateway.js → platforms/drivers/claude/openai-gateway.js}
RENAMED
|
@@ -2,13 +2,10 @@ const http = require('http');
|
|
|
2
2
|
const https = require('https');
|
|
3
3
|
const crypto = require('crypto');
|
|
4
4
|
const { HttpsProxyAgent } = require('https-proxy-agent');
|
|
5
|
-
const {
|
|
6
|
-
const {
|
|
7
|
-
const {
|
|
8
|
-
const {
|
|
9
|
-
const { createDecodedStream } = require('./response-decoder');
|
|
10
|
-
const { parseNonStreamingUsage } = require('./base/response-usage-parser');
|
|
11
|
-
const { convertClaudeToOpenCodePayload } = require('./opencode-gateway-converter');
|
|
5
|
+
const { recordSuccess, recordFailure } = require('../../../server/services/channel-health');
|
|
6
|
+
const { createDecodedStream } = require('../../../server/services/response-decoder');
|
|
7
|
+
const { parseNonStreamingUsage } = require('../../../shared/response-usage-parser');
|
|
8
|
+
const { convertClaudeToOpenCodePayload } = require('../opencode/gateway-converter');
|
|
12
9
|
|
|
13
10
|
const REQUEST_TIMEOUT_MS = 120000;
|
|
14
11
|
|
|
@@ -299,18 +296,6 @@ function buildClaudeUsageFromTokens(tokens = {}) {
|
|
|
299
296
|
};
|
|
300
297
|
}
|
|
301
298
|
|
|
302
|
-
function buildPublishTokensFromClaudeUsage(usage = {}) {
|
|
303
|
-
return {
|
|
304
|
-
input: Number(usage.input_tokens || 0),
|
|
305
|
-
output: Number(usage.output_tokens || 0),
|
|
306
|
-
cacheCreation: Number(usage.cache_creation_input_tokens || 0),
|
|
307
|
-
cacheRead: Number(usage.cache_read_input_tokens || 0),
|
|
308
|
-
cached: Number(usage.cache_read_input_tokens || 0),
|
|
309
|
-
reasoning: 0,
|
|
310
|
-
total: 0
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
|
|
314
299
|
function mapResponsesStopReason(response, hasToolUse) {
|
|
315
300
|
if (hasToolUse) return 'tool_use';
|
|
316
301
|
|
|
@@ -1086,31 +1071,8 @@ async function relayChatStreamAsClaude(upstreamResponse, res, fallbackModel = ''
|
|
|
1086
1071
|
});
|
|
1087
1072
|
}
|
|
1088
1073
|
|
|
1089
|
-
function
|
|
1090
|
-
return publishUsageLog({
|
|
1091
|
-
source: 'claude',
|
|
1092
|
-
metadata,
|
|
1093
|
-
model: message?.model || '',
|
|
1094
|
-
tokens: buildPublishTokensFromClaudeUsage(message?.usage || {}),
|
|
1095
|
-
calculateCost,
|
|
1096
|
-
broadcastLog,
|
|
1097
|
-
recordRequest,
|
|
1098
|
-
recordSuccess,
|
|
1099
|
-
allowBroadcast: true
|
|
1100
|
-
});
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
function reportGatewayFailure({ channel, metadata, res, statusCode, message, error, stage, onDone }) {
|
|
1074
|
+
function reportGatewayFailure({ channel, res, statusCode, message, error, onDone }) {
|
|
1104
1075
|
recordFailure(channel.id, 'claude', error || new Error(message));
|
|
1105
|
-
publishFailureLog({
|
|
1106
|
-
source: 'claude',
|
|
1107
|
-
metadata,
|
|
1108
|
-
message,
|
|
1109
|
-
error,
|
|
1110
|
-
statusCode,
|
|
1111
|
-
stage,
|
|
1112
|
-
broadcastLog
|
|
1113
|
-
});
|
|
1114
1076
|
sendAnthropicError(res, statusCode, message);
|
|
1115
1077
|
if (typeof onDone === 'function') {
|
|
1116
1078
|
onDone();
|
|
@@ -1118,7 +1080,7 @@ function reportGatewayFailure({ channel, metadata, res, statusCode, message, err
|
|
|
1118
1080
|
return true;
|
|
1119
1081
|
}
|
|
1120
1082
|
|
|
1121
|
-
async function handleClaudeOpenAiGatewayRequest({ req, res, channel, effectiveKey,
|
|
1083
|
+
async function handleClaudeOpenAiGatewayRequest({ req, res, channel, effectiveKey, onDone }) {
|
|
1122
1084
|
const pathname = getRequestPathname(req?.url || '');
|
|
1123
1085
|
if (!isClaudeMessagesPath(pathname)) {
|
|
1124
1086
|
return false;
|
|
@@ -1132,21 +1094,12 @@ async function handleClaudeOpenAiGatewayRequest({ req, res, channel, effectiveKe
|
|
|
1132
1094
|
return true;
|
|
1133
1095
|
}
|
|
1134
1096
|
|
|
1135
|
-
const requestId = `claude-openai-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
1136
|
-
const startTime = Date.now();
|
|
1137
1097
|
const targetApi = resolveClaudeGatewayTargetApi(channel);
|
|
1138
1098
|
const converted = convertClaudeToOpenCodePayload({
|
|
1139
1099
|
payload: req.body,
|
|
1140
1100
|
options: { targetApi }
|
|
1141
1101
|
});
|
|
1142
1102
|
const upstreamModel = converted?.requestBody?.model || req.body.model || '';
|
|
1143
|
-
const metadata = {
|
|
1144
|
-
id: requestId,
|
|
1145
|
-
channel: channel?.name,
|
|
1146
|
-
channelId: channel?.id,
|
|
1147
|
-
startTime,
|
|
1148
|
-
requestModel: upstreamModel
|
|
1149
|
-
};
|
|
1150
1103
|
const wantsStream = req.body.stream === true;
|
|
1151
1104
|
const targetUrl = buildOpenAiTargetUrl(channel?.baseUrl || '', converted.endpoint || '/v1/responses');
|
|
1152
1105
|
const headers = buildOpenAiHeaders(effectiveKey, {
|
|
@@ -1171,12 +1124,10 @@ async function handleClaudeOpenAiGatewayRequest({ req, res, channel, effectiveKe
|
|
|
1171
1124
|
const upstreamMessage = parsedError?.error?.message || parsedError?.message || rawBody || `HTTP ${upstream.statusCode}`;
|
|
1172
1125
|
return reportGatewayFailure({
|
|
1173
1126
|
channel,
|
|
1174
|
-
metadata,
|
|
1175
1127
|
res,
|
|
1176
1128
|
statusCode: upstream.statusCode,
|
|
1177
1129
|
message: String(upstreamMessage).slice(0, 1000),
|
|
1178
1130
|
error: new Error(String(upstreamMessage).slice(0, 200)),
|
|
1179
|
-
stage: 'openai_gateway_upstream',
|
|
1180
1131
|
onDone
|
|
1181
1132
|
});
|
|
1182
1133
|
}
|
|
@@ -1185,9 +1136,7 @@ async function handleClaudeOpenAiGatewayRequest({ req, res, channel, effectiveKe
|
|
|
1185
1136
|
? await relayChatStreamAsClaude(upstream.response, res, upstreamModel)
|
|
1186
1137
|
: await relayResponsesStreamAsClaude(upstream.response, res, upstreamModel);
|
|
1187
1138
|
|
|
1188
|
-
|
|
1189
|
-
publishClaudeGatewayUsage(metadata, message, calculateCost);
|
|
1190
|
-
}
|
|
1139
|
+
recordSuccess(channel.id, 'claude');
|
|
1191
1140
|
if (typeof onDone === 'function') {
|
|
1192
1141
|
onDone();
|
|
1193
1142
|
}
|
|
@@ -1207,12 +1156,10 @@ async function handleClaudeOpenAiGatewayRequest({ req, res, channel, effectiveKe
|
|
|
1207
1156
|
const upstreamMessage = parsedBody?.error?.message || parsedBody?.message || upstream.rawBody || `HTTP ${upstream.statusCode}`;
|
|
1208
1157
|
return reportGatewayFailure({
|
|
1209
1158
|
channel,
|
|
1210
|
-
metadata,
|
|
1211
1159
|
res,
|
|
1212
1160
|
statusCode: upstream.statusCode,
|
|
1213
1161
|
message: String(upstreamMessage).slice(0, 1000),
|
|
1214
1162
|
error: new Error(String(upstreamMessage).slice(0, 200)),
|
|
1215
|
-
stage: 'openai_gateway_upstream',
|
|
1216
1163
|
onDone
|
|
1217
1164
|
});
|
|
1218
1165
|
}
|
|
@@ -1220,12 +1167,10 @@ async function handleClaudeOpenAiGatewayRequest({ req, res, channel, effectiveKe
|
|
|
1220
1167
|
if (!parsedBody || typeof parsedBody !== 'object') {
|
|
1221
1168
|
return reportGatewayFailure({
|
|
1222
1169
|
channel,
|
|
1223
|
-
metadata,
|
|
1224
1170
|
res,
|
|
1225
1171
|
statusCode: 502,
|
|
1226
1172
|
message: 'Invalid OpenAI gateway response',
|
|
1227
1173
|
error: new Error('Invalid OpenAI gateway response'),
|
|
1228
|
-
stage: 'openai_gateway_parse',
|
|
1229
1174
|
onDone
|
|
1230
1175
|
});
|
|
1231
1176
|
}
|
|
@@ -1235,7 +1180,7 @@ async function handleClaudeOpenAiGatewayRequest({ req, res, channel, effectiveKe
|
|
|
1235
1180
|
: buildClaudeMessageFromResponses(parsedBody, upstreamModel);
|
|
1236
1181
|
|
|
1237
1182
|
res.json(message);
|
|
1238
|
-
|
|
1183
|
+
recordSuccess(channel.id, 'claude');
|
|
1239
1184
|
if (typeof onDone === 'function') {
|
|
1240
1185
|
onDone();
|
|
1241
1186
|
}
|
|
@@ -1243,12 +1188,10 @@ async function handleClaudeOpenAiGatewayRequest({ req, res, channel, effectiveKe
|
|
|
1243
1188
|
} catch (error) {
|
|
1244
1189
|
return reportGatewayFailure({
|
|
1245
1190
|
channel,
|
|
1246
|
-
metadata,
|
|
1247
1191
|
res,
|
|
1248
1192
|
statusCode: 502,
|
|
1249
1193
|
message: `OpenAI gateway network error: ${error.message}`,
|
|
1250
1194
|
error,
|
|
1251
|
-
stage: 'openai_gateway_network',
|
|
1252
1195
|
onDone
|
|
1253
1196
|
});
|
|
1254
1197
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const mcpFormat = require('../../../shared/mcp-format');
|
|
4
|
+
const {
|
|
5
|
+
createProjectAdapter,
|
|
6
|
+
createJsonMcpHandlers
|
|
7
|
+
} = require('../../../shared/project-config');
|
|
8
|
+
|
|
9
|
+
function createAdapter({ manifest, fsImpl } = {}) {
|
|
10
|
+
const relativePath = manifest?.projectResources?.mcp?.path;
|
|
11
|
+
const mcpHandlers = createJsonMcpHandlers({
|
|
12
|
+
relativePath,
|
|
13
|
+
format: manifest?.projectResources?.mcp?.format || 'claude-json',
|
|
14
|
+
toNative: mcpFormat.extractServerSpec,
|
|
15
|
+
fromNative: value => ({ ...value }),
|
|
16
|
+
fsImpl
|
|
17
|
+
});
|
|
18
|
+
return createProjectAdapter({ manifest, fsImpl, mcpHandlers });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = { createAdapter };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { createProjectsDriver } = require('../../../shared/driver-factories/projects');
|
|
6
|
+
|
|
7
|
+
function createProject({ projectName, projectPath } = {}) {
|
|
8
|
+
if (!projectName || !projectPath) {
|
|
9
|
+
const error = new Error('projectName and projectPath are required');
|
|
10
|
+
error.statusCode = 400;
|
|
11
|
+
throw error;
|
|
12
|
+
}
|
|
13
|
+
if (!fs.existsSync(projectPath)) {
|
|
14
|
+
const error = new Error('项目路径不存在');
|
|
15
|
+
error.statusCode = 400;
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
if (!fs.statSync(projectPath).isDirectory()) {
|
|
19
|
+
const error = new Error('项目路径必须是一个目录');
|
|
20
|
+
error.statusCode = 400;
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const claudeDir = path.join(projectPath, '.claude');
|
|
25
|
+
const sessionsDir = path.join(claudeDir, 'sessions');
|
|
26
|
+
if (fs.existsSync(claudeDir)) {
|
|
27
|
+
const error = new Error('该项目已经初始化过 Claude Code');
|
|
28
|
+
error.statusCode = 400;
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
fs.mkdirSync(sessionsDir, { recursive: true });
|
|
33
|
+
const projectConfig = {
|
|
34
|
+
name: projectName,
|
|
35
|
+
createdAt: new Date().toISOString(),
|
|
36
|
+
version: '1.0'
|
|
37
|
+
};
|
|
38
|
+
fs.writeFileSync(
|
|
39
|
+
path.join(claudeDir, 'project.json'),
|
|
40
|
+
JSON.stringify(projectConfig, null, 2),
|
|
41
|
+
'utf8'
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
return { success: true, projectName, projectPath, claudeDir };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function createDriver(context = {}) {
|
|
48
|
+
return createProjectsDriver({
|
|
49
|
+
...context,
|
|
50
|
+
platform: 'claude',
|
|
51
|
+
servicePath: './claude/sessions-implementation',
|
|
52
|
+
localServicePath: '../../platforms/drivers/claude/sessions-implementation',
|
|
53
|
+
createProject,
|
|
54
|
+
onSuccess: operation => {
|
|
55
|
+
if (['saveProjectOrder', 'createProject', 'deleteProject'].includes(operation)) {
|
|
56
|
+
context.sessionHistoryIndex?.invalidateSource('claude');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = { createDriver };
|
|
@@ -6,29 +6,20 @@ const path = require('path');
|
|
|
6
6
|
const os = require('os');
|
|
7
7
|
const chalk = require('chalk');
|
|
8
8
|
const { HttpsProxyAgent } = require('https-proxy-agent');
|
|
9
|
-
const { allocateChannel, releaseChannel, getSchedulerState } = require('
|
|
10
|
-
const { recordSuccess, recordFailure } = require('
|
|
11
|
-
const {
|
|
12
|
-
const { loadConfig } = require('
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const { recordRequest } = require('./services/statistics-service');
|
|
16
|
-
const { saveProxyStartTime, clearProxyStartTime, getProxyStartTime, getProxyRuntime } = require('./services/proxy-runtime');
|
|
17
|
-
const { createDecodedStream } = require('./services/response-decoder');
|
|
18
|
-
const eventBus = require('../plugins/event-bus');
|
|
19
|
-
const { getEffectiveApiKey } = require('./services/channels');
|
|
20
|
-
const { persistProxyRequestSnapshot, persistClaudeRequestTemplate } = require('./services/request-logger');
|
|
21
|
-
const { publishUsageLog, publishFailureLog } = require('./services/proxy-log-helper');
|
|
22
|
-
const { redirectModel, normalizeGatewaySourceType } = require('./services/base/proxy-utils');
|
|
23
|
-
const { parseSSEUsage, parseNonStreamingUsage, mergeUsageIntoTokenData, createTokenData } = require('./services/base/response-usage-parser');
|
|
9
|
+
const { allocateChannel, releaseChannel, getSchedulerState } = require('../../../server/services/channel-scheduler');
|
|
10
|
+
const { recordSuccess, recordFailure } = require('../../../server/services/channel-health');
|
|
11
|
+
const { broadcastSchedulerState } = require('../../../server/websocket-server');
|
|
12
|
+
const { loadConfig } = require('../../../config/loader');
|
|
13
|
+
const { saveProxyStartTime, clearProxyStartTime, getProxyStartTime, getProxyRuntime } = require('../../../server/services/proxy-runtime');
|
|
14
|
+
const eventBus = require('../../../plugins/event-bus');
|
|
24
15
|
const {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
} = require('
|
|
30
|
-
const { handleClaudeOpenAiGatewayRequest } = require('./
|
|
31
|
-
const { attachServerShutdownHandling, expediteServerShutdown } = require('
|
|
16
|
+
getEffectiveApiKey,
|
|
17
|
+
getClaudeProxyExcludedChannelIds = () => []
|
|
18
|
+
} = require('./channels-implementation');
|
|
19
|
+
const { persistProxyRequestSnapshot, persistClaudeRequestTemplate } = require('../../../server/services/request-logger');
|
|
20
|
+
const { redirectModel, normalizeGatewaySourceType } = require('../../../shared/proxy-utils');
|
|
21
|
+
const { handleClaudeOpenAiGatewayRequest } = require('./openai-gateway');
|
|
22
|
+
const { attachServerShutdownHandling, expediteServerShutdown } = require('../../../server/services/server-shutdown');
|
|
32
23
|
|
|
33
24
|
let proxyServer = null;
|
|
34
25
|
let proxyApp = null;
|
|
@@ -41,33 +32,6 @@ const requestMetadata = new Map();
|
|
|
41
32
|
// 格式: { channelId: { "originalModel": "redirectedModel", ... } }
|
|
42
33
|
const printedRedirectCache = new Map();
|
|
43
34
|
|
|
44
|
-
const CLAUDE_BASE_PRICING = DEFAULT_CONFIG.pricing.claude;
|
|
45
|
-
const ONE_MILLION = 1000000;
|
|
46
|
-
|
|
47
|
-
// detectModelTier and redirectModel imported from services/base/proxy-utils
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* 计算请求成本
|
|
51
|
-
* @param {string} model - 模型名称
|
|
52
|
-
* @param {object} tokens - token 使用情况
|
|
53
|
-
* @returns {number} 成本(美元)
|
|
54
|
-
*/
|
|
55
|
-
function calculateCost(model, tokens) {
|
|
56
|
-
const pricing = resolveModelPricing('claude', model, {}, CLAUDE_BASE_PRICING);
|
|
57
|
-
|
|
58
|
-
const inputRate = typeof pricing.input === 'number' ? pricing.input : CLAUDE_BASE_PRICING.input;
|
|
59
|
-
const outputRate = typeof pricing.output === 'number' ? pricing.output : CLAUDE_BASE_PRICING.output;
|
|
60
|
-
const cacheCreationRate = typeof pricing.cacheCreation === 'number' ? pricing.cacheCreation : CLAUDE_BASE_PRICING.cacheCreation;
|
|
61
|
-
const cacheReadRate = typeof pricing.cacheRead === 'number' ? pricing.cacheRead : CLAUDE_BASE_PRICING.cacheRead;
|
|
62
|
-
|
|
63
|
-
return (
|
|
64
|
-
(tokens.input || 0) * inputRate / ONE_MILLION +
|
|
65
|
-
(tokens.output || 0) * outputRate / ONE_MILLION +
|
|
66
|
-
(tokens.cacheCreation || 0) * cacheCreationRate / ONE_MILLION +
|
|
67
|
-
(tokens.cacheRead || 0) * cacheReadRate / ONE_MILLION
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
35
|
const jsonBodyParser = express.json({
|
|
72
36
|
limit: '100mb',
|
|
73
37
|
verify: (req, res, buf) => {
|
|
@@ -179,16 +143,6 @@ function buildClaudeRequestSummary(req, sessionId = null) {
|
|
|
179
143
|
};
|
|
180
144
|
}
|
|
181
145
|
|
|
182
|
-
function clearTokenDataUsage(tokenData) {
|
|
183
|
-
tokenData.inputTokens = 0;
|
|
184
|
-
tokenData.outputTokens = 0;
|
|
185
|
-
tokenData.cacheCreation = 0;
|
|
186
|
-
tokenData.cacheRead = 0;
|
|
187
|
-
tokenData.cachedTokens = 0;
|
|
188
|
-
tokenData.reasoningTokens = 0;
|
|
189
|
-
tokenData.totalTokens = 0;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
146
|
async function startProxyServer(options = {}) {
|
|
193
147
|
const preserveStartTime = options.preserveStartTime || false;
|
|
194
148
|
|
|
@@ -196,6 +150,13 @@ async function startProxyServer(options = {}) {
|
|
|
196
150
|
console.log('Proxy server already running on port', currentPort);
|
|
197
151
|
return { success: true, port: currentPort };
|
|
198
152
|
}
|
|
153
|
+
const excludedChannelIds = getClaudeProxyExcludedChannelIds();
|
|
154
|
+
if (excludedChannelIds.length > 0) {
|
|
155
|
+
const error = new Error('Claude dynamic proxy supports API-key channels only; disable OAuth channels first');
|
|
156
|
+
error.code = 'claude_oauth_proxy_unsupported';
|
|
157
|
+
error.statusCode = 409;
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
199
160
|
|
|
200
161
|
try {
|
|
201
162
|
const config = loadConfig();
|
|
@@ -254,7 +215,12 @@ async function startProxyServer(options = {}) {
|
|
|
254
215
|
const sessionId = extractSessionId(req);
|
|
255
216
|
const config = loadConfig();
|
|
256
217
|
const enableSessionBinding = config.enableSessionBinding !== false; // 默认开启
|
|
257
|
-
const channel = await allocateChannel({
|
|
218
|
+
const channel = await allocateChannel({
|
|
219
|
+
source: 'claude',
|
|
220
|
+
sessionId,
|
|
221
|
+
enableSessionBinding,
|
|
222
|
+
excludeChannelIds: getClaudeProxyExcludedChannelIds()
|
|
223
|
+
});
|
|
258
224
|
|
|
259
225
|
// 广播调度状态(请求开始)
|
|
260
226
|
broadcastSchedulerState('claude', getSchedulerState('claude'));
|
|
@@ -279,14 +245,6 @@ async function startProxyServer(options = {}) {
|
|
|
279
245
|
const effectiveKey = getEffectiveApiKey(channel);
|
|
280
246
|
if (!effectiveKey) {
|
|
281
247
|
release();
|
|
282
|
-
publishFailureLog({
|
|
283
|
-
source: 'claude',
|
|
284
|
-
channel: channel.name,
|
|
285
|
-
message: 'API key not configured or expired. Please update your channel key.',
|
|
286
|
-
statusCode: 401,
|
|
287
|
-
stage: 'preflight',
|
|
288
|
-
broadcastLog
|
|
289
|
-
});
|
|
290
248
|
return res.status(401).json({
|
|
291
249
|
error: 'API key not configured or expired. Please update your channel key.',
|
|
292
250
|
type: 'authentication_error'
|
|
@@ -338,7 +296,6 @@ async function startProxyServer(options = {}) {
|
|
|
338
296
|
res,
|
|
339
297
|
channel,
|
|
340
298
|
effectiveKey,
|
|
341
|
-
calculateCost,
|
|
342
299
|
onDone: release
|
|
343
300
|
});
|
|
344
301
|
if (handled) {
|
|
@@ -362,21 +319,6 @@ async function startProxyServer(options = {}) {
|
|
|
362
319
|
if (err) {
|
|
363
320
|
// 记录请求失败
|
|
364
321
|
recordFailure(channel.id, 'claude', err);
|
|
365
|
-
const metadata = requestMetadata.get(req) || {
|
|
366
|
-
id: null,
|
|
367
|
-
channel: channel.name,
|
|
368
|
-
channelId: channel.id,
|
|
369
|
-
startTime: Date.now()
|
|
370
|
-
};
|
|
371
|
-
publishFailureLog({
|
|
372
|
-
source: 'claude',
|
|
373
|
-
metadata,
|
|
374
|
-
message: err.message,
|
|
375
|
-
error: err,
|
|
376
|
-
statusCode: 502,
|
|
377
|
-
stage: 'proxy_web',
|
|
378
|
-
broadcastLog
|
|
379
|
-
});
|
|
380
322
|
console.error('Proxy error:', err);
|
|
381
323
|
if (res && !res.headersSent) {
|
|
382
324
|
res.status(502).json({
|
|
@@ -388,13 +330,6 @@ async function startProxyServer(options = {}) {
|
|
|
388
330
|
});
|
|
389
331
|
} catch (error) {
|
|
390
332
|
console.error('Channel allocation error:', error);
|
|
391
|
-
publishFailureLog({
|
|
392
|
-
source: 'claude',
|
|
393
|
-
message: error.message || '所有渠道暂时不可用',
|
|
394
|
-
statusCode: 503,
|
|
395
|
-
stage: 'allocate_channel',
|
|
396
|
-
broadcastLog
|
|
397
|
-
});
|
|
398
333
|
if (!res.headersSent) {
|
|
399
334
|
res.status(503).json({
|
|
400
335
|
error: error.message || '所有渠道暂时不可用',
|
|
@@ -404,6 +339,7 @@ async function startProxyServer(options = {}) {
|
|
|
404
339
|
}
|
|
405
340
|
});
|
|
406
341
|
|
|
342
|
+
// 代理响应只负责维护传输健康状态;实时日志和 usage 来自 CLI 原生日志。
|
|
407
343
|
proxy.on('proxyRes', (proxyRes, req, res) => {
|
|
408
344
|
const metadata = requestMetadata.get(req);
|
|
409
345
|
if (!metadata) return;
|
|
@@ -413,185 +349,26 @@ async function startProxyServer(options = {}) {
|
|
|
413
349
|
return;
|
|
414
350
|
}
|
|
415
351
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
res.on('close', () => {
|
|
419
|
-
isResponseClosed = true;
|
|
420
|
-
requestMetadata.delete(req);
|
|
421
|
-
});
|
|
422
|
-
|
|
352
|
+
res.on('close', () => requestMetadata.delete(req));
|
|
423
353
|
res.on('error', (err) => {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
console.error('Response error:', err);
|
|
427
|
-
}
|
|
354
|
+
if (err.code !== 'EPIPE' && err.code !== 'ECONNRESET') console.error('Response error:', err);
|
|
355
|
+
recordFailure(metadata.channelId, 'claude', err);
|
|
428
356
|
requestMetadata.delete(req);
|
|
429
357
|
});
|
|
430
358
|
|
|
431
|
-
const
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
sawAnyUsage: false,
|
|
438
|
-
sawMessageStartUsage: false,
|
|
439
|
-
sawNonInitialUsage: false
|
|
440
|
-
};
|
|
441
|
-
const parsedStream = createDecodedStream(proxyRes);
|
|
442
|
-
|
|
443
|
-
function recordUsageIfReady() {
|
|
444
|
-
if (usageRecorded) return false;
|
|
445
|
-
|
|
446
|
-
const result = publishUsageLog({
|
|
447
|
-
source: 'claude',
|
|
448
|
-
metadata,
|
|
449
|
-
model: tokenData.model,
|
|
450
|
-
tokens: {
|
|
451
|
-
input: tokenData.inputTokens,
|
|
452
|
-
output: tokenData.outputTokens,
|
|
453
|
-
cacheCreation: tokenData.cacheCreation,
|
|
454
|
-
cacheRead: tokenData.cacheRead,
|
|
455
|
-
cached: tokenData.cachedTokens,
|
|
456
|
-
reasoning: tokenData.reasoningTokens,
|
|
457
|
-
total: tokenData.totalTokens
|
|
458
|
-
},
|
|
459
|
-
calculateCost,
|
|
460
|
-
broadcastLog,
|
|
461
|
-
recordRequest,
|
|
462
|
-
recordSuccess,
|
|
463
|
-
allowBroadcast: true
|
|
464
|
-
});
|
|
465
|
-
|
|
466
|
-
if (!result) return false;
|
|
467
|
-
usageRecorded = true;
|
|
468
|
-
return true;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
parsedStream.on('data', (chunk) => {
|
|
472
|
-
if (isResponseClosed) return;
|
|
473
|
-
|
|
474
|
-
buffer += chunk.toString('utf8');
|
|
475
|
-
|
|
476
|
-
if (!isSseResponse) {
|
|
477
|
-
return;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
const events = buffer.split('\n\n');
|
|
481
|
-
buffer = events.pop() || '';
|
|
482
|
-
|
|
483
|
-
events.forEach(eventText => {
|
|
484
|
-
if (!eventText.trim()) return;
|
|
485
|
-
|
|
486
|
-
try {
|
|
487
|
-
const lines = eventText.split('\n');
|
|
488
|
-
let eventType = '';
|
|
489
|
-
let data = '';
|
|
490
|
-
|
|
491
|
-
lines.forEach(line => {
|
|
492
|
-
if (line.startsWith('event:')) {
|
|
493
|
-
eventType = line.substring(6).trim();
|
|
494
|
-
} else if (line.startsWith('data:')) {
|
|
495
|
-
data = line.substring(5).trim();
|
|
496
|
-
}
|
|
497
|
-
});
|
|
498
|
-
|
|
499
|
-
if (!data || data === '[DONE]') return;
|
|
500
|
-
|
|
501
|
-
const parsed = JSON.parse(data);
|
|
502
|
-
mergeClaudeStreamEvent(streamRecoveryState, eventType, parsed);
|
|
503
|
-
const usage = parseSSEUsage(parsed, eventType);
|
|
504
|
-
if (usage.tokens) {
|
|
505
|
-
usageObservation.sawAnyUsage = true;
|
|
506
|
-
if (eventType === 'message_start') {
|
|
507
|
-
usageObservation.sawMessageStartUsage = true;
|
|
508
|
-
} else {
|
|
509
|
-
usageObservation.sawNonInitialUsage = true;
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
mergeUsageIntoTokenData(tokenData, usage);
|
|
513
|
-
|
|
514
|
-
if (usage.isDone && (!isSseResponse || usageObservation.sawNonInitialUsage)) {
|
|
515
|
-
recordUsageIfReady();
|
|
516
|
-
}
|
|
517
|
-
} catch (err) {
|
|
518
|
-
}
|
|
519
|
-
});
|
|
520
|
-
});
|
|
521
|
-
|
|
522
|
-
const finalize = () => {
|
|
523
|
-
if (!isResponseClosed) {
|
|
524
|
-
requestMetadata.delete(req);
|
|
525
|
-
}
|
|
526
|
-
if (typeof req.__releaseChannel === 'function') {
|
|
527
|
-
req.__releaseChannel();
|
|
359
|
+
const statusCode = Number(proxyRes.statusCode) || 200;
|
|
360
|
+
proxyRes.on('end', () => {
|
|
361
|
+
if (statusCode >= 400) {
|
|
362
|
+
recordFailure(metadata.channelId, 'claude', new Error(`Claude upstream HTTP ${statusCode}`));
|
|
363
|
+
} else {
|
|
364
|
+
recordSuccess(metadata.channelId, 'claude');
|
|
528
365
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
parsedStream.on('end', () => {
|
|
532
|
-
void (async () => {
|
|
533
|
-
if (!isSseResponse) {
|
|
534
|
-
try {
|
|
535
|
-
const parsed = JSON.parse(buffer);
|
|
536
|
-
const usage = parseNonStreamingUsage(parsed);
|
|
537
|
-
mergeUsageIntoTokenData(tokenData, usage);
|
|
538
|
-
} catch (err) {
|
|
539
|
-
}
|
|
540
|
-
} else {
|
|
541
|
-
const assistantMessage = buildAssistantMessageFromStreamState(streamRecoveryState);
|
|
542
|
-
const shouldRecoverUsage = Boolean(assistantMessage)
|
|
543
|
-
&& (!usageObservation.sawAnyUsage || (usageObservation.sawMessageStartUsage && !usageObservation.sawNonInitialUsage));
|
|
544
|
-
|
|
545
|
-
if (shouldRecoverUsage) {
|
|
546
|
-
try {
|
|
547
|
-
const recoveredUsage = await recoverClaudeUsageViaCountTokens({
|
|
548
|
-
baseUrl: req.selectedChannel?.baseUrl || '',
|
|
549
|
-
apiKey: req.effectiveApiKey || '',
|
|
550
|
-
requestBody: req.body,
|
|
551
|
-
assistantMessage
|
|
552
|
-
});
|
|
553
|
-
if (recoveredUsage) {
|
|
554
|
-
tokenData.model = tokenData.model || streamRecoveryState.model || '';
|
|
555
|
-
tokenData.inputTokens = Number(recoveredUsage.inputTokens || 0);
|
|
556
|
-
tokenData.outputTokens = Number(recoveredUsage.outputTokens || 0);
|
|
557
|
-
// Let publishUsageLog recompute Claude totals from the recovered
|
|
558
|
-
// prompt/output pair plus any cache fields already present.
|
|
559
|
-
tokenData.totalTokens = 0;
|
|
560
|
-
} else {
|
|
561
|
-
tokenData.model = tokenData.model || streamRecoveryState.model || '';
|
|
562
|
-
clearTokenDataUsage(tokenData);
|
|
563
|
-
}
|
|
564
|
-
} catch (error) {
|
|
565
|
-
tokenData.model = tokenData.model || streamRecoveryState.model || '';
|
|
566
|
-
clearTokenDataUsage(tokenData);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
recordUsageIfReady();
|
|
572
|
-
finalize();
|
|
573
|
-
})();
|
|
366
|
+
requestMetadata.delete(req);
|
|
574
367
|
});
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
}
|
|
580
|
-
// 记录响应错误
|
|
581
|
-
if (metadata && metadata.channelId) {
|
|
582
|
-
recordFailure(metadata.channelId, 'claude', err);
|
|
583
|
-
}
|
|
584
|
-
publishFailureLog({
|
|
585
|
-
source: 'claude',
|
|
586
|
-
metadata,
|
|
587
|
-
message: err.message,
|
|
588
|
-
error: err,
|
|
589
|
-
statusCode: proxyRes.statusCode,
|
|
590
|
-
stage: 'response_stream',
|
|
591
|
-
broadcastLog
|
|
592
|
-
});
|
|
593
|
-
isResponseClosed = true;
|
|
594
|
-
finalize();
|
|
368
|
+
proxyRes.on('error', (err) => {
|
|
369
|
+
if (err.code !== 'EPIPE' && err.code !== 'ECONNRESET') console.error('Proxy response error:', err);
|
|
370
|
+
recordFailure(metadata.channelId, 'claude', err);
|
|
371
|
+
requestMetadata.delete(req);
|
|
595
372
|
});
|
|
596
373
|
});
|
|
597
374
|
|
|
@@ -601,20 +378,6 @@ async function startProxyServer(options = {}) {
|
|
|
601
378
|
if (req && req.selectedChannel && req.selectedChannel.id) {
|
|
602
379
|
recordFailure(req.selectedChannel.id, 'claude', err);
|
|
603
380
|
}
|
|
604
|
-
const metadata = req ? requestMetadata.get(req) : null;
|
|
605
|
-
publishFailureLog({
|
|
606
|
-
source: 'claude',
|
|
607
|
-
metadata: metadata || {
|
|
608
|
-
channel: req?.selectedChannel?.name,
|
|
609
|
-
channelId: req?.selectedChannel?.id,
|
|
610
|
-
model: req?.body?.model
|
|
611
|
-
},
|
|
612
|
-
message: err.message,
|
|
613
|
-
error: err,
|
|
614
|
-
statusCode: 502,
|
|
615
|
-
stage: 'proxy',
|
|
616
|
-
broadcastLog
|
|
617
|
-
});
|
|
618
381
|
if (res && !res.headersSent) {
|
|
619
382
|
res.status(502).json({
|
|
620
383
|
error: 'Proxy error: ' + err.message,
|