coding-tool-x 3.8.3 → 3.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +86 -15
- package/dist/web/assets/AgentsDrawer-BG6P3DX-.js +1 -0
- package/dist/web/assets/AgentsDrawer-Pw2k-ZAa.css +1 -0
- package/dist/web/assets/Analytics--z_FmL5P.js +1 -0
- package/dist/web/assets/Analytics-CyHhR126.css +1 -0
- package/dist/web/assets/AssetPathField-CrJtRKHy.js +1 -0
- package/dist/web/assets/ChatHistoryDrawer-B2qBQs9L.css +1 -0
- package/dist/web/assets/ChatHistoryDrawer-BiuEryJj.js +1 -0
- package/dist/web/assets/CollapseItem-BAYEsL_Q.js +1 -0
- package/dist/web/assets/CommandsDrawer-BZJxlPvV.css +1 -0
- package/dist/web/assets/CommandsDrawer-YJ-U7BXN.js +1 -0
- package/dist/web/assets/ConfigExportDrawer-Ce_hAknW.js +1 -0
- package/dist/web/assets/ConfigExportDrawer-dwruRKWr.css +1 -0
- package/dist/web/assets/ConfigTemplates-Bvr1NN6K.js +1 -0
- package/dist/web/assets/ConfigTemplatesDrawer-BfDi8yO3.css +1 -0
- package/dist/web/assets/ConfigTemplatesDrawer-BjIeHfF-.js +1 -0
- package/dist/web/assets/ConfigTemplatesPanel-AV00tm_D.js +1 -0
- package/dist/web/assets/ConfigTemplatesPanel-CWyPtk67.css +1 -0
- package/dist/web/assets/Divider-D28g1gwj.js +1 -0
- package/dist/web/assets/DrawerContent-ChXgZ1Xm.js +1 -0
- package/dist/web/assets/FavoritesDrawer-CG9eI9Ay.css +1 -0
- package/dist/web/assets/FavoritesDrawer-Cz95o4CD.js +1 -0
- package/dist/web/assets/GatewayConvertDrawer-CuNlf7AC.css +1 -0
- package/dist/web/assets/GatewayConvertDrawer-DLlbMz27.js +1 -0
- package/dist/web/assets/Home-CSuFuJ9Y.css +1 -0
- package/dist/web/assets/Home-D7CCOFQC.js +1 -0
- package/dist/web/assets/MarkdownEditor-qfVgSre_.css +1 -0
- package/dist/web/assets/MarkdownEditor-rEZ-c72L.js +1 -0
- package/dist/web/assets/McpDrawer-C0dnsZhQ.css +1 -0
- package/dist/web/assets/McpDrawer-DfoCyxU2.js +1 -0
- package/dist/web/assets/McpFormDrawer-BRbDcawn.css +1 -0
- package/dist/web/assets/McpFormDrawer-SKu8HNUR.js +1 -0
- package/dist/web/assets/PluginManager-BbeK-3rV.js +1 -0
- package/dist/web/assets/PluginsDrawer-CWVJ7yjS.js +1 -0
- package/dist/web/assets/PluginsDrawer-DM3QjprE.css +1 -0
- package/dist/web/assets/PluginsPanel-Cm2N3StG.js +1 -0
- package/dist/web/assets/PluginsPanel-D6ZqITcR.css +1 -0
- package/dist/web/assets/ProjectList-677RLZ8O.css +1 -0
- package/dist/web/assets/ProjectList-CsjnsNPv.js +1 -0
- package/dist/web/assets/PromptsDrawer-BIgiYCEQ.css +1 -0
- package/dist/web/assets/PromptsDrawer-EGd3djFa.js +1 -0
- package/dist/web/assets/RecentSessionsDrawer-CKFsOYDM.css +1 -0
- package/dist/web/assets/RecentSessionsDrawer-D1vtoLAs.js +1 -0
- package/dist/web/assets/SessionCard-BgLg49U3.js +1 -0
- package/dist/web/assets/SessionCard-aM2NHu6i.css +1 -0
- package/dist/web/assets/SessionList-BpYRz1cX.css +1 -0
- package/dist/web/assets/SessionList-TRiTklWk.js +1 -0
- package/dist/web/assets/SettingsDrawer-d5liXjHq.css +1 -0
- package/dist/web/assets/SettingsDrawer-rz2WejGY.js +1 -0
- package/dist/web/assets/SkillManager-BTjTUbHq.js +1 -0
- package/dist/web/assets/SkillManager-C0GCT2Kb.css +1 -0
- package/dist/web/assets/SkillsDrawer-BCNme-aJ.css +1 -0
- package/dist/web/assets/SkillsDrawer-pN0hbkK3.js +1 -0
- package/dist/web/assets/SkillsPanel-B3Nm5j32.css +1 -0
- package/dist/web/assets/SkillsPanel-gmBWEoiF.js +1 -0
- package/dist/web/assets/SpeedTestDrawer-Dc0WToJ_.css +1 -0
- package/dist/web/assets/SpeedTestDrawer-DcNzuVJk.js +1 -0
- package/dist/web/assets/Upload-v5rV0qTT.js +1 -0
- package/dist/web/assets/WorkspaceDrawer-CGnMu9p1.css +1 -0
- package/dist/web/assets/WorkspaceDrawer-DxmicM4U.js +1 -0
- package/dist/web/assets/WorkspaceManager-CJHCjARN.js +1 -0
- package/dist/web/assets/WorkspacePanel-D0-zHTHP.css +1 -0
- package/dist/web/assets/WorkspacePanel-D8_Zq-du.js +1 -0
- package/dist/web/assets/_plugin-vue_export-helper-BeLZJZij.js +1 -0
- package/dist/web/assets/client-UDwXBP9i.js +1 -0
- package/dist/web/assets/clipboard-B46yyoz_.js +1 -0
- package/dist/web/assets/headers-cf-QapUq.js +1 -0
- package/dist/web/assets/icons-B2Vg-IOC.js +1 -0
- package/dist/web/assets/index-C0EHRym0.css +1 -0
- package/dist/web/assets/index-DnEocV0M.js +2 -0
- package/dist/web/assets/light-Cp0t7v32.js +1 -0
- package/dist/web/assets/markdown-u082bbtj.js +1 -0
- package/dist/web/assets/platforms-CBkB45JP.js +1 -0
- package/dist/web/assets/platforms-FTpjxAT6.js +1 -0
- package/dist/web/assets/project-config-B2baPzYL.js +1 -0
- package/dist/web/assets/sessions-Cmy31slD.js +1 -0
- package/dist/web/assets/sessions-yC_oVVh8.js +1 -0
- package/dist/web/assets/skill-repo-input-B_fkGlUK.js +1 -0
- package/dist/web/assets/use-lock-html-scroll-DXIqmUW4.js +1 -0
- package/dist/web/assets/use-message-DoLM8C1C.js +1 -0
- package/dist/web/assets/useResponsiveDrawer-B3uojyvV.js +1 -0
- package/dist/web/assets/vendors-D0c_0KLP.js +1 -0
- package/dist/web/assets/vue-vendor-BqZbbXHA.js +1 -0
- package/dist/web/assets/workspaces-mjyPVGS8.js +1 -0
- package/dist/web/assets/xml-CgY3RMlX.js +1 -0
- package/dist/web/index.html +12 -8
- package/docs/native-log-memory-2026-09-16.md +43 -0
- package/docs/superpowers/plans/2026-08-26-configuration-driven-cli-platform.md +1578 -0
- package/docs/superpowers/plans/2026-08-30-project-scoped-config-control.md +1260 -0
- package/docs/superpowers/plans/2026-09-01-omp-skill-refresh-boundary.md +213 -0
- package/docs/superpowers/plans/2026-09-01-realtime-logs-panel.md +125 -0
- package/docs/superpowers/plans/2026-09-02-config-driven-platform-boundary.md +86 -0
- package/docs/superpowers/plans/2026-09-02-platform-implementation-drivers.md +508 -0
- package/docs/superpowers/plans/2026-09-03-cli-platform-correctness-boundary.md +760 -0
- package/docs/superpowers/plans/2026-09-03-dependency-security-remediation.md +133 -0
- package/docs/superpowers/plans/2026-09-04-model-catalog-pricing-sync.md +1078 -0
- package/docs/superpowers/plans/2026-09-05-codex-project-scan-500.md +128 -0
- package/docs/superpowers/plans/2026-09-06-project-skill-path-fix.md +97 -0
- package/docs/superpowers/specs/2026-08-26-configuration-driven-cli-platform-design.md +408 -0
- package/docs/superpowers/specs/2026-08-30-project-scoped-config-control-design.md +467 -0
- package/docs/superpowers/specs/2026-09-01-claude-session-content-repair-design.md +111 -0
- package/docs/superpowers/specs/2026-09-01-omp-skill-refresh-boundary-design.md +83 -0
- package/docs/superpowers/specs/2026-09-01-realtime-logs-panel-design.md +27 -0
- package/docs/superpowers/specs/2026-09-02-config-driven-platform-boundary-design.md +64 -0
- package/docs/superpowers/specs/2026-09-02-platform-implementation-driver-boundary-design.md +123 -0
- package/docs/superpowers/specs/2026-09-03-cli-platform-correctness-boundary-design.md +249 -0
- package/docs/superpowers/specs/2026-09-03-dependency-security-remediation-design.md +87 -0
- package/docs/superpowers/specs/2026-09-04-model-catalog-pricing-sync-design.md +301 -0
- package/docs/superpowers/specs/2026-09-05-codex-project-scan-500-design.md +24 -0
- package/package.json +13 -8
- package/src/commands/channels.js +88 -191
- package/src/commands/cli-type.js +19 -11
- package/src/commands/daemon.js +94 -32
- package/src/commands/doctor.js +16 -9
- package/src/commands/export-config.js +23 -5
- package/src/commands/list.js +130 -52
- package/src/commands/logs.js +44 -27
- package/src/commands/platform-command-registry.js +78 -0
- package/src/commands/plugin.js +11 -5
- package/src/commands/port-config.js +34 -52
- package/src/commands/proxy-control.js +207 -145
- package/src/commands/proxy.js +2 -2
- package/src/commands/resume.js +87 -91
- package/src/commands/search.js +137 -67
- package/src/commands/stats.js +30 -22
- package/src/commands/switch.js +1 -1
- package/src/commands/toggle-proxy.js +94 -111
- package/src/commands/ui.js +67 -6
- package/src/commands/workspace.js +90 -8
- package/src/config/default.js +6 -53
- package/src/config/loader.js +61 -24
- package/src/config/model-metadata.js +49 -59
- package/src/config/model-metadata.json +4095 -99
- package/src/config/paths.js +181 -8
- package/src/index.js +134 -71
- package/src/platforms/access.js +196 -0
- package/src/{server/services → platforms}/agents-service.js +354 -197
- package/src/platforms/channel-auth-registry.js +162 -0
- package/src/platforms/channel-auth-service.js +82 -0
- package/src/{server/services → platforms}/commands-service.js +195 -115
- package/src/platforms/driver-registry.js +59 -0
- package/src/platforms/drivers/claude/api-operations.js +62 -0
- package/src/{server/services/channels.js → platforms/drivers/claude/channels-implementation.js} +131 -33
- package/src/platforms/drivers/claude/channels.js +39 -0
- package/src/{server/services/config-sync-service.js → platforms/drivers/claude/config-sync.js} +29 -5
- package/src/{server/services → platforms/drivers/claude}/health-check.js +4 -4
- package/src/platforms/drivers/claude/health.js +22 -0
- package/src/platforms/drivers/claude/mcp.js +9 -0
- package/src/{server/services/settings-manager.js → platforms/drivers/claude/native-config-implementation.js} +13 -4
- package/src/platforms/drivers/claude/native-config.js +46 -0
- package/src/platforms/drivers/claude/native-logs.js +72 -0
- package/src/{server/services/claude-openai-gateway.js → platforms/drivers/claude/openai-gateway.js} +8 -65
- package/src/platforms/drivers/claude/project-config.js +21 -0
- package/src/platforms/drivers/claude/projects.js +62 -0
- package/src/platforms/drivers/claude/prompts.js +9 -0
- package/src/{server/proxy-server.js → platforms/drivers/claude/proxy-implementation.js} +41 -278
- package/src/platforms/drivers/claude/proxy.js +16 -0
- package/src/platforms/drivers/claude/resource-sync.js +9 -0
- package/src/platforms/drivers/claude/session-history-adapter.js +313 -0
- package/src/{server/services/sessions.js → platforms/drivers/claude/sessions-implementation.js} +145 -35
- package/src/platforms/drivers/claude/sessions.js +76 -0
- package/src/{server/services/claude-statistics-service.js → platforms/drivers/claude/statistics-implementation.js} +2 -2
- package/src/platforms/drivers/claude/statistics.js +16 -0
- package/src/{server/services/claude-wire.js → platforms/drivers/claude/wire.js} +1 -1
- package/src/platforms/drivers/codex/api-operations.js +9 -0
- package/src/{server/services/codex-channels.js → platforms/drivers/codex/channels-implementation.js} +149 -14
- package/src/platforms/drivers/codex/channels.js +45 -0
- package/src/{server/services/codex-config.js → platforms/drivers/codex/config.js} +12 -2
- package/src/{server/services/codex-env-manager.js → platforms/drivers/codex/env-manager.js} +1 -1
- package/src/platforms/drivers/codex/mcp.js +9 -0
- package/src/{server/services/codex-settings-manager.js → platforms/drivers/codex/native-config-implementation.js} +15 -6
- package/src/platforms/drivers/codex/native-config.js +47 -0
- package/src/platforms/drivers/codex/native-logs.js +166 -0
- package/src/platforms/drivers/codex/project-config.js +21 -0
- package/src/platforms/drivers/codex/projects.js +26 -0
- package/src/platforms/drivers/codex/prompts.js +9 -0
- package/src/{server/codex-proxy-server.js → platforms/drivers/codex/proxy-implementation.js} +30 -233
- package/src/platforms/drivers/codex/proxy.js +16 -0
- package/src/platforms/drivers/codex/resource-sync.js +9 -0
- package/src/{server/services/session-history-adapters/codex.js → platforms/drivers/codex/session-history-adapter.js} +37 -23
- package/src/{server/services/codex-sessions.js → platforms/drivers/codex/sessions-implementation.js} +81 -75
- package/src/platforms/drivers/codex/sessions.js +36 -0
- package/src/{server/services/codex-statistics-service.js → platforms/drivers/codex/statistics-implementation.js} +2 -2
- package/src/platforms/drivers/codex/statistics.js +16 -0
- package/src/platforms/drivers/gemini/api-operations.js +9 -0
- package/src/{server/services/gemini-channels.js → platforms/drivers/gemini/channels-implementation.js} +202 -37
- package/src/platforms/drivers/gemini/channels.js +39 -0
- package/src/{server/services/gemini-config.js → platforms/drivers/gemini/config.js} +12 -2
- package/src/platforms/drivers/gemini/mcp.js +9 -0
- package/src/{server/services/gemini-settings-manager.js → platforms/drivers/gemini/native-config-implementation.js} +14 -5
- package/src/platforms/drivers/gemini/native-config.js +39 -0
- package/src/platforms/drivers/gemini/native-logs.js +180 -0
- package/src/platforms/drivers/gemini/project-config.js +21 -0
- package/src/platforms/drivers/gemini/projects.js +26 -0
- package/src/platforms/drivers/gemini/prompts.js +9 -0
- package/src/{server/gemini-proxy-server.js → platforms/drivers/gemini/proxy-implementation.js} +36 -267
- package/src/platforms/drivers/gemini/proxy.js +16 -0
- package/src/platforms/drivers/gemini/resource-sync.js +9 -0
- package/src/{server/services/session-history-adapters/gemini.js → platforms/drivers/gemini/session-history-adapter.js} +70 -18
- package/src/{server/services/gemini-sessions.js → platforms/drivers/gemini/sessions-implementation.js} +53 -37
- package/src/platforms/drivers/gemini/sessions.js +47 -0
- package/src/{server/services/gemini-statistics-service.js → platforms/drivers/gemini/statistics-implementation.js} +2 -2
- package/src/platforms/drivers/gemini/statistics.js +16 -0
- package/src/{server/services/gemini-wire.js → platforms/drivers/gemini/wire.js} +1 -1
- package/src/platforms/drivers/generic-filesystem.js +166 -0
- package/src/platforms/drivers/generic-jsonl.js +141 -0
- package/src/platforms/drivers/generic-mcp.js +44 -0
- package/src/platforms/drivers/generic-openai-compatible.js +45 -0
- package/src/platforms/drivers/generic-prompt.js +41 -0
- package/src/platforms/drivers/legacy.js +677 -0
- package/src/platforms/drivers/native-config-snapshot.js +125 -0
- package/src/platforms/drivers/native-log-utils.js +1292 -0
- package/src/platforms/drivers/notification-hooks.js +578 -0
- package/src/{server/api/omp-config.js → platforms/drivers/omp/api-operations.js} +52 -64
- package/src/{server/services/omp-auth-provider-worker.js → platforms/drivers/omp/auth-provider-worker.js} +1 -1
- package/src/{server/services/omp-auth-providers.js → platforms/drivers/omp/auth-providers.js} +1 -1
- package/src/{server/services/omp-channels.js → platforms/drivers/omp/channels-implementation.js} +166 -39
- package/src/platforms/drivers/omp/channels.js +58 -0
- package/src/{server/services/omp-config.js → platforms/drivers/omp/config.js} +53 -13
- package/src/{server/services/omp-gateway-routing.js → platforms/drivers/omp/gateway-routing.js} +14 -7
- package/src/{server/services/omp-gateway.js → platforms/drivers/omp/gateway.js} +4 -262
- package/src/platforms/drivers/omp/mcp.js +9 -0
- package/src/{server/services/omp-settings-manager.js → platforms/drivers/omp/native-config-implementation.js} +75 -10
- package/src/platforms/drivers/omp/native-config.js +46 -0
- package/src/platforms/drivers/omp/native-logs.js +52 -0
- package/src/{server/services/omp-native-plugin-adapter.js → platforms/drivers/omp/native-plugin-adapter.js} +91 -4
- package/src/platforms/drivers/omp/project-config.js +29 -0
- package/src/platforms/drivers/omp/projects.js +20 -0
- package/src/platforms/drivers/omp/prompts.js +9 -0
- package/src/{server/omp-proxy-server.js → platforms/drivers/omp/proxy-implementation.js} +6 -15
- package/src/platforms/drivers/omp/proxy.js +16 -0
- package/src/platforms/drivers/omp/resource-sync.js +9 -0
- package/src/{server/services/session-history-adapters/omp.js → platforms/drivers/omp/session-history-adapter.js} +42 -18
- package/src/{server/services/omp-sessions.js → platforms/drivers/omp/sessions-implementation.js} +130 -94
- package/src/platforms/drivers/omp/sessions.js +36 -0
- package/src/{server/services/omp-skill-discovery.js → platforms/drivers/omp/skill-discovery.js} +159 -32
- package/src/{server/services/omp-skill-settings-service.js → platforms/drivers/omp/skill-settings.js} +1 -1
- package/src/{server/services/omp-statistics-service.js → platforms/drivers/omp/statistics-implementation.js} +3 -3
- package/src/platforms/drivers/omp/statistics.js +16 -0
- package/src/platforms/drivers/opencode/api-operations.js +9 -0
- package/src/{server/services/opencode-channels.js → platforms/drivers/opencode/channels-implementation.js} +28 -9
- package/src/platforms/drivers/opencode/channels.js +40 -0
- package/src/platforms/drivers/opencode/conversion.js +41 -0
- package/src/{server/services/opencode-gateway-converter.js → platforms/drivers/opencode/gateway-converter.js} +1 -1
- package/src/platforms/drivers/opencode/mcp.js +9 -0
- package/src/{server/services/opencode-settings-manager.js → platforms/drivers/opencode/native-config-implementation.js} +17 -6
- package/src/platforms/drivers/opencode/native-config.js +26 -0
- package/src/platforms/drivers/opencode/native-logs.js +133 -0
- package/src/platforms/drivers/opencode/project-config.js +98 -0
- package/src/platforms/drivers/opencode/projects.js +20 -0
- package/src/platforms/drivers/opencode/prompts.js +9 -0
- package/src/{server/opencode-proxy-server.js → platforms/drivers/opencode/proxy-implementation.js} +27 -362
- package/src/platforms/drivers/opencode/proxy.js +16 -0
- package/src/platforms/drivers/opencode/resource-sync.js +9 -0
- package/src/{server/services/opencode-sessions.js → platforms/drivers/opencode/sessions-implementation.js} +211 -64
- package/src/platforms/drivers/opencode/sessions.js +48 -0
- package/src/{server/services/opencode-statistics-service.js → platforms/drivers/opencode/statistics-implementation.js} +2 -2
- package/src/platforms/drivers/opencode/statistics.js +16 -0
- package/src/platforms/drivers/secure-file-driver.js +272 -0
- package/src/platforms/drivers/unsupported.js +7 -0
- package/src/platforms/manifest-schema.js +398 -0
- package/src/platforms/manifests/claude.json +489 -0
- package/src/platforms/manifests/codex.json +448 -0
- package/src/platforms/manifests/gemini.json +447 -0
- package/src/platforms/manifests/omp.json +496 -0
- package/src/platforms/manifests/opencode.json +422 -0
- package/src/{server/services → platforms}/native-oauth-adapters.js +87 -417
- package/src/{server/services → platforms}/notification-hooks.js +140 -1023
- package/src/platforms/oauth-credentials-service.js +506 -0
- package/src/platforms/path-resolver.js +179 -0
- package/src/platforms/platform-path-context.js +305 -0
- package/src/platforms/registry.js +248 -0
- package/src/platforms/remote-notification-providers.js +678 -0
- package/src/platforms/runtime.js +259 -0
- package/src/{server/services/session-history-adapters/index.js → platforms/session-history-adapters.js} +8 -1
- package/src/plugins/plugin-loader.js +2 -2
- package/src/plugins/plugin-manager.js +2 -2
- package/src/plugins/registry.js +27 -10
- package/src/reset-config.js +21 -5
- package/src/server/api/agents.js +46 -72
- package/src/server/api/commands.js +16 -6
- package/src/server/api/config-registry.js +206 -59
- package/src/server/api/config-sync.js +3 -2
- package/src/server/api/config-templates.js +20 -4
- package/src/server/api/config.js +111 -102
- package/src/server/api/convert.js +54 -178
- package/src/server/api/dashboard.js +168 -102
- package/src/server/api/hooks.js +1 -1
- package/src/server/api/mcp.js +224 -84
- package/src/server/api/platform-api-config.js +102 -0
- package/src/server/api/platform-route-factory.js +215 -0
- package/src/server/api/platform-routes.js +26 -0
- package/src/server/api/platforms.js +110 -0
- package/src/server/api/plugins.js +57 -57
- package/src/server/api/project-config.js +168 -0
- package/src/server/api/prompts.js +58 -45
- package/src/server/api/settings.js +55 -8
- package/src/server/api/skills.js +262 -133
- package/src/server/api/ui-config.js +2 -2
- package/src/server/api/validation-errors.js +18 -4
- package/src/server/api/workspaces.js +47 -12
- package/src/server/index.js +164 -213
- package/src/server/platform-context.js +81 -0
- package/src/server/services/channel-balance.js +11 -20
- package/src/server/services/channel-health.js +21 -6
- package/src/server/services/channel-scheduler.js +29 -39
- package/src/server/services/channel-sync-utils.js +23 -13
- package/src/server/services/config-export-service.js +350 -319
- package/src/server/services/config-registry-service.js +280 -54
- package/src/server/services/config-sync-manager.js +172 -944
- package/src/server/services/config-templates-service.js +419 -353
- package/src/server/services/control-manifest-store.js +206 -0
- package/src/server/services/dashboard-snapshot-worker.js +656 -100
- package/src/server/services/effective-control-service.js +557 -0
- package/src/server/services/local-resource-index.js +314 -0
- package/src/server/services/mcp-service.js +607 -977
- package/src/server/services/model-detector.js +15 -10
- package/src/server/services/native-log-channel-resolver.js +214 -0
- package/src/server/services/native-log-observer.js +291 -0
- package/src/server/services/network-access.js +1 -1
- package/src/server/services/platform-catalog.js +82 -0
- package/src/server/services/platform-resolution.js +32 -29
- package/src/server/services/plugins-service.js +376 -122
- package/src/server/services/project-config-service.js +550 -0
- package/src/server/services/project-path-validation.js +31 -0
- package/src/server/services/prompts-service.js +267 -233
- package/src/server/services/proxy-runtime.js +7 -4
- package/src/server/services/remote-credential-cache.js +43 -0
- package/src/server/services/repo-scanner-base.js +182 -75
- package/src/server/services/request-logger.js +7 -4
- package/src/server/services/session-history-index.js +780 -213
- package/src/server/services/session-history-worker.js +228 -33
- package/src/server/services/session-snapshot-worker.js +124 -34
- package/src/server/services/session-snapshots.js +4 -2
- package/src/server/services/skill-artifact-store.js +355 -0
- package/src/server/services/skill-format-adapters.js +65 -0
- package/src/server/services/skill-projection-service.js +323 -0
- package/src/server/services/skill-refresh-task-service.js +275 -0
- package/src/server/services/skill-service.js +2020 -677
- package/src/server/services/snapshot-cache.js +30 -8
- package/src/server/services/speed-test.js +8 -8
- package/src/server/services/sqlite-connection.js +19 -3
- package/src/server/services/statistics-service.js +240 -57
- package/src/server/services/ui-config.js +107 -76
- package/src/server/services/usage-log-utils.js +98 -0
- package/src/server/services/web-build.js +1 -1
- package/src/server/services/workspace-service.js +59 -29
- package/src/server/utils/pricing.js +10 -19
- package/src/server/websocket-server.js +153 -28
- package/src/{server/services/base → shared}/base-channel-service.js +156 -28
- package/src/shared/capability-driver.js +104 -0
- package/src/shared/channel-auth-policy.js +27 -0
- package/src/shared/channel-driver.js +161 -0
- package/src/shared/driver-factories/api.js +180 -0
- package/src/shared/driver-factories/mcp.js +244 -0
- package/src/shared/driver-factories/projects.js +121 -0
- package/src/shared/driver-factories/prompts.js +58 -0
- package/src/shared/driver-factories/resource-sync.js +375 -0
- package/src/shared/driver-result.js +78 -0
- package/src/shared/mcp-format.js +189 -0
- package/src/shared/platforms.js +104 -167
- package/src/shared/project-config.js +512 -0
- package/src/shared/proxy-driver.js +57 -0
- package/src/{server/services/base → shared}/response-usage-parser.js +8 -4
- package/src/ui/menu.js +91 -67
- package/src/utils/port-helper.js +35 -19
- package/src/utils/session.js +1 -1
- package/dist/web/assets/Analytics-COEUXmd4.js +0 -25
- package/dist/web/assets/Analytics-RNn1BUbG.css +0 -1
- package/dist/web/assets/ConfigTemplates-DLLkS53H.js +0 -1
- package/dist/web/assets/Home-DCEyLzwX.js +0 -1
- package/dist/web/assets/Home-DYVy4cGZ.css +0 -1
- package/dist/web/assets/PluginManager-Oe1ZHoMO.js +0 -1
- package/dist/web/assets/ProjectList-CWjuAFof.js +0 -1
- package/dist/web/assets/ProjectList-VeMNXfgO.css +0 -1
- package/dist/web/assets/SessionList-C4chCPxK.css +0 -1
- package/dist/web/assets/SessionList-v3ehGg9U.js +0 -1
- package/dist/web/assets/SkillManager-BsX8Qpj0.css +0 -1
- package/dist/web/assets/SkillManager-DUF_qP5S.js +0 -1
- package/dist/web/assets/WorkspaceManager-CjAWBosO.js +0 -1
- package/dist/web/assets/icons-nGdSKQn2.js +0 -1
- package/dist/web/assets/index-D5hyHPj7.js +0 -2
- package/dist/web/assets/index-bJRG3RXH.css +0 -1
- package/dist/web/assets/markdown-BNJfEgCA.js +0 -1
- package/dist/web/assets/markdown-BfC0goYb.css +0 -10
- package/dist/web/assets/naive-ui-BALVpMgd.js +0 -1
- package/dist/web/assets/vendors-DQA4Hw9v.js +0 -7
- package/dist/web/assets/vue-vendor-B5RqjEjQ.js +0 -45
- package/src/config/model-pricing.js +0 -35
- package/src/server/api/channels.js +0 -439
- package/src/server/api/claude-hooks.js +0 -56
- package/src/server/api/claude-statistics.js +0 -57
- package/src/server/api/codex-channels.js +0 -491
- package/src/server/api/codex-projects.js +0 -117
- package/src/server/api/codex-proxy.js +0 -293
- package/src/server/api/codex-sessions.js +0 -652
- package/src/server/api/codex-statistics.js +0 -57
- package/src/server/api/gemini-channels.js +0 -392
- package/src/server/api/gemini-projects.js +0 -103
- package/src/server/api/gemini-proxy.js +0 -231
- package/src/server/api/gemini-sessions.js +0 -550
- package/src/server/api/gemini-statistics.js +0 -57
- package/src/server/api/health-check.js +0 -31
- package/src/server/api/oauth-credentials.js +0 -198
- package/src/server/api/omp-channels.js +0 -575
- package/src/server/api/omp-projects.js +0 -88
- package/src/server/api/omp-proxy.js +0 -125
- package/src/server/api/omp-sessions.js +0 -386
- package/src/server/api/omp-statistics.js +0 -40
- package/src/server/api/opencode-channels.js +0 -471
- package/src/server/api/opencode-projects.js +0 -110
- package/src/server/api/opencode-proxy.js +0 -354
- package/src/server/api/opencode-sessions.js +0 -466
- package/src/server/api/opencode-statistics.js +0 -57
- package/src/server/api/projects.js +0 -120
- package/src/server/api/proxy.js +0 -330
- package/src/server/api/sessions.js +0 -810
- package/src/server/services/oauth-credentials-service.js +0 -945
- package/src/server/services/omp-session-log-observer.js +0 -138
- package/src/server/services/opencode-gateway-adapters.js +0 -43
- package/src/server/services/project-snapshots.js +0 -59
- package/src/server/services/proxy-log-helper.js +0 -298
- package/src/server/services/session-cache.js +0 -127
- package/src/server/services/session-converter.js +0 -582
- package/src/server/services/session-history-adapters/claude.js +0 -235
- /package/src/{server/services/claude-token-recovery.js → platforms/drivers/claude/token-recovery.js} +0 -0
- /package/src/{server/services/codex-parser.js → platforms/drivers/codex/parser.js} +0 -0
- /package/src/{server/services/codex-speed-test-template.json → platforms/drivers/codex/speed-test-template.json} +0 -0
- /package/src/{server/services/codex-wire.js → platforms/drivers/codex/wire.js} +0 -0
- /package/src/{server/services/omp-auth-gateway-client.js → platforms/drivers/omp/auth-gateway-client.js} +0 -0
- /package/src/{server/services/opencode-normalization.js → platforms/drivers/opencode/normalization.js} +0 -0
- /package/src/{server/services → shared}/config-artifact-paths.js +0 -0
- /package/src/{server/services/base → shared}/proxy-utils.js +0 -0
|
@@ -17,8 +17,12 @@ const { listPlugins, getPlugin, updatePlugin: updatePluginRegistry } = require('
|
|
|
17
17
|
const { installPlugin: installPluginCore, uninstallPlugin: uninstallPluginCore } = require('../../plugins/plugin-installer');
|
|
18
18
|
const { initializePlugins, shutdownPlugins } = require('../../plugins/plugin-manager');
|
|
19
19
|
const { INSTALLED_DIR, CONFIG_DIR } = require('../../plugins/constants');
|
|
20
|
-
const
|
|
21
|
-
const {
|
|
20
|
+
const pathsModule = require('../../config/paths');
|
|
21
|
+
const { NATIVE_PATHS, PATHS } = pathsModule;
|
|
22
|
+
const getPlatformStatePath = typeof pathsModule.getPlatformStatePath === 'function'
|
|
23
|
+
? pathsModule.getPlatformStatePath
|
|
24
|
+
: (category, platform) => PATHS[category]?.[platform];
|
|
25
|
+
const { OmpNativePluginAdapter } = require('../../platforms/drivers/omp/native-plugin-adapter');
|
|
22
26
|
const {
|
|
23
27
|
importLegacyPluginRepos,
|
|
24
28
|
migratePiStorage
|
|
@@ -30,7 +34,7 @@ const {
|
|
|
30
34
|
normalizeSafeFileStem,
|
|
31
35
|
normalizeSafeRelativePath,
|
|
32
36
|
resolveInsideRoot
|
|
33
|
-
} = require('
|
|
37
|
+
} = require('../../shared/config-artifact-paths');
|
|
34
38
|
|
|
35
39
|
const CLAUDE_PLUGINS_DIR = NATIVE_PATHS.claude.plugins
|
|
36
40
|
|| path.join(NATIVE_PATHS.claude.dir || path.dirname(NATIVE_PATHS.claude.settings), 'plugins');
|
|
@@ -43,6 +47,8 @@ const CODEX_PLUGINS_CACHE_DIR = path.join(CODEX_PLUGINS_DIR, 'cache');
|
|
|
43
47
|
const OPENCODE_CONFIG_DIR = NATIVE_PATHS.opencode.config;
|
|
44
48
|
const REPO_SOURCE_META_FILE = '.cc-tool-plugin-source.json';
|
|
45
49
|
const SUPPORTED_REPO_PROVIDERS = ['github', 'gitlab', 'local'];
|
|
50
|
+
const PLUGIN_LIST_TTL_MS = 1000;
|
|
51
|
+
const MARKET_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
46
52
|
const DEFAULT_GITHUB_HOST = 'https://github.com';
|
|
47
53
|
const DEFAULT_GITLAB_HOST = 'https://gitlab.com';
|
|
48
54
|
const DEFAULT_REPOS_BY_PLATFORM = {
|
|
@@ -116,7 +122,8 @@ const PLATFORM_CAPABILITIES = {
|
|
|
116
122
|
pluginKindLabel: 'plugins/extensions',
|
|
117
123
|
repositoryMode: 'native-marketplace',
|
|
118
124
|
repositoryToggle: false,
|
|
119
|
-
repositoryAuth: false
|
|
125
|
+
repositoryAuth: false,
|
|
126
|
+
installMetadataMode: 'omp'
|
|
120
127
|
}
|
|
121
128
|
};
|
|
122
129
|
|
|
@@ -435,27 +442,128 @@ function stripJsonComments(input = '') {
|
|
|
435
442
|
}
|
|
436
443
|
|
|
437
444
|
class PluginsService {
|
|
438
|
-
constructor(platform = 'claude') {
|
|
445
|
+
constructor(platform = 'claude', { registry } = {}) {
|
|
439
446
|
this.platform = resolveManagedPlatform(platform).platform;
|
|
447
|
+
this.registry = registry || (() => {
|
|
448
|
+
try {
|
|
449
|
+
return require('../../platforms/runtime').getPlatformRegistry();
|
|
450
|
+
} catch {
|
|
451
|
+
return null;
|
|
452
|
+
}
|
|
453
|
+
})();
|
|
454
|
+
this.pathContext = null;
|
|
455
|
+
try {
|
|
456
|
+
if (typeof this.registry?.resolvePathContext === 'function') {
|
|
457
|
+
this.pathContext = this.registry.resolvePathContext(this.platform);
|
|
458
|
+
}
|
|
459
|
+
} catch {
|
|
460
|
+
this.pathContext = null;
|
|
461
|
+
}
|
|
462
|
+
const native = this.pathContext?.customized
|
|
463
|
+
? (this.pathContext.native || {})
|
|
464
|
+
: (NATIVE_PATHS[this.platform] || {});
|
|
440
465
|
this._ompMigrationWarnings = [];
|
|
441
466
|
if (this.platform === 'omp') {
|
|
442
467
|
const migration = migratePiStorage(PATHS);
|
|
443
468
|
this._ompMigrationWarnings.push(...migration.warnings);
|
|
444
469
|
}
|
|
445
470
|
this.configDir = PATHS.config || path.join((PATHS.base || process.env.HOME || os.homedir()), 'config');
|
|
446
|
-
this.ccToolConfigDir = path.dirname(
|
|
447
|
-
this.
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
this.
|
|
471
|
+
this.ccToolConfigDir = path.dirname(getPlatformStatePath('pluginRepos', this.platform));
|
|
472
|
+
this.claudePluginsDir = this.platform === 'claude'
|
|
473
|
+
? native.plugins || CLAUDE_PLUGINS_DIR
|
|
474
|
+
: CLAUDE_PLUGINS_DIR;
|
|
475
|
+
this.claudeInstalledFile = path.join(this.claudePluginsDir, 'installed_plugins.json');
|
|
476
|
+
this.claudeMarketplacesFile = path.join(this.claudePluginsDir, 'known_marketplaces.json');
|
|
477
|
+
this.claudePluginsCacheDir = path.join(this.claudePluginsDir, 'cache');
|
|
478
|
+
this.claudeMarketplacesDir = path.join(this.claudePluginsDir, 'marketplaces');
|
|
479
|
+
this.codexPluginsDir = this.platform === 'codex'
|
|
480
|
+
? path.join(native.dir || path.dirname(native.config || ''), 'plugins')
|
|
481
|
+
: CODEX_PLUGINS_DIR;
|
|
482
|
+
this.codexPluginsCacheDir = path.join(this.codexPluginsDir, 'cache');
|
|
483
|
+
this.opencodeConfigDir = this.platform === 'opencode'
|
|
484
|
+
? (native.config || OPENCODE_CONFIG_DIR)
|
|
485
|
+
: OPENCODE_CONFIG_DIR;
|
|
486
|
+
this.opencodePluginsDir = path.join(this.opencodeConfigDir, 'plugins');
|
|
487
|
+
this.opencodeLegacyPluginsDir = path.join(this.opencodeConfigDir, 'plugin');
|
|
488
|
+
this.marketCachePath = getPlatformStatePath('pluginMarketCache', this.platform);
|
|
451
489
|
this._marketCache = null;
|
|
490
|
+
this._marketCacheByKey = new Map();
|
|
491
|
+
this._pluginListCache = new Map();
|
|
492
|
+
this._pluginListGeneration = 0;
|
|
493
|
+
this._marketRefreshPromise = null;
|
|
494
|
+
this._marketFetchedAt = 0;
|
|
495
|
+
this._marketRepoFingerprint = null;
|
|
496
|
+
this._marketLastUsedStale = false;
|
|
452
497
|
this.ompNativeAdapter = this.platform === 'omp'
|
|
453
|
-
? new OmpNativePluginAdapter()
|
|
498
|
+
? new OmpNativePluginAdapter({ pathContext: this.pathContext })
|
|
454
499
|
: null;
|
|
455
500
|
}
|
|
456
501
|
|
|
502
|
+
_pluginListKey(options = {}) {
|
|
503
|
+
const cwd = options.cwd ? path.resolve(options.cwd) : '';
|
|
504
|
+
const scope = options.scope || '';
|
|
505
|
+
return `${this.platform}:${cwd}:${scope}`;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
_clone(value) {
|
|
509
|
+
if (value === undefined) return value;
|
|
510
|
+
try {
|
|
511
|
+
return JSON.parse(JSON.stringify(value));
|
|
512
|
+
} catch (_) {
|
|
513
|
+
return value;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
_invalidatePluginList(options = null) {
|
|
518
|
+
this._pluginListGeneration++;
|
|
519
|
+
if (!options) {
|
|
520
|
+
this._pluginListCache.clear();
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
const key = this._pluginListKey(options);
|
|
524
|
+
this._pluginListCache.delete(key);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
|
|
457
528
|
getCapabilities() {
|
|
458
|
-
return {
|
|
529
|
+
return {
|
|
530
|
+
platform: this.platform,
|
|
531
|
+
supportsPlugins: false,
|
|
532
|
+
repositories: false,
|
|
533
|
+
market: false,
|
|
534
|
+
install: false,
|
|
535
|
+
uninstall: false,
|
|
536
|
+
toggle: false,
|
|
537
|
+
config: false,
|
|
538
|
+
import: false,
|
|
539
|
+
syncRepos: false,
|
|
540
|
+
...(PLATFORM_CAPABILITIES[this.platform] || {})
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
getRequestOptions(requestData = {}) {
|
|
544
|
+
return {
|
|
545
|
+
...(requestData.cwd ? { cwd: requestData.cwd } : {}),
|
|
546
|
+
...(requestData.scope ? { scope: requestData.scope } : {})
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
normalizeInstallMetadata(requestBody = {}, repoPayload = null) {
|
|
551
|
+
if (repoPayload) return repoPayload;
|
|
552
|
+
if (this.getCapabilities().installMetadataMode !== 'omp') return null;
|
|
553
|
+
const metadata = requestBody.metadata && typeof requestBody.metadata === 'object'
|
|
554
|
+
? requestBody.metadata
|
|
555
|
+
: {};
|
|
556
|
+
return {
|
|
557
|
+
...metadata,
|
|
558
|
+
name: requestBody.name || metadata.name,
|
|
559
|
+
pluginId: requestBody.pluginId || metadata.pluginId,
|
|
560
|
+
pluginKind: requestBody.pluginKind || metadata.pluginKind,
|
|
561
|
+
marketplace: requestBody.marketplace || metadata.marketplace,
|
|
562
|
+
installSource: requestBody.installSource || metadata.installSource,
|
|
563
|
+
version: requestBody.version || metadata.version,
|
|
564
|
+
description: requestBody.description || metadata.description,
|
|
565
|
+
resourceTypes: requestBody.resourceTypes || metadata.resourceTypes
|
|
566
|
+
};
|
|
459
567
|
}
|
|
460
568
|
|
|
461
569
|
getMigrationWarnings() {
|
|
@@ -595,6 +703,10 @@ class PluginsService {
|
|
|
595
703
|
|
|
596
704
|
clearMarketCache({ removeFile = true } = {}) {
|
|
597
705
|
this._marketCache = null;
|
|
706
|
+
this._marketCacheByKey.clear();
|
|
707
|
+
this._marketFetchedAt = 0;
|
|
708
|
+
this._marketRepoFingerprint = null;
|
|
709
|
+
this._invalidatePluginList();
|
|
598
710
|
if (removeFile) {
|
|
599
711
|
try {
|
|
600
712
|
if (fs.existsSync(this.marketCachePath)) {
|
|
@@ -606,13 +718,19 @@ class PluginsService {
|
|
|
606
718
|
}
|
|
607
719
|
}
|
|
608
720
|
|
|
609
|
-
|
|
721
|
+
loadMarketCacheEnvelope() {
|
|
610
722
|
try {
|
|
611
|
-
if (fs.existsSync(this.marketCachePath))
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
723
|
+
if (!fs.existsSync(this.marketCachePath)) return null;
|
|
724
|
+
const data = JSON.parse(fs.readFileSync(this.marketCachePath, 'utf-8'));
|
|
725
|
+
if (Array.isArray(data)) {
|
|
726
|
+
return { plugins: data, fetchedAt: 0, reposFingerprint: null };
|
|
727
|
+
}
|
|
728
|
+
if (Array.isArray(data?.plugins)) {
|
|
729
|
+
return {
|
|
730
|
+
plugins: data.plugins,
|
|
731
|
+
fetchedAt: Number(data.fetchedAt || data.time || 0) || 0,
|
|
732
|
+
reposFingerprint: typeof data.reposFingerprint === 'string' ? data.reposFingerprint : null
|
|
733
|
+
};
|
|
616
734
|
}
|
|
617
735
|
} catch (err) {
|
|
618
736
|
// ignore cache read errors
|
|
@@ -620,10 +738,22 @@ class PluginsService {
|
|
|
620
738
|
return null;
|
|
621
739
|
}
|
|
622
740
|
|
|
623
|
-
|
|
741
|
+
loadMarketCacheFromFile() {
|
|
742
|
+
const cached = this.loadMarketCacheEnvelope();
|
|
743
|
+
return cached?.fetchedAt && Date.now() - cached.fetchedAt < MARKET_CACHE_TTL_MS
|
|
744
|
+
? cached.plugins
|
|
745
|
+
: null;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
saveMarketCacheToFile(plugins, fetchedAt = Date.now(), reposFingerprint = null) {
|
|
624
749
|
try {
|
|
625
750
|
this._ensureDir(path.dirname(this.marketCachePath));
|
|
626
|
-
fs.writeFileSync(this.marketCachePath, JSON.stringify({
|
|
751
|
+
fs.writeFileSync(this.marketCachePath, JSON.stringify({
|
|
752
|
+
fetchedAt,
|
|
753
|
+
time: fetchedAt,
|
|
754
|
+
reposFingerprint,
|
|
755
|
+
plugins
|
|
756
|
+
}), 'utf-8');
|
|
627
757
|
} catch (err) {
|
|
628
758
|
// ignore cache write errors
|
|
629
759
|
}
|
|
@@ -694,9 +824,9 @@ class PluginsService {
|
|
|
694
824
|
}
|
|
695
825
|
|
|
696
826
|
_getOpenCodeConfigPath() {
|
|
697
|
-
const jsonc = path.join(
|
|
698
|
-
const json = path.join(
|
|
699
|
-
const config = path.join(
|
|
827
|
+
const jsonc = path.join(this.opencodeConfigDir, 'opencode.jsonc');
|
|
828
|
+
const json = path.join(this.opencodeConfigDir, 'opencode.json');
|
|
829
|
+
const config = path.join(this.opencodeConfigDir, 'config.json');
|
|
700
830
|
if (fs.existsSync(jsonc)) return jsonc;
|
|
701
831
|
if (fs.existsSync(json)) return json;
|
|
702
832
|
if (fs.existsSync(config)) return config;
|
|
@@ -723,6 +853,7 @@ class PluginsService {
|
|
|
723
853
|
_writeOpenCodeConfig(filePath, config) {
|
|
724
854
|
this._ensureDir(path.dirname(filePath));
|
|
725
855
|
fs.writeFileSync(filePath, JSON.stringify(config, null, 2), 'utf8');
|
|
856
|
+
this._invalidatePluginList();
|
|
726
857
|
}
|
|
727
858
|
|
|
728
859
|
_listOpenCodeConfiguredPlugins() {
|
|
@@ -850,7 +981,7 @@ class PluginsService {
|
|
|
850
981
|
marketplace: marketplaceEntry.name,
|
|
851
982
|
version: manifest.version || selectedVersion.name,
|
|
852
983
|
installPath: selectedVersion.fullPath,
|
|
853
|
-
directory: path.relative(
|
|
984
|
+
directory: path.relative(this.codexPluginsDir, selectedVersion.fullPath),
|
|
854
985
|
source: 'codex-cache',
|
|
855
986
|
installed: true,
|
|
856
987
|
enabled: this._isCodexPluginEnabled(manifest.name || pluginEntry.name, marketplaceEntry.name),
|
|
@@ -881,7 +1012,9 @@ class PluginsService {
|
|
|
881
1012
|
}
|
|
882
1013
|
|
|
883
1014
|
_readCodexConfig() {
|
|
884
|
-
const filePath =
|
|
1015
|
+
const filePath = this.pathContext?.customized
|
|
1016
|
+
? (this.pathContext.native?.config || NATIVE_PATHS.codex.config)
|
|
1017
|
+
: NATIVE_PATHS.codex.config;
|
|
885
1018
|
if (!fs.existsSync(filePath)) return { filePath, config: {} };
|
|
886
1019
|
try {
|
|
887
1020
|
const raw = fs.readFileSync(filePath, 'utf8');
|
|
@@ -894,14 +1027,19 @@ class PluginsService {
|
|
|
894
1027
|
}
|
|
895
1028
|
|
|
896
1029
|
_writeCodexConfig(config) {
|
|
897
|
-
const filePath =
|
|
1030
|
+
const filePath = this.pathContext?.customized
|
|
1031
|
+
? (this.pathContext.native?.config || NATIVE_PATHS.codex.config)
|
|
1032
|
+
: NATIVE_PATHS.codex.config;
|
|
898
1033
|
this._ensureDir(path.dirname(filePath));
|
|
899
1034
|
const safeConfig = JSON.parse(JSON.stringify(config || {}));
|
|
900
1035
|
fs.writeFileSync(filePath, tomlStringify(safeConfig), 'utf8');
|
|
1036
|
+
this._invalidatePluginList();
|
|
901
1037
|
}
|
|
902
1038
|
|
|
903
1039
|
_readClaudeSettings() {
|
|
904
|
-
const filePath =
|
|
1040
|
+
const filePath = this.pathContext?.customized
|
|
1041
|
+
? (this.pathContext.native?.settings || NATIVE_PATHS.claude.settings)
|
|
1042
|
+
: NATIVE_PATHS.claude.settings;
|
|
905
1043
|
if (!fs.existsSync(filePath)) return { filePath, settings: {} };
|
|
906
1044
|
try {
|
|
907
1045
|
const raw = fs.readFileSync(filePath, 'utf8');
|
|
@@ -913,9 +1051,12 @@ class PluginsService {
|
|
|
913
1051
|
}
|
|
914
1052
|
|
|
915
1053
|
_writeClaudeSettings(settings) {
|
|
916
|
-
const filePath =
|
|
1054
|
+
const filePath = this.pathContext?.customized
|
|
1055
|
+
? (this.pathContext.native?.settings || NATIVE_PATHS.claude.settings)
|
|
1056
|
+
: NATIVE_PATHS.claude.settings;
|
|
917
1057
|
this._ensureDir(path.dirname(filePath));
|
|
918
1058
|
fs.writeFileSync(filePath, JSON.stringify(settings || {}, null, 2), 'utf8');
|
|
1059
|
+
this._invalidatePluginList();
|
|
919
1060
|
}
|
|
920
1061
|
|
|
921
1062
|
_setClaudePluginEnabled(name, marketplace, enabled) {
|
|
@@ -967,7 +1108,7 @@ class PluginsService {
|
|
|
967
1108
|
|
|
968
1109
|
_resolveClaudePluginCachePath(marketplace, pluginName, version) {
|
|
969
1110
|
return resolveInsideRoot(
|
|
970
|
-
|
|
1111
|
+
this.claudePluginsCacheDir,
|
|
971
1112
|
path.posix.join(
|
|
972
1113
|
normalizeClaudeMarketplaceSegment(marketplace),
|
|
973
1114
|
normalizePluginCacheSegment(pluginName, 'Claude plugin name', 'plugin'),
|
|
@@ -979,7 +1120,7 @@ class PluginsService {
|
|
|
979
1120
|
|
|
980
1121
|
_resolveClaudeMarketplacePath(marketplace) {
|
|
981
1122
|
return resolveInsideRoot(
|
|
982
|
-
|
|
1123
|
+
this.claudeMarketplacesDir,
|
|
983
1124
|
normalizeClaudeMarketplaceSegment(marketplace),
|
|
984
1125
|
'Claude marketplace path'
|
|
985
1126
|
);
|
|
@@ -1000,11 +1141,11 @@ class PluginsService {
|
|
|
1000
1141
|
};
|
|
1001
1142
|
fs.writeFileSync(manifestPath, JSON.stringify(marketplaceManifest, null, 2), 'utf8');
|
|
1002
1143
|
|
|
1003
|
-
this._ensureDir(
|
|
1144
|
+
this._ensureDir(this.claudePluginsDir);
|
|
1004
1145
|
let known = {};
|
|
1005
|
-
if (fs.existsSync(
|
|
1146
|
+
if (fs.existsSync(this.claudeMarketplacesFile)) {
|
|
1006
1147
|
try {
|
|
1007
|
-
known = JSON.parse(fs.readFileSync(
|
|
1148
|
+
known = JSON.parse(fs.readFileSync(this.claudeMarketplacesFile, 'utf8'));
|
|
1008
1149
|
} catch {
|
|
1009
1150
|
known = {};
|
|
1010
1151
|
}
|
|
@@ -1017,7 +1158,7 @@ class PluginsService {
|
|
|
1017
1158
|
installLocation: marketplaceDir,
|
|
1018
1159
|
lastUpdated: new Date().toISOString()
|
|
1019
1160
|
};
|
|
1020
|
-
fs.writeFileSync(
|
|
1161
|
+
fs.writeFileSync(this.claudeMarketplacesFile, JSON.stringify(known, null, 2), 'utf8');
|
|
1021
1162
|
|
|
1022
1163
|
const { settings } = this._readClaudeSettings();
|
|
1023
1164
|
const nextSettings = (settings && typeof settings === 'object') ? { ...settings } : {};
|
|
@@ -1085,11 +1226,11 @@ class PluginsService {
|
|
|
1085
1226
|
}
|
|
1086
1227
|
|
|
1087
1228
|
_registerClaudeInstalledPlugin(name, marketplace, installPath, installData = {}) {
|
|
1088
|
-
this._ensureDir(
|
|
1229
|
+
this._ensureDir(this.claudePluginsDir);
|
|
1089
1230
|
let nativeData = { version: 2, plugins: {} };
|
|
1090
|
-
if (fs.existsSync(
|
|
1231
|
+
if (fs.existsSync(this.claudeInstalledFile)) {
|
|
1091
1232
|
try {
|
|
1092
|
-
nativeData = JSON.parse(fs.readFileSync(
|
|
1233
|
+
nativeData = JSON.parse(fs.readFileSync(this.claudeInstalledFile, 'utf8'));
|
|
1093
1234
|
} catch {
|
|
1094
1235
|
nativeData = { version: 2, plugins: {} };
|
|
1095
1236
|
}
|
|
@@ -1109,7 +1250,8 @@ class PluginsService {
|
|
|
1109
1250
|
...(installData.source ? { source: installData.source } : {}),
|
|
1110
1251
|
...(installData.repoSourceMeta || {})
|
|
1111
1252
|
}];
|
|
1112
|
-
fs.writeFileSync(
|
|
1253
|
+
fs.writeFileSync(this.claudeInstalledFile, JSON.stringify(nativeData, null, 2), 'utf8');
|
|
1254
|
+
this._invalidatePluginList();
|
|
1113
1255
|
this._setClaudePluginEnabled(name, marketplace, true);
|
|
1114
1256
|
}
|
|
1115
1257
|
|
|
@@ -1176,12 +1318,31 @@ class PluginsService {
|
|
|
1176
1318
|
return slugifyCodexKey(repo.name || repo.owner || 'github');
|
|
1177
1319
|
}
|
|
1178
1320
|
|
|
1321
|
+
listPlugins(options = {}) {
|
|
1322
|
+
const key = this._pluginListKey(options);
|
|
1323
|
+
const now = Date.now();
|
|
1324
|
+
const cached = this._pluginListCache.get(key);
|
|
1325
|
+
if (!options.force && cached
|
|
1326
|
+
&& now - cached.cachedAt < PLUGIN_LIST_TTL_MS
|
|
1327
|
+
&& cached.generation === this._pluginListGeneration) {
|
|
1328
|
+
return this._clone(cached.value);
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
const value = this._listPluginsUncached(options);
|
|
1332
|
+
this._pluginListCache.set(key, {
|
|
1333
|
+
value: this._clone(value),
|
|
1334
|
+
cachedAt: now,
|
|
1335
|
+
generation: this._pluginListGeneration
|
|
1336
|
+
});
|
|
1337
|
+
return this._clone(value);
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1179
1340
|
/**
|
|
1180
1341
|
* List all installed plugins with their status
|
|
1181
1342
|
* Reads from Claude Code's native installed_plugins.json
|
|
1182
1343
|
* @returns {Object} { plugins: Array }
|
|
1183
1344
|
*/
|
|
1184
|
-
|
|
1345
|
+
_listPluginsUncached(options = {}) {
|
|
1185
1346
|
if (!this._pluginsSupported()) {
|
|
1186
1347
|
return { plugins: [] };
|
|
1187
1348
|
}
|
|
@@ -1224,11 +1385,32 @@ class PluginsService {
|
|
|
1224
1385
|
}
|
|
1225
1386
|
|
|
1226
1387
|
const plugins = [];
|
|
1388
|
+
const legacyPlugins = (() => {
|
|
1389
|
+
try {
|
|
1390
|
+
return listPlugins();
|
|
1391
|
+
} catch {
|
|
1392
|
+
return [];
|
|
1393
|
+
}
|
|
1394
|
+
})();
|
|
1395
|
+
const legacyByName = new Map(legacyPlugins.map(plugin => [plugin.name, plugin]));
|
|
1396
|
+
const claudeSettings = this._readClaudeSettings().settings || {};
|
|
1397
|
+
const enabledPlugins = claudeSettings.enabledPlugins || {};
|
|
1398
|
+
const isEnabled = (name, marketplace) => {
|
|
1399
|
+
const exactKey = marketplace ? `${name}@${marketplace}` : name;
|
|
1400
|
+
if (Object.prototype.hasOwnProperty.call(enabledPlugins, exactKey)) {
|
|
1401
|
+
return enabledPlugins[exactKey] !== false;
|
|
1402
|
+
}
|
|
1403
|
+
for (const [key, value] of Object.entries(enabledPlugins)) {
|
|
1404
|
+
const parsed = splitPluginMarketplaceKey(key);
|
|
1405
|
+
if (parsed.name === name) return value !== false;
|
|
1406
|
+
}
|
|
1407
|
+
return true;
|
|
1408
|
+
};
|
|
1227
1409
|
|
|
1228
1410
|
// Read Claude Code's installed_plugins.json
|
|
1229
|
-
if (fs.existsSync(
|
|
1411
|
+
if (fs.existsSync(this.claudeInstalledFile)) {
|
|
1230
1412
|
try {
|
|
1231
|
-
const data = JSON.parse(fs.readFileSync(
|
|
1413
|
+
const data = JSON.parse(fs.readFileSync(this.claudeInstalledFile, 'utf8'));
|
|
1232
1414
|
if (data.plugins) {
|
|
1233
1415
|
for (const [key, installations] of Object.entries(data.plugins)) {
|
|
1234
1416
|
if (installations && installations.length > 0) {
|
|
@@ -1285,9 +1467,10 @@ class PluginsService {
|
|
|
1285
1467
|
}
|
|
1286
1468
|
|
|
1287
1469
|
// Read enabled state from CTX registry (defaults to true if not set)
|
|
1288
|
-
|
|
1289
|
-
const
|
|
1290
|
-
|
|
1470
|
+
// Project settings and legacy registry are loaded once per list call.
|
|
1471
|
+
const legacyInfo = legacyByName.get(name);
|
|
1472
|
+
const enabledState = isEnabled(name, marketplace)
|
|
1473
|
+
&& (legacyInfo ? legacyInfo.enabled !== false : true);
|
|
1291
1474
|
|
|
1292
1475
|
plugins.push({
|
|
1293
1476
|
name,
|
|
@@ -1319,16 +1502,11 @@ class PluginsService {
|
|
|
1319
1502
|
}
|
|
1320
1503
|
}
|
|
1321
1504
|
|
|
1322
|
-
//
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
if (!plugins.find(p => p.name === plugin.name)) {
|
|
1327
|
-
plugins.push(plugin);
|
|
1328
|
-
}
|
|
1505
|
+
// Add registry-only plugins without rereading the registry.
|
|
1506
|
+
for (const plugin of legacyPlugins) {
|
|
1507
|
+
if (!plugins.find(existing => existing.name === plugin.name)) {
|
|
1508
|
+
plugins.push(plugin);
|
|
1329
1509
|
}
|
|
1330
|
-
} catch (err) {
|
|
1331
|
-
// Ignore legacy registry errors
|
|
1332
1510
|
}
|
|
1333
1511
|
|
|
1334
1512
|
return { plugins };
|
|
@@ -1442,7 +1620,9 @@ class PluginsService {
|
|
|
1442
1620
|
|| metadata.installSource
|
|
1443
1621
|
|| ''
|
|
1444
1622
|
).trim();
|
|
1445
|
-
|
|
1623
|
+
const result = this.ompNativeAdapter.installPlugin(target, metadata, options);
|
|
1624
|
+
this._invalidatePluginList(options);
|
|
1625
|
+
return result;
|
|
1446
1626
|
}
|
|
1447
1627
|
|
|
1448
1628
|
if (this._isOpenCode()) {
|
|
@@ -1868,9 +2048,10 @@ class PluginsService {
|
|
|
1868
2048
|
error: `${this.platform} plugin management is not supported`
|
|
1869
2049
|
};
|
|
1870
2050
|
}
|
|
1871
|
-
|
|
1872
2051
|
if (this._isOmp()) {
|
|
1873
|
-
|
|
2052
|
+
const result = this.ompNativeAdapter.uninstallPlugin(name, options);
|
|
2053
|
+
this._invalidatePluginList(options);
|
|
2054
|
+
return result;
|
|
1874
2055
|
}
|
|
1875
2056
|
|
|
1876
2057
|
if (this._isOpenCode()) {
|
|
@@ -1915,9 +2096,9 @@ class PluginsService {
|
|
|
1915
2096
|
// Claude: Remove from native installed_plugins.json and delete install directories
|
|
1916
2097
|
let removed = false;
|
|
1917
2098
|
const safeName = normalizePluginPathName(name, 'plugin name');
|
|
1918
|
-
if (fs.existsSync(
|
|
2099
|
+
if (fs.existsSync(this.claudeInstalledFile)) {
|
|
1919
2100
|
try {
|
|
1920
|
-
const data = JSON.parse(fs.readFileSync(
|
|
2101
|
+
const data = JSON.parse(fs.readFileSync(this.claudeInstalledFile, 'utf8'));
|
|
1921
2102
|
if (data.plugins) {
|
|
1922
2103
|
const keysToDelete = [];
|
|
1923
2104
|
const baseName = safeName.split('/').pop(); // handle "plugins/pr-review-toolkit" → "pr-review-toolkit"
|
|
@@ -1932,7 +2113,7 @@ class PluginsService {
|
|
|
1932
2113
|
try {
|
|
1933
2114
|
const installPath = assertInsideAllowedRoots(
|
|
1934
2115
|
install.installPath,
|
|
1935
|
-
[INSTALLED_DIR,
|
|
2116
|
+
[INSTALLED_DIR, this.claudePluginsDir],
|
|
1936
2117
|
'Claude plugin install path'
|
|
1937
2118
|
);
|
|
1938
2119
|
fs.rmSync(installPath, { recursive: true, force: true });
|
|
@@ -1950,7 +2131,8 @@ class PluginsService {
|
|
|
1950
2131
|
this._removeClaudePluginEnabled(parsed.name || safeName, parsed.marketplace || '');
|
|
1951
2132
|
delete data.plugins[key];
|
|
1952
2133
|
}
|
|
1953
|
-
fs.writeFileSync(
|
|
2134
|
+
fs.writeFileSync(this.claudeInstalledFile, JSON.stringify(data, null, 2), 'utf8');
|
|
2135
|
+
this._invalidatePluginList();
|
|
1954
2136
|
removed = true;
|
|
1955
2137
|
}
|
|
1956
2138
|
}
|
|
@@ -2006,7 +2188,9 @@ class PluginsService {
|
|
|
2006
2188
|
}
|
|
2007
2189
|
|
|
2008
2190
|
if (this._isOmp()) {
|
|
2009
|
-
|
|
2191
|
+
const result = this.ompNativeAdapter.togglePlugin(name, enabled, options);
|
|
2192
|
+
this._invalidatePluginList(options);
|
|
2193
|
+
return result;
|
|
2010
2194
|
}
|
|
2011
2195
|
|
|
2012
2196
|
if (this._isOpenCode()) {
|
|
@@ -2029,9 +2213,9 @@ class PluginsService {
|
|
|
2029
2213
|
// First check if plugin exists in native installed_plugins.json
|
|
2030
2214
|
let pluginExists = false;
|
|
2031
2215
|
const baseName = name.split('/').pop();
|
|
2032
|
-
if (fs.existsSync(
|
|
2216
|
+
if (fs.existsSync(this.claudeInstalledFile)) {
|
|
2033
2217
|
try {
|
|
2034
|
-
const data = JSON.parse(fs.readFileSync(
|
|
2218
|
+
const data = JSON.parse(fs.readFileSync(this.claudeInstalledFile, 'utf8'));
|
|
2035
2219
|
if (data.plugins) {
|
|
2036
2220
|
for (const key of Object.keys(data.plugins)) {
|
|
2037
2221
|
const { name: pluginName } = splitPluginMarketplaceKey(key);
|
|
@@ -2064,9 +2248,9 @@ class PluginsService {
|
|
|
2064
2248
|
} catch (e) {
|
|
2065
2249
|
console.warn('[PluginsService] Failed to update plugin registry:', e.message);
|
|
2066
2250
|
}
|
|
2067
|
-
if (fs.existsSync(
|
|
2251
|
+
if (fs.existsSync(this.claudeInstalledFile)) {
|
|
2068
2252
|
try {
|
|
2069
|
-
const data = JSON.parse(fs.readFileSync(
|
|
2253
|
+
const data = JSON.parse(fs.readFileSync(this.claudeInstalledFile, 'utf8'));
|
|
2070
2254
|
for (const key of Object.keys(data.plugins || {})) {
|
|
2071
2255
|
const parsed = splitPluginMarketplaceKey(key);
|
|
2072
2256
|
if (parsed.name === name || key === name || parsed.name === baseName) {
|
|
@@ -2103,11 +2287,13 @@ class PluginsService {
|
|
|
2103
2287
|
}
|
|
2104
2288
|
|
|
2105
2289
|
if (this._isOmp()) {
|
|
2106
|
-
|
|
2290
|
+
const result = this.ompNativeAdapter.updatePluginConfig(name, config, options);
|
|
2291
|
+
this._invalidatePluginList(options);
|
|
2292
|
+
return result;
|
|
2107
2293
|
}
|
|
2108
2294
|
|
|
2109
2295
|
if (this._isOpenCode()) {
|
|
2110
|
-
const configDir = path.join(
|
|
2296
|
+
const configDir = path.join(this.opencodeConfigDir, 'plugins-config');
|
|
2111
2297
|
const configFile = resolvePluginConfigFile(configDir, name);
|
|
2112
2298
|
this._ensureDir(path.dirname(configFile));
|
|
2113
2299
|
fs.writeFileSync(configFile, JSON.stringify(config, null, 2), 'utf8');
|
|
@@ -2142,13 +2328,14 @@ class PluginsService {
|
|
|
2142
2328
|
* @returns {string} Config file path
|
|
2143
2329
|
*/
|
|
2144
2330
|
getReposConfigPath() {
|
|
2145
|
-
const filePath =
|
|
2331
|
+
const filePath = getPlatformStatePath('pluginRepos', this.platform);
|
|
2332
|
+
if (!filePath) throw new Error(`Plugin repository path is not configured for ${this.platform}`);
|
|
2146
2333
|
this._ensureDir(path.dirname(filePath));
|
|
2147
2334
|
return filePath;
|
|
2148
2335
|
}
|
|
2149
2336
|
|
|
2150
2337
|
_getDefaultRepos() {
|
|
2151
|
-
return cloneRepos(DEFAULT_REPOS_BY_PLATFORM[this.platform] ||
|
|
2338
|
+
return cloneRepos(DEFAULT_REPOS_BY_PLATFORM[this.platform] || []);
|
|
2152
2339
|
}
|
|
2153
2340
|
|
|
2154
2341
|
/**
|
|
@@ -2227,9 +2414,9 @@ class PluginsService {
|
|
|
2227
2414
|
}
|
|
2228
2415
|
|
|
2229
2416
|
// 2. Load Claude Code's native marketplace config (Claude only)
|
|
2230
|
-
if (this.platform === 'claude' && fs.existsSync(
|
|
2417
|
+
if (this.platform === 'claude' && fs.existsSync(this.claudeMarketplacesFile)) {
|
|
2231
2418
|
try {
|
|
2232
|
-
const marketplaces = JSON.parse(fs.readFileSync(
|
|
2419
|
+
const marketplaces = JSON.parse(fs.readFileSync(this.claudeMarketplacesFile, 'utf8'));
|
|
2233
2420
|
const entries = [];
|
|
2234
2421
|
if (Array.isArray(marketplaces)) {
|
|
2235
2422
|
entries.push(...marketplaces.map(item => ({ key: '', data: item })));
|
|
@@ -2975,45 +3162,46 @@ class PluginsService {
|
|
|
2975
3162
|
};
|
|
2976
3163
|
}
|
|
2977
3164
|
|
|
2978
|
-
async _fetchOpenCodeMarketplacePlugins(repo, branch) {
|
|
3165
|
+
async _fetchOpenCodeMarketplacePlugins(repo, branch, tree = null) {
|
|
2979
3166
|
if (!this._isOpenCode()) return [];
|
|
2980
3167
|
|
|
2981
|
-
const
|
|
2982
|
-
const manifestFiles =
|
|
3168
|
+
const sourceTree = tree || await this.fetchRepoTree(repo);
|
|
3169
|
+
const manifestFiles = sourceTree.filter(item =>
|
|
2983
3170
|
item.type === 'blob' &&
|
|
2984
3171
|
item.path.startsWith('plugins/') &&
|
|
2985
3172
|
item.path.endsWith('.plugin.json')
|
|
2986
3173
|
);
|
|
2987
3174
|
if (manifestFiles.length === 0) return [];
|
|
2988
3175
|
|
|
2989
|
-
const results =
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3176
|
+
const results = [];
|
|
3177
|
+
for (let start = 0; start < manifestFiles.length; start += 5) {
|
|
3178
|
+
const batch = manifestFiles.slice(start, start + 5);
|
|
3179
|
+
results.push(...await Promise.allSettled(
|
|
3180
|
+
batch.map(async (file) => {
|
|
3181
|
+
const manifest = await this.fetchRepoJson(repo, file.path, file);
|
|
3182
|
+
|
|
3183
|
+
const author = Array.isArray(manifest.authors)
|
|
3184
|
+
? manifest.authors.map(item => item?.name).filter(Boolean).join(', ')
|
|
3185
|
+
: '';
|
|
3186
|
+
const firstCategory = Array.isArray(manifest.categories) ? manifest.categories[0] : '';
|
|
3187
|
+
const repoUrl = manifest.links?.repository || `https://github.com/${repo.owner}/${repo.name}`;
|
|
3188
|
+
const installSource = String(manifest.name || '').trim();
|
|
3189
|
+
|
|
3190
|
+
return this.buildMarketPluginItem(repo, {
|
|
3191
|
+
name: manifest.name || file.name.replace(/\.plugin\.json$/, ''),
|
|
3192
|
+
displayName: manifest.displayName || '',
|
|
3193
|
+
description: manifest.description || '',
|
|
3194
|
+
author: author || repo.owner,
|
|
3195
|
+
version: manifest.version || manifest.opencode?.minimumVersion || '1.0.0',
|
|
3196
|
+
category: firstCategory ? String(firstCategory).toLowerCase() : 'general',
|
|
3197
|
+
directory: file.path,
|
|
3198
|
+
installSource,
|
|
3199
|
+
marketplaceFormat: 'opencode-plugin-json',
|
|
3200
|
+
repoUrl
|
|
3201
|
+
});
|
|
3202
|
+
})
|
|
3203
|
+
));
|
|
3204
|
+
}
|
|
3017
3205
|
|
|
3018
3206
|
return results
|
|
3019
3207
|
.filter(item => item.status === 'fulfilled' && item.value)
|
|
@@ -3219,14 +3407,85 @@ class PluginsService {
|
|
|
3219
3407
|
return results;
|
|
3220
3408
|
}
|
|
3221
3409
|
|
|
3410
|
+
_marketKey(options = {}) {
|
|
3411
|
+
return `${this.platform}:${options.cwd ? path.resolve(options.cwd) : ''}:${options.scope || ''}`;
|
|
3412
|
+
}
|
|
3413
|
+
|
|
3414
|
+
_marketReposFingerprint(repos) {
|
|
3415
|
+
return JSON.stringify((repos || []).map(repo => ({
|
|
3416
|
+
id: repo.id || '',
|
|
3417
|
+
enabled: repo.enabled !== false,
|
|
3418
|
+
source: repo.source || repo.repoUrl || repo.localPath || ''
|
|
3419
|
+
})));
|
|
3420
|
+
}
|
|
3421
|
+
|
|
3422
|
+
async getMarketPlugins(forceRefresh = false, options = {}) {
|
|
3423
|
+
if (!this.getCapabilities().market) return [];
|
|
3424
|
+
const key = this._marketKey(options);
|
|
3425
|
+
const repos = this._isOmp() ? [] : this.getRepos(options).filter(repo => repo.enabled);
|
|
3426
|
+
const reposFingerprint = this._marketReposFingerprint(repos);
|
|
3427
|
+
const now = Date.now();
|
|
3428
|
+
const cached = this._marketCacheByKey.get(key);
|
|
3429
|
+
const memoryPlugins = Array.isArray(cached?.plugins)
|
|
3430
|
+
? cached.plugins
|
|
3431
|
+
: (Array.isArray(this._marketCache) ? this._marketCache : null);
|
|
3432
|
+
if (!forceRefresh && cached && now - cached.fetchedAt < MARKET_CACHE_TTL_MS
|
|
3433
|
+
&& cached.reposFingerprint === reposFingerprint) {
|
|
3434
|
+
return this._clone(cached.plugins);
|
|
3435
|
+
}
|
|
3436
|
+
|
|
3437
|
+
const disk = this.loadMarketCacheEnvelope();
|
|
3438
|
+
if (!forceRefresh && Array.isArray(disk?.plugins)) {
|
|
3439
|
+
const diskPlugins = this.prepareMarketPlugins(disk.plugins);
|
|
3440
|
+
const diskIsFresh = disk.fetchedAt
|
|
3441
|
+
&& now - disk.fetchedAt < MARKET_CACHE_TTL_MS
|
|
3442
|
+
&& (!disk.reposFingerprint || disk.reposFingerprint === reposFingerprint);
|
|
3443
|
+
const diskIsMoreComplete = memoryPlugins && diskPlugins.length > memoryPlugins.length;
|
|
3444
|
+
if (diskIsFresh || diskIsMoreComplete) {
|
|
3445
|
+
this._marketCacheByKey.set(key, {
|
|
3446
|
+
plugins: diskPlugins,
|
|
3447
|
+
fetchedAt: disk.fetchedAt,
|
|
3448
|
+
reposFingerprint: disk.reposFingerprint || reposFingerprint
|
|
3449
|
+
});
|
|
3450
|
+
this._marketCache = diskPlugins;
|
|
3451
|
+
this._marketFetchedAt = disk.fetchedAt;
|
|
3452
|
+
this._marketRepoFingerprint = disk.reposFingerprint || reposFingerprint;
|
|
3453
|
+
return this._clone(diskPlugins);
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
|
|
3457
|
+
if (this._marketRefreshPromise) return this._marketRefreshPromise;
|
|
3458
|
+
const promise = this._getMarketPluginsUncached(forceRefresh, options, { reposFingerprint })
|
|
3459
|
+
.then(plugins => {
|
|
3460
|
+
this._marketCache = Array.isArray(plugins) ? plugins : [];
|
|
3461
|
+
this._marketFetchedAt = this._marketLastUsedStale ? 0 : Date.now();
|
|
3462
|
+
this._marketRepoFingerprint = reposFingerprint;
|
|
3463
|
+
this._marketCacheByKey.set(key, {
|
|
3464
|
+
plugins: this._marketCache,
|
|
3465
|
+
fetchedAt: this._marketFetchedAt,
|
|
3466
|
+
reposFingerprint
|
|
3467
|
+
});
|
|
3468
|
+
if (!this._marketLastUsedStale) {
|
|
3469
|
+
this.saveMarketCacheToFile(this._marketCache, this._marketFetchedAt, reposFingerprint);
|
|
3470
|
+
}
|
|
3471
|
+
return this._clone(this._marketCache);
|
|
3472
|
+
})
|
|
3473
|
+
.finally(() => {
|
|
3474
|
+
if (this._marketRefreshPromise === promise) this._marketRefreshPromise = null;
|
|
3475
|
+
});
|
|
3476
|
+
this._marketRefreshPromise = promise;
|
|
3477
|
+
return promise;
|
|
3478
|
+
}
|
|
3479
|
+
|
|
3222
3480
|
/**
|
|
3223
3481
|
* Get market plugins from configured repositories
|
|
3224
3482
|
* @returns {Promise<Array>} List of available market plugins
|
|
3225
3483
|
*/
|
|
3226
|
-
async
|
|
3484
|
+
async _getMarketPluginsUncached(forceRefresh = false, options = {}, { reposFingerprint = null } = {}) {
|
|
3227
3485
|
if (!this.getCapabilities().market) {
|
|
3228
3486
|
return [];
|
|
3229
3487
|
}
|
|
3488
|
+
this._marketLastUsedStale = false;
|
|
3230
3489
|
if (this._isOmp()) {
|
|
3231
3490
|
if (forceRefresh) {
|
|
3232
3491
|
this.ompNativeAdapter.updateMarketplaces('', options);
|
|
@@ -3238,27 +3497,16 @@ class PluginsService {
|
|
|
3238
3497
|
this.clearMarketCache({ removeFile: false });
|
|
3239
3498
|
}
|
|
3240
3499
|
|
|
3241
|
-
const fileCache = this.
|
|
3242
|
-
|
|
3243
|
-
if (!forceRefresh && Array.isArray(this._marketCache) && this._marketCache.length > 0) {
|
|
3244
|
-
if (Array.isArray(fileCache) && fileCache.length > this._marketCache.length) {
|
|
3245
|
-
this._marketCache = this.prepareMarketPlugins(fileCache);
|
|
3246
|
-
return this._marketCache;
|
|
3247
|
-
}
|
|
3248
|
-
this._marketCache = this.prepareMarketPlugins(this._marketCache);
|
|
3249
|
-
return this._marketCache;
|
|
3250
|
-
}
|
|
3251
|
-
|
|
3252
|
-
if (!forceRefresh && Array.isArray(fileCache) && fileCache.length > 0) {
|
|
3253
|
-
this._marketCache = this.prepareMarketPlugins(fileCache);
|
|
3254
|
-
return this._marketCache;
|
|
3255
|
-
}
|
|
3500
|
+
const fileCache = this.loadMarketCacheEnvelope()?.plugins || null;
|
|
3256
3501
|
|
|
3257
3502
|
const repos = this.getRepos().filter(r => r.enabled);
|
|
3258
3503
|
const marketPlugins = [];
|
|
3259
3504
|
let repoFailureCount = 0;
|
|
3260
3505
|
|
|
3261
|
-
|
|
3506
|
+
let nextRepoIndex = 0;
|
|
3507
|
+
const processRepo = async () => {
|
|
3508
|
+
while (nextRepoIndex < repos.length) {
|
|
3509
|
+
const repo = repos[nextRepoIndex++];
|
|
3262
3510
|
const repoLabel = repo.label || repo.repoUrl || repo.localPath || `${repo.owner || ''}/${repo.name || ''}`;
|
|
3263
3511
|
const pluginsBefore = marketPlugins.length;
|
|
3264
3512
|
try {
|
|
@@ -3294,7 +3542,7 @@ class PluginsService {
|
|
|
3294
3542
|
|
|
3295
3543
|
// OpenCode plugin marketplace format: plugins/*.plugin.json
|
|
3296
3544
|
if (this._isOpenCode()) {
|
|
3297
|
-
const openCodeMarketplacePlugins = await this._fetchOpenCodeMarketplacePlugins(repo, repo.branch || 'main');
|
|
3545
|
+
const openCodeMarketplacePlugins = await this._fetchOpenCodeMarketplacePlugins(repo, repo.branch || 'main', tree);
|
|
3298
3546
|
if (openCodeMarketplacePlugins.length > 0) {
|
|
3299
3547
|
marketPlugins.push(...openCodeMarketplacePlugins);
|
|
3300
3548
|
continue;
|
|
@@ -3344,7 +3592,9 @@ class PluginsService {
|
|
|
3344
3592
|
.filter(dir => dir && !dir.startsWith('.') && dir !== 'node_modules')
|
|
3345
3593
|
));
|
|
3346
3594
|
|
|
3347
|
-
for (
|
|
3595
|
+
for (let batchStart = 0; batchStart < pluginDirs.length; batchStart += 5) {
|
|
3596
|
+
const batch = pluginDirs.slice(batchStart, batchStart + 5);
|
|
3597
|
+
await Promise.all(batch.map(async (dir) => {
|
|
3348
3598
|
try {
|
|
3349
3599
|
const manifest = await readJson(`${dir}/plugin.json`);
|
|
3350
3600
|
if (!manifest) {
|
|
@@ -3377,6 +3627,7 @@ class PluginsService {
|
|
|
3377
3627
|
}
|
|
3378
3628
|
}
|
|
3379
3629
|
}
|
|
3630
|
+
}));
|
|
3380
3631
|
}
|
|
3381
3632
|
} catch (err) {
|
|
3382
3633
|
repoFailureCount++;
|
|
@@ -3386,6 +3637,8 @@ class PluginsService {
|
|
|
3386
3637
|
const added = marketPlugins.length - pluginsBefore;
|
|
3387
3638
|
console.log(`[PluginsService] ${repoLabel}: ${added} plugins loaded`);
|
|
3388
3639
|
}
|
|
3640
|
+
};
|
|
3641
|
+
await Promise.all(Array.from({ length: Math.min(3, repos.length) }, () => processRepo()));
|
|
3389
3642
|
|
|
3390
3643
|
const preparedPlugins = this.prepareMarketPlugins(marketPlugins);
|
|
3391
3644
|
const preparedFileCache = Array.isArray(fileCache) && fileCache.length > 0
|
|
@@ -3397,12 +3650,13 @@ class PluginsService {
|
|
|
3397
3650
|
);
|
|
3398
3651
|
|
|
3399
3652
|
if (shouldUseStaleFileCache) {
|
|
3653
|
+
this._marketLastUsedStale = true;
|
|
3400
3654
|
this._marketCache = preparedFileCache;
|
|
3401
3655
|
return this._marketCache;
|
|
3402
3656
|
}
|
|
3403
3657
|
|
|
3404
3658
|
this._marketCache = preparedPlugins;
|
|
3405
|
-
this.saveMarketCacheToFile(preparedPlugins);
|
|
3659
|
+
this.saveMarketCacheToFile(preparedPlugins, Date.now(), reposFingerprint);
|
|
3406
3660
|
|
|
3407
3661
|
return preparedPlugins;
|
|
3408
3662
|
}
|