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
|
@@ -10,18 +10,42 @@ const path = require('path');
|
|
|
10
10
|
const os = require('os');
|
|
11
11
|
const toml = require('toml');
|
|
12
12
|
const tomlStringify = require('@iarna/toml').stringify;
|
|
13
|
-
const { RepoScannerBase } = require('
|
|
14
|
-
const {
|
|
15
|
-
const {
|
|
13
|
+
const { RepoScannerBase } = require('../server/services/repo-scanner-base');
|
|
14
|
+
const { LocalResourceIndex } = require('../server/services/local-resource-index');
|
|
15
|
+
const { NATIVE_PATHS } = require('../config/paths');
|
|
16
|
+
const { resolvePreferredHomeDir } = require('../utils/home-dir');
|
|
17
|
+
const { createPlatformAccessError } = require('./access');
|
|
18
|
+
const { getPlatformContext } = require('../server/platform-context');
|
|
16
19
|
const {
|
|
17
20
|
normalizeSafeFileStem,
|
|
18
21
|
normalizeSafeRelativePath,
|
|
19
22
|
resolveInsideRoot
|
|
20
|
-
} = require('
|
|
23
|
+
} = require('../shared/config-artifact-paths');
|
|
24
|
+
|
|
25
|
+
function readAgentMetadata(filePath) {
|
|
26
|
+
const fd = fs.openSync(filePath, 'r');
|
|
27
|
+
const buffer = Buffer.alloc(8192);
|
|
28
|
+
const chunks = [];
|
|
29
|
+
let total = 0;
|
|
30
|
+
try {
|
|
31
|
+
while (total < 1024 * 1024) {
|
|
32
|
+
const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, null);
|
|
33
|
+
if (!bytesRead) break;
|
|
34
|
+
chunks.push(Buffer.from(buffer.subarray(0, bytesRead)));
|
|
35
|
+
total += bytesRead;
|
|
36
|
+
let text = Buffer.concat(chunks).toString('utf8');
|
|
37
|
+
if (text.charCodeAt(0) === 0xFEFF) text = text.slice(1);
|
|
38
|
+
if (total === bytesRead && !/^\s*---\s*(?:\n|$)/.test(text)) return {};
|
|
39
|
+
if (/^\s*---\s*\n[\s\S]*?\n\s*---\s*(?:\n|$)/.test(text)) return parseFrontmatter(text).frontmatter;
|
|
40
|
+
}
|
|
41
|
+
return {};
|
|
42
|
+
} finally {
|
|
43
|
+
fs.closeSync(fd);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
21
46
|
|
|
22
47
|
// 默认仓库源
|
|
23
48
|
const DEFAULT_REPOS = [];
|
|
24
|
-
const SUPPORTED_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode'];
|
|
25
49
|
const OPENCODE_CONFIG_DIR = NATIVE_PATHS.opencode.config;
|
|
26
50
|
const CODEX_CONFIG_PATH = NATIVE_PATHS.codex.config;
|
|
27
51
|
const HOME_DIR = resolvePreferredHomeDir(process.platform, process.env, os.homedir());
|
|
@@ -35,7 +59,8 @@ const PLATFORM_CONFIG = {
|
|
|
35
59
|
claude: {
|
|
36
60
|
userAgentsDir: CLAUDE_AGENTS_DIR,
|
|
37
61
|
projectAgentsDir: (projectPath) => path.join(projectPath, '.claude', 'agents'),
|
|
38
|
-
repoType: 'agents'
|
|
62
|
+
repoType: 'agents',
|
|
63
|
+
capabilities: { projectScope: true, repoOperations: true }
|
|
39
64
|
},
|
|
40
65
|
opencode: {
|
|
41
66
|
userAgentsDir: path.join(OPENCODE_CONFIG_DIR, 'agents'),
|
|
@@ -48,24 +73,33 @@ const PLATFORM_CONFIG = {
|
|
|
48
73
|
}
|
|
49
74
|
return modern;
|
|
50
75
|
},
|
|
51
|
-
repoType: 'opencode-agents'
|
|
76
|
+
repoType: 'opencode-agents',
|
|
77
|
+
capabilities: { projectScope: true, repoOperations: true }
|
|
52
78
|
},
|
|
53
79
|
codex: {
|
|
54
80
|
userAgentsDir: CODEX_AGENTS_DIR,
|
|
55
81
|
projectAgentsDir: () => null,
|
|
56
|
-
repoType: 'agents'
|
|
82
|
+
repoType: 'agents',
|
|
83
|
+
capabilities: { projectScope: false, repoOperations: false }
|
|
57
84
|
},
|
|
58
85
|
gemini: {
|
|
59
86
|
userAgentsDir: GEMINI_AGENTS_DIR,
|
|
60
87
|
projectAgentsDir: (projectPath) => path.join(projectPath, '.gemini', 'agents'),
|
|
61
|
-
repoType: 'gemini-agents'
|
|
88
|
+
repoType: 'gemini-agents',
|
|
89
|
+
capabilities: { projectScope: true, repoOperations: true }
|
|
62
90
|
}
|
|
63
91
|
};
|
|
64
92
|
|
|
65
93
|
function normalizePlatform(platform) {
|
|
66
|
-
const normalized = typeof platform === 'string' && platform.trim()
|
|
67
|
-
|
|
68
|
-
|
|
94
|
+
const normalized = typeof platform === 'string' && platform.trim()
|
|
95
|
+
? platform.trim().toLowerCase()
|
|
96
|
+
: 'claude';
|
|
97
|
+
if (!Object.prototype.hasOwnProperty.call(PLATFORM_CONFIG, normalized)) {
|
|
98
|
+
throw createPlatformAccessError('unsupported', {
|
|
99
|
+
platform: normalized,
|
|
100
|
+
capability: 'agents',
|
|
101
|
+
message: `Agents are not supported for ${normalized}`
|
|
102
|
+
});
|
|
69
103
|
}
|
|
70
104
|
return normalized;
|
|
71
105
|
}
|
|
@@ -137,31 +171,31 @@ function writeFileAtomic(filePath, content) {
|
|
|
137
171
|
fs.renameSync(tempPath, filePath);
|
|
138
172
|
}
|
|
139
173
|
|
|
140
|
-
function readCodexTomlConfig() {
|
|
141
|
-
if (!fs.existsSync(
|
|
174
|
+
function readCodexTomlConfig(configPath = CODEX_CONFIG_PATH) {
|
|
175
|
+
if (!fs.existsSync(configPath)) {
|
|
142
176
|
return {};
|
|
143
177
|
}
|
|
144
178
|
|
|
145
179
|
try {
|
|
146
|
-
const content = fs.readFileSync(
|
|
180
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
147
181
|
return toml.parse(content);
|
|
148
182
|
} catch (err) {
|
|
149
183
|
throw new Error(`读取 Codex config.toml 失败: ${err.message}`);
|
|
150
184
|
}
|
|
151
185
|
}
|
|
152
186
|
|
|
153
|
-
function writeCodexTomlConfig(config) {
|
|
154
|
-
ensureDir(path.dirname(
|
|
155
|
-
writeFileAtomic(
|
|
187
|
+
function writeCodexTomlConfig(config, configPath = CODEX_CONFIG_PATH) {
|
|
188
|
+
ensureDir(path.dirname(configPath));
|
|
189
|
+
writeFileAtomic(configPath, tomlStringify(config));
|
|
156
190
|
}
|
|
157
191
|
|
|
158
192
|
function isPlainObject(value) {
|
|
159
193
|
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
160
194
|
}
|
|
161
195
|
|
|
162
|
-
function getCodexManagedAgentConfigPath(fileName) {
|
|
196
|
+
function getCodexManagedAgentConfigPath(fileName, agentsDir = CODEX_AGENTS_DIR) {
|
|
163
197
|
const safeFileName = assertSafeAgentFileName(fileName);
|
|
164
|
-
return resolveInsideRoot(
|
|
198
|
+
return resolveInsideRoot(agentsDir, `${safeFileName}.toml`, 'Codex agent config path');
|
|
165
199
|
}
|
|
166
200
|
|
|
167
201
|
function normalizeCodexConfigPath(configPath) {
|
|
@@ -207,34 +241,34 @@ function assertSafeCodexConfigPath(configPath) {
|
|
|
207
241
|
return relative;
|
|
208
242
|
}
|
|
209
243
|
|
|
210
|
-
function resolveCodexConfigPath(configPath) {
|
|
244
|
+
function resolveCodexConfigPath(configPath, configBasePath = CODEX_CONFIG_PATH, homeDir = HOME_DIR) {
|
|
211
245
|
const normalized = normalizeCodexConfigPath(configPath);
|
|
212
246
|
if (!normalized) return '';
|
|
213
247
|
|
|
214
248
|
if (normalized.startsWith('~/')) {
|
|
215
|
-
return path.join(
|
|
249
|
+
return path.join(homeDir, normalized.slice(2));
|
|
216
250
|
}
|
|
217
251
|
|
|
218
252
|
if (path.isAbsolute(normalized)) {
|
|
219
253
|
return normalized;
|
|
220
254
|
}
|
|
221
255
|
|
|
222
|
-
return path.resolve(path.dirname(
|
|
256
|
+
return path.resolve(path.dirname(configBasePath), normalized);
|
|
223
257
|
}
|
|
224
258
|
|
|
225
|
-
function isManagedCodexConfigPath(configPath) {
|
|
226
|
-
const resolved = resolveCodexConfigPath(configPath);
|
|
259
|
+
function isManagedCodexConfigPath(configPath, configBasePath = CODEX_CONFIG_PATH, agentsDir = CODEX_AGENTS_DIR, homeDir = HOME_DIR) {
|
|
260
|
+
const resolved = resolveCodexConfigPath(configPath, configBasePath, homeDir);
|
|
227
261
|
if (!resolved) return false;
|
|
228
|
-
const managedRoot = path.resolve(
|
|
229
|
-
return resolved.startsWith(managedRoot) || resolved === path.resolve(
|
|
262
|
+
const managedRoot = path.resolve(agentsDir) + path.sep;
|
|
263
|
+
return resolved.startsWith(managedRoot) || resolved === path.resolve(agentsDir);
|
|
230
264
|
}
|
|
231
265
|
|
|
232
|
-
function getManagedCodexConfigResolvedPath(configPath) {
|
|
266
|
+
function getManagedCodexConfigResolvedPath(configPath, configBasePath = CODEX_CONFIG_PATH, agentsDir = CODEX_AGENTS_DIR, homeDir = HOME_DIR) {
|
|
233
267
|
const normalized = normalizeCodexConfigPath(configPath);
|
|
234
|
-
if (!normalized || !isManagedCodexConfigPath(normalized)) {
|
|
268
|
+
if (!normalized || !isManagedCodexConfigPath(normalized, configBasePath, agentsDir, homeDir)) {
|
|
235
269
|
return '';
|
|
236
270
|
}
|
|
237
|
-
return resolveCodexConfigPath(normalized);
|
|
271
|
+
return resolveCodexConfigPath(normalized, configBasePath, homeDir);
|
|
238
272
|
}
|
|
239
273
|
|
|
240
274
|
function normalizeCodexConfigMode(mode) {
|
|
@@ -317,6 +351,7 @@ function readCodexAgentConfigFile(configFilePath) {
|
|
|
317
351
|
* 解析 YAML frontmatter
|
|
318
352
|
*/
|
|
319
353
|
function parseFrontmatter(content) {
|
|
354
|
+
|
|
320
355
|
const result = {
|
|
321
356
|
frontmatter: {},
|
|
322
357
|
body: content
|
|
@@ -354,6 +389,32 @@ function parseFrontmatter(content) {
|
|
|
354
389
|
|
|
355
390
|
return result;
|
|
356
391
|
}
|
|
392
|
+
function readCodexTomlMetadata(filePath) {
|
|
393
|
+
const fd = fs.openSync(filePath, 'r');
|
|
394
|
+
const buffer = Buffer.alloc(8192);
|
|
395
|
+
const chunks = [];
|
|
396
|
+
let total = 0;
|
|
397
|
+
let updatedAt = 0;
|
|
398
|
+
try {
|
|
399
|
+
updatedAt = fs.fstatSync(fd).mtimeMs;
|
|
400
|
+
while (total < 1024 * 1024) {
|
|
401
|
+
const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, null);
|
|
402
|
+
if (!bytesRead) break;
|
|
403
|
+
chunks.push(Buffer.from(buffer.subarray(0, bytesRead)));
|
|
404
|
+
total += bytesRead;
|
|
405
|
+
let content = Buffer.concat(chunks).toString('utf8');
|
|
406
|
+
if (content.charCodeAt(0) === 0xFEFF) content = content.slice(1);
|
|
407
|
+
if (/^\s*model\s*=.*(?:\n|$)/m.test(content)) return { data: toml.parse(content), content, updatedAt };
|
|
408
|
+
}
|
|
409
|
+
let content = Buffer.concat(chunks).toString('utf8');
|
|
410
|
+
if (content.charCodeAt(0) === 0xFEFF) content = content.slice(1);
|
|
411
|
+
return { data: toml.parse(content), content, updatedAt };
|
|
412
|
+
} catch (error) {
|
|
413
|
+
return { data: {}, content: '', updatedAt, error };
|
|
414
|
+
} finally {
|
|
415
|
+
fs.closeSync(fd);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
357
418
|
|
|
358
419
|
/**
|
|
359
420
|
* 生成 frontmatter 字符串
|
|
@@ -536,21 +597,122 @@ class AgentsRepoScanner extends RepoScannerBase {
|
|
|
536
597
|
* Agents 服务类
|
|
537
598
|
*/
|
|
538
599
|
class AgentsService {
|
|
539
|
-
constructor(platform = 'claude') {
|
|
600
|
+
constructor(platform = 'claude', { registry } = {}) {
|
|
540
601
|
this.platform = normalizePlatform(platform);
|
|
541
602
|
const config = PLATFORM_CONFIG[this.platform];
|
|
542
|
-
|
|
543
|
-
|
|
603
|
+
this.registry = registry || (() => {
|
|
604
|
+
try {
|
|
605
|
+
return getPlatformContext().registry;
|
|
606
|
+
} catch {
|
|
607
|
+
return null;
|
|
608
|
+
}
|
|
609
|
+
})();
|
|
610
|
+
this.pathContext = null;
|
|
611
|
+
try {
|
|
612
|
+
if (typeof this.registry?.resolvePathContext === 'function') {
|
|
613
|
+
this.pathContext = this.registry.resolvePathContext(this.platform);
|
|
614
|
+
}
|
|
615
|
+
} catch {
|
|
616
|
+
this.pathContext = null;
|
|
617
|
+
}
|
|
618
|
+
const native = this.pathContext?.customized
|
|
619
|
+
? (this.pathContext.native || {})
|
|
620
|
+
: (NATIVE_PATHS[this.platform] || {});
|
|
621
|
+
this.homeDir = this.pathContext?.customized ? this.pathContext.home : HOME_DIR;
|
|
622
|
+
this.codexConfigPath = this.platform === 'codex'
|
|
623
|
+
? (native.config || CODEX_CONFIG_PATH)
|
|
624
|
+
: CODEX_CONFIG_PATH;
|
|
625
|
+
this.codexAgentsDir = this.platform === 'codex'
|
|
626
|
+
? (native.agents || path.join(native.dir || path.dirname(this.codexConfigPath), 'agents'))
|
|
627
|
+
: CODEX_AGENTS_DIR;
|
|
628
|
+
const configuredUserAgentsDir = this.pathContext?.customized
|
|
629
|
+
? (native.agents
|
|
630
|
+
|| (this.platform === 'opencode' ? path.join(native.config || OPENCODE_CONFIG_DIR, 'agents') : null)
|
|
631
|
+
|| (native.dir ? path.join(native.dir, 'agents') : null))
|
|
632
|
+
: null;
|
|
633
|
+
this.userAgentsDir = configuredUserAgentsDir || config.userAgentsDir;
|
|
544
634
|
if (this.platform === 'opencode') {
|
|
545
635
|
const legacyUserDir = config.legacyUserAgentsDir;
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
636
|
+
const configuredLegacyUserDir = this.pathContext?.customized && native.config
|
|
637
|
+
? path.join(native.config, 'agent')
|
|
638
|
+
: legacyUserDir;
|
|
639
|
+
if (configuredLegacyUserDir && fs.existsSync(configuredLegacyUserDir) && !fs.existsSync(this.userAgentsDir)) this.userAgentsDir = configuredLegacyUserDir;
|
|
549
640
|
}
|
|
550
|
-
|
|
551
641
|
this.projectAgentsDir = config.projectAgentsDir;
|
|
552
642
|
this.repoScanner = new AgentsRepoScanner(this.platform, this.userAgentsDir);
|
|
553
643
|
ensureDir(this.userAgentsDir);
|
|
644
|
+
this._localIndexes = new Map();
|
|
645
|
+
this._localIndexLimit = 32;
|
|
646
|
+
}
|
|
647
|
+
_readCodexTomlConfig() {
|
|
648
|
+
return readCodexTomlConfig(this.codexConfigPath);
|
|
649
|
+
}
|
|
650
|
+
_writeCodexTomlConfig(config) {
|
|
651
|
+
return writeCodexTomlConfig(config, this.codexConfigPath);
|
|
652
|
+
}
|
|
653
|
+
_resolveCodexConfigPath(configPath) {
|
|
654
|
+
return resolveCodexConfigPath(configPath, this.codexConfigPath, this.homeDir);
|
|
655
|
+
}
|
|
656
|
+
_isManagedCodexConfigPath(configPath) {
|
|
657
|
+
return isManagedCodexConfigPath(configPath, this.codexConfigPath, this.codexAgentsDir, this.homeDir);
|
|
658
|
+
}
|
|
659
|
+
_getCodexManagedAgentConfigPath(fileName) {
|
|
660
|
+
return getCodexManagedAgentConfigPath(fileName, this.codexAgentsDir);
|
|
661
|
+
}
|
|
662
|
+
_inferCodexConfigMode(configFile) {
|
|
663
|
+
const normalized = normalizeCodexConfigPath(configFile);
|
|
664
|
+
if (!normalized) return 'none';
|
|
665
|
+
return this._isManagedCodexConfigPath(normalized) ? 'managed' : 'custom';
|
|
666
|
+
}
|
|
667
|
+
_configUpdatedAt() {
|
|
668
|
+
return fs.existsSync(this.codexConfigPath) ? fs.statSync(this.codexConfigPath).mtime.getTime() : 0;
|
|
669
|
+
}
|
|
670
|
+
getCapabilities() {
|
|
671
|
+
return { ...(PLATFORM_CONFIG[this.platform]?.capabilities || { projectScope: true, repoOperations: true }) };
|
|
672
|
+
}
|
|
673
|
+
getRepoOperationError(operation = '') {
|
|
674
|
+
const suffix = operation === 'install' ? '安装' : operation === 'uninstall' ? '卸载' : '';
|
|
675
|
+
return `${this.platform === 'codex' ? 'Codex 平台' : '该平台'}暂不支持远程仓库代理${suffix}`;
|
|
676
|
+
}
|
|
677
|
+
_getLocalIndex(scope, projectPath = null) {
|
|
678
|
+
const root = scope === 'user' ? this.userAgentsDir : this.getProjectAgentsDir(projectPath);
|
|
679
|
+
if (!root) return null;
|
|
680
|
+
const key = `${this.platform}:${scope}:${path.resolve(root)}`;
|
|
681
|
+
if (!this._localIndexes.has(key)) {
|
|
682
|
+
if (this._localIndexes.size >= this._localIndexLimit) {
|
|
683
|
+
const [oldKey, oldIndex] = this._localIndexes.entries().next().value;
|
|
684
|
+
oldIndex.dispose();
|
|
685
|
+
this._localIndexes.delete(oldKey);
|
|
686
|
+
}
|
|
687
|
+
this._localIndexes.set(key, new LocalResourceIndex({ key, roots: [root],
|
|
688
|
+
scanFile: (descriptor) => {
|
|
689
|
+
if (!descriptor.fullPath.endsWith('.md') || descriptor.relativePath.includes(path.sep)) return null;
|
|
690
|
+
const frontmatter = readAgentMetadata(descriptor.fullPath);
|
|
691
|
+
const fileName = path.basename(descriptor.relativePath, '.md');
|
|
692
|
+
return { name: frontmatter.name || fileName, fileName, scope, path: descriptor.relativePath, fullPath: descriptor.fullPath, description: frontmatter.description || '', tools: frontmatter.tools || '', model: frontmatter.model || '', permissionMode: frontmatter.permissionMode || '', skills: frontmatter.skills || '', updatedAt: descriptor.stat.mtime.getTime() };
|
|
693
|
+
},
|
|
694
|
+
detailFile: (summary) => {
|
|
695
|
+
const content = fs.readFileSync(summary.fullPath, 'utf-8');
|
|
696
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
697
|
+
return { systemPrompt: body, fullContent: content, updatedAt: fs.statSync(summary.fullPath).mtime.getTime(), name: frontmatter.name || summary.fileName, description: frontmatter.description || '', tools: frontmatter.tools || '', model: frontmatter.model || '', permissionMode: frontmatter.permissionMode || '', skills: frontmatter.skills || '' };
|
|
698
|
+
}
|
|
699
|
+
}));
|
|
700
|
+
}
|
|
701
|
+
const index = this._localIndexes.get(key);
|
|
702
|
+
this._localIndexes.delete(key);
|
|
703
|
+
this._localIndexes.set(key, index);
|
|
704
|
+
return index;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
dispose() {
|
|
708
|
+
for (const index of this._localIndexes.values()) index.dispose();
|
|
709
|
+
this._localIndexes.clear();
|
|
710
|
+
this._codexIndex?.dispose();
|
|
711
|
+
this._codexIndex = null;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
_invalidateLocal(scope, projectPath = null) {
|
|
715
|
+
this._getLocalIndex(scope, projectPath)?.invalidate();
|
|
554
716
|
}
|
|
555
717
|
|
|
556
718
|
getProjectAgentsDir(projectPath) {
|
|
@@ -575,52 +737,20 @@ class AgentsService {
|
|
|
575
737
|
return { safeFileName, relativePath, fullPath };
|
|
576
738
|
}
|
|
577
739
|
|
|
578
|
-
/**
|
|
579
|
-
* 获取所有代理列表
|
|
580
|
-
* @param {string} projectPath - 项目路径(可选,用于获取项目级代理)
|
|
581
|
-
*/
|
|
582
740
|
listAgents(projectPath = null) {
|
|
583
|
-
if (this.platform === 'codex')
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
const agents = [];
|
|
588
|
-
|
|
589
|
-
// 获取用户级代理
|
|
590
|
-
const userAgents = scanAgentsDir(this.userAgentsDir, this.userAgentsDir, 'user');
|
|
591
|
-
agents.push(...userAgents);
|
|
592
|
-
|
|
593
|
-
// 获取项目级代理(如果提供了项目路径)
|
|
594
|
-
if (projectPath) {
|
|
595
|
-
const projectAgentsDir = this.getProjectAgentsDir(projectPath);
|
|
596
|
-
const projectAgents = scanAgentsDir(projectAgentsDir, projectAgentsDir, 'project');
|
|
597
|
-
agents.push(...projectAgents);
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
// 按名称排序
|
|
741
|
+
if (this.platform === 'codex') return this.listCodexAgents(false);
|
|
742
|
+
const userAgents = this._getLocalIndex('user').listSync();
|
|
743
|
+
const projectAgents = projectPath ? this._getLocalIndex('project', projectPath).listSync() : [];
|
|
744
|
+
const agents = [...userAgents, ...projectAgents];
|
|
601
745
|
agents.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
|
|
602
|
-
|
|
603
|
-
return {
|
|
604
|
-
agents,
|
|
605
|
-
total: agents.length,
|
|
606
|
-
userCount: userAgents.length,
|
|
607
|
-
projectCount: agents.length - userAgents.length
|
|
608
|
-
};
|
|
746
|
+
return { agents, total: agents.length, userCount: userAgents.length, projectCount: projectAgents.length };
|
|
609
747
|
}
|
|
610
748
|
|
|
611
|
-
/**
|
|
612
|
-
* 获取所有代理(包括远程仓库)
|
|
613
|
-
*/
|
|
749
|
+
/** 获取所有代理(包括远程仓库) */
|
|
614
750
|
async listAllAgents(projectPath = null, forceRefresh = false) {
|
|
615
751
|
if (this.platform === 'codex') {
|
|
616
|
-
const { agents, userCount, projectCount } = this.listCodexAgents();
|
|
617
|
-
return {
|
|
618
|
-
agents,
|
|
619
|
-
total: agents.length,
|
|
620
|
-
userCount,
|
|
621
|
-
projectCount,
|
|
622
|
-
remoteCount: 0
|
|
623
|
-
};
|
|
752
|
+
const { agents, userCount, projectCount } = this.listCodexAgents(false);
|
|
753
|
+
return { agents, total: agents.length, userCount, projectCount, remoteCount: 0 };
|
|
624
754
|
}
|
|
625
755
|
|
|
626
756
|
// 获取本地代理
|
|
@@ -666,36 +796,12 @@ class AgentsService {
|
|
|
666
796
|
*/
|
|
667
797
|
getAgent(fileName, scope, projectPath = null) {
|
|
668
798
|
const safeFileName = assertSafeAgentFileName(fileName);
|
|
669
|
-
|
|
670
|
-
if (this.platform === 'codex') {
|
|
671
|
-
return this.getCodexAgent(safeFileName, scope);
|
|
672
|
-
}
|
|
673
|
-
|
|
799
|
+
if (this.platform === 'codex') return this.getCodexAgent(safeFileName, scope);
|
|
674
800
|
const baseDir = this._getBaseDir(scope, projectPath);
|
|
675
|
-
const { relativePath
|
|
676
|
-
|
|
677
|
-
if (!
|
|
678
|
-
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
const content = fs.readFileSync(filePath, 'utf-8');
|
|
682
|
-
const { frontmatter, body } = parseFrontmatter(content);
|
|
683
|
-
|
|
684
|
-
return {
|
|
685
|
-
name: frontmatter.name || safeFileName,
|
|
686
|
-
fileName: safeFileName,
|
|
687
|
-
scope,
|
|
688
|
-
path: relativePath,
|
|
689
|
-
fullPath: filePath,
|
|
690
|
-
description: frontmatter.description || '',
|
|
691
|
-
tools: frontmatter.tools || '',
|
|
692
|
-
model: frontmatter.model || '',
|
|
693
|
-
permissionMode: frontmatter.permissionMode || '',
|
|
694
|
-
skills: frontmatter.skills || '',
|
|
695
|
-
systemPrompt: body,
|
|
696
|
-
fullContent: content,
|
|
697
|
-
updatedAt: fs.statSync(filePath).mtime.getTime()
|
|
698
|
-
};
|
|
801
|
+
const { relativePath } = this._resolveAgentFilePath(baseDir, safeFileName);
|
|
802
|
+
const detail = this._getLocalIndex(scope, projectPath)?.getSync(relativePath);
|
|
803
|
+
if (!detail) return null;
|
|
804
|
+
return { ...detail, fileName: safeFileName, scope, path: relativePath };
|
|
699
805
|
}
|
|
700
806
|
|
|
701
807
|
/**
|
|
@@ -734,9 +840,8 @@ class AgentsService {
|
|
|
734
840
|
if (skills) frontmatterData.skills = skills;
|
|
735
841
|
|
|
736
842
|
const content = generateFrontmatter(frontmatterData, this.platform) + '\n\n' + (systemPrompt || '');
|
|
737
|
-
|
|
738
843
|
fs.writeFileSync(filePath, content, 'utf-8');
|
|
739
|
-
|
|
844
|
+
this._invalidateLocal(scope, projectPath);
|
|
740
845
|
return this.getAgent(safeFileName, scope, projectPath);
|
|
741
846
|
}
|
|
742
847
|
|
|
@@ -768,9 +873,8 @@ class AgentsService {
|
|
|
768
873
|
if (skills) frontmatterData.skills = skills;
|
|
769
874
|
|
|
770
875
|
const content = generateFrontmatter(frontmatterData, this.platform) + '\n\n' + (systemPrompt || '');
|
|
771
|
-
|
|
772
876
|
fs.writeFileSync(filePath, content, 'utf-8');
|
|
773
|
-
|
|
877
|
+
this._invalidateLocal(scope, projectPath);
|
|
774
878
|
return this.getAgent(safeFileName, scope, projectPath);
|
|
775
879
|
}
|
|
776
880
|
|
|
@@ -792,7 +896,7 @@ class AgentsService {
|
|
|
792
896
|
}
|
|
793
897
|
|
|
794
898
|
fs.unlinkSync(filePath);
|
|
795
|
-
|
|
899
|
+
this._invalidateLocal(scope, projectPath);
|
|
796
900
|
return { success: true, message: '代理已删除' };
|
|
797
901
|
}
|
|
798
902
|
|
|
@@ -864,28 +968,24 @@ class AgentsService {
|
|
|
864
968
|
* 从远程仓库安装代理
|
|
865
969
|
*/
|
|
866
970
|
async installFromRemote(agent) {
|
|
867
|
-
if (!agent || typeof agent !== 'object')
|
|
868
|
-
throw new Error('无效的代理安装参数');
|
|
869
|
-
}
|
|
971
|
+
if (!agent || typeof agent !== 'object') throw new Error('无效的代理安装参数');
|
|
870
972
|
const safeFileName = assertSafeAgentFileName(agent.fileName);
|
|
871
973
|
const safeRepoPath = assertSafeRepoPath(agent.repoPath);
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
repoPath: safeRepoPath
|
|
876
|
-
});
|
|
974
|
+
const result = await this.repoScanner.installAgent({ ...agent, fileName: safeFileName, repoPath: safeRepoPath });
|
|
975
|
+
if (result && result.success !== false) this._invalidateLocal('user');
|
|
976
|
+
return result;
|
|
877
977
|
}
|
|
878
978
|
|
|
879
|
-
/**
|
|
880
|
-
* 卸载代理
|
|
881
|
-
*/
|
|
979
|
+
/** 卸载代理 */
|
|
882
980
|
uninstallAgent(fileName) {
|
|
883
981
|
const safeFileName = assertSafeAgentFileName(fileName);
|
|
884
|
-
|
|
982
|
+
const result = this.repoScanner.uninstall(`${safeFileName}.md`);
|
|
983
|
+
if (result && result.success !== false) this._invalidateLocal('user');
|
|
984
|
+
return result;
|
|
885
985
|
}
|
|
886
986
|
|
|
887
|
-
|
|
888
|
-
const config =
|
|
987
|
+
_readCodexAgents(includeDetails = true) {
|
|
988
|
+
const config = this._readCodexTomlConfig();
|
|
889
989
|
const agentsTable = isPlainObject(config.agents) ? config.agents : {};
|
|
890
990
|
const agentsByName = new Map();
|
|
891
991
|
|
|
@@ -895,26 +995,19 @@ class AgentsService {
|
|
|
895
995
|
}
|
|
896
996
|
|
|
897
997
|
const configFile = normalizeCodexConfigPath(value.config_file);
|
|
898
|
-
const resolvedConfigFile =
|
|
899
|
-
const configMode =
|
|
998
|
+
const resolvedConfigFile = this._resolveCodexConfigPath(configFile);
|
|
999
|
+
const configMode = this._inferCodexConfigMode(configFile);
|
|
900
1000
|
const fullPath = configFile || `${key}.toml`;
|
|
901
1001
|
let model = '';
|
|
902
1002
|
let fullContent = '';
|
|
903
1003
|
let configReadError = '';
|
|
904
|
-
let updatedAt = fs.existsSync(
|
|
905
|
-
|
|
906
|
-
if (configFile) {
|
|
1004
|
+
let updatedAt = this.codexConfigPath && fs.existsSync(this.codexConfigPath) ? fs.statSync(this.codexConfigPath).mtime.getTime() : Date.now();
|
|
1005
|
+
if (includeDetails && configFile) {
|
|
907
1006
|
const parsedConfigFile = readCodexAgentConfigFile(resolvedConfigFile);
|
|
908
1007
|
fullContent = parsedConfigFile.content;
|
|
909
|
-
if (isPlainObject(parsedConfigFile.data) && typeof parsedConfigFile.data.model === 'string')
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
if (parsedConfigFile.updatedAt) {
|
|
913
|
-
updatedAt = Math.max(updatedAt, parsedConfigFile.updatedAt);
|
|
914
|
-
}
|
|
915
|
-
if (parsedConfigFile.error) {
|
|
916
|
-
configReadError = parsedConfigFile.error;
|
|
917
|
-
}
|
|
1008
|
+
if (isPlainObject(parsedConfigFile.data) && typeof parsedConfigFile.data.model === 'string') model = parsedConfigFile.data.model;
|
|
1009
|
+
if (parsedConfigFile.updatedAt) updatedAt = Math.max(updatedAt, parsedConfigFile.updatedAt);
|
|
1010
|
+
if (parsedConfigFile.error) configReadError = parsedConfigFile.error;
|
|
918
1011
|
}
|
|
919
1012
|
|
|
920
1013
|
agentsByName.set(key, {
|
|
@@ -939,9 +1032,9 @@ class AgentsService {
|
|
|
939
1032
|
});
|
|
940
1033
|
}
|
|
941
1034
|
|
|
942
|
-
if (fs.existsSync(
|
|
1035
|
+
if (fs.existsSync(this.codexAgentsDir)) {
|
|
943
1036
|
try {
|
|
944
|
-
const entries = fs.readdirSync(
|
|
1037
|
+
const entries = fs.readdirSync(this.codexAgentsDir, { withFileTypes: true });
|
|
945
1038
|
for (const entry of entries) {
|
|
946
1039
|
if (!entry.isFile() || !entry.name.endsWith('.toml')) {
|
|
947
1040
|
continue;
|
|
@@ -954,35 +1047,25 @@ class AgentsService {
|
|
|
954
1047
|
continue;
|
|
955
1048
|
}
|
|
956
1049
|
|
|
957
|
-
const configFilePath = path.join(
|
|
958
|
-
const parsedConfigFile = readCodexAgentConfigFile(configFilePath);
|
|
1050
|
+
const configFilePath = path.join(this.codexAgentsDir, entry.name);
|
|
1051
|
+
const parsedConfigFile = includeDetails ? readCodexAgentConfigFile(configFilePath) : { content: '', data: null, updatedAt: fs.statSync(configFilePath).mtime.getTime(), error: null };
|
|
959
1052
|
const existing = agentsByName.get(fileName);
|
|
960
|
-
const model = isPlainObject(parsedConfigFile.data) && typeof parsedConfigFile.data.model === 'string'
|
|
961
|
-
? parsedConfigFile.data.model
|
|
962
|
-
: '';
|
|
1053
|
+
const model = isPlainObject(parsedConfigFile.data) && typeof parsedConfigFile.data.model === 'string' ? parsedConfigFile.data.model : '';
|
|
963
1054
|
const updatedAt = parsedConfigFile.updatedAt || fs.statSync(configFilePath).mtime.getTime();
|
|
964
1055
|
|
|
965
1056
|
if (existing) {
|
|
966
1057
|
if (!existing.configFile) {
|
|
967
1058
|
existing.configFile = configFilePath;
|
|
968
|
-
existing.configMode =
|
|
1059
|
+
existing.configMode = this._inferCodexConfigMode(configFilePath);
|
|
969
1060
|
existing.path = configFilePath;
|
|
970
1061
|
existing.fullPath = configFilePath;
|
|
971
1062
|
existing.resolvedConfigFile = configFilePath;
|
|
972
1063
|
}
|
|
973
|
-
if (!existing.fullContent && parsedConfigFile.content)
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
if (!existing.model && model) {
|
|
977
|
-
existing.model = model;
|
|
978
|
-
}
|
|
979
|
-
if (parsedConfigFile.error && !existing.configReadError) {
|
|
980
|
-
existing.configReadError = parsedConfigFile.error;
|
|
981
|
-
}
|
|
1064
|
+
if (includeDetails && !existing.fullContent && parsedConfigFile.content) existing.fullContent = parsedConfigFile.content;
|
|
1065
|
+
if (!existing.model && model) existing.model = model;
|
|
1066
|
+
if (parsedConfigFile.error && !existing.configReadError) existing.configReadError = parsedConfigFile.error;
|
|
982
1067
|
existing.updatedAt = Math.max(existing.updatedAt || 0, updatedAt);
|
|
983
|
-
existing.source = existing.source === 'codex-config'
|
|
984
|
-
? 'codex-config+native-file'
|
|
985
|
-
: existing.source;
|
|
1068
|
+
existing.source = existing.source === 'codex-config' ? 'codex-config+native-file' : existing.source;
|
|
986
1069
|
continue;
|
|
987
1070
|
}
|
|
988
1071
|
|
|
@@ -1000,7 +1083,7 @@ class AgentsService {
|
|
|
1000
1083
|
systemPrompt: '',
|
|
1001
1084
|
fullContent: parsedConfigFile.content,
|
|
1002
1085
|
configFile: configFilePath,
|
|
1003
|
-
configMode:
|
|
1086
|
+
configMode: this._inferCodexConfigMode(configFilePath),
|
|
1004
1087
|
configReadError: parsedConfigFile.error || '',
|
|
1005
1088
|
resolvedConfigFile: configFilePath,
|
|
1006
1089
|
updatedAt,
|
|
@@ -1012,27 +1095,101 @@ class AgentsService {
|
|
|
1012
1095
|
}
|
|
1013
1096
|
}
|
|
1014
1097
|
|
|
1015
|
-
const agents = Array.from(agentsByName.values());
|
|
1016
1098
|
|
|
1099
|
+
const agents = Array.from(agentsByName.values());
|
|
1017
1100
|
agents.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
|
|
1101
|
+
return { agents, total: agents.length, userCount: agents.length, projectCount: 0 };
|
|
1102
|
+
}
|
|
1103
|
+
_getCodexIndex() {
|
|
1104
|
+
if (!this._codexIndex) {
|
|
1105
|
+
this._codexIndex = new LocalResourceIndex({
|
|
1106
|
+
key: `${this.platform}:codex`, roots: [this.codexAgentsDir], ttlMs: 1000,
|
|
1107
|
+
scanFile: ({ fullPath, relativePath, stat }) => {
|
|
1108
|
+
if (!fullPath.endsWith('.toml') || relativePath.includes(path.sep)) return null;
|
|
1109
|
+
const fileName = path.basename(fullPath, '.toml');
|
|
1110
|
+
const metadata = readCodexTomlMetadata(fullPath);
|
|
1111
|
+
const data = metadata.data || {};
|
|
1112
|
+
return { name: fileName, fileName, scope: 'user', path: fullPath, fullPath, description: '', tools: '', model: typeof data.model === 'string' ? data.model : '', permissionMode: '', skills: '', configFile: fullPath, configMode: 'managed', resolvedConfigFile: fullPath, configReadError: metadata.error?.message || '', updatedAt: stat.mtime.getTime(), source: 'native-file' };
|
|
1113
|
+
},
|
|
1114
|
+
detailFile: (summary) => this._hydrateCodexAgent(summary)
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
return this._codexIndex;
|
|
1118
|
+
}
|
|
1018
1119
|
|
|
1120
|
+
_hydrateCodexAgent(summary) {
|
|
1121
|
+
const configPath = summary.resolvedConfigFile || this._resolveCodexConfigPath(summary.configFile) || (summary.source === 'native-file' ? summary.fullPath : '');
|
|
1122
|
+
if (!configPath) return { ...summary, fullContent: '', systemPrompt: '', configReadError: '' };
|
|
1123
|
+
const parsed = readCodexAgentConfigFile(configPath);
|
|
1124
|
+
const data = parsed.data || {};
|
|
1019
1125
|
return {
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1126
|
+
...summary,
|
|
1127
|
+
model: typeof data.model === 'string' ? data.model : summary.model || '',
|
|
1128
|
+
fullContent: parsed.content,
|
|
1129
|
+
systemPrompt: '',
|
|
1130
|
+
configReadError: parsed.error || summary.configReadError || '',
|
|
1131
|
+
updatedAt: parsed.updatedAt || summary.updatedAt
|
|
1024
1132
|
};
|
|
1025
1133
|
}
|
|
1026
1134
|
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1135
|
+
listCodexAgents(includeDetails = true) {
|
|
1136
|
+
const indexed = this._getCodexIndex().listSync();
|
|
1137
|
+
const config = this._readCodexTomlConfig();
|
|
1138
|
+
const table = isPlainObject(config.agents) ? config.agents : {};
|
|
1139
|
+
const merged = new Map(indexed.map((agent) => [agent.fileName, { ...agent }]));
|
|
1140
|
+
for (const [fileName, entry] of Object.entries(table)) {
|
|
1141
|
+
if (!isPlainObject(entry)) continue;
|
|
1142
|
+
const configFile = normalizeCodexConfigPath(entry.config_file);
|
|
1143
|
+
const resolvedConfigFile = this._resolveCodexConfigPath(configFile);
|
|
1144
|
+
const existing = merged.get(fileName);
|
|
1145
|
+
const externalMetadata = configFile && !this._isManagedCodexConfigPath(configFile) && fs.existsSync(resolvedConfigFile)
|
|
1146
|
+
? readCodexTomlMetadata(resolvedConfigFile)
|
|
1147
|
+
: { data: {}, error: null };
|
|
1148
|
+
const summary = {
|
|
1149
|
+
...(existing || {
|
|
1150
|
+
name: fileName, fileName, scope: 'user', path: configFile || `${fileName}.toml`,
|
|
1151
|
+
fullPath: resolvedConfigFile || `${fileName}.toml`, tools: '', model: '',
|
|
1152
|
+
permissionMode: '', skills: '', updatedAt: this._configUpdatedAt(), source: 'codex-config'
|
|
1153
|
+
}),
|
|
1154
|
+
description: entry.description || existing?.description || '',
|
|
1155
|
+
model: existing?.model || (typeof externalMetadata.data?.model === 'string' ? externalMetadata.data.model : ''),
|
|
1156
|
+
configReadError: externalMetadata.error?.message || existing?.configReadError || '',
|
|
1157
|
+
updatedAt: externalMetadata.updatedAt || existing?.updatedAt || this._configUpdatedAt(),
|
|
1158
|
+
configFile: configFile || existing?.configFile || '',
|
|
1159
|
+
configMode: this._inferCodexConfigMode(configFile || existing?.configFile || ''),
|
|
1160
|
+
resolvedConfigFile: resolvedConfigFile || existing?.resolvedConfigFile || existing?.fullPath || '',
|
|
1161
|
+
source: existing ? 'codex-config+native-file' : 'codex-config'
|
|
1162
|
+
};
|
|
1163
|
+
merged.set(fileName, summary);
|
|
1032
1164
|
}
|
|
1165
|
+
const agents = Array.from(merged.values()).map((summary) => includeDetails ? this._hydrateCodexAgent(summary) : summary);
|
|
1166
|
+
agents.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
|
|
1167
|
+
return { agents, total: agents.length, userCount: agents.length, projectCount: 0 };
|
|
1168
|
+
}
|
|
1033
1169
|
|
|
1034
|
-
|
|
1035
|
-
|
|
1170
|
+
getCodexAgent(fileName, scope) {
|
|
1171
|
+
const safeFileName = assertSafeAgentFileName(fileName);
|
|
1172
|
+
if (scope !== 'user') return null;
|
|
1173
|
+
const config = this._readCodexTomlConfig();
|
|
1174
|
+
const entry = isPlainObject(config.agents) && isPlainObject(config.agents[safeFileName]) ? config.agents[safeFileName] : null;
|
|
1175
|
+
const managedPath = path.join(this.codexAgentsDir, `${safeFileName}.toml`);
|
|
1176
|
+
const configFile = normalizeCodexConfigPath(entry?.config_file);
|
|
1177
|
+
const resolvedConfigFile = this._resolveCodexConfigPath(configFile);
|
|
1178
|
+
const nativePath = !configFile && fs.existsSync(managedPath) ? managedPath : '';
|
|
1179
|
+
const exists = Boolean(entry || nativePath || (resolvedConfigFile && fs.existsSync(resolvedConfigFile)));
|
|
1180
|
+
if (!exists) return null;
|
|
1181
|
+
const targetPath = resolvedConfigFile || nativePath;
|
|
1182
|
+
const summary = {
|
|
1183
|
+
name: safeFileName, fileName: safeFileName, scope: 'user',
|
|
1184
|
+
path: targetPath || `${safeFileName}.toml`, fullPath: targetPath || `${safeFileName}.toml`,
|
|
1185
|
+
description: entry?.description || '', tools: '', model: '', permissionMode: '', skills: '',
|
|
1186
|
+
configFile: configFile || nativePath,
|
|
1187
|
+
configMode: this._inferCodexConfigMode(configFile || nativePath),
|
|
1188
|
+
resolvedConfigFile: targetPath,
|
|
1189
|
+
updatedAt: this._configUpdatedAt(),
|
|
1190
|
+
source: entry ? 'codex-config' : 'native-file'
|
|
1191
|
+
};
|
|
1192
|
+
return this._hydrateCodexAgent(summary);
|
|
1036
1193
|
}
|
|
1037
1194
|
|
|
1038
1195
|
createCodexAgent({ fileName, scope, description, model, configMode, configFile, configContent }) {
|
|
@@ -1046,7 +1203,7 @@ class AgentsService {
|
|
|
1046
1203
|
throw new Error('代理描述不能为空');
|
|
1047
1204
|
}
|
|
1048
1205
|
|
|
1049
|
-
const config =
|
|
1206
|
+
const config = this._readCodexTomlConfig();
|
|
1050
1207
|
config.features = isPlainObject(config.features) ? config.features : {};
|
|
1051
1208
|
config.features.multi_agent = true;
|
|
1052
1209
|
config.agents = isPlainObject(config.agents) ? config.agents : {};
|
|
@@ -1064,20 +1221,20 @@ class AgentsService {
|
|
|
1064
1221
|
if (!normalizedMode) {
|
|
1065
1222
|
const trimmedModel = (model || '').trim();
|
|
1066
1223
|
if (trimmedModel) {
|
|
1067
|
-
ensureDir(
|
|
1068
|
-
const configFilePath =
|
|
1224
|
+
ensureDir(this.codexAgentsDir);
|
|
1225
|
+
const configFilePath = this._getCodexManagedAgentConfigPath(fileName);
|
|
1069
1226
|
writeFileAtomic(configFilePath, tomlStringify({ model: trimmedModel }));
|
|
1070
1227
|
agentConfig.config_file = configFilePath;
|
|
1071
1228
|
}
|
|
1072
1229
|
} else if (normalizedMode === 'managed') {
|
|
1073
|
-
ensureDir(
|
|
1074
|
-
const configFilePath =
|
|
1230
|
+
ensureDir(this.codexAgentsDir);
|
|
1231
|
+
const configFilePath = this._getCodexManagedAgentConfigPath(fileName);
|
|
1075
1232
|
const content = resolveCodexConfigContent({ configContent, model });
|
|
1076
1233
|
writeFileAtomic(configFilePath, content);
|
|
1077
1234
|
agentConfig.config_file = configFilePath;
|
|
1078
1235
|
} else if (normalizedMode === 'custom') {
|
|
1079
1236
|
const safeConfigFile = assertSafeCodexConfigPath(configFile);
|
|
1080
|
-
const resolvedPath =
|
|
1237
|
+
const resolvedPath = this._resolveCodexConfigPath(safeConfigFile);
|
|
1081
1238
|
ensureDir(path.dirname(resolvedPath));
|
|
1082
1239
|
const content = resolveCodexConfigContent({ configContent, model });
|
|
1083
1240
|
writeFileAtomic(resolvedPath, content);
|
|
@@ -1085,8 +1242,8 @@ class AgentsService {
|
|
|
1085
1242
|
}
|
|
1086
1243
|
|
|
1087
1244
|
config.agents[fileName] = agentConfig;
|
|
1088
|
-
|
|
1089
|
-
|
|
1245
|
+
this._writeCodexTomlConfig(config);
|
|
1246
|
+
this._codexIndex?.invalidate();
|
|
1090
1247
|
return this.getCodexAgent(fileName, scope);
|
|
1091
1248
|
}
|
|
1092
1249
|
|
|
@@ -1097,7 +1254,7 @@ class AgentsService {
|
|
|
1097
1254
|
throw new Error('Codex 仅支持用户级代理');
|
|
1098
1255
|
}
|
|
1099
1256
|
|
|
1100
|
-
const config =
|
|
1257
|
+
const config = this._readCodexTomlConfig();
|
|
1101
1258
|
config.features = isPlainObject(config.features) ? config.features : {};
|
|
1102
1259
|
config.features.multi_agent = true;
|
|
1103
1260
|
config.agents = isPlainObject(config.agents) ? config.agents : {};
|
|
@@ -1111,16 +1268,16 @@ class AgentsService {
|
|
|
1111
1268
|
agentConfig.description = (description || '').trim();
|
|
1112
1269
|
|
|
1113
1270
|
const existingConfigFile = normalizeCodexConfigPath(agentConfig.config_file);
|
|
1114
|
-
const isExistingManagedConfig =
|
|
1115
|
-
const resolvedExistingConfigFile = isExistingManagedConfig ?
|
|
1271
|
+
const isExistingManagedConfig = this._isManagedCodexConfigPath(existingConfigFile);
|
|
1272
|
+
const resolvedExistingConfigFile = isExistingManagedConfig ? this._resolveCodexConfigPath(existingConfigFile) : '';
|
|
1116
1273
|
const normalizedMode = normalizeCodexConfigMode(configMode);
|
|
1117
1274
|
|
|
1118
1275
|
if (!normalizedMode) {
|
|
1119
1276
|
const trimmedModel = (model || '').trim();
|
|
1120
1277
|
if (trimmedModel) {
|
|
1121
|
-
ensureDir(
|
|
1122
|
-
const configFilePath = isExistingManagedConfig ? existingConfigFile :
|
|
1123
|
-
const resolvedConfigFilePath =
|
|
1278
|
+
ensureDir(this.codexAgentsDir);
|
|
1279
|
+
const configFilePath = isExistingManagedConfig ? existingConfigFile : this._getCodexManagedAgentConfigPath(fileName);
|
|
1280
|
+
const resolvedConfigFilePath = this._resolveCodexConfigPath(configFilePath);
|
|
1124
1281
|
const parsedConfigFile = readCodexAgentConfigFile(resolvedConfigFilePath);
|
|
1125
1282
|
const configFileData = isPlainObject(parsedConfigFile?.data) ? parsedConfigFile.data : {};
|
|
1126
1283
|
configFileData.model = trimmedModel;
|
|
@@ -1139,9 +1296,9 @@ class AgentsService {
|
|
|
1139
1296
|
fs.unlinkSync(resolvedExistingConfigFile);
|
|
1140
1297
|
}
|
|
1141
1298
|
} else if (normalizedMode === 'managed') {
|
|
1142
|
-
ensureDir(
|
|
1143
|
-
const configFilePath =
|
|
1144
|
-
const resolvedConfigFilePath =
|
|
1299
|
+
ensureDir(this.codexAgentsDir);
|
|
1300
|
+
const configFilePath = this._getCodexManagedAgentConfigPath(fileName);
|
|
1301
|
+
const resolvedConfigFilePath = this._resolveCodexConfigPath(configFilePath);
|
|
1145
1302
|
const existingManagedContent = fs.existsSync(resolvedConfigFilePath)
|
|
1146
1303
|
? fs.readFileSync(resolvedConfigFilePath, 'utf-8')
|
|
1147
1304
|
: '';
|
|
@@ -1170,7 +1327,7 @@ class AgentsService {
|
|
|
1170
1327
|
targetConfigPath = assertSafeCodexConfigPath(targetConfigPath);
|
|
1171
1328
|
}
|
|
1172
1329
|
|
|
1173
|
-
const resolvedTargetConfigPath =
|
|
1330
|
+
const resolvedTargetConfigPath = this._resolveCodexConfigPath(targetConfigPath);
|
|
1174
1331
|
const existingContent = fs.existsSync(resolvedTargetConfigPath)
|
|
1175
1332
|
? fs.readFileSync(resolvedTargetConfigPath, 'utf-8')
|
|
1176
1333
|
: '';
|
|
@@ -1190,8 +1347,8 @@ class AgentsService {
|
|
|
1190
1347
|
}
|
|
1191
1348
|
|
|
1192
1349
|
config.agents[fileName] = agentConfig;
|
|
1193
|
-
|
|
1194
|
-
|
|
1350
|
+
this._writeCodexTomlConfig(config);
|
|
1351
|
+
this._codexIndex?.invalidate();
|
|
1195
1352
|
return this.getCodexAgent(fileName, scope);
|
|
1196
1353
|
}
|
|
1197
1354
|
|
|
@@ -1202,7 +1359,7 @@ class AgentsService {
|
|
|
1202
1359
|
throw new Error('Codex 仅支持用户级代理');
|
|
1203
1360
|
}
|
|
1204
1361
|
|
|
1205
|
-
const config =
|
|
1362
|
+
const config = this._readCodexTomlConfig();
|
|
1206
1363
|
config.agents = isPlainObject(config.agents) ? config.agents : {};
|
|
1207
1364
|
|
|
1208
1365
|
const existingAgent = config.agents[fileName];
|
|
@@ -1211,17 +1368,17 @@ class AgentsService {
|
|
|
1211
1368
|
}
|
|
1212
1369
|
|
|
1213
1370
|
const existingConfigFile = normalizeCodexConfigPath(existingAgent.config_file);
|
|
1214
|
-
const resolvedExistingConfigFile =
|
|
1371
|
+
const resolvedExistingConfigFile = this._resolveCodexConfigPath(existingConfigFile);
|
|
1215
1372
|
if (existingConfigFile &&
|
|
1216
|
-
|
|
1373
|
+
this._isManagedCodexConfigPath(existingConfigFile) &&
|
|
1217
1374
|
resolvedExistingConfigFile &&
|
|
1218
1375
|
fs.existsSync(resolvedExistingConfigFile)) {
|
|
1219
1376
|
fs.unlinkSync(resolvedExistingConfigFile);
|
|
1220
1377
|
}
|
|
1221
1378
|
|
|
1222
1379
|
delete config.agents[fileName];
|
|
1223
|
-
|
|
1224
|
-
|
|
1380
|
+
this._writeCodexTomlConfig(config);
|
|
1381
|
+
this._codexIndex?.invalidate();
|
|
1225
1382
|
return { success: true, message: '代理已删除' };
|
|
1226
1383
|
}
|
|
1227
1384
|
}
|