@thevinci/web 1.0.0
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/README.md +197 -0
- package/bin/cli-entry.js +55 -0
- package/bin/cli-output.js +145 -0
- package/bin/cli.js +4887 -0
- package/bin/cli.test.js +64 -0
- package/dist/apple-touch-icon-120x120.png +0 -0
- package/dist/apple-touch-icon-152x152.png +0 -0
- package/dist/apple-touch-icon-167x167.png +0 -0
- package/dist/apple-touch-icon-180x180.png +0 -0
- package/dist/apple-touch-icon.png +0 -0
- package/dist/apple-touch-icon.svg +528 -0
- package/dist/assets/JsonTreeView-CSm9OzXG.js +1 -0
- package/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/dist/assets/MarkdownRendererImpl-DensKOLc.js +6 -0
- package/dist/assets/MultiRunWindow-Bo7THayo.js +1 -0
- package/dist/assets/OnboardingScreen-BDqmzTVR.js +2 -0
- package/dist/assets/SettingsWindow-coz__Ykw.js +1 -0
- package/dist/assets/TerminalView-DrZ-i3Dr.js +1 -0
- package/dist/assets/ToolOutputDialog-Eglzslt3.js +16 -0
- package/dist/assets/es-4o9ciP61.js +15 -0
- package/dist/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
- package/dist/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
- package/dist/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
- package/dist/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
- package/dist/assets/ibm-plex-mono-latin-600-normal-BgSNZQsw.woff2 +0 -0
- package/dist/assets/ibm-plex-mono-latin-600-normal-DWFSQ4vo.woff +0 -0
- package/dist/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
- package/dist/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
- package/dist/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
- package/dist/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
- package/dist/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
- package/dist/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
- package/dist/assets/index-DLTDToSP.css +1 -0
- package/dist/assets/index-DgiFEKGN.js +1 -0
- package/dist/assets/ko-B20imCHE.js +15 -0
- package/dist/assets/main-BV3KOtdA.css +1 -0
- package/dist/assets/main-CDKJj0sH.js +226 -0
- package/dist/assets/main-LC-PSNVM.js +2 -0
- package/dist/assets/miniChat-CQUiG_cr.js +2 -0
- package/dist/assets/modelPrefsAutoSave-Dm799vzR.js +6986 -0
- package/dist/assets/pl-DQJ7LSzj.js +15 -0
- package/dist/assets/pt-BR-OmjHUz9y.js +15 -0
- package/dist/assets/renderElectronMiniChatApp-CARbeW0G.js +2 -0
- package/dist/assets/uk-BNFxOlO4.js +15 -0
- package/dist/assets/vendor--DBfsbEis.css +1 -0
- package/dist/assets/vendor-.bun-B9l0ZNi2.js +4094 -0
- package/dist/assets/wasm-CG6Dc4jp.js +1 -0
- package/dist/assets/wasmSttWorker-Dtlxac_K.js +1 -0
- package/dist/assets/wasmSttWorker-oo7Dm_jy.js +1806 -0
- package/dist/assets/worker-CbT6TVo7.js +155 -0
- package/dist/assets/zh-CN-C6T-Ac7F.js +15 -0
- package/dist/favicon-16.png +0 -0
- package/dist/favicon-32.png +0 -0
- package/dist/favicon.png +0 -0
- package/dist/favicon.svg +528 -0
- package/dist/index.html +607 -0
- package/dist/logo-dark-192x192.png +0 -0
- package/dist/logo-dark-512x512.png +0 -0
- package/dist/logo-dark-512x512.svg +528 -0
- package/dist/logo-light-192x192.png +0 -0
- package/dist/logo-light-512x512.png +0 -0
- package/dist/logo-light-512x512.svg +528 -0
- package/dist/mini-chat.html +16 -0
- package/dist/pwa-192.png +0 -0
- package/dist/pwa-512.png +0 -0
- package/dist/pwa-maskable-192.png +0 -0
- package/dist/pwa-maskable-512.png +0 -0
- package/dist/site.webmanifest +21 -0
- package/dist/sw.js +1 -0
- package/package.json +118 -0
- package/public/apple-touch-icon-120x120.png +0 -0
- package/public/apple-touch-icon-152x152.png +0 -0
- package/public/apple-touch-icon-167x167.png +0 -0
- package/public/apple-touch-icon-180x180.png +0 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/apple-touch-icon.svg +528 -0
- package/public/favicon-16.png +0 -0
- package/public/favicon-32.png +0 -0
- package/public/favicon.png +0 -0
- package/public/favicon.svg +528 -0
- package/public/logo-dark-192x192.png +0 -0
- package/public/logo-dark-512x512.png +0 -0
- package/public/logo-dark-512x512.svg +528 -0
- package/public/logo-light-192x192.png +0 -0
- package/public/logo-light-512x512.png +0 -0
- package/public/logo-light-512x512.svg +528 -0
- package/public/pwa-192.png +0 -0
- package/public/pwa-512.png +0 -0
- package/public/pwa-maskable-192.png +0 -0
- package/public/pwa-maskable-512.png +0 -0
- package/public/site.webmanifest +21 -0
- package/server/TERMINAL_WS_PROTOCOL.md +48 -0
- package/server/index.d.ts +39 -0
- package/server/index.js +1311 -0
- package/server/lib/cloudflare-tunnel.js +650 -0
- package/server/lib/event-stream/DOCUMENTATION.md +61 -0
- package/server/lib/event-stream/directory-ws-bridge.js +185 -0
- package/server/lib/event-stream/global-hub.js +158 -0
- package/server/lib/event-stream/global-hub.test.js +140 -0
- package/server/lib/event-stream/global-ws-bridge.js +206 -0
- package/server/lib/event-stream/index.js +25 -0
- package/server/lib/event-stream/protocol.js +131 -0
- package/server/lib/event-stream/protocol.test.js +182 -0
- package/server/lib/event-stream/runtime.js +180 -0
- package/server/lib/event-stream/runtime.test.js +512 -0
- package/server/lib/event-stream/upstream-reader.js +226 -0
- package/server/lib/event-stream/upstream-reader.test.js +276 -0
- package/server/lib/fs/DOCUMENTATION.md +36 -0
- package/server/lib/fs/routes.js +1040 -0
- package/server/lib/fs/search.js +238 -0
- package/server/lib/git/DOCUMENTATION.md +152 -0
- package/server/lib/git/credentials.js +74 -0
- package/server/lib/git/identity-storage.js +112 -0
- package/server/lib/git/index.js +6 -0
- package/server/lib/git/routes.js +972 -0
- package/server/lib/git/service.js +3432 -0
- package/server/lib/git/service.test.js +39 -0
- package/server/lib/github/DOCUMENTATION.md +171 -0
- package/server/lib/github/auth.js +307 -0
- package/server/lib/github/device-flow.js +50 -0
- package/server/lib/github/index.js +24 -0
- package/server/lib/github/octokit.js +10 -0
- package/server/lib/github/pr-status.js +519 -0
- package/server/lib/github/repo/fork-detection.js +102 -0
- package/server/lib/github/repo/index.js +55 -0
- package/server/lib/github/routes.js +1560 -0
- package/server/lib/magic-prompts/routes.js +63 -0
- package/server/lib/magic-prompts/runtime.js +119 -0
- package/server/lib/notifications/DOCUMENTATION.md +122 -0
- package/server/lib/notifications/emitter-runtime.js +102 -0
- package/server/lib/notifications/index.js +4 -0
- package/server/lib/notifications/message.js +52 -0
- package/server/lib/notifications/message.test.js +34 -0
- package/server/lib/notifications/push-runtime.js +304 -0
- package/server/lib/notifications/routes.js +315 -0
- package/server/lib/notifications/runtime.js +566 -0
- package/server/lib/notifications/template-runtime.js +349 -0
- package/server/lib/notifications/template-runtime.test.js +26 -0
- package/server/lib/opencode/DOCUMENTATION.md +362 -0
- package/server/lib/opencode/agents.js +634 -0
- package/server/lib/opencode/auth-state-runtime.js +88 -0
- package/server/lib/opencode/auth.js +83 -0
- package/server/lib/opencode/bootstrap-runtime.js +131 -0
- package/server/lib/opencode/cli-entry-runtime.js +43 -0
- package/server/lib/opencode/cli-options.js +128 -0
- package/server/lib/opencode/commands.js +339 -0
- package/server/lib/opencode/config-entity-routes.js +370 -0
- package/server/lib/opencode/core-routes.js +500 -0
- package/server/lib/opencode/core-routes.test.js +26 -0
- package/server/lib/opencode/env-config.js +74 -0
- package/server/lib/opencode/env-keys.js +68 -0
- package/server/lib/opencode/env-runtime.js +1162 -0
- package/server/lib/opencode/env-runtime.test.js +116 -0
- package/server/lib/opencode/feature-routes-runtime.js +244 -0
- package/server/lib/opencode/hmr-state-runtime.js +85 -0
- package/server/lib/opencode/index.js +66 -0
- package/server/lib/opencode/lifecycle.js +1019 -0
- package/server/lib/opencode/lifecycle.test.js +240 -0
- package/server/lib/opencode/mcp.js +278 -0
- package/server/lib/opencode/network-runtime.js +104 -0
- package/server/lib/opencode/network-runtime.test.js +37 -0
- package/server/lib/opencode/opencode-resolution-runtime.js +71 -0
- package/server/lib/opencode/path-utils.js +100 -0
- package/server/lib/opencode/path-utils.test.js +71 -0
- package/server/lib/opencode/project-directory-runtime.js +124 -0
- package/server/lib/opencode/project-icon-routes.js +399 -0
- package/server/lib/opencode/project-icon-routes.test.js +107 -0
- package/server/lib/opencode/providers.js +96 -0
- package/server/lib/opencode/proxy.js +445 -0
- package/server/lib/opencode/pwa-manifest-routes.js +257 -0
- package/server/lib/opencode/pwa-manifest-routes.test.js +133 -0
- package/server/lib/opencode/routes.js +541 -0
- package/server/lib/opencode/server-startup-runtime.js +156 -0
- package/server/lib/opencode/server-utils-runtime.js +168 -0
- package/server/lib/opencode/server-utils-runtime.test.js +135 -0
- package/server/lib/opencode/session-runtime.js +356 -0
- package/server/lib/opencode/session-runtime.test.js +151 -0
- package/server/lib/opencode/settings-helpers.js +770 -0
- package/server/lib/opencode/settings-helpers.test.js +109 -0
- package/server/lib/opencode/settings-normalization-runtime.js +428 -0
- package/server/lib/opencode/settings-runtime.js +826 -0
- package/server/lib/opencode/settings-runtime.test.js +85 -0
- package/server/lib/opencode/shared.js +615 -0
- package/server/lib/opencode/shutdown-runtime.js +139 -0
- package/server/lib/opencode/shutdown-runtime.test.js +58 -0
- package/server/lib/opencode/skill-routes.js +701 -0
- package/server/lib/opencode/skills.js +548 -0
- package/server/lib/opencode/startup-pipeline-runtime.js +130 -0
- package/server/lib/opencode/static-routes-runtime.js +65 -0
- package/server/lib/opencode/theme-runtime.js +167 -0
- package/server/lib/opencode/tunnel-auth.js +591 -0
- package/server/lib/opencode/tunnel-wiring-runtime.js +94 -0
- package/server/lib/opencode/vinci-routes.js +76 -0
- package/server/lib/opencode/watcher.js +115 -0
- package/server/lib/opencode/watcher.test.js +239 -0
- package/server/lib/preview/proxy-runtime.js +1333 -0
- package/server/lib/preview/proxy-runtime.test.js +144 -0
- package/server/lib/projects/project-config.js +567 -0
- package/server/lib/projects/project-config.test.js +175 -0
- package/server/lib/projects/project-id.js +13 -0
- package/server/lib/quota/DOCUMENTATION.md +58 -0
- package/server/lib/quota/index.js +25 -0
- package/server/lib/quota/providers/claude.js +107 -0
- package/server/lib/quota/providers/codex.js +113 -0
- package/server/lib/quota/providers/copilot.js +165 -0
- package/server/lib/quota/providers/google/api.js +92 -0
- package/server/lib/quota/providers/google/auth.js +108 -0
- package/server/lib/quota/providers/google/index.js +124 -0
- package/server/lib/quota/providers/google/transforms.js +109 -0
- package/server/lib/quota/providers/index.js +168 -0
- package/server/lib/quota/providers/interface.js +55 -0
- package/server/lib/quota/providers/kimi.js +108 -0
- package/server/lib/quota/providers/minimax-cn-coding-plan.js +140 -0
- package/server/lib/quota/providers/minimax-coding-plan.js +139 -0
- package/server/lib/quota/providers/nanogpt.js +124 -0
- package/server/lib/quota/providers/ollama-cloud.js +112 -0
- package/server/lib/quota/providers/openai.js +91 -0
- package/server/lib/quota/providers/openrouter.js +92 -0
- package/server/lib/quota/providers/zai.js +91 -0
- package/server/lib/quota/providers/zhipuai-coding-plan.js +133 -0
- package/server/lib/quota/providers/zhipuai.js +114 -0
- package/server/lib/quota/routes.js +27 -0
- package/server/lib/quota/utils/auth.js +50 -0
- package/server/lib/quota/utils/formatters.js +85 -0
- package/server/lib/quota/utils/formatters.test.js +54 -0
- package/server/lib/quota/utils/index.js +10 -0
- package/server/lib/quota/utils/transformers.js +55 -0
- package/server/lib/scheduled-tasks/DOCUMENTATION.md +44 -0
- package/server/lib/scheduled-tasks/routes.js +235 -0
- package/server/lib/scheduled-tasks/runtime.js +773 -0
- package/server/lib/scheduled-tasks/runtime.test.js +100 -0
- package/server/lib/security/request-security.js +115 -0
- package/server/lib/session-folders/routes.js +63 -0
- package/server/lib/session-folders/routes.test.js +102 -0
- package/server/lib/skills-catalog/DOCUMENTATION.md +178 -0
- package/server/lib/skills-catalog/cache.js +29 -0
- package/server/lib/skills-catalog/clawdhub/api.js +158 -0
- package/server/lib/skills-catalog/clawdhub/index.js +30 -0
- package/server/lib/skills-catalog/clawdhub/install.js +238 -0
- package/server/lib/skills-catalog/clawdhub/scan.js +113 -0
- package/server/lib/skills-catalog/curated-sources.js +21 -0
- package/server/lib/skills-catalog/git.js +77 -0
- package/server/lib/skills-catalog/index.js +42 -0
- package/server/lib/skills-catalog/install.js +294 -0
- package/server/lib/skills-catalog/scan.js +221 -0
- package/server/lib/skills-catalog/source.js +87 -0
- package/server/lib/terminal/DOCUMENTATION.md +76 -0
- package/server/lib/terminal/index.js +31 -0
- package/server/lib/terminal/output-replay-buffer.js +78 -0
- package/server/lib/terminal/output-replay-buffer.test.js +75 -0
- package/server/lib/terminal/runtime.js +850 -0
- package/server/lib/terminal/runtime.test.js +96 -0
- package/server/lib/terminal/terminal-ws-protocol.js +68 -0
- package/server/lib/terminal/terminal-ws-protocol.test.js +145 -0
- package/server/lib/text/DOCUMENTATION.md +35 -0
- package/server/lib/text/summarization.js +138 -0
- package/server/lib/text/summarization.test.js +34 -0
- package/server/lib/tts/DOCUMENTATION.md +146 -0
- package/server/lib/tts/base-url.js +62 -0
- package/server/lib/tts/capability-runtime.js +31 -0
- package/server/lib/tts/index.js +19 -0
- package/server/lib/tts/routes.js +261 -0
- package/server/lib/tts/routes.test.js +53 -0
- package/server/lib/tts/service.js +178 -0
- package/server/lib/tts/stt.js +75 -0
- package/server/lib/tunnels/DOCUMENTATION.md +18 -0
- package/server/lib/tunnels/index.js +166 -0
- package/server/lib/tunnels/managed-config.js +201 -0
- package/server/lib/tunnels/providers/cloudflare.js +260 -0
- package/server/lib/tunnels/registry.js +51 -0
- package/server/lib/tunnels/routes.js +605 -0
- package/server/lib/tunnels/types.js +219 -0
- package/server/lib/ui-auth/DOCUMENTATION.md +38 -0
- package/server/lib/ui-auth/ui-auth.js +673 -0
- package/server/lib/ui-auth/ui-passkeys.js +545 -0
- package/server/opencode-proxy.test.js +151 -0
- package/server/proxy-headers.js +61 -0
- package/server/proxy-headers.test.js +58 -0
- package/server/sse-routes.test.js +152 -0
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import {
|
|
4
|
+
CONFIG_FILE,
|
|
5
|
+
OPENCODE_CONFIG_DIR,
|
|
6
|
+
COMMAND_DIR,
|
|
7
|
+
COMMAND_SCOPE,
|
|
8
|
+
ensureDirs,
|
|
9
|
+
parseMdFile,
|
|
10
|
+
writeMdFile,
|
|
11
|
+
readConfigLayers,
|
|
12
|
+
writeConfig,
|
|
13
|
+
getJsonEntrySource,
|
|
14
|
+
getJsonWriteTarget,
|
|
15
|
+
isPromptFileReference,
|
|
16
|
+
resolvePromptFilePath,
|
|
17
|
+
writePromptFile,
|
|
18
|
+
} from './shared.js';
|
|
19
|
+
|
|
20
|
+
// ============== COMMAND SCOPE HELPERS ==============
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Ensure project-level command directory exists
|
|
24
|
+
*/
|
|
25
|
+
function ensureProjectCommandDir(workingDirectory) {
|
|
26
|
+
const projectCommandDir = path.join(workingDirectory, '.opencode', 'commands');
|
|
27
|
+
if (!fs.existsSync(projectCommandDir)) {
|
|
28
|
+
fs.mkdirSync(projectCommandDir, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
const legacyProjectCommandDir = path.join(workingDirectory, '.opencode', 'command');
|
|
31
|
+
if (!fs.existsSync(legacyProjectCommandDir)) {
|
|
32
|
+
fs.mkdirSync(legacyProjectCommandDir, { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
return projectCommandDir;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get project-level command path
|
|
39
|
+
*/
|
|
40
|
+
function getProjectCommandPath(workingDirectory, commandName) {
|
|
41
|
+
const pluralPath = path.join(workingDirectory, '.opencode', 'commands', `${commandName}.md`);
|
|
42
|
+
const legacyPath = path.join(workingDirectory, '.opencode', 'command', `${commandName}.md`);
|
|
43
|
+
if (fs.existsSync(legacyPath) && !fs.existsSync(pluralPath)) return legacyPath;
|
|
44
|
+
return pluralPath;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get user-level command path
|
|
49
|
+
*/
|
|
50
|
+
function getUserCommandPath(commandName) {
|
|
51
|
+
const pluralPath = path.join(COMMAND_DIR, `${commandName}.md`);
|
|
52
|
+
const legacyPath = path.join(OPENCODE_CONFIG_DIR, 'command', `${commandName}.md`);
|
|
53
|
+
if (fs.existsSync(legacyPath) && !fs.existsSync(pluralPath)) return legacyPath;
|
|
54
|
+
return pluralPath;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Determine command scope based on where the .md file exists
|
|
59
|
+
* Priority: project level > user level > null (built-in only)
|
|
60
|
+
*/
|
|
61
|
+
function getCommandScope(commandName, workingDirectory) {
|
|
62
|
+
if (workingDirectory) {
|
|
63
|
+
const projectPath = getProjectCommandPath(workingDirectory, commandName);
|
|
64
|
+
if (fs.existsSync(projectPath)) {
|
|
65
|
+
return { scope: COMMAND_SCOPE.PROJECT, path: projectPath };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const userPath = getUserCommandPath(commandName);
|
|
70
|
+
if (fs.existsSync(userPath)) {
|
|
71
|
+
return { scope: COMMAND_SCOPE.USER, path: userPath };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return { scope: null, path: null };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get the path where a command should be written based on scope
|
|
79
|
+
*/
|
|
80
|
+
function getCommandWritePath(commandName, workingDirectory, requestedScope) {
|
|
81
|
+
// For updates: check existing location first (project takes precedence)
|
|
82
|
+
const existing = getCommandScope(commandName, workingDirectory);
|
|
83
|
+
if (existing.path) {
|
|
84
|
+
return existing;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// For new commands or built-in overrides: use requested scope or default to user
|
|
88
|
+
const scope = requestedScope || COMMAND_SCOPE.USER;
|
|
89
|
+
if (scope === COMMAND_SCOPE.PROJECT && workingDirectory) {
|
|
90
|
+
return {
|
|
91
|
+
scope: COMMAND_SCOPE.PROJECT,
|
|
92
|
+
path: getProjectCommandPath(workingDirectory, commandName)
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
scope: COMMAND_SCOPE.USER,
|
|
98
|
+
path: getUserCommandPath(commandName)
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function getCommandSources(commandName, workingDirectory) {
|
|
103
|
+
const projectPath = workingDirectory ? getProjectCommandPath(workingDirectory, commandName) : null;
|
|
104
|
+
const projectExists = projectPath && fs.existsSync(projectPath);
|
|
105
|
+
|
|
106
|
+
const userPath = getUserCommandPath(commandName);
|
|
107
|
+
const userExists = fs.existsSync(userPath);
|
|
108
|
+
|
|
109
|
+
const mdPath = projectExists ? projectPath : (userExists ? userPath : null);
|
|
110
|
+
const mdExists = !!mdPath;
|
|
111
|
+
const mdScope = projectExists ? COMMAND_SCOPE.PROJECT : (userExists ? COMMAND_SCOPE.USER : null);
|
|
112
|
+
|
|
113
|
+
const layers = readConfigLayers(workingDirectory);
|
|
114
|
+
const jsonSource = getJsonEntrySource(layers, 'command', commandName);
|
|
115
|
+
const jsonSection = jsonSource.section;
|
|
116
|
+
const jsonPath = jsonSource.path || layers.paths.customPath || layers.paths.projectPath || layers.paths.userPath;
|
|
117
|
+
const jsonScope = jsonSource.path === layers.paths.projectPath ? COMMAND_SCOPE.PROJECT : COMMAND_SCOPE.USER;
|
|
118
|
+
|
|
119
|
+
const sources = {
|
|
120
|
+
md: {
|
|
121
|
+
exists: mdExists,
|
|
122
|
+
path: mdPath,
|
|
123
|
+
scope: mdScope,
|
|
124
|
+
fields: []
|
|
125
|
+
},
|
|
126
|
+
json: {
|
|
127
|
+
exists: jsonSource.exists,
|
|
128
|
+
path: jsonPath,
|
|
129
|
+
scope: jsonSource.exists ? jsonScope : null,
|
|
130
|
+
fields: []
|
|
131
|
+
},
|
|
132
|
+
projectMd: {
|
|
133
|
+
exists: projectExists,
|
|
134
|
+
path: projectPath
|
|
135
|
+
},
|
|
136
|
+
userMd: {
|
|
137
|
+
exists: userExists,
|
|
138
|
+
path: userPath
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
if (mdExists) {
|
|
143
|
+
const { frontmatter, body } = parseMdFile(mdPath);
|
|
144
|
+
sources.md.fields = Object.keys(frontmatter);
|
|
145
|
+
if (body) {
|
|
146
|
+
sources.md.fields.push('template');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (jsonSection) {
|
|
151
|
+
sources.json.fields = Object.keys(jsonSection);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return sources;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function createCommand(commandName, config, workingDirectory, scope) {
|
|
158
|
+
ensureDirs();
|
|
159
|
+
|
|
160
|
+
const projectPath = workingDirectory ? getProjectCommandPath(workingDirectory, commandName) : null;
|
|
161
|
+
const userPath = getUserCommandPath(commandName);
|
|
162
|
+
|
|
163
|
+
if (projectPath && fs.existsSync(projectPath)) {
|
|
164
|
+
throw new Error(`Command ${commandName} already exists as project-level .md file`);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (fs.existsSync(userPath)) {
|
|
168
|
+
throw new Error(`Command ${commandName} already exists as user-level .md file`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const layers = readConfigLayers(workingDirectory);
|
|
172
|
+
const jsonSource = getJsonEntrySource(layers, 'command', commandName);
|
|
173
|
+
if (jsonSource.exists) {
|
|
174
|
+
throw new Error(`Command ${commandName} already exists in opencode.json`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
let targetPath;
|
|
178
|
+
let targetScope;
|
|
179
|
+
|
|
180
|
+
if (scope === COMMAND_SCOPE.PROJECT && workingDirectory) {
|
|
181
|
+
ensureProjectCommandDir(workingDirectory);
|
|
182
|
+
targetPath = projectPath;
|
|
183
|
+
targetScope = COMMAND_SCOPE.PROJECT;
|
|
184
|
+
} else {
|
|
185
|
+
targetPath = userPath;
|
|
186
|
+
targetScope = COMMAND_SCOPE.USER;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const { template, scope: _scopeFromConfig, ...frontmatter } = config;
|
|
190
|
+
|
|
191
|
+
writeMdFile(targetPath, frontmatter, template || '');
|
|
192
|
+
console.log(`Created new command: ${commandName} (scope: ${targetScope}, path: ${targetPath})`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function updateCommand(commandName, updates, workingDirectory) {
|
|
196
|
+
ensureDirs();
|
|
197
|
+
|
|
198
|
+
const { scope, path: mdPath } = getCommandWritePath(commandName, workingDirectory);
|
|
199
|
+
const mdExists = mdPath && fs.existsSync(mdPath);
|
|
200
|
+
|
|
201
|
+
const layers = readConfigLayers(workingDirectory);
|
|
202
|
+
const jsonSource = getJsonEntrySource(layers, 'command', commandName);
|
|
203
|
+
const jsonSection = jsonSource.section;
|
|
204
|
+
const hasJsonFields = jsonSource.exists && jsonSection && Object.keys(jsonSection).length > 0;
|
|
205
|
+
const jsonTarget = jsonSource.exists
|
|
206
|
+
? { config: jsonSource.config, path: jsonSource.path }
|
|
207
|
+
: getJsonWriteTarget(layers, workingDirectory ? COMMAND_SCOPE.PROJECT : COMMAND_SCOPE.USER);
|
|
208
|
+
let config = jsonTarget.config || {};
|
|
209
|
+
|
|
210
|
+
const isBuiltinOverride = !mdExists && !hasJsonFields;
|
|
211
|
+
|
|
212
|
+
let targetPath = mdPath;
|
|
213
|
+
let targetScope = scope;
|
|
214
|
+
|
|
215
|
+
if (!mdExists && isBuiltinOverride) {
|
|
216
|
+
targetPath = getUserCommandPath(commandName);
|
|
217
|
+
targetScope = COMMAND_SCOPE.USER;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const mdData = mdExists ? parseMdFile(mdPath) : (isBuiltinOverride ? { frontmatter: {}, body: '' } : null);
|
|
221
|
+
|
|
222
|
+
let mdModified = false;
|
|
223
|
+
let jsonModified = false;
|
|
224
|
+
const creatingNewMd = isBuiltinOverride;
|
|
225
|
+
|
|
226
|
+
for (const [field, value] of Object.entries(updates)) {
|
|
227
|
+
if (field === 'template') {
|
|
228
|
+
const normalizedValue = typeof value === 'string' ? value : (value == null ? '' : String(value));
|
|
229
|
+
|
|
230
|
+
if (mdExists || creatingNewMd) {
|
|
231
|
+
if (mdData) {
|
|
232
|
+
mdData.body = normalizedValue;
|
|
233
|
+
mdModified = true;
|
|
234
|
+
}
|
|
235
|
+
continue;
|
|
236
|
+
} else if (isPromptFileReference(jsonSection?.template)) {
|
|
237
|
+
const templateFilePath = resolvePromptFilePath(jsonSection.template);
|
|
238
|
+
if (!templateFilePath) {
|
|
239
|
+
throw new Error(`Invalid template file reference for command ${commandName}`);
|
|
240
|
+
}
|
|
241
|
+
writePromptFile(templateFilePath, normalizedValue);
|
|
242
|
+
continue;
|
|
243
|
+
} else if (isPromptFileReference(normalizedValue)) {
|
|
244
|
+
if (!config.command) config.command = {};
|
|
245
|
+
if (!config.command[commandName]) config.command[commandName] = {};
|
|
246
|
+
config.command[commandName].template = normalizedValue;
|
|
247
|
+
jsonModified = true;
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (!config.command) config.command = {};
|
|
252
|
+
if (!config.command[commandName]) config.command[commandName] = {};
|
|
253
|
+
config.command[commandName].template = normalizedValue;
|
|
254
|
+
jsonModified = true;
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const inMd = mdData?.frontmatter?.[field] !== undefined;
|
|
259
|
+
const inJson = jsonSection?.[field] !== undefined;
|
|
260
|
+
|
|
261
|
+
if (inJson) {
|
|
262
|
+
if (!config.command) config.command = {};
|
|
263
|
+
if (!config.command[commandName]) config.command[commandName] = {};
|
|
264
|
+
config.command[commandName][field] = value;
|
|
265
|
+
jsonModified = true;
|
|
266
|
+
} else if (inMd || creatingNewMd) {
|
|
267
|
+
if (mdData) {
|
|
268
|
+
mdData.frontmatter[field] = value;
|
|
269
|
+
mdModified = true;
|
|
270
|
+
}
|
|
271
|
+
} else {
|
|
272
|
+
if ((mdExists || creatingNewMd) && mdData) {
|
|
273
|
+
mdData.frontmatter[field] = value;
|
|
274
|
+
mdModified = true;
|
|
275
|
+
} else {
|
|
276
|
+
if (!config.command) config.command = {};
|
|
277
|
+
if (!config.command[commandName]) config.command[commandName] = {};
|
|
278
|
+
config.command[commandName][field] = value;
|
|
279
|
+
jsonModified = true;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (mdModified && mdData) {
|
|
285
|
+
writeMdFile(targetPath, mdData.frontmatter, mdData.body);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (jsonModified) {
|
|
289
|
+
writeConfig(config, jsonTarget.path || CONFIG_FILE);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
console.log(`Updated command: ${commandName} (scope: ${targetScope}, md: ${mdModified}, json: ${jsonModified})`);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function deleteCommand(commandName, workingDirectory) {
|
|
296
|
+
let deleted = false;
|
|
297
|
+
|
|
298
|
+
if (workingDirectory) {
|
|
299
|
+
const projectPath = getProjectCommandPath(workingDirectory, commandName);
|
|
300
|
+
if (fs.existsSync(projectPath)) {
|
|
301
|
+
fs.unlinkSync(projectPath);
|
|
302
|
+
console.log(`Deleted project-level command .md file: ${projectPath}`);
|
|
303
|
+
deleted = true;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const userPath = getUserCommandPath(commandName);
|
|
308
|
+
if (fs.existsSync(userPath)) {
|
|
309
|
+
fs.unlinkSync(userPath);
|
|
310
|
+
console.log(`Deleted user-level command .md file: ${userPath}`);
|
|
311
|
+
deleted = true;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const layers = readConfigLayers(workingDirectory);
|
|
315
|
+
const jsonSource = getJsonEntrySource(layers, 'command', commandName);
|
|
316
|
+
if (jsonSource.exists && jsonSource.config && jsonSource.path) {
|
|
317
|
+
if (!jsonSource.config.command) jsonSource.config.command = {};
|
|
318
|
+
delete jsonSource.config.command[commandName];
|
|
319
|
+
writeConfig(jsonSource.config, jsonSource.path);
|
|
320
|
+
console.log(`Removed command from opencode.json: ${commandName}`);
|
|
321
|
+
deleted = true;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (!deleted) {
|
|
325
|
+
throw new Error(`Command "${commandName}" not found`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export {
|
|
330
|
+
ensureProjectCommandDir,
|
|
331
|
+
getProjectCommandPath,
|
|
332
|
+
getUserCommandPath,
|
|
333
|
+
getCommandScope,
|
|
334
|
+
getCommandWritePath,
|
|
335
|
+
getCommandSources,
|
|
336
|
+
createCommand,
|
|
337
|
+
updateCommand,
|
|
338
|
+
deleteCommand,
|
|
339
|
+
};
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
export const registerConfigEntityRoutes = (app, dependencies) => {
|
|
2
|
+
const {
|
|
3
|
+
resolveProjectDirectory,
|
|
4
|
+
resolveOptionalProjectDirectory,
|
|
5
|
+
refreshOpenCodeAfterConfigChange,
|
|
6
|
+
clientReloadDelayMs,
|
|
7
|
+
getAgentSources,
|
|
8
|
+
getAgentConfig,
|
|
9
|
+
createAgent,
|
|
10
|
+
updateAgent,
|
|
11
|
+
deleteAgent,
|
|
12
|
+
getCommandSources,
|
|
13
|
+
createCommand,
|
|
14
|
+
updateCommand,
|
|
15
|
+
deleteCommand,
|
|
16
|
+
listMcpConfigs,
|
|
17
|
+
getMcpConfig,
|
|
18
|
+
createMcpConfig,
|
|
19
|
+
updateMcpConfig,
|
|
20
|
+
deleteMcpConfig,
|
|
21
|
+
} = dependencies;
|
|
22
|
+
|
|
23
|
+
const completeMcpMutation = async (res, action, name, applyChange) => {
|
|
24
|
+
applyChange();
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
await refreshOpenCodeAfterConfigChange(`mcp ${action}`);
|
|
28
|
+
return res.json({
|
|
29
|
+
success: true,
|
|
30
|
+
requiresReload: true,
|
|
31
|
+
message: `MCP server "${name}" ${action}d. Reloading interface…`,
|
|
32
|
+
reloadDelayMs: clientReloadDelayMs,
|
|
33
|
+
});
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.error(`[API:MCP ${action}] Reload failed after config write:`, error);
|
|
36
|
+
return res.json({
|
|
37
|
+
success: true,
|
|
38
|
+
requiresReload: false,
|
|
39
|
+
reloadFailed: true,
|
|
40
|
+
message: `MCP server "${name}" ${action}d, but OpenCode reload failed.`,
|
|
41
|
+
warning: error.message || 'OpenCode reload failed after the MCP configuration changed',
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
app.get('/api/config/agents/:name', async (req, res) => {
|
|
47
|
+
try {
|
|
48
|
+
const agentName = req.params.name;
|
|
49
|
+
const { directory, error } = await resolveProjectDirectory(req);
|
|
50
|
+
if (!directory) {
|
|
51
|
+
return res.status(400).json({ error });
|
|
52
|
+
}
|
|
53
|
+
const sources = getAgentSources(agentName, directory);
|
|
54
|
+
|
|
55
|
+
const scope = sources.md.exists
|
|
56
|
+
? sources.md.scope
|
|
57
|
+
: (sources.json.exists ? sources.json.scope : null);
|
|
58
|
+
|
|
59
|
+
res.json({
|
|
60
|
+
name: agentName,
|
|
61
|
+
sources: sources,
|
|
62
|
+
scope,
|
|
63
|
+
isBuiltIn: !sources.md.exists && !sources.json.exists
|
|
64
|
+
});
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error('Failed to get agent sources:', error);
|
|
67
|
+
res.status(500).json({ error: 'Failed to get agent configuration metadata' });
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
app.get('/api/config/agents/:name/config', async (req, res) => {
|
|
72
|
+
try {
|
|
73
|
+
const agentName = req.params.name;
|
|
74
|
+
const { directory, error } = await resolveProjectDirectory(req);
|
|
75
|
+
if (!directory) {
|
|
76
|
+
return res.status(400).json({ error });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const configInfo = getAgentConfig(agentName, directory);
|
|
80
|
+
res.json(configInfo);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error('Failed to get agent config:', error);
|
|
83
|
+
res.status(500).json({ error: 'Failed to get agent configuration' });
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
app.post('/api/config/agents/:name', async (req, res) => {
|
|
88
|
+
try {
|
|
89
|
+
const agentName = req.params.name;
|
|
90
|
+
const { scope, ...config } = req.body;
|
|
91
|
+
const { directory, error } = await resolveProjectDirectory(req);
|
|
92
|
+
if (!directory) {
|
|
93
|
+
return res.status(400).json({ error });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log('[Server] Creating agent:', agentName);
|
|
97
|
+
console.log('[Server] Config received:', JSON.stringify(config, null, 2));
|
|
98
|
+
console.log('[Server] Scope:', scope, 'Working directory:', directory);
|
|
99
|
+
|
|
100
|
+
createAgent(agentName, config, directory, scope);
|
|
101
|
+
await refreshOpenCodeAfterConfigChange('agent creation', {
|
|
102
|
+
agentName
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
res.json({
|
|
106
|
+
success: true,
|
|
107
|
+
requiresReload: true,
|
|
108
|
+
message: `Agent ${agentName} created successfully. Reloading interface…`,
|
|
109
|
+
reloadDelayMs: clientReloadDelayMs,
|
|
110
|
+
});
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.error('Failed to create agent:', error);
|
|
113
|
+
res.status(500).json({ error: error.message || 'Failed to create agent' });
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
app.patch('/api/config/agents/:name', async (req, res) => {
|
|
118
|
+
try {
|
|
119
|
+
const agentName = req.params.name;
|
|
120
|
+
const updates = req.body;
|
|
121
|
+
const { directory, error } = await resolveProjectDirectory(req);
|
|
122
|
+
if (!directory) {
|
|
123
|
+
return res.status(400).json({ error });
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
console.log(`[Server] Updating agent: ${agentName}`);
|
|
127
|
+
console.log('[Server] Updates:', JSON.stringify(updates, null, 2));
|
|
128
|
+
console.log('[Server] Working directory:', directory);
|
|
129
|
+
|
|
130
|
+
updateAgent(agentName, updates, directory);
|
|
131
|
+
await refreshOpenCodeAfterConfigChange('agent update');
|
|
132
|
+
|
|
133
|
+
console.log(`[Server] Agent ${agentName} updated successfully`);
|
|
134
|
+
|
|
135
|
+
res.json({
|
|
136
|
+
success: true,
|
|
137
|
+
requiresReload: true,
|
|
138
|
+
message: `Agent ${agentName} updated successfully. Reloading interface…`,
|
|
139
|
+
reloadDelayMs: clientReloadDelayMs,
|
|
140
|
+
});
|
|
141
|
+
} catch (error) {
|
|
142
|
+
console.error('[Server] Failed to update agent:', error);
|
|
143
|
+
console.error('[Server] Error stack:', error.stack);
|
|
144
|
+
res.status(500).json({ error: error.message || 'Failed to update agent' });
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
app.delete('/api/config/agents/:name', async (req, res) => {
|
|
149
|
+
try {
|
|
150
|
+
const agentName = req.params.name;
|
|
151
|
+
const { directory, error } = await resolveProjectDirectory(req);
|
|
152
|
+
if (!directory) {
|
|
153
|
+
return res.status(400).json({ error });
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
deleteAgent(agentName, directory);
|
|
157
|
+
await refreshOpenCodeAfterConfigChange('agent deletion');
|
|
158
|
+
|
|
159
|
+
res.json({
|
|
160
|
+
success: true,
|
|
161
|
+
requiresReload: true,
|
|
162
|
+
message: `Agent ${agentName} deleted successfully. Reloading interface…`,
|
|
163
|
+
reloadDelayMs: clientReloadDelayMs,
|
|
164
|
+
});
|
|
165
|
+
} catch (error) {
|
|
166
|
+
console.error('Failed to delete agent:', error);
|
|
167
|
+
res.status(500).json({ error: error.message || 'Failed to delete agent' });
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
app.get('/api/config/mcp', async (req, res) => {
|
|
172
|
+
try {
|
|
173
|
+
const { directory, error } = await resolveOptionalProjectDirectory(req);
|
|
174
|
+
if (error) {
|
|
175
|
+
return res.status(400).json({ error });
|
|
176
|
+
}
|
|
177
|
+
const configs = listMcpConfigs(directory);
|
|
178
|
+
res.json(configs);
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.error('[API:GET /api/config/mcp] Failed:', error);
|
|
181
|
+
res.status(500).json({ error: error.message || 'Failed to list MCP configs' });
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
app.get('/api/config/mcp/:name', async (req, res) => {
|
|
186
|
+
try {
|
|
187
|
+
const name = req.params.name;
|
|
188
|
+
const { directory, error } = await resolveOptionalProjectDirectory(req);
|
|
189
|
+
if (error) {
|
|
190
|
+
return res.status(400).json({ error });
|
|
191
|
+
}
|
|
192
|
+
const config = getMcpConfig(name, directory);
|
|
193
|
+
if (!config) {
|
|
194
|
+
return res.status(404).json({ error: `MCP server "${name}" not found` });
|
|
195
|
+
}
|
|
196
|
+
res.json(config);
|
|
197
|
+
} catch (error) {
|
|
198
|
+
console.error('[API:GET /api/config/mcp/:name] Failed:', error);
|
|
199
|
+
res.status(500).json({ error: error.message || 'Failed to get MCP config' });
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
app.post('/api/config/mcp/:name', async (req, res) => {
|
|
204
|
+
try {
|
|
205
|
+
const name = req.params.name;
|
|
206
|
+
const { scope, ...config } = req.body || {};
|
|
207
|
+
const { directory, error } = await resolveOptionalProjectDirectory(req);
|
|
208
|
+
if (error) {
|
|
209
|
+
return res.status(400).json({ error });
|
|
210
|
+
}
|
|
211
|
+
console.log(`[API:POST /api/config/mcp] Creating MCP server: ${name}`);
|
|
212
|
+
|
|
213
|
+
await completeMcpMutation(res, 'create', name, () => {
|
|
214
|
+
createMcpConfig(name, config, directory, scope);
|
|
215
|
+
});
|
|
216
|
+
} catch (error) {
|
|
217
|
+
console.error('[API:POST /api/config/mcp/:name] Failed:', error);
|
|
218
|
+
res.status(500).json({ error: error.message || 'Failed to create MCP server' });
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
app.patch('/api/config/mcp/:name', async (req, res) => {
|
|
223
|
+
try {
|
|
224
|
+
const name = req.params.name;
|
|
225
|
+
const updates = req.body;
|
|
226
|
+
const { directory, error } = await resolveOptionalProjectDirectory(req);
|
|
227
|
+
if (error) {
|
|
228
|
+
return res.status(400).json({ error });
|
|
229
|
+
}
|
|
230
|
+
console.log(`[API:PATCH /api/config/mcp] Updating MCP server: ${name}`);
|
|
231
|
+
|
|
232
|
+
await completeMcpMutation(res, 'update', name, () => {
|
|
233
|
+
updateMcpConfig(name, updates, directory);
|
|
234
|
+
});
|
|
235
|
+
} catch (error) {
|
|
236
|
+
console.error('[API:PATCH /api/config/mcp/:name] Failed:', error);
|
|
237
|
+
if (error?.message === `MCP server "${req.params.name}" not found`) {
|
|
238
|
+
return res.status(404).json({ error: error.message });
|
|
239
|
+
}
|
|
240
|
+
res.status(500).json({ error: error.message || 'Failed to update MCP server' });
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
app.delete('/api/config/mcp/:name', async (req, res) => {
|
|
245
|
+
try {
|
|
246
|
+
const name = req.params.name;
|
|
247
|
+
const { directory, error } = await resolveOptionalProjectDirectory(req);
|
|
248
|
+
if (error) {
|
|
249
|
+
return res.status(400).json({ error });
|
|
250
|
+
}
|
|
251
|
+
console.log(`[API:DELETE /api/config/mcp] Deleting MCP server: ${name}`);
|
|
252
|
+
|
|
253
|
+
await completeMcpMutation(res, 'delete', name, () => {
|
|
254
|
+
deleteMcpConfig(name, directory);
|
|
255
|
+
});
|
|
256
|
+
} catch (error) {
|
|
257
|
+
console.error('[API:DELETE /api/config/mcp/:name] Failed:', error);
|
|
258
|
+
res.status(500).json({ error: error.message || 'Failed to delete MCP server' });
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
app.get('/api/config/commands/:name', async (req, res) => {
|
|
263
|
+
try {
|
|
264
|
+
const commandName = req.params.name;
|
|
265
|
+
const { directory, error } = await resolveProjectDirectory(req);
|
|
266
|
+
if (!directory) {
|
|
267
|
+
return res.status(400).json({ error });
|
|
268
|
+
}
|
|
269
|
+
const sources = getCommandSources(commandName, directory);
|
|
270
|
+
|
|
271
|
+
const scope = sources.md.exists
|
|
272
|
+
? sources.md.scope
|
|
273
|
+
: (sources.json.exists ? sources.json.scope : null);
|
|
274
|
+
|
|
275
|
+
res.json({
|
|
276
|
+
name: commandName,
|
|
277
|
+
sources: sources,
|
|
278
|
+
scope,
|
|
279
|
+
isBuiltIn: !sources.md.exists && !sources.json.exists
|
|
280
|
+
});
|
|
281
|
+
} catch (error) {
|
|
282
|
+
console.error('Failed to get command sources:', error);
|
|
283
|
+
res.status(500).json({ error: 'Failed to get command configuration metadata' });
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
app.post('/api/config/commands/:name', async (req, res) => {
|
|
288
|
+
try {
|
|
289
|
+
const commandName = req.params.name;
|
|
290
|
+
const { scope, ...config } = req.body;
|
|
291
|
+
const { directory, error } = await resolveProjectDirectory(req);
|
|
292
|
+
if (!directory) {
|
|
293
|
+
return res.status(400).json({ error });
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
console.log('[Server] Creating command:', commandName);
|
|
297
|
+
console.log('[Server] Config received:', JSON.stringify(config, null, 2));
|
|
298
|
+
console.log('[Server] Scope:', scope, 'Working directory:', directory);
|
|
299
|
+
|
|
300
|
+
createCommand(commandName, config, directory, scope);
|
|
301
|
+
await refreshOpenCodeAfterConfigChange('command creation', {
|
|
302
|
+
commandName
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
res.json({
|
|
306
|
+
success: true,
|
|
307
|
+
requiresReload: true,
|
|
308
|
+
message: `Command ${commandName} created successfully. Reloading interface…`,
|
|
309
|
+
reloadDelayMs: clientReloadDelayMs,
|
|
310
|
+
});
|
|
311
|
+
} catch (error) {
|
|
312
|
+
console.error('Failed to create command:', error);
|
|
313
|
+
res.status(500).json({ error: error.message || 'Failed to create command' });
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
app.patch('/api/config/commands/:name', async (req, res) => {
|
|
318
|
+
try {
|
|
319
|
+
const commandName = req.params.name;
|
|
320
|
+
const updates = req.body;
|
|
321
|
+
const { directory, error } = await resolveProjectDirectory(req);
|
|
322
|
+
if (!directory) {
|
|
323
|
+
return res.status(400).json({ error });
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
console.log(`[Server] Updating command: ${commandName}`);
|
|
327
|
+
console.log('[Server] Updates:', JSON.stringify(updates, null, 2));
|
|
328
|
+
console.log('[Server] Working directory:', directory);
|
|
329
|
+
|
|
330
|
+
updateCommand(commandName, updates, directory);
|
|
331
|
+
await refreshOpenCodeAfterConfigChange('command update');
|
|
332
|
+
|
|
333
|
+
console.log(`[Server] Command ${commandName} updated successfully`);
|
|
334
|
+
|
|
335
|
+
res.json({
|
|
336
|
+
success: true,
|
|
337
|
+
requiresReload: true,
|
|
338
|
+
message: `Command ${commandName} updated successfully. Reloading interface…`,
|
|
339
|
+
reloadDelayMs: clientReloadDelayMs,
|
|
340
|
+
});
|
|
341
|
+
} catch (error) {
|
|
342
|
+
console.error('[Server] Failed to update command:', error);
|
|
343
|
+
console.error('[Server] Error stack:', error.stack);
|
|
344
|
+
res.status(500).json({ error: error.message || 'Failed to update command' });
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
app.delete('/api/config/commands/:name', async (req, res) => {
|
|
349
|
+
try {
|
|
350
|
+
const commandName = req.params.name;
|
|
351
|
+
const { directory, error } = await resolveProjectDirectory(req);
|
|
352
|
+
if (!directory) {
|
|
353
|
+
return res.status(400).json({ error });
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
deleteCommand(commandName, directory);
|
|
357
|
+
await refreshOpenCodeAfterConfigChange('command deletion');
|
|
358
|
+
|
|
359
|
+
res.json({
|
|
360
|
+
success: true,
|
|
361
|
+
requiresReload: true,
|
|
362
|
+
message: `Command ${commandName} deleted successfully. Reloading interface…`,
|
|
363
|
+
reloadDelayMs: clientReloadDelayMs,
|
|
364
|
+
});
|
|
365
|
+
} catch (error) {
|
|
366
|
+
console.error('Failed to delete command:', error);
|
|
367
|
+
res.status(500).json({ error: error.message || 'Failed to delete command' });
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
};
|