coding-tool-x 3.8.3 → 3.8.5
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 +18 -0
- package/README.md +86 -15
- package/dist/web/assets/AgentsDrawer-BG6P3DX-.js +1 -0
- package/dist/web/assets/AgentsDrawer-Pw2k-ZAa.css +1 -0
- package/dist/web/assets/Analytics--z_FmL5P.js +1 -0
- package/dist/web/assets/Analytics-CyHhR126.css +1 -0
- package/dist/web/assets/AssetPathField-CrJtRKHy.js +1 -0
- package/dist/web/assets/ChatHistoryDrawer-B2qBQs9L.css +1 -0
- package/dist/web/assets/ChatHistoryDrawer-BiuEryJj.js +1 -0
- package/dist/web/assets/CollapseItem-BAYEsL_Q.js +1 -0
- package/dist/web/assets/CommandsDrawer-BZJxlPvV.css +1 -0
- package/dist/web/assets/CommandsDrawer-YJ-U7BXN.js +1 -0
- package/dist/web/assets/ConfigExportDrawer-Ce_hAknW.js +1 -0
- package/dist/web/assets/ConfigExportDrawer-dwruRKWr.css +1 -0
- package/dist/web/assets/ConfigTemplates-Bvr1NN6K.js +1 -0
- package/dist/web/assets/ConfigTemplatesDrawer-BfDi8yO3.css +1 -0
- package/dist/web/assets/ConfigTemplatesDrawer-BjIeHfF-.js +1 -0
- package/dist/web/assets/ConfigTemplatesPanel-AV00tm_D.js +1 -0
- package/dist/web/assets/ConfigTemplatesPanel-CWyPtk67.css +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-Cz95o4CD.js +1 -0
- package/dist/web/assets/GatewayConvertDrawer-CuNlf7AC.css +1 -0
- package/dist/web/assets/GatewayConvertDrawer-DLlbMz27.js +1 -0
- package/dist/web/assets/Home-CSuFuJ9Y.css +1 -0
- package/dist/web/assets/Home-D7CCOFQC.js +1 -0
- package/dist/web/assets/MarkdownEditor-qfVgSre_.css +1 -0
- package/dist/web/assets/MarkdownEditor-rEZ-c72L.js +1 -0
- package/dist/web/assets/McpDrawer-C0dnsZhQ.css +1 -0
- package/dist/web/assets/McpDrawer-DfoCyxU2.js +1 -0
- package/dist/web/assets/McpFormDrawer-BRbDcawn.css +1 -0
- package/dist/web/assets/McpFormDrawer-SKu8HNUR.js +1 -0
- package/dist/web/assets/PluginManager-BbeK-3rV.js +1 -0
- package/dist/web/assets/PluginsDrawer-CWVJ7yjS.js +1 -0
- package/dist/web/assets/PluginsDrawer-DM3QjprE.css +1 -0
- package/dist/web/assets/PluginsPanel-Cm2N3StG.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-CsjnsNPv.js +1 -0
- package/dist/web/assets/PromptsDrawer-BIgiYCEQ.css +1 -0
- package/dist/web/assets/PromptsDrawer-EGd3djFa.js +1 -0
- package/dist/web/assets/RecentSessionsDrawer-CKFsOYDM.css +1 -0
- package/dist/web/assets/RecentSessionsDrawer-D1vtoLAs.js +1 -0
- package/dist/web/assets/SessionCard-BgLg49U3.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-TRiTklWk.js +1 -0
- package/dist/web/assets/SettingsDrawer-d5liXjHq.css +1 -0
- package/dist/web/assets/SettingsDrawer-rz2WejGY.js +1 -0
- package/dist/web/assets/SkillManager-BTjTUbHq.js +1 -0
- package/dist/web/assets/SkillManager-C0GCT2Kb.css +1 -0
- package/dist/web/assets/SkillsDrawer-BCNme-aJ.css +1 -0
- package/dist/web/assets/SkillsDrawer-pN0hbkK3.js +1 -0
- package/dist/web/assets/SkillsPanel-B3Nm5j32.css +1 -0
- package/dist/web/assets/SkillsPanel-gmBWEoiF.js +1 -0
- package/dist/web/assets/SpeedTestDrawer-Dc0WToJ_.css +1 -0
- package/dist/web/assets/SpeedTestDrawer-DcNzuVJk.js +1 -0
- package/dist/web/assets/Upload-v5rV0qTT.js +1 -0
- package/dist/web/assets/WorkspaceDrawer-CGnMu9p1.css +1 -0
- package/dist/web/assets/WorkspaceDrawer-DxmicM4U.js +1 -0
- package/dist/web/assets/WorkspaceManager-CJHCjARN.js +1 -0
- package/dist/web/assets/WorkspacePanel-D0-zHTHP.css +1 -0
- package/dist/web/assets/WorkspacePanel-D8_Zq-du.js +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-C0EHRym0.css +1 -0
- package/dist/web/assets/index-DnEocV0M.js +2 -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-B3uojyvV.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 +13 -8
- package/src/commands/channels.js +88 -191
- package/src/commands/cli-type.js +19 -11
- package/src/commands/daemon.js +94 -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 +162 -0
- package/src/platforms/channel-auth-service.js +82 -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 +87 -417
- package/src/{server/services → platforms}/notification-hooks.js +140 -1023
- package/src/platforms/oauth-credentials-service.js +506 -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 +11 -20
- 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 +1 -1
- 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 +780 -213
- 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
|
@@ -147,17 +147,27 @@ function runRefresh(refresh, defer, deferMs) {
|
|
|
147
147
|
|
|
148
148
|
function refreshSnapshot(key, refresh, {
|
|
149
149
|
defer = false,
|
|
150
|
-
deferMs = DEFAULT_BACKGROUND_DELAY_MS
|
|
151
|
-
bypassInflight = false
|
|
150
|
+
deferMs = DEFAULT_BACKGROUND_DELAY_MS
|
|
152
151
|
} = {}) {
|
|
153
|
-
if (
|
|
152
|
+
if (inflight.has(key)) {
|
|
154
153
|
return inflight.get(key);
|
|
155
154
|
}
|
|
156
155
|
|
|
157
|
-
const startedVersion =
|
|
156
|
+
const startedVersion = getInvalidationVersion(key);
|
|
158
157
|
const promise = runRefresh(refresh, defer, deferMs)
|
|
159
158
|
.then((value) => {
|
|
160
|
-
|
|
159
|
+
let nextValue = value;
|
|
160
|
+
const existing = getEntry(key);
|
|
161
|
+
if (value && Array.isArray(value.__errors) && existing?.value && typeof existing.value === 'object') {
|
|
162
|
+
nextValue = { ...existing.value, ...value };
|
|
163
|
+
for (const failure of value.__errors) {
|
|
164
|
+
const capability = failure?.capability === 'statistics' ? 'todayStats' : failure?.capability;
|
|
165
|
+
if (capability && Object.prototype.hasOwnProperty.call(existing.value, capability)) {
|
|
166
|
+
nextValue[capability] = existing.value[capability];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const entry = makeEntry(key, nextValue);
|
|
161
171
|
if (getInvalidationVersion(key) === startedVersion) {
|
|
162
172
|
snapshots.set(key, entry);
|
|
163
173
|
lastErrors.delete(key);
|
|
@@ -271,7 +281,7 @@ async function getSnapshot(key, {
|
|
|
271
281
|
: 0;
|
|
272
282
|
|
|
273
283
|
if (!entry && Number(coldWaitMs) > 0) {
|
|
274
|
-
refreshPromise = refreshSnapshot(key, refresh
|
|
284
|
+
refreshPromise = refreshSnapshot(key, refresh);
|
|
275
285
|
const waitResult = await waitForRefresh(refreshPromise, coldWaitMs);
|
|
276
286
|
if (waitResult.entry) {
|
|
277
287
|
entry = waitResult.entry;
|
|
@@ -287,7 +297,7 @@ async function getSnapshot(key, {
|
|
|
287
297
|
}
|
|
288
298
|
} else if (!entry && backgroundOnMiss === false) {
|
|
289
299
|
try {
|
|
290
|
-
entry = await refreshSnapshot(key, refresh
|
|
300
|
+
entry = await refreshSnapshot(key, refresh);
|
|
291
301
|
expired = false;
|
|
292
302
|
} catch (error) {
|
|
293
303
|
refreshError = error?.message || String(error);
|
|
@@ -295,7 +305,7 @@ async function getSnapshot(key, {
|
|
|
295
305
|
if (!entry) throw error;
|
|
296
306
|
}
|
|
297
307
|
} else {
|
|
298
|
-
refreshPromise = refreshSnapshot(key, refresh, { defer: !force, deferMs
|
|
308
|
+
refreshPromise = refreshSnapshot(key, refresh, { defer: !force, deferMs });
|
|
299
309
|
}
|
|
300
310
|
}
|
|
301
311
|
|
|
@@ -370,6 +380,16 @@ function invalidateSnapshot(keyOrPrefix) {
|
|
|
370
380
|
}
|
|
371
381
|
}
|
|
372
382
|
|
|
383
|
+
function dashboardSourceKey(source) {
|
|
384
|
+
return `dashboard:source:${source}`;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function invalidateDashboardSourceSnapshot(source) {
|
|
388
|
+
if (source) {
|
|
389
|
+
invalidateSnapshot(dashboardSourceKey(source));
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
373
393
|
function clearSnapshotCache({ deleteFiles = false } = {}) {
|
|
374
394
|
for (const key of inflight.keys()) {
|
|
375
395
|
bumpInvalidationVersion(key);
|
|
@@ -392,6 +412,8 @@ module.exports = {
|
|
|
392
412
|
refreshSnapshot,
|
|
393
413
|
invalidateSnapshot,
|
|
394
414
|
clearSnapshotCache,
|
|
415
|
+
dashboardSourceKey,
|
|
416
|
+
invalidateDashboardSourceSnapshot,
|
|
395
417
|
_test: {
|
|
396
418
|
getSnapshotDir,
|
|
397
419
|
getSnapshotPath,
|
|
@@ -8,18 +8,18 @@ const https = require('https');
|
|
|
8
8
|
const http = require('http');
|
|
9
9
|
const { URL } = require('url');
|
|
10
10
|
const { probeModelAvailability } = require('./model-detector');
|
|
11
|
-
const { getEffectiveApiKey: getClaudeEffectiveApiKey } = require('
|
|
12
|
-
const { getEffectiveApiKey: getCodexEffectiveApiKey } = require('
|
|
13
|
-
const { getEffectiveApiKey: getGeminiEffectiveApiKey } = require('
|
|
14
|
-
const { getEffectiveApiKey: getOpenCodeEffectiveApiKey } = require('
|
|
15
|
-
const { getEffectiveApiKey: getOmpEffectiveApiKey } = require('
|
|
16
|
-
const { createCodexRequest } = require('
|
|
17
|
-
const { createClaudeRequest, buildClaudeTargetUrl } = require('
|
|
11
|
+
const { getEffectiveApiKey: getClaudeEffectiveApiKey } = require('../../platforms/drivers/claude/channels-implementation');
|
|
12
|
+
const { getEffectiveApiKey: getCodexEffectiveApiKey } = require('../../platforms/drivers/codex/channels-implementation');
|
|
13
|
+
const { getEffectiveApiKey: getGeminiEffectiveApiKey } = require('../../platforms/drivers/gemini/channels-implementation');
|
|
14
|
+
const { getEffectiveApiKey: getOpenCodeEffectiveApiKey } = require('../../platforms/drivers/opencode/channels-implementation');
|
|
15
|
+
const { getEffectiveApiKey: getOmpEffectiveApiKey } = require('../../platforms/drivers/omp/channels-implementation');
|
|
16
|
+
const { createCodexRequest } = require('../../platforms/drivers/codex/wire');
|
|
17
|
+
const { createClaudeRequest, buildClaudeTargetUrl } = require('../../platforms/drivers/claude/wire');
|
|
18
18
|
const {
|
|
19
19
|
createGeminiRequest,
|
|
20
20
|
buildGeminiTargetUrl,
|
|
21
21
|
shouldUseGeminiCliFormat
|
|
22
|
-
} = require('
|
|
22
|
+
} = require('../../platforms/drivers/gemini/wire');
|
|
23
23
|
|
|
24
24
|
// 测试结果缓存
|
|
25
25
|
const testResultsCache = new Map();
|
|
@@ -39,7 +39,17 @@ function openDatabase(filePath, options = {}) {
|
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
if (!options.readOnly) {
|
|
42
|
-
|
|
42
|
+
// Keep the per-process SQLite footprint bounded. The session history
|
|
43
|
+
// database can contain hundreds of thousands of messages, and the
|
|
44
|
+
// default page cache plus an unbounded WAL made repeated inventories
|
|
45
|
+
// compete with the Node heap for memory and disk space.
|
|
46
|
+
db.exec(`
|
|
47
|
+
PRAGMA foreign_keys = ON;
|
|
48
|
+
PRAGMA journal_mode = WAL;
|
|
49
|
+
PRAGMA synchronous = NORMAL;
|
|
50
|
+
PRAGMA wal_autocheckpoint = 1000;
|
|
51
|
+
PRAGMA cache_size = -16384;
|
|
52
|
+
`);
|
|
43
53
|
}
|
|
44
54
|
|
|
45
55
|
_connections.set(key, db);
|
|
@@ -57,8 +67,13 @@ function closeDatabase(filePath) {
|
|
|
57
67
|
if (!db) {
|
|
58
68
|
return;
|
|
59
69
|
}
|
|
60
|
-
|
|
61
|
-
|
|
70
|
+
try {
|
|
71
|
+
// A clean shutdown should not leave a multi-gigabyte WAL behind. This is
|
|
72
|
+
// best effort because another process may still be using the database.
|
|
73
|
+
try { db.exec('PRAGMA wal_checkpoint(TRUNCATE)'); } catch (_err) {}
|
|
74
|
+
} finally {
|
|
75
|
+
try { db.close(); } finally { _connections.delete(key); }
|
|
76
|
+
}
|
|
62
77
|
}
|
|
63
78
|
|
|
64
79
|
/**
|
|
@@ -67,6 +82,7 @@ function closeDatabase(filePath) {
|
|
|
67
82
|
function closeAllDatabases() {
|
|
68
83
|
for (const db of _connections.values()) {
|
|
69
84
|
try {
|
|
85
|
+
try { db.exec('PRAGMA wal_checkpoint(TRUNCATE)'); } catch (_err) {}
|
|
70
86
|
db.close();
|
|
71
87
|
} catch (_err) {
|
|
72
88
|
// swallow — best-effort teardown
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { PATHS } = require('../../config/paths');
|
|
4
|
-
const { normalizeUsageTokens, resolveActualModel } = require('./
|
|
4
|
+
const { normalizeUsageTokens, resolveActualModel } = require('./usage-log-utils');
|
|
5
5
|
|
|
6
6
|
// 北京时间辅助(UTC+8),统一所有时间计算
|
|
7
7
|
const CST_OFFSET_MS = 8 * 60 * 60 * 1000;
|
|
8
8
|
|
|
9
|
+
const pendingEntries = [];
|
|
10
|
+
let flushPromise = null;
|
|
11
|
+
let flushScheduled = false;
|
|
12
|
+
let aggregateStatsCache = null;
|
|
13
|
+
const dailyStatsCache = new Map();
|
|
14
|
+
|
|
9
15
|
function toCSTDate(ts) {
|
|
10
16
|
// 返回以北京时间解释的 Date 对象各字段(通过偏移 UTC)
|
|
11
17
|
return new Date(new Date(ts).getTime() + CST_OFFSET_MS);
|
|
@@ -181,6 +187,111 @@ function appendRequestLog(logEntry) {
|
|
|
181
187
|
}
|
|
182
188
|
}
|
|
183
189
|
|
|
190
|
+
function cloneJson(value) {
|
|
191
|
+
return value == null ? value : JSON.parse(JSON.stringify(value));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function getRequestLogPath(timestamp) {
|
|
195
|
+
const cst = toCSTDate(timestamp);
|
|
196
|
+
const year = cst.getUTCFullYear();
|
|
197
|
+
const month = String(cst.getUTCMonth() + 1).padStart(2, '0');
|
|
198
|
+
const day = String(cst.getUTCDate()).padStart(2, '0');
|
|
199
|
+
return path.join(PATHS.statistics.requestLogs, `${year}-${month}`, `${day}.jsonl`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function appendRequestLogsAsync(entries) {
|
|
203
|
+
const byPath = new Map();
|
|
204
|
+
for (const entry of entries) {
|
|
205
|
+
const filePath = getRequestLogPath(entry.logEntry.timestamp);
|
|
206
|
+
const group = byPath.get(filePath) || [];
|
|
207
|
+
group.push(entry);
|
|
208
|
+
byPath.set(filePath, group);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const persisted = [];
|
|
212
|
+
for (const [filePath, group] of byPath) {
|
|
213
|
+
await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
|
|
214
|
+
await fs.promises.appendFile(
|
|
215
|
+
filePath,
|
|
216
|
+
group.map(entry => `${JSON.stringify(entry.logEntry)}\n`).join(''),
|
|
217
|
+
'utf8'
|
|
218
|
+
);
|
|
219
|
+
persisted.push(...group);
|
|
220
|
+
}
|
|
221
|
+
return persisted;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async function writeJsonAsync(filePath, value) {
|
|
225
|
+
await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
|
|
226
|
+
await fs.promises.writeFile(filePath, JSON.stringify(value, null, 2), 'utf8');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function scheduleStatisticsFlush() {
|
|
230
|
+
if (flushScheduled) return;
|
|
231
|
+
flushScheduled = true;
|
|
232
|
+
queueMicrotask(() => {
|
|
233
|
+
flushScheduled = false;
|
|
234
|
+
void flushStatistics();
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async function flushStatistics() {
|
|
239
|
+
if (flushPromise) return flushPromise;
|
|
240
|
+
if (pendingEntries.length === 0) {
|
|
241
|
+
return { flushed: 0, pending: 0 };
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const batch = pendingEntries.splice(0, pendingEntries.length);
|
|
245
|
+
const globalSnapshot = aggregateStatsCache ? cloneJson(aggregateStatsCache) : null;
|
|
246
|
+
const dates = [...new Set(batch.map(entry => entry.date))];
|
|
247
|
+
const dailySnapshots = new Map(
|
|
248
|
+
dates.map(date => [date, cloneJson(dailyStatsCache.get(date))])
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
const writer = (async () => {
|
|
252
|
+
try {
|
|
253
|
+
const pendingLogEntries = batch.filter(entry => !entry.logPersisted);
|
|
254
|
+
const persisted = await appendRequestLogsAsync(pendingLogEntries);
|
|
255
|
+
persisted.forEach(entry => { entry.logPersisted = true; });
|
|
256
|
+
if (globalSnapshot) {
|
|
257
|
+
globalSnapshot.lastUpdated = new Date().toISOString();
|
|
258
|
+
await writeJsonAsync(getStatisticsFilePath(), globalSnapshot);
|
|
259
|
+
}
|
|
260
|
+
for (const [date, stats] of dailySnapshots) {
|
|
261
|
+
if (stats) await writeJsonAsync(getDailyStatsFilePathWithoutCreate(date), stats);
|
|
262
|
+
}
|
|
263
|
+
return { flushed: batch.length, pending: pendingEntries.length };
|
|
264
|
+
} catch (error) {
|
|
265
|
+
pendingEntries.unshift(...batch);
|
|
266
|
+
console.error('[Statistics] Failed to flush queued entries:', error.message);
|
|
267
|
+
return {
|
|
268
|
+
flushed: 0,
|
|
269
|
+
pending: pendingEntries.length,
|
|
270
|
+
error: error.message
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
})();
|
|
274
|
+
|
|
275
|
+
flushPromise = writer.finally(() => {
|
|
276
|
+
flushPromise = null;
|
|
277
|
+
if (pendingEntries.length > 0) scheduleStatisticsFlush();
|
|
278
|
+
});
|
|
279
|
+
return flushPromise;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function getDailyStatsFilePathWithoutCreate(date) {
|
|
283
|
+
return path.join(PATHS.statistics.dailyStats, `${date}.json`);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async function shutdownStatistics() {
|
|
287
|
+
let result = await flushStatistics();
|
|
288
|
+
while (result.pending > 0 && !result.error) {
|
|
289
|
+
result = await flushStatistics();
|
|
290
|
+
}
|
|
291
|
+
return result;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
|
|
184
295
|
// 初始化统计对象
|
|
185
296
|
function initStatsObject() {
|
|
186
297
|
return {
|
|
@@ -257,11 +368,10 @@ function recordRequest(requestData) {
|
|
|
257
368
|
if (requestData.redirectedModel) {
|
|
258
369
|
logEntry.redirectedModel = requestData.redirectedModel;
|
|
259
370
|
}
|
|
260
|
-
appendRequestLog(logEntry);
|
|
261
371
|
|
|
262
372
|
// 2. 更新总体统计
|
|
263
|
-
const globalStats = loadStatistics();
|
|
264
|
-
|
|
373
|
+
const globalStats = aggregateStatsCache || loadStatistics();
|
|
374
|
+
aggregateStatsCache = globalStats;
|
|
265
375
|
// 更新全局统计
|
|
266
376
|
globalStats.global.totalRequests += 1;
|
|
267
377
|
globalStats.global.totalTokens += totalTokens;
|
|
@@ -277,18 +387,22 @@ function recordRequest(requestData) {
|
|
|
277
387
|
}
|
|
278
388
|
updateStats(globalStats.byToolType[toolType], tokens, cost);
|
|
279
389
|
|
|
280
|
-
//
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
390
|
+
// Native CLI logs do not always contain a reliable configured-channel id.
|
|
391
|
+
// Keep aggregate totals in that case instead of creating an "undefined"
|
|
392
|
+
// channel bucket.
|
|
393
|
+
if (channelId) {
|
|
394
|
+
if (!globalStats.byToolType[toolType].channels[channelId]) {
|
|
395
|
+
globalStats.byToolType[toolType].channels[channelId] = {
|
|
396
|
+
name: channel,
|
|
397
|
+
...initStatsObject(),
|
|
398
|
+
firstUsed: timestamp,
|
|
399
|
+
lastUsed: timestamp
|
|
400
|
+
};
|
|
401
|
+
} else {
|
|
402
|
+
globalStats.byToolType[toolType].channels[channelId].lastUsed = timestamp;
|
|
403
|
+
}
|
|
404
|
+
updateStats(globalStats.byToolType[toolType].channels[channelId], tokens, cost);
|
|
290
405
|
}
|
|
291
|
-
updateStats(globalStats.byToolType[toolType].channels[channelId], tokens, cost);
|
|
292
406
|
|
|
293
407
|
// 按工具类型 -> 模型统计
|
|
294
408
|
if (!globalStats.byToolType[toolType].models[modelKey]) {
|
|
@@ -296,19 +410,21 @@ function recordRequest(requestData) {
|
|
|
296
410
|
}
|
|
297
411
|
updateStats(globalStats.byToolType[toolType].models[modelKey], tokens, cost);
|
|
298
412
|
|
|
299
|
-
//
|
|
300
|
-
if (
|
|
301
|
-
globalStats.byChannel[channelId]
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
413
|
+
// Cross-tool channel totals follow the same rule.
|
|
414
|
+
if (channelId) {
|
|
415
|
+
if (!globalStats.byChannel[channelId]) {
|
|
416
|
+
globalStats.byChannel[channelId] = {
|
|
417
|
+
toolType,
|
|
418
|
+
name: channel,
|
|
419
|
+
...initStatsObject(),
|
|
420
|
+
firstUsed: timestamp,
|
|
421
|
+
lastUsed: timestamp
|
|
422
|
+
};
|
|
423
|
+
} else {
|
|
424
|
+
globalStats.byChannel[channelId].lastUsed = timestamp;
|
|
425
|
+
}
|
|
426
|
+
updateStats(globalStats.byChannel[channelId], tokens, cost);
|
|
310
427
|
}
|
|
311
|
-
updateStats(globalStats.byChannel[channelId], tokens, cost);
|
|
312
428
|
|
|
313
429
|
// 按模型统计(跨工具)
|
|
314
430
|
if (!globalStats.byModel[modelKey]) {
|
|
@@ -319,13 +435,13 @@ function recordRequest(requestData) {
|
|
|
319
435
|
}
|
|
320
436
|
updateStats(globalStats.byModel[modelKey], tokens, cost);
|
|
321
437
|
|
|
322
|
-
saveStatistics(globalStats);
|
|
323
438
|
|
|
324
439
|
// 3. 更新每日统计(使用北京时间)
|
|
325
440
|
const date = getCSTDateStr(timestamp); // YYYY-MM-DD (CST)
|
|
326
441
|
const hour = getCSTHour(timestamp).toString().padStart(2, '0'); // HH (CST)
|
|
327
442
|
|
|
328
|
-
const dailyStats = loadDailyStats(date);
|
|
443
|
+
const dailyStats = dailyStatsCache.get(date) || loadDailyStats(date);
|
|
444
|
+
dailyStatsCache.set(date, dailyStats);
|
|
329
445
|
|
|
330
446
|
// 更新每日汇总
|
|
331
447
|
dailyStats.summary.requests += 1;
|
|
@@ -357,17 +473,19 @@ function recordRequest(requestData) {
|
|
|
357
473
|
}
|
|
358
474
|
updateStats(dailyStats.byToolType[toolType], tokens, cost);
|
|
359
475
|
|
|
360
|
-
// 按工具类型 ->
|
|
361
|
-
if (
|
|
362
|
-
dailyStats.byToolType[toolType].channels
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
dailyStats.byToolType[toolType].channels[channelId]
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
476
|
+
// 按工具类型 -> 渠道;原生日志没有可靠渠道时只保留平台级统计。
|
|
477
|
+
if (channelId) {
|
|
478
|
+
if (!dailyStats.byToolType[toolType].channels) {
|
|
479
|
+
dailyStats.byToolType[toolType].channels = {};
|
|
480
|
+
}
|
|
481
|
+
if (!dailyStats.byToolType[toolType].channels[channelId]) {
|
|
482
|
+
dailyStats.byToolType[toolType].channels[channelId] = {
|
|
483
|
+
name: channel,
|
|
484
|
+
...initStatsObject()
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
updateStats(dailyStats.byToolType[toolType].channels[channelId], tokens, cost);
|
|
369
488
|
}
|
|
370
|
-
updateStats(dailyStats.byToolType[toolType].channels[channelId], tokens, cost);
|
|
371
489
|
|
|
372
490
|
// 按工具类型 -> 模型
|
|
373
491
|
if (!dailyStats.byToolType[toolType].models) {
|
|
@@ -378,15 +496,17 @@ function recordRequest(requestData) {
|
|
|
378
496
|
}
|
|
379
497
|
updateStats(dailyStats.byToolType[toolType].models[modelKey], tokens, cost);
|
|
380
498
|
|
|
381
|
-
//
|
|
382
|
-
if (
|
|
383
|
-
dailyStats.byChannel[channelId]
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
499
|
+
// 按渠道统计;不创建 undefined 渠道桶。
|
|
500
|
+
if (channelId) {
|
|
501
|
+
if (!dailyStats.byChannel[channelId]) {
|
|
502
|
+
dailyStats.byChannel[channelId] = {
|
|
503
|
+
toolType,
|
|
504
|
+
name: channel,
|
|
505
|
+
...initStatsObject()
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
updateStats(dailyStats.byChannel[channelId], tokens, cost);
|
|
388
509
|
}
|
|
389
|
-
updateStats(dailyStats.byChannel[channelId], tokens, cost);
|
|
390
510
|
|
|
391
511
|
// 按模型统计
|
|
392
512
|
if (!dailyStats.byModel[modelKey]) {
|
|
@@ -397,10 +517,16 @@ function recordRequest(requestData) {
|
|
|
397
517
|
}
|
|
398
518
|
updateStats(dailyStats.byModel[modelKey], tokens, cost);
|
|
399
519
|
|
|
400
|
-
saveDailyStats(date, dailyStats);
|
|
401
520
|
|
|
402
521
|
// Invalidate cached trend results that cover this date
|
|
403
522
|
invalidateTrendCacheForDate(date);
|
|
523
|
+
pendingEntries.push({ logEntry, date });
|
|
524
|
+
if (pendingEntries.length >= 1000) {
|
|
525
|
+
console.error('[Statistics] Write queue reached its limit; flushing immediately');
|
|
526
|
+
void flushStatistics();
|
|
527
|
+
} else {
|
|
528
|
+
scheduleStatisticsFlush();
|
|
529
|
+
}
|
|
404
530
|
} catch (err) {
|
|
405
531
|
console.error('[Statistics] Failed to record request:', err);
|
|
406
532
|
}
|
|
@@ -410,14 +536,14 @@ function recordRequest(requestData) {
|
|
|
410
536
|
* 获取统计数据
|
|
411
537
|
*/
|
|
412
538
|
function getStatistics() {
|
|
413
|
-
return loadStatistics();
|
|
539
|
+
return aggregateStatsCache ? cloneJson(aggregateStatsCache) : loadStatistics();
|
|
414
540
|
}
|
|
415
541
|
|
|
416
542
|
/**
|
|
417
543
|
* 获取每日统计
|
|
418
544
|
*/
|
|
419
545
|
function getDailyStatistics(date) {
|
|
420
|
-
return aggregateDailyStatistics(date);
|
|
546
|
+
return aggregateDailyStatistics(date, dailyStatsCache.get(date) || null);
|
|
421
547
|
}
|
|
422
548
|
|
|
423
549
|
/**
|
|
@@ -425,7 +551,7 @@ function getDailyStatistics(date) {
|
|
|
425
551
|
*/
|
|
426
552
|
function getTodayStatistics() {
|
|
427
553
|
const today = getCSTDateStr(Date.now());
|
|
428
|
-
return
|
|
554
|
+
return getDailyStatistics(today);
|
|
429
555
|
}
|
|
430
556
|
|
|
431
557
|
/**
|
|
@@ -448,6 +574,35 @@ function extractMetric(stats, metric) {
|
|
|
448
574
|
* @param {string} groupBy
|
|
449
575
|
* @param {Object} [filters] - optional { toolType, channel, model }
|
|
450
576
|
*/
|
|
577
|
+
function addLogEntryToResult(result, entry, groupBy, filters = null) {
|
|
578
|
+
const actualModel = resolveActualModel(entry.model, entry);
|
|
579
|
+
if (filters) {
|
|
580
|
+
if (filters.toolType && entry.toolType !== filters.toolType) return;
|
|
581
|
+
if (filters.channel && entry.channel !== filters.channel) return;
|
|
582
|
+
if (filters.model && actualModel !== filters.model) return;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
let key;
|
|
586
|
+
if (groupBy === 'model') key = actualModel || 'unknown';
|
|
587
|
+
else if (groupBy === 'channel') key = entry.channel || entry.channelId || 'unknown';
|
|
588
|
+
else if (groupBy === 'toolType') key = entry.toolType || 'claude-code';
|
|
589
|
+
else return;
|
|
590
|
+
|
|
591
|
+
if (!result[key]) result[key] = { tokens: { total: 0 }, cost: 0, requests: 0 };
|
|
592
|
+
result[key].tokens.total += getTokenTotal(entry.tokens);
|
|
593
|
+
result[key].cost += entry.cost || 0;
|
|
594
|
+
result[key].requests += 1;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function mergePendingLogEntries(result, dateStr, hour, groupBy, filters = null) {
|
|
598
|
+
for (const { logEntry } of pendingEntries) {
|
|
599
|
+
const timestamp = new Date(logEntry.timestamp);
|
|
600
|
+
if (getCSTDateStr(timestamp) !== dateStr) continue;
|
|
601
|
+
if (hour !== undefined && getCSTHour(timestamp) !== hour) continue;
|
|
602
|
+
addLogEntryToResult(result, logEntry, groupBy, filters);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
451
606
|
function readJsonlForHour(year, month, day, hour, groupBy, filters) {
|
|
452
607
|
const filePath = getRequestLogFilePath(year, month, day);
|
|
453
608
|
const result = {};
|
|
@@ -488,6 +643,8 @@ function readJsonlForHour(year, month, day, hour, groupBy, filters) {
|
|
|
488
643
|
console.error('Failed to read JSONL for hour:', err);
|
|
489
644
|
}
|
|
490
645
|
|
|
646
|
+
const dateStr = `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
|
|
647
|
+
mergePendingLogEntries(result, dateStr, hour, groupBy, filters);
|
|
491
648
|
return result;
|
|
492
649
|
}
|
|
493
650
|
|
|
@@ -536,6 +693,8 @@ function readJsonlForDay(year, month, day, groupBy, filters) {
|
|
|
536
693
|
console.error('Failed to read JSONL for day:', err);
|
|
537
694
|
}
|
|
538
695
|
|
|
696
|
+
const dateStr = `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
|
|
697
|
+
mergePendingLogEntries(result, dateStr, undefined, groupBy, filters);
|
|
539
698
|
return result;
|
|
540
699
|
}
|
|
541
700
|
|
|
@@ -629,7 +788,22 @@ const TOOL_PREFIXES = {
|
|
|
629
788
|
function getTokenTotal(tokens) {
|
|
630
789
|
if (typeof tokens === 'number') return tokens;
|
|
631
790
|
if (tokens && typeof tokens === 'object') {
|
|
632
|
-
if (typeof tokens.total === 'number') return tokens.total;
|
|
791
|
+
if (typeof tokens.total === 'number' && tokens.total > 0) return tokens.total;
|
|
792
|
+
|
|
793
|
+
// Token detail fields overlap: cached/reasoning tokens are usually
|
|
794
|
+
// sub-counts of input/output. The canonical shape stores cached input
|
|
795
|
+
// separately, so include it once and never add reasoning independently.
|
|
796
|
+
const usageKeys = ['input', 'output', 'cacheCreation', 'cacheRead', 'cached'];
|
|
797
|
+
if (usageKeys.some(key => typeof tokens[key] === 'number')) {
|
|
798
|
+
const input = typeof tokens.input === 'number' ? tokens.input : 0;
|
|
799
|
+
const output = typeof tokens.output === 'number' ? tokens.output : 0;
|
|
800
|
+
const cacheCreation = typeof tokens.cacheCreation === 'number' ? tokens.cacheCreation : 0;
|
|
801
|
+
const cacheRead = typeof tokens.cacheRead === 'number'
|
|
802
|
+
? tokens.cacheRead
|
|
803
|
+
: (typeof tokens.cached === 'number' ? tokens.cached : 0);
|
|
804
|
+
return input + output + cacheCreation + cacheRead;
|
|
805
|
+
}
|
|
806
|
+
|
|
633
807
|
return Object.entries(tokens).reduce((sum, [key, value]) => {
|
|
634
808
|
if (key === 'total') return sum;
|
|
635
809
|
return typeof value === 'number' ? sum + value : sum;
|
|
@@ -725,7 +899,7 @@ function hasStatsData(stats = {}) {
|
|
|
725
899
|
return requests > 0 || cost > 0 || totalTokens > 0;
|
|
726
900
|
}
|
|
727
901
|
|
|
728
|
-
function aggregateDailyStatistics(dateStr) {
|
|
902
|
+
function aggregateDailyStatistics(dateStr, sharedStatsOverride = null) {
|
|
729
903
|
const aggregated = {
|
|
730
904
|
date: dateStr,
|
|
731
905
|
summary: {
|
|
@@ -739,7 +913,7 @@ function aggregateDailyStatistics(dateStr) {
|
|
|
739
913
|
byModel: {}
|
|
740
914
|
};
|
|
741
915
|
|
|
742
|
-
const sharedStats = loadDailyStats(dateStr);
|
|
916
|
+
const sharedStats = sharedStatsOverride || loadDailyStats(dateStr);
|
|
743
917
|
const sharedSummaryEntry = createEmptyEntry();
|
|
744
918
|
mergeStatsEntry(sharedSummaryEntry, {
|
|
745
919
|
requests: sharedStats.summary?.requests || 0,
|
|
@@ -814,7 +988,7 @@ function aggregateDailyStatistics(dateStr) {
|
|
|
814
988
|
function mergeAllToolsDailyStats(dateStr, groupBy) {
|
|
815
989
|
const merged = {};
|
|
816
990
|
|
|
817
|
-
const aggregated = aggregateDailyStatistics(dateStr);
|
|
991
|
+
const aggregated = aggregateDailyStatistics(dateStr, dailyStatsCache.get(dateStr) || null);
|
|
818
992
|
if (!aggregated) return merged;
|
|
819
993
|
|
|
820
994
|
if (groupBy === 'toolType') {
|
|
@@ -891,6 +1065,13 @@ function getAvailableFilters(startDate, endDate) {
|
|
|
891
1065
|
}
|
|
892
1066
|
}
|
|
893
1067
|
|
|
1068
|
+
for (const { logEntry, date } of pendingEntries) {
|
|
1069
|
+
if (date < startDate || date > endDate) continue;
|
|
1070
|
+
if (logEntry.toolType) toolTypes.add(logEntry.toolType);
|
|
1071
|
+
if (logEntry.channel) channels.add(logEntry.channel);
|
|
1072
|
+
const actualModel = resolveActualModel(logEntry.model, logEntry);
|
|
1073
|
+
if (actualModel) models.add(actualModel);
|
|
1074
|
+
}
|
|
894
1075
|
if (includeProxyLogs) {
|
|
895
1076
|
for (const entry of loadProxyLogs()) {
|
|
896
1077
|
if (!entry || entry.type === 'action' || entry.status === 'error') continue;
|
|
@@ -1029,7 +1210,7 @@ async function getTrendStatistics({ startDate, endDate, granularity = 'day', ste
|
|
|
1029
1210
|
if (activeFilters) {
|
|
1030
1211
|
byDimension = readJsonlForHour(year, month, day, hh, groupBy, activeFilters);
|
|
1031
1212
|
} else if (groupBy === 'toolType') {
|
|
1032
|
-
const dailyStats = aggregateDailyStatistics(dateStr);
|
|
1213
|
+
const dailyStats = aggregateDailyStatistics(dateStr, dailyStatsCache.get(dateStr) || null);
|
|
1033
1214
|
const hourData = dailyStats?.hourly?.[hhStr];
|
|
1034
1215
|
for (const [toolType, toolStats] of Object.entries(hourData?.byToolType || {})) {
|
|
1035
1216
|
if (!hasStatsData(toolStats)) continue;
|
|
@@ -1095,5 +1276,7 @@ module.exports = {
|
|
|
1095
1276
|
getDailyStatistics,
|
|
1096
1277
|
getTodayStatistics,
|
|
1097
1278
|
getTrendStatistics,
|
|
1098
|
-
getAvailableFilters
|
|
1279
|
+
getAvailableFilters,
|
|
1280
|
+
flushStatistics,
|
|
1281
|
+
shutdownStatistics
|
|
1099
1282
|
};
|