coding-tool-x 3.8.4 → 3.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +86 -15
- package/dist/web/assets/AgentsDrawer-Db9wAyaZ.js +1 -0
- package/dist/web/assets/AgentsDrawer-Pw2k-ZAa.css +1 -0
- package/dist/web/assets/Analytics-C4gMzv7g.js +1 -0
- package/dist/web/assets/Analytics-CyHhR126.css +1 -0
- package/dist/web/assets/AssetPathField-Bd_nMFu3.js +1 -0
- package/dist/web/assets/ChatHistoryDrawer-B2qBQs9L.css +1 -0
- package/dist/web/assets/ChatHistoryDrawer-CwzJHyNP.js +1 -0
- package/dist/web/assets/CollapseItem-CTCfSiue.js +1 -0
- package/dist/web/assets/CommandsDrawer-BZJxlPvV.css +1 -0
- package/dist/web/assets/CommandsDrawer-vDghB1U9.js +1 -0
- package/dist/web/assets/ConfigExportDrawer-C-XDvlHc.js +1 -0
- package/dist/web/assets/ConfigExportDrawer-dwruRKWr.css +1 -0
- package/dist/web/assets/ConfigTemplates-CYY8XRED.js +1 -0
- package/dist/web/assets/ConfigTemplatesDrawer-BfDi8yO3.css +1 -0
- package/dist/web/assets/ConfigTemplatesDrawer-CbsJp76X.js +1 -0
- package/dist/web/assets/ConfigTemplatesPanel-CWyPtk67.css +1 -0
- package/dist/web/assets/ConfigTemplatesPanel-liMSTh7s.js +1 -0
- package/dist/web/assets/Divider-D28g1gwj.js +1 -0
- package/dist/web/assets/DrawerContent-ChXgZ1Xm.js +1 -0
- package/dist/web/assets/FavoritesDrawer-CG9eI9Ay.css +1 -0
- package/dist/web/assets/FavoritesDrawer-D6QSiYaL.js +1 -0
- package/dist/web/assets/GatewayConvertDrawer-CuNlf7AC.css +1 -0
- package/dist/web/assets/GatewayConvertDrawer-RvET6iNG.js +1 -0
- package/dist/web/assets/Home-CSuFuJ9Y.css +1 -0
- package/dist/web/assets/Home-P8eseZcf.js +1 -0
- package/dist/web/assets/MarkdownEditor-CYmLGYju.js +1 -0
- package/dist/web/assets/MarkdownEditor-qfVgSre_.css +1 -0
- package/dist/web/assets/McpDrawer-C0dnsZhQ.css +1 -0
- package/dist/web/assets/McpDrawer-Ch3bkKIq.js +1 -0
- package/dist/web/assets/McpFormDrawer-BRbDcawn.css +1 -0
- package/dist/web/assets/McpFormDrawer-BcTlgSpN.js +1 -0
- package/dist/web/assets/PluginManager-Dh2o0xkZ.js +1 -0
- package/dist/web/assets/PluginsDrawer-DM3QjprE.css +1 -0
- package/dist/web/assets/PluginsDrawer-deKQ5u3I.js +1 -0
- package/dist/web/assets/PluginsPanel-BofsgQgj.js +1 -0
- package/dist/web/assets/PluginsPanel-D6ZqITcR.css +1 -0
- package/dist/web/assets/ProjectList-677RLZ8O.css +1 -0
- package/dist/web/assets/ProjectList-DhSWJHTA.js +1 -0
- package/dist/web/assets/PromptsDrawer-BIgiYCEQ.css +1 -0
- package/dist/web/assets/PromptsDrawer-Cfb2FeeU.js +1 -0
- package/dist/web/assets/RecentSessionsDrawer-3eXamJhr.js +1 -0
- package/dist/web/assets/RecentSessionsDrawer-CKFsOYDM.css +1 -0
- package/dist/web/assets/SessionCard-CW0eHURI.js +1 -0
- package/dist/web/assets/SessionCard-aM2NHu6i.css +1 -0
- package/dist/web/assets/SessionList-BpYRz1cX.css +1 -0
- package/dist/web/assets/SessionList-DQfyDGWU.js +1 -0
- package/dist/web/assets/SettingsDrawer-Dw5NUgHC.css +1 -0
- package/dist/web/assets/SettingsDrawer-u_vwUuPS.js +1 -0
- package/dist/web/assets/SkillManager-C0GCT2Kb.css +1 -0
- package/dist/web/assets/SkillManager-DHMprJLA.js +1 -0
- package/dist/web/assets/SkillsDrawer-BCNme-aJ.css +1 -0
- package/dist/web/assets/SkillsDrawer-CkF3BpyC.js +1 -0
- package/dist/web/assets/SkillsPanel-B3Nm5j32.css +1 -0
- package/dist/web/assets/SkillsPanel-DWE4pFGx.js +1 -0
- package/dist/web/assets/SpeedTestDrawer-BgfG1Ked.js +1 -0
- package/dist/web/assets/SpeedTestDrawer-Dc0WToJ_.css +1 -0
- package/dist/web/assets/Upload-CNwaGOws.js +1 -0
- package/dist/web/assets/WorkspaceDrawer-Br5CeCzL.js +1 -0
- package/dist/web/assets/WorkspaceDrawer-CGnMu9p1.css +1 -0
- package/dist/web/assets/WorkspaceManager-B2O8-qIw.js +1 -0
- package/dist/web/assets/WorkspacePanel-5oDKit3c.js +1 -0
- package/dist/web/assets/WorkspacePanel-D0-zHTHP.css +1 -0
- package/dist/web/assets/_plugin-vue_export-helper-BeLZJZij.js +1 -0
- package/dist/web/assets/client-UDwXBP9i.js +1 -0
- package/dist/web/assets/clipboard-B46yyoz_.js +1 -0
- package/dist/web/assets/headers-cf-QapUq.js +1 -0
- package/dist/web/assets/icons-B2Vg-IOC.js +1 -0
- package/dist/web/assets/index-BMAUPq5K.js +2 -0
- package/dist/web/assets/index-Bi1MQHYX.css +1 -0
- package/dist/web/assets/light-Cp0t7v32.js +1 -0
- package/dist/web/assets/markdown-u082bbtj.js +1 -0
- package/dist/web/assets/platforms-CBkB45JP.js +1 -0
- package/dist/web/assets/platforms-FTpjxAT6.js +1 -0
- package/dist/web/assets/project-config-B2baPzYL.js +1 -0
- package/dist/web/assets/sessions-Cmy31slD.js +1 -0
- package/dist/web/assets/sessions-yC_oVVh8.js +1 -0
- package/dist/web/assets/skill-repo-input-B_fkGlUK.js +1 -0
- package/dist/web/assets/use-lock-html-scroll-DXIqmUW4.js +1 -0
- package/dist/web/assets/use-message-DoLM8C1C.js +1 -0
- package/dist/web/assets/useResponsiveDrawer-jbmqZuJs.js +1 -0
- package/dist/web/assets/vendors-D0c_0KLP.js +1 -0
- package/dist/web/assets/vue-vendor-BqZbbXHA.js +1 -0
- package/dist/web/assets/workspaces-mjyPVGS8.js +1 -0
- package/dist/web/assets/xml-CgY3RMlX.js +1 -0
- package/dist/web/index.html +12 -8
- package/docs/native-log-memory-2026-09-16.md +43 -0
- package/docs/superpowers/plans/2026-08-26-configuration-driven-cli-platform.md +1578 -0
- package/docs/superpowers/plans/2026-08-30-project-scoped-config-control.md +1260 -0
- package/docs/superpowers/plans/2026-09-01-omp-skill-refresh-boundary.md +213 -0
- package/docs/superpowers/plans/2026-09-01-realtime-logs-panel.md +125 -0
- package/docs/superpowers/plans/2026-09-02-config-driven-platform-boundary.md +86 -0
- package/docs/superpowers/plans/2026-09-02-platform-implementation-drivers.md +508 -0
- package/docs/superpowers/plans/2026-09-03-cli-platform-correctness-boundary.md +760 -0
- package/docs/superpowers/plans/2026-09-03-dependency-security-remediation.md +133 -0
- package/docs/superpowers/plans/2026-09-04-model-catalog-pricing-sync.md +1078 -0
- package/docs/superpowers/plans/2026-09-05-codex-project-scan-500.md +128 -0
- package/docs/superpowers/plans/2026-09-06-project-skill-path-fix.md +97 -0
- package/docs/superpowers/specs/2026-08-26-configuration-driven-cli-platform-design.md +408 -0
- package/docs/superpowers/specs/2026-08-30-project-scoped-config-control-design.md +467 -0
- package/docs/superpowers/specs/2026-09-01-claude-session-content-repair-design.md +111 -0
- package/docs/superpowers/specs/2026-09-01-omp-skill-refresh-boundary-design.md +83 -0
- package/docs/superpowers/specs/2026-09-01-realtime-logs-panel-design.md +27 -0
- package/docs/superpowers/specs/2026-09-02-config-driven-platform-boundary-design.md +64 -0
- package/docs/superpowers/specs/2026-09-02-platform-implementation-driver-boundary-design.md +123 -0
- package/docs/superpowers/specs/2026-09-03-cli-platform-correctness-boundary-design.md +249 -0
- package/docs/superpowers/specs/2026-09-03-dependency-security-remediation-design.md +87 -0
- package/docs/superpowers/specs/2026-09-04-model-catalog-pricing-sync-design.md +301 -0
- package/docs/superpowers/specs/2026-09-05-codex-project-scan-500-design.md +24 -0
- package/package.json +8 -4
- package/src/commands/channels.js +88 -191
- package/src/commands/cli-type.js +19 -11
- package/src/commands/daemon.js +104 -32
- package/src/commands/doctor.js +16 -9
- package/src/commands/export-config.js +23 -5
- package/src/commands/list.js +130 -52
- package/src/commands/logs.js +44 -27
- package/src/commands/platform-command-registry.js +78 -0
- package/src/commands/plugin.js +11 -5
- package/src/commands/port-config.js +34 -52
- package/src/commands/proxy-control.js +207 -145
- package/src/commands/proxy.js +2 -2
- package/src/commands/resume.js +87 -91
- package/src/commands/search.js +137 -67
- package/src/commands/stats.js +30 -22
- package/src/commands/switch.js +1 -1
- package/src/commands/toggle-proxy.js +94 -111
- package/src/commands/ui.js +67 -6
- package/src/commands/workspace.js +90 -8
- package/src/config/default.js +6 -53
- package/src/config/loader.js +61 -24
- package/src/config/model-metadata.js +49 -59
- package/src/config/model-metadata.json +4095 -99
- package/src/config/paths.js +181 -8
- package/src/index.js +134 -71
- package/src/platforms/access.js +196 -0
- package/src/{server/services → platforms}/agents-service.js +354 -197
- package/src/platforms/channel-auth-registry.js +165 -0
- package/src/platforms/channel-auth-service.js +107 -0
- package/src/{server/services → platforms}/commands-service.js +195 -115
- package/src/platforms/driver-registry.js +59 -0
- package/src/platforms/drivers/claude/api-operations.js +62 -0
- package/src/{server/services/channels.js → platforms/drivers/claude/channels-implementation.js} +131 -33
- package/src/platforms/drivers/claude/channels.js +39 -0
- package/src/{server/services/config-sync-service.js → platforms/drivers/claude/config-sync.js} +29 -5
- package/src/{server/services → platforms/drivers/claude}/health-check.js +4 -4
- package/src/platforms/drivers/claude/health.js +22 -0
- package/src/platforms/drivers/claude/mcp.js +9 -0
- package/src/{server/services/settings-manager.js → platforms/drivers/claude/native-config-implementation.js} +13 -4
- package/src/platforms/drivers/claude/native-config.js +46 -0
- package/src/platforms/drivers/claude/native-logs.js +72 -0
- package/src/{server/services/claude-openai-gateway.js → platforms/drivers/claude/openai-gateway.js} +8 -65
- package/src/platforms/drivers/claude/project-config.js +21 -0
- package/src/platforms/drivers/claude/projects.js +62 -0
- package/src/platforms/drivers/claude/prompts.js +9 -0
- package/src/{server/proxy-server.js → platforms/drivers/claude/proxy-implementation.js} +41 -278
- package/src/platforms/drivers/claude/proxy.js +16 -0
- package/src/platforms/drivers/claude/resource-sync.js +9 -0
- package/src/platforms/drivers/claude/session-history-adapter.js +313 -0
- package/src/{server/services/sessions.js → platforms/drivers/claude/sessions-implementation.js} +145 -35
- package/src/platforms/drivers/claude/sessions.js +76 -0
- package/src/{server/services/claude-statistics-service.js → platforms/drivers/claude/statistics-implementation.js} +2 -2
- package/src/platforms/drivers/claude/statistics.js +16 -0
- package/src/{server/services/claude-wire.js → platforms/drivers/claude/wire.js} +1 -1
- package/src/platforms/drivers/codex/api-operations.js +9 -0
- package/src/{server/services/codex-channels.js → platforms/drivers/codex/channels-implementation.js} +149 -14
- package/src/platforms/drivers/codex/channels.js +45 -0
- package/src/{server/services/codex-config.js → platforms/drivers/codex/config.js} +12 -2
- package/src/{server/services/codex-env-manager.js → platforms/drivers/codex/env-manager.js} +1 -1
- package/src/platforms/drivers/codex/mcp.js +9 -0
- package/src/{server/services/codex-settings-manager.js → platforms/drivers/codex/native-config-implementation.js} +15 -6
- package/src/platforms/drivers/codex/native-config.js +47 -0
- package/src/platforms/drivers/codex/native-logs.js +166 -0
- package/src/platforms/drivers/codex/project-config.js +21 -0
- package/src/platforms/drivers/codex/projects.js +26 -0
- package/src/platforms/drivers/codex/prompts.js +9 -0
- package/src/{server/codex-proxy-server.js → platforms/drivers/codex/proxy-implementation.js} +30 -233
- package/src/platforms/drivers/codex/proxy.js +16 -0
- package/src/platforms/drivers/codex/resource-sync.js +9 -0
- package/src/{server/services/session-history-adapters/codex.js → platforms/drivers/codex/session-history-adapter.js} +37 -23
- package/src/{server/services/codex-sessions.js → platforms/drivers/codex/sessions-implementation.js} +81 -75
- package/src/platforms/drivers/codex/sessions.js +36 -0
- package/src/{server/services/codex-statistics-service.js → platforms/drivers/codex/statistics-implementation.js} +2 -2
- package/src/platforms/drivers/codex/statistics.js +16 -0
- package/src/platforms/drivers/gemini/api-operations.js +9 -0
- package/src/{server/services/gemini-channels.js → platforms/drivers/gemini/channels-implementation.js} +202 -37
- package/src/platforms/drivers/gemini/channels.js +39 -0
- package/src/{server/services/gemini-config.js → platforms/drivers/gemini/config.js} +12 -2
- package/src/platforms/drivers/gemini/mcp.js +9 -0
- package/src/{server/services/gemini-settings-manager.js → platforms/drivers/gemini/native-config-implementation.js} +14 -5
- package/src/platforms/drivers/gemini/native-config.js +39 -0
- package/src/platforms/drivers/gemini/native-logs.js +180 -0
- package/src/platforms/drivers/gemini/project-config.js +21 -0
- package/src/platforms/drivers/gemini/projects.js +26 -0
- package/src/platforms/drivers/gemini/prompts.js +9 -0
- package/src/{server/gemini-proxy-server.js → platforms/drivers/gemini/proxy-implementation.js} +36 -267
- package/src/platforms/drivers/gemini/proxy.js +16 -0
- package/src/platforms/drivers/gemini/resource-sync.js +9 -0
- package/src/{server/services/session-history-adapters/gemini.js → platforms/drivers/gemini/session-history-adapter.js} +70 -18
- package/src/{server/services/gemini-sessions.js → platforms/drivers/gemini/sessions-implementation.js} +53 -37
- package/src/platforms/drivers/gemini/sessions.js +47 -0
- package/src/{server/services/gemini-statistics-service.js → platforms/drivers/gemini/statistics-implementation.js} +2 -2
- package/src/platforms/drivers/gemini/statistics.js +16 -0
- package/src/{server/services/gemini-wire.js → platforms/drivers/gemini/wire.js} +1 -1
- package/src/platforms/drivers/generic-filesystem.js +166 -0
- package/src/platforms/drivers/generic-jsonl.js +141 -0
- package/src/platforms/drivers/generic-mcp.js +44 -0
- package/src/platforms/drivers/generic-openai-compatible.js +45 -0
- package/src/platforms/drivers/generic-prompt.js +41 -0
- package/src/platforms/drivers/legacy.js +677 -0
- package/src/platforms/drivers/native-config-snapshot.js +125 -0
- package/src/platforms/drivers/native-log-utils.js +1292 -0
- package/src/platforms/drivers/notification-hooks.js +578 -0
- package/src/{server/api/omp-config.js → platforms/drivers/omp/api-operations.js} +52 -64
- package/src/{server/services/omp-auth-provider-worker.js → platforms/drivers/omp/auth-provider-worker.js} +1 -1
- package/src/{server/services/omp-auth-providers.js → platforms/drivers/omp/auth-providers.js} +1 -1
- package/src/{server/services/omp-channels.js → platforms/drivers/omp/channels-implementation.js} +166 -39
- package/src/platforms/drivers/omp/channels.js +58 -0
- package/src/{server/services/omp-config.js → platforms/drivers/omp/config.js} +53 -13
- package/src/{server/services/omp-gateway-routing.js → platforms/drivers/omp/gateway-routing.js} +14 -7
- package/src/{server/services/omp-gateway.js → platforms/drivers/omp/gateway.js} +4 -262
- package/src/platforms/drivers/omp/mcp.js +9 -0
- package/src/{server/services/omp-settings-manager.js → platforms/drivers/omp/native-config-implementation.js} +75 -10
- package/src/platforms/drivers/omp/native-config.js +46 -0
- package/src/platforms/drivers/omp/native-logs.js +52 -0
- package/src/{server/services/omp-native-plugin-adapter.js → platforms/drivers/omp/native-plugin-adapter.js} +91 -4
- package/src/platforms/drivers/omp/project-config.js +29 -0
- package/src/platforms/drivers/omp/projects.js +20 -0
- package/src/platforms/drivers/omp/prompts.js +9 -0
- package/src/{server/omp-proxy-server.js → platforms/drivers/omp/proxy-implementation.js} +6 -15
- package/src/platforms/drivers/omp/proxy.js +16 -0
- package/src/platforms/drivers/omp/resource-sync.js +9 -0
- package/src/{server/services/session-history-adapters/omp.js → platforms/drivers/omp/session-history-adapter.js} +42 -18
- package/src/{server/services/omp-sessions.js → platforms/drivers/omp/sessions-implementation.js} +130 -94
- package/src/platforms/drivers/omp/sessions.js +36 -0
- package/src/{server/services/omp-skill-discovery.js → platforms/drivers/omp/skill-discovery.js} +159 -32
- package/src/{server/services/omp-skill-settings-service.js → platforms/drivers/omp/skill-settings.js} +1 -1
- package/src/{server/services/omp-statistics-service.js → platforms/drivers/omp/statistics-implementation.js} +3 -3
- package/src/platforms/drivers/omp/statistics.js +16 -0
- package/src/platforms/drivers/opencode/api-operations.js +9 -0
- package/src/{server/services/opencode-channels.js → platforms/drivers/opencode/channels-implementation.js} +28 -9
- package/src/platforms/drivers/opencode/channels.js +40 -0
- package/src/platforms/drivers/opencode/conversion.js +41 -0
- package/src/{server/services/opencode-gateway-converter.js → platforms/drivers/opencode/gateway-converter.js} +1 -1
- package/src/platforms/drivers/opencode/mcp.js +9 -0
- package/src/{server/services/opencode-settings-manager.js → platforms/drivers/opencode/native-config-implementation.js} +17 -6
- package/src/platforms/drivers/opencode/native-config.js +26 -0
- package/src/platforms/drivers/opencode/native-logs.js +133 -0
- package/src/platforms/drivers/opencode/project-config.js +98 -0
- package/src/platforms/drivers/opencode/projects.js +20 -0
- package/src/platforms/drivers/opencode/prompts.js +9 -0
- package/src/{server/opencode-proxy-server.js → platforms/drivers/opencode/proxy-implementation.js} +27 -362
- package/src/platforms/drivers/opencode/proxy.js +16 -0
- package/src/platforms/drivers/opencode/resource-sync.js +9 -0
- package/src/{server/services/opencode-sessions.js → platforms/drivers/opencode/sessions-implementation.js} +211 -64
- package/src/platforms/drivers/opencode/sessions.js +48 -0
- package/src/{server/services/opencode-statistics-service.js → platforms/drivers/opencode/statistics-implementation.js} +2 -2
- package/src/platforms/drivers/opencode/statistics.js +16 -0
- package/src/platforms/drivers/secure-file-driver.js +272 -0
- package/src/platforms/drivers/unsupported.js +7 -0
- package/src/platforms/manifest-schema.js +398 -0
- package/src/platforms/manifests/claude.json +489 -0
- package/src/platforms/manifests/codex.json +448 -0
- package/src/platforms/manifests/gemini.json +447 -0
- package/src/platforms/manifests/omp.json +496 -0
- package/src/platforms/manifests/opencode.json +422 -0
- package/src/{server/services → platforms}/native-oauth-adapters.js +98 -418
- package/src/{server/services → platforms}/notification-hooks.js +190 -1023
- package/src/platforms/oauth-credentials-service.js +635 -0
- package/src/platforms/path-resolver.js +179 -0
- package/src/platforms/platform-path-context.js +305 -0
- package/src/platforms/registry.js +248 -0
- package/src/platforms/remote-notification-providers.js +678 -0
- package/src/platforms/runtime.js +259 -0
- package/src/{server/services/session-history-adapters/index.js → platforms/session-history-adapters.js} +8 -1
- package/src/plugins/plugin-loader.js +2 -2
- package/src/plugins/plugin-manager.js +2 -2
- package/src/plugins/registry.js +27 -10
- package/src/reset-config.js +21 -5
- package/src/server/api/agents.js +46 -72
- package/src/server/api/commands.js +16 -6
- package/src/server/api/config-registry.js +206 -59
- package/src/server/api/config-sync.js +3 -2
- package/src/server/api/config-templates.js +20 -4
- package/src/server/api/config.js +111 -102
- package/src/server/api/convert.js +54 -178
- package/src/server/api/dashboard.js +168 -102
- package/src/server/api/hooks.js +1 -1
- package/src/server/api/mcp.js +224 -84
- package/src/server/api/platform-api-config.js +102 -0
- package/src/server/api/platform-route-factory.js +215 -0
- package/src/server/api/platform-routes.js +26 -0
- package/src/server/api/platforms.js +110 -0
- package/src/server/api/plugins.js +57 -57
- package/src/server/api/project-config.js +168 -0
- package/src/server/api/prompts.js +58 -45
- package/src/server/api/settings.js +55 -8
- package/src/server/api/skills.js +262 -133
- package/src/server/api/ui-config.js +2 -2
- package/src/server/api/validation-errors.js +18 -4
- package/src/server/api/workspaces.js +47 -12
- package/src/server/index.js +164 -213
- package/src/server/platform-context.js +81 -0
- package/src/server/services/channel-balance.js +133 -24
- package/src/server/services/channel-health.js +21 -6
- package/src/server/services/channel-scheduler.js +29 -39
- package/src/server/services/channel-sync-utils.js +23 -13
- package/src/server/services/config-export-service.js +350 -319
- package/src/server/services/config-registry-service.js +280 -54
- package/src/server/services/config-sync-manager.js +172 -944
- package/src/server/services/config-templates-service.js +419 -353
- package/src/server/services/control-manifest-store.js +206 -0
- package/src/server/services/dashboard-snapshot-worker.js +656 -100
- package/src/server/services/effective-control-service.js +557 -0
- package/src/server/services/local-resource-index.js +314 -0
- package/src/server/services/mcp-service.js +607 -977
- package/src/server/services/model-detector.js +15 -10
- package/src/server/services/native-log-channel-resolver.js +214 -0
- package/src/server/services/native-log-observer.js +291 -0
- package/src/server/services/network-access.js +12 -2
- package/src/server/services/platform-catalog.js +82 -0
- package/src/server/services/platform-resolution.js +32 -29
- package/src/server/services/plugins-service.js +376 -122
- package/src/server/services/project-config-service.js +550 -0
- package/src/server/services/project-path-validation.js +31 -0
- package/src/server/services/prompts-service.js +267 -233
- package/src/server/services/proxy-runtime.js +7 -4
- package/src/server/services/remote-credential-cache.js +43 -0
- package/src/server/services/repo-scanner-base.js +182 -75
- package/src/server/services/request-logger.js +7 -4
- package/src/server/services/session-history-index.js +876 -214
- package/src/server/services/session-history-worker.js +228 -33
- package/src/server/services/session-snapshot-worker.js +124 -34
- package/src/server/services/session-snapshots.js +4 -2
- package/src/server/services/skill-artifact-store.js +355 -0
- package/src/server/services/skill-format-adapters.js +65 -0
- package/src/server/services/skill-projection-service.js +323 -0
- package/src/server/services/skill-refresh-task-service.js +275 -0
- package/src/server/services/skill-service.js +2020 -677
- package/src/server/services/snapshot-cache.js +30 -8
- package/src/server/services/speed-test.js +8 -8
- package/src/server/services/sqlite-connection.js +19 -3
- package/src/server/services/statistics-service.js +240 -57
- package/src/server/services/ui-config.js +107 -76
- package/src/server/services/usage-log-utils.js +98 -0
- package/src/server/services/web-build.js +1 -1
- package/src/server/services/workspace-service.js +59 -29
- package/src/server/utils/pricing.js +10 -19
- package/src/server/websocket-server.js +153 -28
- package/src/{server/services/base → shared}/base-channel-service.js +156 -28
- package/src/shared/capability-driver.js +104 -0
- package/src/shared/channel-auth-policy.js +27 -0
- package/src/shared/channel-driver.js +161 -0
- package/src/shared/driver-factories/api.js +180 -0
- package/src/shared/driver-factories/mcp.js +244 -0
- package/src/shared/driver-factories/projects.js +121 -0
- package/src/shared/driver-factories/prompts.js +58 -0
- package/src/shared/driver-factories/resource-sync.js +375 -0
- package/src/shared/driver-result.js +78 -0
- package/src/shared/mcp-format.js +189 -0
- package/src/shared/platforms.js +104 -167
- package/src/shared/project-config.js +512 -0
- package/src/shared/proxy-driver.js +57 -0
- package/src/{server/services/base → shared}/response-usage-parser.js +8 -4
- package/src/ui/menu.js +91 -67
- package/src/utils/port-helper.js +35 -19
- package/src/utils/session.js +1 -1
- package/dist/web/assets/Analytics-COEUXmd4.js +0 -25
- package/dist/web/assets/Analytics-RNn1BUbG.css +0 -1
- package/dist/web/assets/ConfigTemplates-DLLkS53H.js +0 -1
- package/dist/web/assets/Home-DCEyLzwX.js +0 -1
- package/dist/web/assets/Home-DYVy4cGZ.css +0 -1
- package/dist/web/assets/PluginManager-Oe1ZHoMO.js +0 -1
- package/dist/web/assets/ProjectList-CWjuAFof.js +0 -1
- package/dist/web/assets/ProjectList-VeMNXfgO.css +0 -1
- package/dist/web/assets/SessionList-C4chCPxK.css +0 -1
- package/dist/web/assets/SessionList-v3ehGg9U.js +0 -1
- package/dist/web/assets/SkillManager-BsX8Qpj0.css +0 -1
- package/dist/web/assets/SkillManager-DUF_qP5S.js +0 -1
- package/dist/web/assets/WorkspaceManager-CjAWBosO.js +0 -1
- package/dist/web/assets/icons-nGdSKQn2.js +0 -1
- package/dist/web/assets/index-D5hyHPj7.js +0 -2
- package/dist/web/assets/index-bJRG3RXH.css +0 -1
- package/dist/web/assets/markdown-BNJfEgCA.js +0 -1
- package/dist/web/assets/markdown-BfC0goYb.css +0 -10
- package/dist/web/assets/naive-ui-BALVpMgd.js +0 -1
- package/dist/web/assets/vendors-DQA4Hw9v.js +0 -7
- package/dist/web/assets/vue-vendor-B5RqjEjQ.js +0 -45
- package/src/config/model-pricing.js +0 -35
- package/src/server/api/channels.js +0 -439
- package/src/server/api/claude-hooks.js +0 -56
- package/src/server/api/claude-statistics.js +0 -57
- package/src/server/api/codex-channels.js +0 -491
- package/src/server/api/codex-projects.js +0 -117
- package/src/server/api/codex-proxy.js +0 -293
- package/src/server/api/codex-sessions.js +0 -652
- package/src/server/api/codex-statistics.js +0 -57
- package/src/server/api/gemini-channels.js +0 -392
- package/src/server/api/gemini-projects.js +0 -103
- package/src/server/api/gemini-proxy.js +0 -231
- package/src/server/api/gemini-sessions.js +0 -550
- package/src/server/api/gemini-statistics.js +0 -57
- package/src/server/api/health-check.js +0 -31
- package/src/server/api/oauth-credentials.js +0 -198
- package/src/server/api/omp-channels.js +0 -575
- package/src/server/api/omp-projects.js +0 -88
- package/src/server/api/omp-proxy.js +0 -125
- package/src/server/api/omp-sessions.js +0 -386
- package/src/server/api/omp-statistics.js +0 -40
- package/src/server/api/opencode-channels.js +0 -471
- package/src/server/api/opencode-projects.js +0 -110
- package/src/server/api/opencode-proxy.js +0 -354
- package/src/server/api/opencode-sessions.js +0 -466
- package/src/server/api/opencode-statistics.js +0 -57
- package/src/server/api/projects.js +0 -120
- package/src/server/api/proxy.js +0 -330
- package/src/server/api/sessions.js +0 -810
- package/src/server/services/oauth-credentials-service.js +0 -945
- package/src/server/services/omp-session-log-observer.js +0 -138
- package/src/server/services/opencode-gateway-adapters.js +0 -43
- package/src/server/services/project-snapshots.js +0 -59
- package/src/server/services/proxy-log-helper.js +0 -298
- package/src/server/services/session-cache.js +0 -127
- package/src/server/services/session-converter.js +0 -582
- package/src/server/services/session-history-adapters/claude.js +0 -235
- /package/src/{server/services/claude-token-recovery.js → platforms/drivers/claude/token-recovery.js} +0 -0
- /package/src/{server/services/codex-parser.js → platforms/drivers/codex/parser.js} +0 -0
- /package/src/{server/services/codex-speed-test-template.json → platforms/drivers/codex/speed-test-template.json} +0 -0
- /package/src/{server/services/codex-wire.js → platforms/drivers/codex/wire.js} +0 -0
- /package/src/{server/services/omp-auth-gateway-client.js → platforms/drivers/omp/auth-gateway-client.js} +0 -0
- /package/src/{server/services/opencode-normalization.js → platforms/drivers/opencode/normalization.js} +0 -0
- /package/src/{server/services → shared}/config-artifact-paths.js +0 -0
- /package/src/{server/services/base → shared}/proxy-utils.js +0 -0
package/src/server/api/agents.js
CHANGED
|
@@ -7,48 +7,55 @@
|
|
|
7
7
|
const express = require('express');
|
|
8
8
|
const fs = require('fs');
|
|
9
9
|
const path = require('path');
|
|
10
|
-
const { AgentsService } = require('
|
|
10
|
+
const { AgentsService } = require('../../platforms/agents-service');
|
|
11
11
|
const { PATHS, HOME_DIR } = require('../../config/paths');
|
|
12
|
+
const { resolvePlatform, createPlatformAccessError } = require('../../platforms/access');
|
|
13
|
+
const { getPlatformCatalog } = require('../services/platform-catalog');
|
|
12
14
|
const { sendApiError } = require('./validation-errors');
|
|
13
15
|
|
|
14
16
|
const router = express.Router();
|
|
15
|
-
const SUPPORTED_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode'];
|
|
16
17
|
const agentServices = new Map();
|
|
17
18
|
const DEFAULT_PROJECT_ALLOWED_ROOTS = [HOME_DIR, process.cwd()];
|
|
18
19
|
|
|
19
|
-
function isSupportedPlatform(platform) {
|
|
20
|
-
return SUPPORTED_PLATFORMS.includes(platform);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
20
|
function getRawPlatform(req) {
|
|
24
21
|
const queryPlatform = typeof req.query?.platform === 'string' ? req.query.platform.trim() : '';
|
|
25
22
|
const bodyPlatform = typeof req.body?.platform === 'string' ? req.body.platform.trim() : '';
|
|
26
23
|
return queryPlatform || bodyPlatform || '';
|
|
27
24
|
}
|
|
28
25
|
|
|
29
|
-
function resolvePlatform(rawPlatform) {
|
|
30
|
-
return rawPlatform || 'claude';
|
|
31
|
-
}
|
|
32
|
-
|
|
33
26
|
function getPlatform(req) {
|
|
34
|
-
return resolvePlatform(getRawPlatform(req));
|
|
27
|
+
return resolvePlatform(getRawPlatform(req), { fallback: 'claude' }).key;
|
|
35
28
|
}
|
|
36
29
|
|
|
37
30
|
function getAgentsService(req) {
|
|
38
31
|
const platform = getPlatform(req);
|
|
39
32
|
if (!agentServices.has(platform)) {
|
|
40
|
-
|
|
33
|
+
const service = new AgentsService(platform);
|
|
34
|
+
if (!service || typeof service.listAgents !== 'function') {
|
|
35
|
+
throw createPlatformAccessError('unsupported', {
|
|
36
|
+
platform,
|
|
37
|
+
capability: 'agents',
|
|
38
|
+
message: `Agents are not supported for ${platform}`
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
agentServices.set(platform, service);
|
|
41
42
|
}
|
|
42
43
|
return { platform, service: agentServices.get(platform) };
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
function
|
|
46
|
+
function getAgentCapabilities(platform, service) {
|
|
47
|
+
const capabilities = service?.getCapabilities?.();
|
|
48
|
+
if (capabilities && typeof capabilities === 'object') return capabilities;
|
|
49
|
+
return getPlatformCatalog().get(platform)?.agentCapabilities || { projectScope: true, repoOperations: true };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function validateScopeForPlatform(scope, _platform, projectPath, capabilities = {}) {
|
|
46
53
|
if (!['user', 'project'].includes(scope)) {
|
|
47
54
|
return '无效的 scope,必须是 user 或 project';
|
|
48
55
|
}
|
|
49
56
|
|
|
50
|
-
if (
|
|
51
|
-
return '
|
|
57
|
+
if (scope === 'project' && capabilities.projectScope === false) {
|
|
58
|
+
return '该平台仅支持 user 作用域代理';
|
|
52
59
|
}
|
|
53
60
|
|
|
54
61
|
if (scope === 'project' && !projectPath) {
|
|
@@ -73,11 +80,16 @@ function validateAgentFileName(fileName) {
|
|
|
73
80
|
}
|
|
74
81
|
return null;
|
|
75
82
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
83
|
+
function rejectUnsupportedRepo(res, platform, service, operation = '') {
|
|
84
|
+
const capabilities = getAgentCapabilities(platform, service);
|
|
85
|
+
if (capabilities.repoOperations !== false) return false;
|
|
86
|
+
return res.status(400).json({
|
|
87
|
+
success: false,
|
|
88
|
+
message: service.getRepoOperationError?.(operation) || `该平台暂不支持远程仓库代理${operation ? ' ' + operation : ''}`
|
|
89
|
+
});
|
|
79
90
|
}
|
|
80
91
|
|
|
92
|
+
|
|
81
93
|
function validateRepoPath(repoPath) {
|
|
82
94
|
if (typeof repoPath !== 'string' || !repoPath.trim()) {
|
|
83
95
|
return '代理仓库路径不能为空';
|
|
@@ -211,14 +223,13 @@ function normalizeOptionalProjectPath(projectPath) {
|
|
|
211
223
|
}
|
|
212
224
|
|
|
213
225
|
router.use((req, res, next) => {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
return
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
226
|
+
try {
|
|
227
|
+
getPlatform(req);
|
|
228
|
+
return next();
|
|
229
|
+
} catch (error) {
|
|
230
|
+
console.error('[Agents API] Platform resolution failed:', error);
|
|
231
|
+
return sendApiError(res, error, error.status || 404);
|
|
220
232
|
}
|
|
221
|
-
next();
|
|
222
233
|
});
|
|
223
234
|
|
|
224
235
|
/**
|
|
@@ -279,7 +290,6 @@ router.get('/stats', (req, res) => {
|
|
|
279
290
|
});
|
|
280
291
|
|
|
281
292
|
/**
|
|
282
|
-
* 获取单个代理详情
|
|
283
293
|
* GET /api/agents/:scope/:fileName
|
|
284
294
|
*/
|
|
285
295
|
router.get('/:scope/:fileName', (req, res) => {
|
|
@@ -288,7 +298,7 @@ router.get('/:scope/:fileName', (req, res) => {
|
|
|
288
298
|
const { scope, fileName } = req.params;
|
|
289
299
|
const { projectPath } = req.query;
|
|
290
300
|
|
|
291
|
-
const scopeError = validateScopeForPlatform(scope, platform, projectPath);
|
|
301
|
+
const scopeError = validateScopeForPlatform(scope, platform, projectPath, getAgentCapabilities(platform, service));
|
|
292
302
|
if (scopeError) {
|
|
293
303
|
return res.status(400).json({
|
|
294
304
|
success: false,
|
|
@@ -362,7 +372,6 @@ router.post('/', (req, res) => {
|
|
|
362
372
|
message: '代理文件名不能为空'
|
|
363
373
|
});
|
|
364
374
|
}
|
|
365
|
-
|
|
366
375
|
const fileNameError = validateAgentFileName(fileName);
|
|
367
376
|
if (fileNameError) {
|
|
368
377
|
return res.status(400).json({
|
|
@@ -371,7 +380,7 @@ router.post('/', (req, res) => {
|
|
|
371
380
|
});
|
|
372
381
|
}
|
|
373
382
|
|
|
374
|
-
const scopeError = validateScopeForPlatform(scope, platform, projectPath);
|
|
383
|
+
const scopeError = validateScopeForPlatform(scope, platform, projectPath, getAgentCapabilities(platform, service));
|
|
375
384
|
if (scopeError) {
|
|
376
385
|
return res.status(400).json({
|
|
377
386
|
success: false,
|
|
@@ -437,7 +446,7 @@ router.put('/:scope/:fileName', (req, res) => {
|
|
|
437
446
|
configContent
|
|
438
447
|
} = req.body;
|
|
439
448
|
|
|
440
|
-
const scopeError = validateScopeForPlatform(scope, platform, projectPath);
|
|
449
|
+
const scopeError = validateScopeForPlatform(scope, platform, projectPath, getAgentCapabilities(platform, service));
|
|
441
450
|
if (scopeError) {
|
|
442
451
|
return res.status(400).json({
|
|
443
452
|
success: false,
|
|
@@ -490,7 +499,6 @@ router.put('/:scope/:fileName', (req, res) => {
|
|
|
490
499
|
});
|
|
491
500
|
|
|
492
501
|
/**
|
|
493
|
-
* 删除代理
|
|
494
502
|
* DELETE /api/agents/:scope/:fileName
|
|
495
503
|
*/
|
|
496
504
|
router.delete('/:scope/:fileName', (req, res) => {
|
|
@@ -499,7 +507,7 @@ router.delete('/:scope/:fileName', (req, res) => {
|
|
|
499
507
|
const { scope, fileName } = req.params;
|
|
500
508
|
const { projectPath } = req.query;
|
|
501
509
|
|
|
502
|
-
const scopeError = validateScopeForPlatform(scope, platform, projectPath);
|
|
510
|
+
const scopeError = validateScopeForPlatform(scope, platform, projectPath, getAgentCapabilities(platform, service));
|
|
503
511
|
if (scopeError) {
|
|
504
512
|
return res.status(400).json({
|
|
505
513
|
success: false,
|
|
@@ -568,19 +576,10 @@ router.get('/all', async (req, res) => {
|
|
|
568
576
|
}
|
|
569
577
|
});
|
|
570
578
|
|
|
571
|
-
/**
|
|
572
|
-
* 获取仓库列表
|
|
573
|
-
* GET /api/agents/repos
|
|
574
|
-
*/
|
|
575
579
|
router.get('/repos', (req, res) => {
|
|
576
580
|
try {
|
|
577
581
|
const { platform, service } = getAgentsService(req);
|
|
578
|
-
if (
|
|
579
|
-
return res.status(400).json({
|
|
580
|
-
success: false,
|
|
581
|
-
message: 'Codex 平台暂不支持远程仓库代理'
|
|
582
|
-
});
|
|
583
|
-
}
|
|
582
|
+
if (rejectUnsupportedRepo(res, platform, service)) return;
|
|
584
583
|
const repos = service.getRepos();
|
|
585
584
|
res.json({
|
|
586
585
|
success: true,
|
|
@@ -601,12 +600,7 @@ router.get('/repos', (req, res) => {
|
|
|
601
600
|
router.post('/repos', (req, res) => {
|
|
602
601
|
try {
|
|
603
602
|
const { platform, service } = getAgentsService(req);
|
|
604
|
-
if (
|
|
605
|
-
return res.status(400).json({
|
|
606
|
-
success: false,
|
|
607
|
-
message: 'Codex 平台暂不支持远程仓库代理'
|
|
608
|
-
});
|
|
609
|
-
}
|
|
603
|
+
if (rejectUnsupportedRepo(res, platform, service)) return;
|
|
610
604
|
const { owner, name, branch = 'main', directory = '', enabled = true } = req.body;
|
|
611
605
|
|
|
612
606
|
if (!owner || !name) {
|
|
@@ -637,12 +631,7 @@ router.post('/repos', (req, res) => {
|
|
|
637
631
|
router.delete('/repos/:owner/:name', (req, res) => {
|
|
638
632
|
try {
|
|
639
633
|
const { platform, service } = getAgentsService(req);
|
|
640
|
-
if (
|
|
641
|
-
return res.status(400).json({
|
|
642
|
-
success: false,
|
|
643
|
-
message: 'Codex 平台暂不支持远程仓库代理'
|
|
644
|
-
});
|
|
645
|
-
}
|
|
634
|
+
if (rejectUnsupportedRepo(res, platform, service)) return;
|
|
646
635
|
const { owner, name } = req.params;
|
|
647
636
|
const { directory = '' } = req.query;
|
|
648
637
|
const repos = service.removeRepo(owner, name, directory);
|
|
@@ -666,12 +655,7 @@ router.delete('/repos/:owner/:name', (req, res) => {
|
|
|
666
655
|
router.put('/repos/:owner/:name/toggle', (req, res) => {
|
|
667
656
|
try {
|
|
668
657
|
const { platform, service } = getAgentsService(req);
|
|
669
|
-
if (
|
|
670
|
-
return res.status(400).json({
|
|
671
|
-
success: false,
|
|
672
|
-
message: 'Codex 平台暂不支持远程仓库代理'
|
|
673
|
-
});
|
|
674
|
-
}
|
|
658
|
+
if (rejectUnsupportedRepo(res, platform, service)) return;
|
|
675
659
|
const { owner, name } = req.params;
|
|
676
660
|
const { enabled, directory = '' } = req.body;
|
|
677
661
|
|
|
@@ -696,12 +680,7 @@ router.put('/repos/:owner/:name/toggle', (req, res) => {
|
|
|
696
680
|
router.post('/install', async (req, res) => {
|
|
697
681
|
try {
|
|
698
682
|
const { platform, service } = getAgentsService(req);
|
|
699
|
-
if (
|
|
700
|
-
return res.status(400).json({
|
|
701
|
-
success: false,
|
|
702
|
-
message: 'Codex 平台暂不支持远程仓库代理安装'
|
|
703
|
-
});
|
|
704
|
-
}
|
|
683
|
+
if (rejectUnsupportedRepo(res, platform, service, 'install')) return;
|
|
705
684
|
const agent = req.body;
|
|
706
685
|
|
|
707
686
|
if (!agent || !agent.repoOwner || !agent.repoName) {
|
|
@@ -748,12 +727,7 @@ router.post('/install', async (req, res) => {
|
|
|
748
727
|
router.post('/uninstall', (req, res) => {
|
|
749
728
|
try {
|
|
750
729
|
const { platform, service } = getAgentsService(req);
|
|
751
|
-
if (
|
|
752
|
-
return res.status(400).json({
|
|
753
|
-
success: false,
|
|
754
|
-
message: 'Codex 平台暂不支持远程仓库代理卸载'
|
|
755
|
-
});
|
|
756
|
-
}
|
|
730
|
+
if (rejectUnsupportedRepo(res, platform, service, 'uninstall')) return;
|
|
757
731
|
const { fileName } = req.body;
|
|
758
732
|
|
|
759
733
|
if (!fileName) {
|
|
@@ -5,25 +5,35 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const express = require('express');
|
|
8
|
-
const { CommandsService } = require('
|
|
8
|
+
const { CommandsService } = require('../../platforms/commands-service');
|
|
9
|
+
const { resolvePlatform, createPlatformAccessError } = require('../../platforms/access');
|
|
9
10
|
const { sendApiError } = require('./validation-errors');
|
|
10
11
|
|
|
11
12
|
const router = express.Router();
|
|
12
|
-
const SUPPORTED_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode', 'omp'];
|
|
13
13
|
const commandServices = new Map();
|
|
14
14
|
|
|
15
|
-
function
|
|
16
|
-
|
|
15
|
+
function getRawPlatform(req) {
|
|
16
|
+
const queryPlatform = typeof req.query?.platform === 'string' ? req.query.platform.trim() : '';
|
|
17
|
+
const bodyPlatform = typeof req.body?.platform === 'string' ? req.body.platform.trim() : '';
|
|
18
|
+
return queryPlatform || bodyPlatform || '';
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
function getPlatform(req) {
|
|
20
|
-
return resolvePlatform(req
|
|
22
|
+
return resolvePlatform(getRawPlatform(req), { fallback: 'claude' }).key;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
function getCommandsService(req) {
|
|
24
26
|
const platform = getPlatform(req);
|
|
25
27
|
if (!commandServices.has(platform)) {
|
|
26
|
-
|
|
28
|
+
const service = new CommandsService(platform);
|
|
29
|
+
if (!service || typeof service.listCommands !== 'function') {
|
|
30
|
+
throw createPlatformAccessError('unsupported', {
|
|
31
|
+
platform,
|
|
32
|
+
capability: 'commands',
|
|
33
|
+
message: `Commands are not supported for ${platform}`
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
commandServices.set(platform, service);
|
|
27
37
|
}
|
|
28
38
|
return { platform, service: commandServices.get(platform) };
|
|
29
39
|
}
|
|
@@ -6,8 +6,47 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
const express = require('express');
|
|
9
|
-
const
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { ConfigRegistryService, CONFIG_TYPES } = require('../services/config-registry-service');
|
|
10
11
|
const { ConfigSyncManager } = require('../services/config-sync-manager');
|
|
12
|
+
const { ControlManifestStore } = require('../services/control-manifest-store');
|
|
13
|
+
const { EffectiveControlService } = require('../services/effective-control-service');
|
|
14
|
+
const { PATHS } = require('../../config/paths');
|
|
15
|
+
const { SkillProjectionService } = require('../services/skill-projection-service');
|
|
16
|
+
const { validateKnownProjectCwd } = require('../services/project-path-validation');
|
|
17
|
+
const { getPlatformCatalog } = require('../services/platform-catalog');
|
|
18
|
+
const { getPlatformContext } = require('../platform-context');
|
|
19
|
+
|
|
20
|
+
let effectiveControlService;
|
|
21
|
+
|
|
22
|
+
function getEffectiveControlService() {
|
|
23
|
+
if (!effectiveControlService && PATHS.effectiveControlManifest) {
|
|
24
|
+
effectiveControlService = new EffectiveControlService({
|
|
25
|
+
store: new ControlManifestStore({
|
|
26
|
+
userPath: PATHS.effectiveControlManifest,
|
|
27
|
+
projectPathResolver: ({ projectPath }) => require('path').join(projectPath, '.ctx-control.json')
|
|
28
|
+
}),
|
|
29
|
+
projection: new SkillProjectionService({
|
|
30
|
+
registry: getPlatformContext().registry
|
|
31
|
+
})
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return effectiveControlService;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function resolveSkillControlScope(body = {}) {
|
|
38
|
+
const scope = body.scope || 'user';
|
|
39
|
+
if (scope === 'user') return { scope, projectPath: null };
|
|
40
|
+
if (scope !== 'project') throw new Error('Invalid scope: expected "user" or "project"');
|
|
41
|
+
const projectPath = await validateKnownProjectCwd(body.projectPath || body.cwd);
|
|
42
|
+
if (!projectPath) throw new Error('Project scope requires a valid projectPath');
|
|
43
|
+
return { scope, projectPath };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function fallbackSkillControlKey(name, platform, scopeOptions) {
|
|
47
|
+
const location = scopeOptions.scope === 'project' ? scopeOptions.projectPath : 'user';
|
|
48
|
+
return `skill:${platform}:${scopeOptions.scope}:${location}:local:${platform}:${name}`;
|
|
49
|
+
}
|
|
11
50
|
|
|
12
51
|
const router = express.Router();
|
|
13
52
|
const registryService = new ConfigRegistryService();
|
|
@@ -16,8 +55,9 @@ const syncManager = new ConfigSyncManager();
|
|
|
16
55
|
// Valid config types
|
|
17
56
|
const VALID_TYPES = CONFIG_TYPES;
|
|
18
57
|
|
|
19
|
-
|
|
20
|
-
|
|
58
|
+
function getValidPlatforms() {
|
|
59
|
+
return getPlatformCatalog().keys();
|
|
60
|
+
}
|
|
21
61
|
|
|
22
62
|
/**
|
|
23
63
|
* Validate config type parameter
|
|
@@ -37,46 +77,78 @@ function validateType(type) {
|
|
|
37
77
|
* @returns {string|null} Error message or null if valid
|
|
38
78
|
*/
|
|
39
79
|
function validatePlatform(platform) {
|
|
40
|
-
|
|
41
|
-
|
|
80
|
+
const validPlatforms = getValidPlatforms();
|
|
81
|
+
if (!validPlatforms.includes(String(platform || '').trim().toLowerCase())) {
|
|
82
|
+
return `Invalid platform: ${platform}. Must be one of: ${validPlatforms.join(', ')}`;
|
|
42
83
|
}
|
|
43
84
|
return null;
|
|
44
85
|
}
|
|
45
86
|
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
87
|
+
const RESERVED_REGISTRY_NAMES = new Set(['__proto__', 'constructor', 'prototype']);
|
|
88
|
+
|
|
89
|
+
function normalizeRegistryName(name) {
|
|
90
|
+
const raw = String(name || '').replace(/\\/g, '/').trim();
|
|
91
|
+
const normalized = path.posix.normalize(raw).replace(/^(\.\/)+/, '');
|
|
92
|
+
if (
|
|
93
|
+
!raw
|
|
94
|
+
|| raw.includes('\0')
|
|
95
|
+
|| path.posix.isAbsolute(raw)
|
|
96
|
+
|| !normalized
|
|
97
|
+
|| normalized === '.'
|
|
98
|
+
|| normalized === '..'
|
|
99
|
+
|| normalized.startsWith('../')
|
|
100
|
+
|| RESERVED_REGISTRY_NAMES.has(normalized)
|
|
101
|
+
) {
|
|
102
|
+
throw new Error('Invalid config name');
|
|
58
103
|
}
|
|
104
|
+
return normalized;
|
|
59
105
|
}
|
|
60
106
|
|
|
61
|
-
function
|
|
62
|
-
|
|
63
|
-
if (method && typeof syncManager[method] === 'function') {
|
|
64
|
-
syncManager[method](type, name);
|
|
65
|
-
}
|
|
107
|
+
async function syncPlatform(type, name, platform) {
|
|
108
|
+
return syncManager.syncToPlatform(platform, type, name);
|
|
66
109
|
}
|
|
67
110
|
|
|
68
|
-
function
|
|
69
|
-
|
|
70
|
-
if (platforms?.[platform]) {
|
|
71
|
-
syncPlatform(type, name, platform);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
111
|
+
async function removePlatform(type, name, platform) {
|
|
112
|
+
return syncManager.removeFromPlatform(platform, type, name);
|
|
74
113
|
}
|
|
75
114
|
|
|
76
|
-
function
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
115
|
+
async function syncEnabledPlatforms(type, name, platforms = {}) {
|
|
116
|
+
await Promise.all(getValidPlatforms()
|
|
117
|
+
.filter(platform => platforms?.[platform])
|
|
118
|
+
.map(platform => syncPlatform(type, name, platform)));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function removeAllPlatforms(type, name) {
|
|
122
|
+
await Promise.all(getValidPlatforms().map(platform => removePlatform(type, name, platform)));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function getEffectiveSkillRegistryItems() {
|
|
126
|
+
registryService.migrateSkillControls?.();
|
|
127
|
+
const items = registryService.listItems('skills');
|
|
128
|
+
const controlService = getEffectiveControlService();
|
|
129
|
+
if (!controlService?.getSkill) return items;
|
|
130
|
+
|
|
131
|
+
return Object.fromEntries(Object.entries(items || {}).map(([rawName, item]) => {
|
|
132
|
+
const name = normalizeRegistryName(rawName);
|
|
133
|
+
const controlKeys = {};
|
|
134
|
+
let hasControl = false;
|
|
135
|
+
for (const platform of getValidPlatforms()) {
|
|
136
|
+
const controlKey = item.controlKey || fallbackSkillControlKey(name, platform, { scope: 'user', projectPath: null });
|
|
137
|
+
const control = controlService.getSkill(controlKey, { scope: 'user' });
|
|
138
|
+
if (!control) continue;
|
|
139
|
+
hasControl = true;
|
|
140
|
+
controlKeys[platform] = control.controlKey || controlKey;
|
|
141
|
+
platforms[platform] = control.enabled === true
|
|
142
|
+
&& control.trust === 'approved'
|
|
143
|
+
&& control.artifact?.state === 'ready';
|
|
144
|
+
}
|
|
145
|
+
return [
|
|
146
|
+
name,
|
|
147
|
+
hasControl
|
|
148
|
+
? { ...item, enabled: Object.values(platforms).some(Boolean), platforms, controlKeys }
|
|
149
|
+
: { ...item, platforms }
|
|
150
|
+
];
|
|
151
|
+
}));
|
|
80
152
|
}
|
|
81
153
|
|
|
82
154
|
/**
|
|
@@ -86,6 +158,19 @@ function removeAllPlatforms(type, name) {
|
|
|
86
158
|
router.get('/stats', async (req, res) => {
|
|
87
159
|
try {
|
|
88
160
|
const stats = registryService.getStats();
|
|
161
|
+
if (stats?.byType?.skills) {
|
|
162
|
+
const effectiveSkills = Object.values(getEffectiveSkillRegistryItems());
|
|
163
|
+
const skillStats = {
|
|
164
|
+
total: effectiveSkills.length,
|
|
165
|
+
enabled: effectiveSkills.filter(item => item.enabled).length,
|
|
166
|
+
disabled: effectiveSkills.filter(item => !item.enabled).length
|
|
167
|
+
};
|
|
168
|
+
for (const platform of getValidPlatforms()) {
|
|
169
|
+
skillStats[platform] = effectiveSkills.filter(item => item.platforms?.[platform]).length;
|
|
170
|
+
stats.byPlatform[platform] += skillStats[platform] - (stats.byType.skills[platform] || 0);
|
|
171
|
+
}
|
|
172
|
+
stats.byType.skills = skillStats;
|
|
173
|
+
}
|
|
89
174
|
|
|
90
175
|
res.json({
|
|
91
176
|
success: true,
|
|
@@ -117,7 +202,9 @@ router.get('/:type', async (req, res) => {
|
|
|
117
202
|
});
|
|
118
203
|
}
|
|
119
204
|
|
|
120
|
-
const items =
|
|
205
|
+
const items = type === 'skills'
|
|
206
|
+
? getEffectiveSkillRegistryItems()
|
|
207
|
+
: registryService.listItems(type);
|
|
121
208
|
|
|
122
209
|
res.json({
|
|
123
210
|
success: true,
|
|
@@ -163,7 +250,7 @@ router.post('/:type/import', async (req, res) => {
|
|
|
163
250
|
for (const name of result.items) {
|
|
164
251
|
const item = registryService.getItem(type, name);
|
|
165
252
|
if (item && item.enabled && item.platforms?.claude) {
|
|
166
|
-
|
|
253
|
+
await syncPlatform(type, name, 'claude');
|
|
167
254
|
}
|
|
168
255
|
}
|
|
169
256
|
}
|
|
@@ -196,7 +283,7 @@ router.post('/:type/import', async (req, res) => {
|
|
|
196
283
|
router.put('/:type/:name/toggle', async (req, res) => {
|
|
197
284
|
try {
|
|
198
285
|
const { type } = req.params;
|
|
199
|
-
const name = decodeURIComponent(req.params.name);
|
|
286
|
+
const name = normalizeRegistryName(decodeURIComponent(req.params.name));
|
|
200
287
|
const { enabled } = req.body;
|
|
201
288
|
|
|
202
289
|
const typeError = validateType(type);
|
|
@@ -223,25 +310,43 @@ router.put('/:type/:name/toggle', async (req, res) => {
|
|
|
223
310
|
});
|
|
224
311
|
}
|
|
225
312
|
|
|
226
|
-
|
|
227
|
-
|
|
313
|
+
if (type === 'skills') {
|
|
314
|
+
const platform = String(req.body.platform || 'claude').trim().toLowerCase();
|
|
315
|
+
const platformError = validatePlatform(platform);
|
|
316
|
+
if (platformError) {
|
|
317
|
+
return res.status(400).json({ success: false, message: platformError });
|
|
318
|
+
}
|
|
319
|
+
registryService.migrateSkillControls?.();
|
|
320
|
+
const controlService = getEffectiveControlService();
|
|
321
|
+
if (!controlService) throw new Error('Effective control service is unavailable');
|
|
322
|
+
const scopeOptions = await resolveSkillControlScope(req.body);
|
|
323
|
+
const item = controlService.setSkillEnabled({
|
|
324
|
+
controlKey: req.body.controlKey || existing.controlKey || fallbackSkillControlKey(name, platform, scopeOptions),
|
|
325
|
+
platform,
|
|
326
|
+
...scopeOptions,
|
|
327
|
+
enabled
|
|
328
|
+
});
|
|
329
|
+
return res.json({ success: true, item });
|
|
330
|
+
}
|
|
228
331
|
|
|
229
|
-
//
|
|
332
|
+
// Commands, agents and plugins retain the legacy registry behaviour.
|
|
333
|
+
const item = registryService.toggleEnabled(type, name, enabled);
|
|
230
334
|
if (enabled) {
|
|
231
335
|
// Sync to platforms where platform=true
|
|
232
|
-
syncEnabledPlatforms(type, name, item.platforms);
|
|
336
|
+
await syncEnabledPlatforms(type, name, item.platforms);
|
|
233
337
|
} else {
|
|
234
|
-
// Remove from all platforms
|
|
235
|
-
removeAllPlatforms(type, name);
|
|
338
|
+
// Remove from all platforms.
|
|
339
|
+
await removeAllPlatforms(type, name);
|
|
236
340
|
}
|
|
237
341
|
|
|
238
|
-
res.json({
|
|
342
|
+
return res.json({
|
|
239
343
|
success: true,
|
|
240
344
|
item
|
|
241
345
|
});
|
|
242
346
|
} catch (err) {
|
|
243
347
|
console.error('[ConfigRegistry API] Toggle enabled error:', err);
|
|
244
|
-
|
|
348
|
+
const status = /Invalid config name|Absolute path|Invalid platform/i.test(err.message || '') ? 400 : 500;
|
|
349
|
+
res.status(status).json({
|
|
245
350
|
success: false,
|
|
246
351
|
message: err.message
|
|
247
352
|
});
|
|
@@ -259,8 +364,9 @@ router.put('/:type/:name/toggle', async (req, res) => {
|
|
|
259
364
|
*/
|
|
260
365
|
router.put('/:type/:name/platform/:platform', async (req, res) => {
|
|
261
366
|
try {
|
|
262
|
-
const { type
|
|
263
|
-
const
|
|
367
|
+
const { type } = req.params;
|
|
368
|
+
const platform = String(req.params.platform || '').trim().toLowerCase();
|
|
369
|
+
const name = normalizeRegistryName(decodeURIComponent(req.params.name));
|
|
264
370
|
const { enabled } = req.body;
|
|
265
371
|
|
|
266
372
|
const typeError = validateType(type);
|
|
@@ -295,25 +401,37 @@ router.put('/:type/:name/platform/:platform', async (req, res) => {
|
|
|
295
401
|
});
|
|
296
402
|
}
|
|
297
403
|
|
|
298
|
-
|
|
299
|
-
|
|
404
|
+
if (type === 'skills') {
|
|
405
|
+
registryService.migrateSkillControls?.();
|
|
406
|
+
const controlService = getEffectiveControlService();
|
|
407
|
+
if (!controlService) throw new Error('Effective control service is unavailable');
|
|
408
|
+
const scopeOptions = await resolveSkillControlScope(req.body);
|
|
409
|
+
const item = controlService.setSkillEnabled({
|
|
410
|
+
controlKey: req.body.controlKey || existing.controlKey || fallbackSkillControlKey(name, platform, scopeOptions),
|
|
411
|
+
platform,
|
|
412
|
+
...scopeOptions,
|
|
413
|
+
enabled
|
|
414
|
+
});
|
|
415
|
+
return res.json({ success: true, item });
|
|
416
|
+
}
|
|
300
417
|
|
|
301
|
-
|
|
418
|
+
const item = registryService.togglePlatform(type, name, platform, enabled);
|
|
302
419
|
if (enabled && item.enabled) {
|
|
303
420
|
// Sync to this platform (only if item is enabled)
|
|
304
|
-
syncPlatform(type, name, platform);
|
|
421
|
+
await syncPlatform(type, name, platform);
|
|
305
422
|
} else {
|
|
306
|
-
// Remove from this platform
|
|
307
|
-
removePlatform(type, name, platform);
|
|
423
|
+
// Remove from this platform.
|
|
424
|
+
await removePlatform(type, name, platform);
|
|
308
425
|
}
|
|
309
426
|
|
|
310
|
-
res.json({
|
|
427
|
+
return res.json({
|
|
311
428
|
success: true,
|
|
312
429
|
item
|
|
313
430
|
});
|
|
314
431
|
} catch (err) {
|
|
315
432
|
console.error('[ConfigRegistry API] Toggle platform error:', err);
|
|
316
|
-
|
|
433
|
+
const status = /Invalid config name|Absolute path|Invalid platform/i.test(err.message || '') ? 400 : 500;
|
|
434
|
+
res.status(status).json({
|
|
317
435
|
success: false,
|
|
318
436
|
message: err.message
|
|
319
437
|
});
|
|
@@ -337,13 +455,42 @@ router.post('/:type/sync', async (req, res) => {
|
|
|
337
455
|
});
|
|
338
456
|
}
|
|
339
457
|
|
|
340
|
-
|
|
341
|
-
|
|
458
|
+
const items = type === 'skills'
|
|
459
|
+
? getEffectiveSkillRegistryItems()
|
|
460
|
+
: registryService.listItems(type);
|
|
461
|
+
if (type === 'skills') {
|
|
462
|
+
const controlService = getEffectiveControlService();
|
|
463
|
+
if (!controlService) throw new Error('Effective control service is unavailable');
|
|
464
|
+
const results = [];
|
|
465
|
+
const errors = [];
|
|
466
|
+
for (const [name, item] of Object.entries(items || {})) {
|
|
467
|
+
for (const platform of getValidPlatforms()) {
|
|
468
|
+
if (!item?.platforms?.[platform]) continue;
|
|
469
|
+
try {
|
|
470
|
+
results.push(controlService.setSkillEnabled({
|
|
471
|
+
controlKey: item.controlKeys?.[platform] || item.controlKey || fallbackSkillControlKey(name, platform, { scope: 'user', projectPath: null }),
|
|
472
|
+
platform,
|
|
473
|
+
scope: 'user',
|
|
474
|
+
enabled: true
|
|
475
|
+
}));
|
|
476
|
+
} catch (error) {
|
|
477
|
+
errors.push({ name, platform, message: error.message });
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return res.json({
|
|
482
|
+
success: true,
|
|
483
|
+
type,
|
|
484
|
+
synced: results.filter(result => result.enabled).length,
|
|
485
|
+
removed: 0,
|
|
486
|
+
errors,
|
|
487
|
+
warnings: []
|
|
488
|
+
});
|
|
489
|
+
}
|
|
342
490
|
|
|
343
491
|
// Sync all items based on their registry state
|
|
344
|
-
const result = syncManager.syncAll(type, items);
|
|
345
|
-
|
|
346
|
-
res.json({
|
|
492
|
+
const result = await syncManager.syncAll(type, items);
|
|
493
|
+
return res.json({
|
|
347
494
|
success: true,
|
|
348
495
|
type,
|
|
349
496
|
synced: result.synced.length,
|
|
@@ -352,7 +499,7 @@ router.post('/:type/sync', async (req, res) => {
|
|
|
352
499
|
warnings: result.warnings
|
|
353
500
|
});
|
|
354
501
|
} catch (err) {
|
|
355
|
-
console.error('[ConfigRegistry API] Sync
|
|
502
|
+
console.error('[ConfigRegistry API] Sync error:', err);
|
|
356
503
|
res.status(500).json({
|
|
357
504
|
success: false,
|
|
358
505
|
message: err.message
|