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
|
@@ -6,18 +6,19 @@
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const https = require('https');
|
|
9
|
+
const { getPlatformCatalog } = require('./platform-catalog');
|
|
9
10
|
const http = require('http');
|
|
10
11
|
const { URL } = require('url');
|
|
11
12
|
const crypto = require('crypto');
|
|
12
13
|
const zlib = require('zlib');
|
|
13
14
|
const { loadConfig } = require('../../config/loader');
|
|
14
|
-
const { createCodexRequest, buildCodexTargetUrl } = require('
|
|
15
|
-
const { createClaudeRequest, buildClaudeTargetUrl } = require('
|
|
15
|
+
const { createCodexRequest, buildCodexTargetUrl } = require('../../platforms/drivers/codex/wire');
|
|
16
|
+
const { createClaudeRequest, buildClaudeTargetUrl } = require('../../platforms/drivers/claude/wire');
|
|
16
17
|
const {
|
|
17
18
|
createGeminiRequest,
|
|
18
19
|
buildGeminiTargetUrl,
|
|
19
20
|
shouldUseGeminiCliFormat
|
|
20
|
-
} = require('
|
|
21
|
+
} = require('../../platforms/drivers/gemini/wire');
|
|
21
22
|
const { PATHS } = require('../../config/paths');
|
|
22
23
|
|
|
23
24
|
// 内置模型优先级(当配置缺失时兜底)
|
|
@@ -60,11 +61,14 @@ MODEL_PRIORITY.openai_compatible = MODEL_PRIORITY.codex;
|
|
|
60
61
|
|
|
61
62
|
function normalizeModelToolType(type) {
|
|
62
63
|
const value = String(type || '').trim().toLowerCase();
|
|
64
|
+
if (!value) return '';
|
|
63
65
|
if (value === 'openai_compatible') return 'codex';
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getModelCatalogKey(toolType) {
|
|
70
|
+
const manifest = getPlatformCatalog().get(toolType);
|
|
71
|
+
return manifest?.modelConfig?.catalogKey || toolType;
|
|
68
72
|
}
|
|
69
73
|
|
|
70
74
|
/**
|
|
@@ -88,12 +92,12 @@ function getModelPriority(channelType, options = {}) {
|
|
|
88
92
|
if (String(channelType || '').trim().toLowerCase() === 'openai_compatible' && !candidateTypes.includes('openai_compatible')) {
|
|
89
93
|
candidateTypes.push('openai_compatible');
|
|
90
94
|
}
|
|
91
|
-
|
|
92
95
|
try {
|
|
93
96
|
const config = loadConfig();
|
|
94
97
|
const defaultModels = config?.defaultModels || {};
|
|
95
98
|
for (const toolType of candidateTypes) {
|
|
96
|
-
const
|
|
99
|
+
const catalogKey = getModelCatalogKey(toolType);
|
|
100
|
+
const models = defaultModels[catalogKey] || defaultModels[toolType];
|
|
97
101
|
if (Array.isArray(models) && models.length > 0) {
|
|
98
102
|
return normalizeModelCandidates(models);
|
|
99
103
|
}
|
|
@@ -103,7 +107,8 @@ function getModelPriority(channelType, options = {}) {
|
|
|
103
107
|
}
|
|
104
108
|
|
|
105
109
|
for (const toolType of candidateTypes) {
|
|
106
|
-
const
|
|
110
|
+
const catalogKey = getModelCatalogKey(toolType);
|
|
111
|
+
const models = MODEL_PRIORITY[catalogKey] || MODEL_PRIORITY[toolType];
|
|
107
112
|
if (Array.isArray(models) && models.length > 0) {
|
|
108
113
|
return normalizeModelCandidates(models);
|
|
109
114
|
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const CHANNEL_PLACEHOLDERS = new Set(['', 'unknown', 'unkonwn', 'n/a', 'na', 'none', '-']);
|
|
4
|
+
|
|
5
|
+
function text(value) {
|
|
6
|
+
return typeof value === 'string' ? value.trim() : String(value || '').trim();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function normalizeIdentity(value) {
|
|
10
|
+
return text(value).toLowerCase();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isChannelPlaceholder(value) {
|
|
14
|
+
return CHANNEL_PLACEHOLDERS.has(normalizeIdentity(value));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function meaningfulText(value) {
|
|
18
|
+
const result = text(value);
|
|
19
|
+
return isChannelPlaceholder(result) ? '' : result;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function identityAliases(value) {
|
|
23
|
+
const normalized = normalizeIdentity(value);
|
|
24
|
+
if (!normalized) return [];
|
|
25
|
+
const aliases = new Set([normalized]);
|
|
26
|
+
if (normalized.startsWith('ctx-')) aliases.add(normalized.slice(4));
|
|
27
|
+
if (normalized.startsWith('provider:')) aliases.add(normalized.slice('provider:'.length));
|
|
28
|
+
return [...aliases].filter(Boolean);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function normalizeUrl(value) {
|
|
32
|
+
const raw = text(value);
|
|
33
|
+
if (!raw) return '';
|
|
34
|
+
try {
|
|
35
|
+
const url = new URL(raw);
|
|
36
|
+
url.hash = '';
|
|
37
|
+
url.search = '';
|
|
38
|
+
url.pathname = url.pathname.replace(/\/+$/, '');
|
|
39
|
+
return url.toString().toLowerCase().replace(/\/$/, '');
|
|
40
|
+
} catch (_) {
|
|
41
|
+
return raw.toLowerCase().replace(/\/+$/, '');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function unwrapChannels(value) {
|
|
46
|
+
if (value && typeof value === 'object' && typeof value.status === 'string' && value.status !== 'ok') {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
const data = value && typeof value === 'object' && Object.prototype.hasOwnProperty.call(value, 'data')
|
|
50
|
+
? value.data
|
|
51
|
+
: value;
|
|
52
|
+
const channels = Array.isArray(data) ? data : data?.channels;
|
|
53
|
+
return Array.isArray(channels) ? channels.filter(channel => channel && typeof channel === 'object') : [];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function nestedValues(source, keys) {
|
|
57
|
+
if (!source || typeof source !== 'object') return [];
|
|
58
|
+
const values = [];
|
|
59
|
+
for (const key of keys) {
|
|
60
|
+
if (source[key] != null) values.push(source[key]);
|
|
61
|
+
}
|
|
62
|
+
return values;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function channelSources(channel) {
|
|
66
|
+
return [channel, channel?.extra, channel?.providerConfig]
|
|
67
|
+
.filter(source => source && typeof source === 'object');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function channelIdentityValues(channel) {
|
|
71
|
+
const values = [];
|
|
72
|
+
for (const source of channelSources(channel)) {
|
|
73
|
+
values.push(...nestedValues(source, [
|
|
74
|
+
'id', 'name', 'providerKey', 'provider', 'providerId', 'providerID',
|
|
75
|
+
'managedProviderId', 'oauthProviderId', 'providerName'
|
|
76
|
+
]));
|
|
77
|
+
if (source.authRef && typeof source.authRef === 'object') {
|
|
78
|
+
values.push(...nestedValues(source.authRef, ['providerId', 'providerID', 'accountId', 'identityKey']));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return values.flatMap(identityAliases);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function eventIdentityValues(event) {
|
|
85
|
+
return nestedValues(event, [
|
|
86
|
+
'provider', 'providerKey', 'providerId', 'providerID', 'managedProviderId', 'oauthProviderId'
|
|
87
|
+
]).flatMap(identityAliases);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function channelUrlValues(channel) {
|
|
91
|
+
const values = [];
|
|
92
|
+
for (const source of channelSources(channel)) {
|
|
93
|
+
values.push(...nestedValues(source, ['baseUrl', 'baseURL', 'apiBaseUrl', 'apiBaseURL', 'endpoint', 'url']));
|
|
94
|
+
}
|
|
95
|
+
return values.map(normalizeUrl).filter(Boolean);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function eventUrlValues(event) {
|
|
99
|
+
return nestedValues(event, [
|
|
100
|
+
'baseUrl', 'baseURL', 'apiBaseUrl', 'apiBaseURL', 'endpoint', 'url', 'providerBaseUrl'
|
|
101
|
+
])
|
|
102
|
+
.concat(event?.providerConfig && typeof event.providerConfig === 'object'
|
|
103
|
+
? nestedValues(event.providerConfig, ['baseUrl', 'baseURL', 'apiBaseUrl', 'apiBaseURL', 'endpoint', 'url'])
|
|
104
|
+
: [])
|
|
105
|
+
.map(normalizeUrl)
|
|
106
|
+
.filter(Boolean);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function modelAliases(value) {
|
|
110
|
+
const normalized = normalizeIdentity(value);
|
|
111
|
+
if (!normalized) return [];
|
|
112
|
+
const aliases = new Set([normalized]);
|
|
113
|
+
const slashIndex = normalized.lastIndexOf('/');
|
|
114
|
+
if (slashIndex >= 0) aliases.add(normalized.slice(slashIndex + 1));
|
|
115
|
+
return [...aliases].filter(Boolean);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function modelValues(value) {
|
|
119
|
+
if (Array.isArray(value)) return value.flatMap(modelValues);
|
|
120
|
+
if (value && typeof value === 'object') {
|
|
121
|
+
return nestedValues(value, ['id', 'name', 'model', 'modelId', 'modelID']).flatMap(modelValues);
|
|
122
|
+
}
|
|
123
|
+
return modelAliases(value);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function channelModelValues(channel) {
|
|
127
|
+
const values = [];
|
|
128
|
+
for (const source of channelSources(channel)) {
|
|
129
|
+
values.push(...nestedValues(source, [
|
|
130
|
+
'model', 'models', 'allowedModels', 'modelConfig', 'modelRedirects', 'modelBindings'
|
|
131
|
+
]).flatMap(modelValues));
|
|
132
|
+
}
|
|
133
|
+
return values;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function eventModelValues(event) {
|
|
137
|
+
return modelAliases(event?.model || event?.modelId || event?.modelID);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function matchesBy(channels, values, getValues) {
|
|
141
|
+
const wanted = new Set(values);
|
|
142
|
+
if (!wanted.size) return [];
|
|
143
|
+
return channels.filter(channel => getValues(channel).some(value => wanted.has(value)));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function matchedChannel(channel) {
|
|
147
|
+
const channelId = text(channel?.id);
|
|
148
|
+
const channelName = text(channel?.name) || text(channel?.providerKey) || channelId;
|
|
149
|
+
return {
|
|
150
|
+
...(channelId ? { channelId } : {}),
|
|
151
|
+
...(channelName ? { channel: channelName } : {})
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function fallbackChannel(event) {
|
|
156
|
+
const channel = meaningfulText(event?.channel) || meaningfulText(event?.provider)
|
|
157
|
+
|| meaningfulText(event?.providerKey) || meaningfulText(event?.providerId)
|
|
158
|
+
|| meaningfulText(event?.channelId);
|
|
159
|
+
return channel ? { channel } : {};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Resolve the configured channel responsible for a native CLI usage event.
|
|
164
|
+
* Native log formats do not agree on a channel field, so provider/model metadata
|
|
165
|
+
* is matched against the channel registry at the observation boundary.
|
|
166
|
+
*/
|
|
167
|
+
function resolveNativeLogChannel(event = {}, channels = []) {
|
|
168
|
+
const allChannels = unwrapChannels(channels);
|
|
169
|
+
const enabledChannels = allChannels.filter(channel => channel.enabled !== false);
|
|
170
|
+
|
|
171
|
+
const explicitId = normalizeIdentity(event.channelId);
|
|
172
|
+
if (explicitId) {
|
|
173
|
+
const match = allChannels.find(channel => normalizeIdentity(channel.id) === explicitId);
|
|
174
|
+
if (match) return matchedChannel(match);
|
|
175
|
+
return { channelId: text(event.channelId), channel: text(event.channel) || text(event.channelId) };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const explicitChannel = isChannelPlaceholder(event.channel) ? '' : normalizeIdentity(event.channel);
|
|
179
|
+
if (explicitChannel) {
|
|
180
|
+
const matches = matchesBy(allChannels, identityAliases(event.channel), channelIdentityValues);
|
|
181
|
+
if (matches.length === 1) return matchedChannel(matches[0]);
|
|
182
|
+
return { channel: meaningfulText(event.channel) };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const providerMatches = matchesBy(allChannels, eventIdentityValues(event), channelIdentityValues);
|
|
186
|
+
if (providerMatches.length === 1) return matchedChannel(providerMatches[0]);
|
|
187
|
+
|
|
188
|
+
const eventUrls = eventUrlValues(event);
|
|
189
|
+
const eventModels = eventModelValues(event);
|
|
190
|
+
if (providerMatches.length > 1 && eventModels.length) {
|
|
191
|
+
const providerModelMatches = matchesBy(providerMatches, eventModels, channelModelValues);
|
|
192
|
+
if (providerModelMatches.length === 1) return matchedChannel(providerModelMatches[0]);
|
|
193
|
+
}
|
|
194
|
+
if (providerMatches.length > 1 && eventUrls.length) {
|
|
195
|
+
const providerUrlMatches = matchesBy(providerMatches, eventUrls, channelUrlValues);
|
|
196
|
+
if (providerUrlMatches.length === 1) return matchedChannel(providerUrlMatches[0]);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (eventUrls.length) {
|
|
200
|
+
const urlMatches = matchesBy(allChannels, eventUrls, channelUrlValues);
|
|
201
|
+
if (urlMatches.length === 1) return matchedChannel(urlMatches[0]);
|
|
202
|
+
}
|
|
203
|
+
if (eventModels.length) {
|
|
204
|
+
const modelMatches = matchesBy(allChannels, eventModels, channelModelValues);
|
|
205
|
+
if (modelMatches.length === 1) return matchedChannel(modelMatches[0]);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// A provider-less native format is still resolvable when only one channel is
|
|
209
|
+
// currently enabled. Do not guess when several channels could have produced it.
|
|
210
|
+
if (enabledChannels.length === 1) return matchedChannel(enabledChannels[0]);
|
|
211
|
+
return fallbackChannel(event);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
module.exports = { resolveNativeLogChannel, unwrapChannels, isChannelPlaceholder };
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const eventBus = require('../../plugins/event-bus');
|
|
4
|
+
const { getPlatformRegistry, getPlatformRuntime } = require('../../platforms/runtime');
|
|
5
|
+
const { buildSuccessLogPayload, hasMeaningfulUsage, normalizeUsageTokens } = require('./usage-log-utils');
|
|
6
|
+
const { resolveNativeLogChannel, unwrapChannels, isChannelPlaceholder } = require('./native-log-channel-resolver');
|
|
7
|
+
const { broadcastLog } = require('../websocket-server');
|
|
8
|
+
|
|
9
|
+
const DEFAULT_INTERVAL_MS = 5000;
|
|
10
|
+
let pollTimer = null;
|
|
11
|
+
let enabled = false;
|
|
12
|
+
let intervalMs = DEFAULT_INTERVAL_MS;
|
|
13
|
+
let cursors = new Map();
|
|
14
|
+
let cursorDiagnostics = new Map();
|
|
15
|
+
let firstPollObserved = false;
|
|
16
|
+
let configSavedListener = null;
|
|
17
|
+
let lifecycleState = 'stopped';
|
|
18
|
+
|
|
19
|
+
function memoryTraceEnabled() {
|
|
20
|
+
return process.env.CC_TOOL_MEMORY_TRACE === '1';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function traceMemory(platform, stage, details = {}, durationMs = null) {
|
|
24
|
+
if (!memoryTraceEnabled()) return;
|
|
25
|
+
const usage = process.memoryUsage();
|
|
26
|
+
const payload = {
|
|
27
|
+
pid: process.pid,
|
|
28
|
+
platform,
|
|
29
|
+
stage,
|
|
30
|
+
...(durationMs === null ? {} : { durationMs: Number(durationMs.toFixed(3)) }),
|
|
31
|
+
files: Number(details.files) || 0,
|
|
32
|
+
bytesRead: Number(details.bytesRead) || 0,
|
|
33
|
+
parsedRecords: Number(details.parsedRecords) || 0,
|
|
34
|
+
maxLineLength: Number(details.maxLineLength) || 0,
|
|
35
|
+
rss: usage.rss,
|
|
36
|
+
heapTotal: usage.heapTotal,
|
|
37
|
+
heapUsed: usage.heapUsed,
|
|
38
|
+
external: usage.external,
|
|
39
|
+
arrayBuffers: usage.arrayBuffers
|
|
40
|
+
};
|
|
41
|
+
console.log(`[MEM] native-log ${JSON.stringify(payload)}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function traceClock() {
|
|
45
|
+
return Number(process.hrtime.bigint()) / 1e6;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function normalizeInterval(value) {
|
|
49
|
+
const seconds = Number(value);
|
|
50
|
+
return Number.isFinite(seconds) && seconds >= 1 && seconds <= 60
|
|
51
|
+
? seconds * 1000
|
|
52
|
+
: DEFAULT_INTERVAL_MS;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function getStatus() {
|
|
56
|
+
return {
|
|
57
|
+
running: Boolean(pollTimer),
|
|
58
|
+
state: lifecycleState,
|
|
59
|
+
enabled,
|
|
60
|
+
intervalMs,
|
|
61
|
+
platforms: [...cursors.keys()]
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function closeCursor(cursor) {
|
|
66
|
+
try { cursor?.close?.(); } catch (_) {}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function resolveCursors(runtime = getPlatformRuntime(), registry = getPlatformRegistry()) {
|
|
70
|
+
const next = new Map();
|
|
71
|
+
const traceEnabled = memoryTraceEnabled();
|
|
72
|
+
const platforms = registry.list({ enabledOnly: true });
|
|
73
|
+
for (const platform of platforms) {
|
|
74
|
+
const key = platform.key;
|
|
75
|
+
let driver;
|
|
76
|
+
try { driver = runtime.getDriver(key, 'nativeLogs'); } catch (_) { driver = null; }
|
|
77
|
+
if (!driver || typeof driver.createNativeLogCursor !== 'function') continue;
|
|
78
|
+
|
|
79
|
+
const existing = cursors.get(key);
|
|
80
|
+
let cursor = existing;
|
|
81
|
+
if (!cursor) {
|
|
82
|
+
const options = { skipInitialParse: true };
|
|
83
|
+
if (traceEnabled) {
|
|
84
|
+
options.onDiagnostic = details => cursorDiagnostics.set(key, details || {});
|
|
85
|
+
}
|
|
86
|
+
cursor = driver.createNativeLogCursor(options);
|
|
87
|
+
const startedAt = traceClock();
|
|
88
|
+
traceMemory(key, 'cursor-initialize-before', {}, 0);
|
|
89
|
+
try { cursor.initialize?.(); } catch (error) {
|
|
90
|
+
console.warn(`[Native Logs] Failed to initialize ${key}:`, error.message);
|
|
91
|
+
}
|
|
92
|
+
traceMemory(key, 'cursor-initialize-after', cursorDiagnostics.get(key), traceClock() - startedAt);
|
|
93
|
+
}
|
|
94
|
+
next.set(key, cursor);
|
|
95
|
+
}
|
|
96
|
+
for (const [key, cursor] of cursors) {
|
|
97
|
+
if (!next.has(key)) {
|
|
98
|
+
closeCursor(cursor);
|
|
99
|
+
cursorDiagnostics.delete(key);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
cursors = next;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function readConfiguredChannels(platform, runtime) {
|
|
106
|
+
let driver;
|
|
107
|
+
try { driver = runtime.getDriver(platform, 'channels'); } catch (_) { driver = null; }
|
|
108
|
+
if (!driver) return [];
|
|
109
|
+
|
|
110
|
+
// Read the full list first: a historical event may belong to a channel that
|
|
111
|
+
// was disabled after the CLI emitted the record.
|
|
112
|
+
for (const method of ['list', 'getChannels', 'getEnabled']) {
|
|
113
|
+
if (typeof driver[method] !== 'function') continue;
|
|
114
|
+
try {
|
|
115
|
+
const result = driver[method]();
|
|
116
|
+
if (result && typeof result.then === 'function') continue;
|
|
117
|
+
const channels = unwrapChannels(result);
|
|
118
|
+
if (channels.length) return channels;
|
|
119
|
+
} catch (_) {}
|
|
120
|
+
}
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function recordEvent(platform, event, runtime = getPlatformRuntime(), channelCache = new Map()) {
|
|
125
|
+
const timestampValue = event.timestamp ? new Date(event.timestamp).getTime() : Date.now();
|
|
126
|
+
const timestamp = Number.isFinite(timestampValue) ? timestampValue : Date.now();
|
|
127
|
+
const tokens = normalizeUsageTokens(platform, event.tokens || event.usage || {});
|
|
128
|
+
if (!hasMeaningfulUsage(platform, tokens)) return false;
|
|
129
|
+
if (!channelCache.has(platform)) channelCache.set(platform, readConfiguredChannels(platform, runtime));
|
|
130
|
+
const resolvedChannel = resolveNativeLogChannel(event, channelCache.get(platform));
|
|
131
|
+
const channel = resolvedChannel.channel
|
|
132
|
+
|| (isChannelPlaceholder(event.channel) ? '' : event.channel)
|
|
133
|
+
|| (isChannelPlaceholder(event.provider) ? '' : event.provider)
|
|
134
|
+
|| 'Unknown';
|
|
135
|
+
const channelId = resolvedChannel.channelId || event.channelId;
|
|
136
|
+
|
|
137
|
+
broadcastLog(buildSuccessLogPayload({
|
|
138
|
+
source: platform,
|
|
139
|
+
requestId: event.id,
|
|
140
|
+
channel,
|
|
141
|
+
model: event.model || '',
|
|
142
|
+
tokens,
|
|
143
|
+
cost: Number(event.cost) || 0,
|
|
144
|
+
timestamp,
|
|
145
|
+
usageMissing: false
|
|
146
|
+
}));
|
|
147
|
+
|
|
148
|
+
let statistics;
|
|
149
|
+
try { statistics = runtime.getDriver(platform, 'statistics'); } catch (_) { statistics = null; }
|
|
150
|
+
if (statistics && typeof statistics.recordRequest === 'function') {
|
|
151
|
+
statistics.recordRequest({
|
|
152
|
+
id: event.id,
|
|
153
|
+
timestamp: new Date(timestamp).toISOString(),
|
|
154
|
+
session: event.sessionId || null,
|
|
155
|
+
model: event.model || '',
|
|
156
|
+
channel,
|
|
157
|
+
channelId,
|
|
158
|
+
tokens,
|
|
159
|
+
cost: Number(event.cost) || 0,
|
|
160
|
+
duration: 0,
|
|
161
|
+
success: true
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function pollNativeCliLogs({ runtime = getPlatformRuntime(), registry = getPlatformRegistry() } = {}) {
|
|
168
|
+
if (!enabled) return getStatus();
|
|
169
|
+
resolveCursors(runtime, registry);
|
|
170
|
+
const channelCache = new Map();
|
|
171
|
+
const traceFirstPoll = memoryTraceEnabled() && !firstPollObserved;
|
|
172
|
+
for (const [platform, cursor] of cursors) {
|
|
173
|
+
const startedAt = traceFirstPoll ? traceClock() : 0;
|
|
174
|
+
if (traceFirstPoll) traceMemory(platform, 'first-poll-before', cursorDiagnostics.get(platform), 0);
|
|
175
|
+
try {
|
|
176
|
+
const events = cursor.readNewEvents?.() || cursor.read?.() || [];
|
|
177
|
+
events.forEach(event => recordEvent(platform, event, runtime, channelCache));
|
|
178
|
+
} catch (error) {
|
|
179
|
+
console.warn(`[Native Logs] Failed to read ${platform}:`, error.message);
|
|
180
|
+
} finally {
|
|
181
|
+
if (traceFirstPoll) traceMemory(platform, 'first-poll-after', cursorDiagnostics.get(platform), traceClock() - startedAt);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (traceFirstPoll) firstPollObserved = true;
|
|
185
|
+
return getStatus();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function clearTimer() {
|
|
189
|
+
if (pollTimer) clearInterval(pollTimer);
|
|
190
|
+
pollTimer = null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function startTimer(options) {
|
|
194
|
+
clearTimer();
|
|
195
|
+
pollTimer = setInterval(() => {
|
|
196
|
+
try { pollNativeCliLogs(options); } catch (error) {
|
|
197
|
+
console.warn('[Native Logs] Poll failed:', error.message);
|
|
198
|
+
}
|
|
199
|
+
}, intervalMs);
|
|
200
|
+
if (typeof pollTimer.unref === 'function') pollTimer.unref();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function ensureConfigListener(options) {
|
|
204
|
+
if (configSavedListener) return;
|
|
205
|
+
configSavedListener = ({ config } = {}) => {
|
|
206
|
+
const next = config?.nativeCliLogs || {};
|
|
207
|
+
const nextOptions = { ...options, enabled: next.enabled, intervalSeconds: next.intervalSeconds };
|
|
208
|
+
if (lifecycleState === 'running') {
|
|
209
|
+
configureNativeCliLogObserver(nextOptions);
|
|
210
|
+
} else {
|
|
211
|
+
prepareNativeCliLogObserver(nextOptions);
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
eventBus.on('config:saved', configSavedListener);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function prepareNativeCliLogObserver({
|
|
218
|
+
enabled: nextEnabled = true,
|
|
219
|
+
intervalSeconds = 5,
|
|
220
|
+
runtime = getPlatformRuntime(),
|
|
221
|
+
registry = getPlatformRegistry()
|
|
222
|
+
} = {}) {
|
|
223
|
+
ensureConfigListener({ runtime, registry });
|
|
224
|
+
enabled = nextEnabled !== false;
|
|
225
|
+
intervalMs = normalizeInterval(intervalSeconds);
|
|
226
|
+
if (!enabled) {
|
|
227
|
+
clearTimer();
|
|
228
|
+
for (const cursor of cursors.values()) closeCursor(cursor);
|
|
229
|
+
cursors = new Map();
|
|
230
|
+
cursorDiagnostics = new Map();
|
|
231
|
+
firstPollObserved = false;
|
|
232
|
+
lifecycleState = 'stopped';
|
|
233
|
+
return getStatus();
|
|
234
|
+
}
|
|
235
|
+
resolveCursors(runtime, registry);
|
|
236
|
+
if (pollTimer) clearTimer();
|
|
237
|
+
lifecycleState = 'prepared';
|
|
238
|
+
return getStatus();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function startNativeCliLogObserver({
|
|
242
|
+
runtime = getPlatformRuntime(),
|
|
243
|
+
registry = getPlatformRegistry(),
|
|
244
|
+
pollImmediately = true
|
|
245
|
+
} = {}) {
|
|
246
|
+
if (!enabled) return getStatus();
|
|
247
|
+
resolveCursors(runtime, registry);
|
|
248
|
+
if (pollImmediately) {
|
|
249
|
+
try { pollNativeCliLogs({ runtime, registry }); } catch (error) {
|
|
250
|
+
console.warn('[Native Logs] Initial poll failed:', error.message);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
startTimer({ runtime, registry });
|
|
254
|
+
lifecycleState = 'running';
|
|
255
|
+
return getStatus();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function configureNativeCliLogObserver(options = {}) {
|
|
259
|
+
prepareNativeCliLogObserver(options);
|
|
260
|
+
return startNativeCliLogObserver({
|
|
261
|
+
...options,
|
|
262
|
+
// Preserve the legacy configure() behavior: it starts the interval but
|
|
263
|
+
// does not synchronously consume a new batch of native logs.
|
|
264
|
+
pollImmediately: options.pollImmediately === true
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function shutdownNativeCliLogObserver() {
|
|
269
|
+
clearTimer();
|
|
270
|
+
for (const cursor of cursors.values()) closeCursor(cursor);
|
|
271
|
+
cursors = new Map();
|
|
272
|
+
cursorDiagnostics = new Map();
|
|
273
|
+
firstPollObserved = false;
|
|
274
|
+
enabled = false;
|
|
275
|
+
intervalMs = DEFAULT_INTERVAL_MS;
|
|
276
|
+
lifecycleState = 'stopped';
|
|
277
|
+
if (configSavedListener) {
|
|
278
|
+
eventBus.off('config:saved', configSavedListener);
|
|
279
|
+
configSavedListener = null;
|
|
280
|
+
}
|
|
281
|
+
return getStatus();
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
module.exports = {
|
|
285
|
+
prepareNativeCliLogObserver,
|
|
286
|
+
startNativeCliLogObserver,
|
|
287
|
+
configureNativeCliLogObserver,
|
|
288
|
+
shutdownNativeCliLogObserver,
|
|
289
|
+
pollNativeCliLogs,
|
|
290
|
+
_test: { getStatus, recordEvent, resolveCursors, readConfiguredChannels }
|
|
291
|
+
};
|
|
@@ -44,7 +44,17 @@ function isSameOriginRequest(req) {
|
|
|
44
44
|
|
|
45
45
|
try {
|
|
46
46
|
const originUrl = new URL(origin);
|
|
47
|
-
|
|
47
|
+
if (originUrl.host === host) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// The Vite dev UI runs on a separate local port and proxies API calls to
|
|
52
|
+
// the backend. Treat that as local-only traffic, but do not extend this
|
|
53
|
+
// exception to requests arriving from a LAN or public address.
|
|
54
|
+
const originHost = String(originUrl.hostname || '').replace(/^\[|\]$/g, '');
|
|
55
|
+
return (originUrl.protocol === 'http:' || originUrl.protocol === 'https:')
|
|
56
|
+
&& isLoopbackRequest(req)
|
|
57
|
+
&& isLoopbackAddress(originHost);
|
|
48
58
|
} catch (error) {
|
|
49
59
|
return false;
|
|
50
60
|
}
|
|
@@ -73,7 +83,7 @@ function createRemoteMutationGuard(options = {}) {
|
|
|
73
83
|
}
|
|
74
84
|
|
|
75
85
|
function isRemoteMutationAllowedByEnv(env = process.env) {
|
|
76
|
-
return String(env.CC_TOOL_ALLOW_REMOTE_WRITE || '').toLowerCase()
|
|
86
|
+
return String(env.CC_TOOL_ALLOW_REMOTE_WRITE || '').toLowerCase() === 'true';
|
|
77
87
|
}
|
|
78
88
|
|
|
79
89
|
function createRemoteRouteGuard(options = {}) {
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { normalizePlatformKey } = require('../../shared/platforms');
|
|
4
|
+
const { getPlatformContext } = require('../platform-context');
|
|
5
|
+
|
|
6
|
+
const WEB_UI_PORT = 19999;
|
|
7
|
+
|
|
8
|
+
function isSupportedCapability(manifest, capability) {
|
|
9
|
+
if (!capability) return true;
|
|
10
|
+
const driver = manifest?.capabilities?.[capability];
|
|
11
|
+
return driver !== undefined && driver !== null && driver !== 'unsupported';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function supportsResourceType(manifest, resourceType) {
|
|
15
|
+
if (!resourceType) return true;
|
|
16
|
+
if (manifest?.resourceTypes?.[resourceType] === false) return false;
|
|
17
|
+
return isSupportedCapability(manifest, 'resourceSync');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function createPlatformCatalog({ registry, runtime } = {}) {
|
|
21
|
+
if (!registry || typeof registry.list !== 'function') {
|
|
22
|
+
throw new TypeError('Platform catalog requires a registry');
|
|
23
|
+
}
|
|
24
|
+
if (!runtime || typeof runtime.getDriver !== 'function') {
|
|
25
|
+
throw new TypeError('Platform catalog requires a runtime');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function list({ capability, resourceType, enabledOnly = true } = {}) {
|
|
29
|
+
return registry.list({ enabledOnly })
|
|
30
|
+
.filter(manifest => manifest && manifest.key)
|
|
31
|
+
.filter(manifest => isSupportedCapability(manifest, capability))
|
|
32
|
+
.filter(manifest => supportsResourceType(manifest, resourceType));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function get(key) {
|
|
36
|
+
const normalizedKey = normalizePlatformKey(key);
|
|
37
|
+
return normalizedKey ? list({ enabledOnly: false }).find(manifest => (
|
|
38
|
+
normalizePlatformKey(manifest.key) === normalizedKey
|
|
39
|
+
)) || null : null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function driver(key, capability) {
|
|
43
|
+
const manifest = get(key);
|
|
44
|
+
if (!manifest || !isSupportedCapability(manifest, capability)) return null;
|
|
45
|
+
const driverId = manifest.capabilities?.[capability];
|
|
46
|
+
if (!driverId || driverId === 'unsupported') return null;
|
|
47
|
+
const driver = runtime.getDriver(manifest.key, capability);
|
|
48
|
+
return driver && driver.status === 'unsupported' ? null : driver;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function ports() {
|
|
52
|
+
const entries = [{ key: 'webUI', defaultPort: WEB_UI_PORT, label: 'Web UI', platform: null }];
|
|
53
|
+
for (const manifest of list({ enabledOnly: true })) {
|
|
54
|
+
if (!manifest.portKey || !Number.isFinite(manifest.defaultPort)) continue;
|
|
55
|
+
entries.push({
|
|
56
|
+
key: manifest.portKey,
|
|
57
|
+
defaultPort: manifest.defaultPort,
|
|
58
|
+
label: manifest.portLabel || manifest.label || manifest.key,
|
|
59
|
+
platform: manifest.key
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return entries;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return Object.freeze({
|
|
66
|
+
list,
|
|
67
|
+
keys(options = {}) {
|
|
68
|
+
return list(options).map(manifest => normalizePlatformKey(manifest.key));
|
|
69
|
+
},
|
|
70
|
+
get,
|
|
71
|
+
driver,
|
|
72
|
+
ports
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
function getPlatformCatalog() {
|
|
78
|
+
const context = getPlatformContext();
|
|
79
|
+
return createPlatformCatalog(context);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = { createPlatformCatalog, getPlatformCatalog, WEB_UI_PORT };
|