@vantran-se/openchamber-web 1.24.4 → 2.0.1
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 +2 -8
- package/bin/lib/commands-connect-url.js +3 -8
- package/bin/lib/commands-tunnel.js +3 -0
- package/dist/assets/{CodeMirrorCommentWidgets-B4a0JkUz.js → CodeMirrorCommentWidgets-ntXIbhV2.js} +1 -1
- package/dist/assets/DiffView-CjxUovbM.js +4 -0
- package/dist/assets/FilesView-DFWD0RQR.js +17 -0
- package/dist/assets/{GitView-Byu2DY7s.js → GitView-BWa7e5vw.js} +4 -4
- package/dist/assets/{GuestAttachDialog-B808E0-d.js → GuestAttachDialog-DdOuyZzp.js} +1 -1
- package/dist/assets/GuestSurfacePane-DDfzid3U.js +1 -0
- package/dist/assets/{JsonTreeView-VW_7czo3.js → JsonTreeView-3sGwqexh.js} +1 -1
- package/dist/assets/LinearIssuesView-C6dpIdjw.js +1 -0
- package/dist/assets/MarkdownImageGallery-Q7YR_tl4.js +1 -0
- package/dist/assets/MarkdownRendererImpl-DxDV56bL.js +12 -0
- package/dist/assets/OnboardingScreen-rK4Stg__.js +1 -0
- package/dist/assets/{PatchDiff-DCjGhlYV.js → PatchDiff-DS-IMU1j.js} +1 -1
- package/dist/assets/{PierreDiffViewer-CEDZNkU2.js → PierreDiffViewer-DW23KWKZ.js} +1 -1
- package/dist/assets/PlanView-BCk_VobP.js +5 -0
- package/dist/assets/PluginPane-BPP1dbck.js +3 -0
- package/dist/assets/SettingsView-kMmrpn43.js +29 -0
- package/dist/assets/SettingsWindow-BpvD6IYQ.js +1 -0
- package/dist/assets/SubmoduleDiffSummary-BLttSS1X.js +1 -0
- package/dist/assets/ToolOutputDialog-CtqDqbyr.js +15 -0
- package/dist/assets/ToolPartDiffPreview-5FcKdyRy.js +8 -0
- package/dist/assets/{VirtualizedFileDiff-NU0FHFhT.js → VirtualizedFileDiff-C763hNCR.js} +4 -4
- package/dist/assets/{WalkthroughView-BZyjhqHv.js → WalkthroughView-lap5btCF.js} +2 -2
- package/dist/assets/appThemeRegistry-u4zw1l0B.js +1 -0
- package/dist/assets/{commitAndPush-BDftrjr6.js → commitAndPush-CwGJfJG-.js} +1 -1
- package/dist/assets/{computeVirtualFileMetrics-DlWlEM3x.js → computeVirtualFileMetrics-DW5jjX4n.js} +1 -1
- package/dist/assets/de-CndEg67T.js +10 -0
- package/dist/assets/es-DglvPlaC.js +16 -0
- package/dist/assets/fr-DejPCpbS.js +14 -0
- package/dist/assets/index-C2r64QfD.js +1 -0
- package/dist/assets/index-CLcM8HKx.css +1 -0
- package/dist/assets/index-CvLgnuNI.js +41 -0
- package/dist/assets/{isDiffPlainText-CO4e5J_S.js → isDiffPlainText-D47a6hMP.js} +1 -1
- package/dist/assets/{isFilePlainText-ue6kCou7.js → isFilePlainText-B-mGgeKl.js} +1 -1
- package/dist/assets/ja-CVpQawCd.js +16 -0
- package/dist/assets/ko-Bw446h9j.js +16 -0
- package/dist/assets/main-Bz4WWXbr.js +2 -0
- package/dist/assets/main-oreqDsOV.js +896 -0
- package/dist/assets/{markdownCore-CK48LG0z.js → markdownCore-D2e5sp9y.js} +4 -4
- package/dist/assets/miniChat-D1v2gsBI.js +2 -0
- package/dist/assets/mobile-DBj3lCf2.js +2 -0
- package/dist/assets/{patchFileDiff-D7QVlovD.js → patchFileDiff-BpeUOV91.js} +3 -3
- package/dist/assets/pl-BYNlPdhm.js +16 -0
- package/dist/assets/pt-BR-CL-Yfv9H.js +16 -0
- package/dist/assets/renderElectronMiniChatApp-DVtfEB2z.js +2 -0
- package/dist/assets/renderMobileApp-DFp9-xbG.js +25 -0
- package/dist/assets/runtimeEndpointReset-CsIpUep1.js +27 -0
- package/dist/assets/scheduledTasksApi-B5OHVpPM.js +1 -0
- package/dist/assets/{shikiHighlight-BPu3JiQs.js → shikiHighlight-DNm2Z_Jc.js} +1 -1
- package/dist/assets/{textMateThemeFromAppTheme-DWqRPo9A.js → textMateThemeFromAppTheme-DGF2kG7g.js} +1 -1
- package/dist/assets/tr-DzmbxLy-.js +7 -0
- package/dist/assets/uk-C7vQnwro.js +16 -0
- package/dist/assets/useAppFontEffects-Ci3FEaPf.js +7337 -0
- package/dist/assets/{useInlineCommentController-CgS0fnw_.js → useInlineCommentController-CuUuakrt.js} +2 -2
- package/dist/assets/useRootScrollLock-CQIsyERq.js +2 -0
- package/dist/assets/{vendor-codemirror-language-data-Bx2EHc1O.js → vendor-codemirror-language-data-CQa7_1cr.js} +2 -2
- package/dist/assets/vendor-cron-parser-CjvYVigk.js +2 -0
- package/dist/assets/vendor-opencode-client-_BAHQo4R.js +12 -0
- package/dist/assets/{vendor-remixicon-react-BDmGmFCA.js → vendor-remixicon-react-D6dZ0UwF.js} +1 -1
- package/dist/assets/{vendor-zod-CgAdXWZO.js → vendor-zod-Gea9uBlY.js} +1 -1
- package/dist/assets/{web-components-DxX8bZ1o.js → web-components-B6tKQyJN.js} +2 -2
- package/dist/assets/zh-CN-Cl0srS-z.js +16 -0
- package/dist/assets/zh-TW-DtII5bDb.js +16 -0
- package/dist/index.html +13 -7
- package/dist/mini-chat.html +5 -5
- package/dist/mobile.html +5 -5
- package/package.json +6 -5
- package/server/index.d.ts +1 -0
- package/server/index.js +246 -91
- package/server/lib/agent-tool/DOCUMENTATION.md +32 -15
- package/server/lib/agent-tool/runtime.js +135 -54
- package/server/lib/agent-tool/runtime.test.js +155 -87
- package/server/lib/browser-control/DOCUMENTATION.md +37 -2
- package/server/lib/browser-control/provider.js +217 -0
- package/server/lib/browser-control/provider.test.js +251 -0
- package/server/lib/context-obligatory/runtime.js +61 -50
- package/server/lib/context-obligatory/runtime.test.js +62 -162
- package/server/lib/event-stream/DOCUMENTATION.md +12 -1
- package/server/lib/event-stream/delta-coalescer.js +0 -0
- package/server/lib/event-stream/delta-coalescer.test.js +68 -57
- package/server/lib/event-stream/directory-ws-bridge.js +1 -1
- package/server/lib/event-stream/global-hub.js +10 -1
- package/server/lib/event-stream/global-hub.test.js +27 -26
- package/server/lib/event-stream/index.js +6 -0
- package/server/lib/event-stream/protocol.js +26 -19
- package/server/lib/event-stream/protocol.test.js +18 -7
- package/server/lib/event-stream/rebind.test.js +5 -5
- package/server/lib/event-stream/runtime.test.js +17 -15
- package/server/lib/event-stream/translate-v2.js +313 -0
- package/server/lib/event-stream/translate-v2.test.js +209 -0
- package/server/lib/event-stream/upstream-reader.test.js +16 -16
- package/server/lib/fs/DOCUMENTATION.md +4 -2
- package/server/lib/fs/byte-range.js +35 -0
- package/server/lib/fs/byte-range.test.js +31 -0
- package/server/lib/fs/routes.js +47 -42
- package/server/lib/fs/routes.test.js +123 -18
- package/server/lib/git/service.js +12 -0
- package/server/lib/github/DOCUMENTATION.md +1 -1
- package/server/lib/github/pr-status.js +22 -2
- package/server/lib/github/pr-status.test.js +27 -14
- package/server/lib/guests/DOCUMENTATION.md +13 -0
- package/server/lib/guests/catalog.js +12 -0
- package/server/lib/guests/catalog.test.js +33 -0
- package/server/lib/guests/routes.js +10 -1
- package/server/lib/guests/service.js +192 -14
- package/server/lib/guests/service.test.js +201 -1
- package/server/lib/guests/surface.js +630 -0
- package/server/lib/guests/surface.test.js +486 -0
- package/server/lib/markdown-image-grants/routes.js +18 -5
- package/server/lib/markdown-image-grants/routes.test.js +9 -6
- package/server/lib/message-queue/DOCUMENTATION.md +33 -22
- package/server/lib/message-queue/runtime.js +155 -100
- package/server/lib/message-queue/runtime.test.js +168 -82
- package/server/lib/notifications/DOCUMENTATION.md +28 -0
- package/server/lib/notifications/control-stream.http.test.js +68 -0
- package/server/lib/notifications/emitter-runtime.js +15 -0
- package/server/lib/notifications/emitter-runtime.test.js +32 -0
- package/server/lib/notifications/push-runtime.js +5 -1
- package/server/lib/notifications/push-runtime.test.js +2 -2
- package/server/lib/notifications/routes.js +1 -1
- package/server/lib/notifications/runtime.js +115 -26
- package/server/lib/notifications/template-runtime.js +13 -15
- package/server/lib/notifications/template-runtime.test.js +16 -9
- package/server/lib/notifications/trigger-runtime.test.js +187 -0
- package/server/lib/openchamber-control/DOCUMENTATION.md +10 -0
- package/server/lib/openchamber-control/actions.js +3 -2
- package/server/lib/openchamber-control/error.js +1 -1
- package/server/lib/openchamber-control/file-open.js +59 -0
- package/server/lib/openchamber-control/file-open.test.js +57 -0
- package/server/lib/openchamber-control/routes.test.js +12 -0
- package/server/lib/openchamber-control/service.js +102 -49
- package/server/lib/openchamber-control/service.test.js +84 -44
- package/server/lib/openchamber-sessions/archive-store.js +224 -0
- package/server/lib/openchamber-sessions/archive-store.test.js +187 -0
- package/server/lib/openchamber-sessions/opencode-client.js +22 -0
- package/server/lib/openchamber-sessions/routes.js +398 -328
- package/server/lib/openchamber-sessions/routes.test.js +777 -740
- package/server/lib/openchamber-sessions/session-metadata-store.js +285 -0
- package/server/lib/openchamber-sessions/session-metadata-store.test.js +259 -0
- package/server/lib/opencode/DOCUMENTATION.md +449 -29
- package/server/lib/opencode/agents.js +233 -327
- package/server/lib/opencode/auth.js +41 -44
- package/server/lib/opencode/auth.test.js +92 -0
- package/server/lib/opencode/cli-upgrade.d.ts +4 -0
- package/server/lib/opencode/cli-upgrade.js +18 -0
- package/server/lib/opencode/cli-upgrade.test.js +36 -0
- package/server/lib/opencode/commands.js +164 -145
- package/server/lib/opencode/compatibility.d.ts +16 -0
- package/server/lib/opencode/compatibility.js +88 -0
- package/server/lib/opencode/compatibility.test.js +62 -0
- package/server/lib/opencode/config-entity-routes.js +58 -29
- package/server/lib/opencode/config-entity-v2.test.js +290 -0
- package/server/lib/opencode/config-mutation-response.js +17 -7
- package/server/lib/opencode/config-mutation-response.test.js +4 -7
- package/server/lib/opencode/config-paths.test.js +2 -2
- package/server/lib/opencode/config-v2.d.ts +183 -0
- package/server/lib/opencode/config-v2.js +801 -0
- package/server/lib/opencode/config-v2.test.js +466 -0
- package/server/lib/opencode/credential-db.js +144 -0
- package/server/lib/opencode/credential-db.test.js +104 -0
- package/server/lib/opencode/env-runtime.js +48 -9
- package/server/lib/opencode/env-runtime.test.js +45 -0
- package/server/lib/opencode/feature-routes-runtime.js +15 -5
- package/server/lib/opencode/lifecycle-process.test.js +7 -4
- package/server/lib/opencode/lifecycle.js +85 -30
- package/server/lib/opencode/lifecycle.test.js +80 -8
- package/server/lib/opencode/login-shell-env.js +27 -0
- package/server/lib/opencode/managed-config-file.js +119 -0
- package/server/lib/opencode/managed-config-file.test.js +152 -0
- package/server/lib/opencode/managed-plugin-config.js +33 -13
- package/server/lib/opencode/mcp.js +43 -149
- package/server/lib/opencode/network-runtime.js +6 -7
- package/server/lib/opencode/openchamber-routes.test.js +2 -5
- package/server/lib/opencode/plugin-routes.js +3 -3
- package/server/lib/opencode/plugin-routes.test.js +6 -24
- package/server/lib/opencode/plugins.js +91 -40
- package/server/lib/opencode/plugins.test.js +28 -15
- package/server/lib/opencode/providers.js +63 -83
- package/server/lib/opencode/providers.test.js +128 -59
- package/server/lib/opencode/proxy-agent-wiring.test.js +4 -2
- package/server/lib/opencode/proxy.js +179 -129
- package/server/lib/opencode/pwa-manifest-routes.js +4 -2
- package/server/lib/opencode/pwa-manifest-routes.test.js +3 -2
- package/server/lib/opencode/response-envelope.js +14 -0
- package/server/lib/opencode/response-envelope.test.js +23 -0
- package/server/lib/opencode/routes-upgrade.test.js +73 -116
- package/server/lib/opencode/routes.js +121 -428
- package/server/lib/opencode/server-utils-runtime.js +10 -4
- package/server/lib/opencode/session-activity.js +78 -0
- package/server/lib/opencode/session-runtime.js +21 -10
- package/server/lib/opencode/session-runtime.test.js +9 -9
- package/server/lib/opencode/settings-helpers.js +9 -9
- package/server/lib/opencode/settings-helpers.test.js +7 -12
- package/server/lib/opencode/settings-registry.json +6 -8
- package/server/lib/opencode/settings-runtime.js +18 -1
- package/server/lib/opencode/settings-runtime.test.js +31 -2
- package/server/lib/opencode/shared.js +54 -22
- package/server/lib/opencode/shared.test.js +8 -4
- package/server/lib/opencode/shutdown-runtime.js +54 -7
- package/server/lib/opencode/shutdown-runtime.test.js +224 -30
- package/server/lib/opencode/skill-routes.js +23 -24
- package/server/lib/opencode/skills.js +7 -5
- package/server/lib/opencode/tunnel-wiring-runtime.js +2 -1
- package/server/lib/opencode/upgrade-capability.test.js +1 -1
- package/server/lib/opencode/v1-migration-topup.js +288 -0
- package/server/lib/opencode/v1-migration-topup.test.js +266 -0
- package/server/lib/opencode/v2-install.d.ts +8 -0
- package/server/lib/opencode/v2-install.js +158 -0
- package/server/lib/opencode/v2-install.test.js +124 -0
- package/server/lib/opencode/watcher.js +10 -3
- package/server/lib/opencode/watcher.test.js +17 -21
- package/server/lib/opencode/websearch-config.js +66 -0
- package/server/lib/package-manager.js +55 -68
- package/server/lib/package-manager.test.js +241 -75
- package/server/lib/permission-auto-accept/runtime.js +38 -22
- package/server/lib/permission-auto-accept/runtime.test.js +39 -17
- package/server/lib/quota/DOCUMENTATION.md +4 -0
- package/server/lib/quota/providers/xai.js +4 -4
- package/server/lib/relay/tunnel-host.js +4 -1
- package/server/lib/relay/tunnel-host.test.js +7 -0
- package/server/lib/routing/DOCUMENTATION.md +52 -21
- package/server/lib/routing/defaults.js +23 -6
- package/server/lib/routing/jev.js +17 -7
- package/server/lib/routing/routes.http.test.js +68 -33
- package/server/lib/routing/routes.js +65 -22
- package/server/lib/routing/runtime.js +145 -48
- package/server/lib/routing/runtime.test.js +79 -55
- package/server/lib/scheduled-tasks/DOCUMENTATION.md +4 -0
- package/server/lib/scheduled-tasks/issue-2710-double-execution.test.js +51 -46
- package/server/lib/scheduled-tasks/runtime.js +81 -74
- package/server/lib/scheduled-tasks/runtime.test.js +53 -1
- package/server/lib/session-assist/DOCUMENTATION.md +8 -3
- package/server/lib/session-assist/context.js +122 -46
- package/server/lib/session-assist/context.test.js +117 -49
- package/server/lib/session-assist/runtime.js +52 -22
- package/server/lib/session-assist/runtime.test.js +145 -173
- package/server/lib/session-goal/DOCUMENTATION.md +23 -15
- package/server/lib/session-goal/create.js +9 -12
- package/server/lib/session-goal/create.test.js +27 -28
- package/server/lib/session-goal/runtime.js +164 -77
- package/server/lib/session-goal/runtime.test.js +271 -519
- package/server/lib/session-knowledge/DOCUMENTATION.md +5 -0
- package/server/lib/session-knowledge/runtime.js +59 -41
- package/server/lib/session-knowledge/runtime.test.js +90 -20
- package/server/lib/small-model/DOCUMENTATION.md +154 -208
- package/server/lib/small-model/client.js +113 -0
- package/server/lib/small-model/index.js +296 -158
- package/server/lib/small-model/index.test.js +423 -256
- package/server/lib/small-model/routes.js +1 -1
- package/server/lib/spaces/DOCUMENTATION.md +807 -0
- package/server/lib/spaces/code-in-bait.js +185 -0
- package/server/lib/spaces/code-in.js +641 -0
- package/server/lib/spaces/code-in.test.js +1048 -0
- package/server/lib/spaces/errors.js +8 -0
- package/server/lib/spaces/exec-http.js +94 -0
- package/server/lib/spaces/gatekeeper-channel.js +202 -0
- package/server/lib/spaces/gatekeeper-channel.test.js +215 -0
- package/server/lib/spaces/gatekeeper-program.cjs +914 -0
- package/server/lib/spaces/gatekeeper-program.test.js +1391 -0
- package/server/lib/spaces/hardening.js +457 -0
- package/server/lib/spaces/hardening.test.js +368 -0
- package/server/lib/spaces/host-git.js +127 -0
- package/server/lib/spaces/host-git.test.js +137 -0
- package/server/lib/spaces/labels.js +234 -0
- package/server/lib/spaces/labels.test.js +219 -0
- package/server/lib/spaces/layout.js +184 -0
- package/server/lib/spaces/layout.test.js +30 -0
- package/server/lib/spaces/manager.js +82 -0
- package/server/lib/spaces/manager.test.js +114 -0
- package/server/lib/spaces/places/code-in.docker.live.test.js +216 -0
- package/server/lib/spaces/places/contract-suite.js +156 -0
- package/server/lib/spaces/places/contract.docker.live.test.js +7 -0
- package/server/lib/spaces/places/contract.test.js +6 -0
- package/server/lib/spaces/places/docker-engine.js +126 -0
- package/server/lib/spaces/places/docker-live-support.js +202 -0
- package/server/lib/spaces/places/docker-tools.js +186 -0
- package/server/lib/spaces/places/docker.js +587 -0
- package/server/lib/spaces/places/docker.test.js +1610 -0
- package/server/lib/spaces/places/escape-suite.js +1018 -0
- package/server/lib/spaces/places/escape.docker.live.test.js +7 -0
- package/server/lib/spaces/places/fake-docker.js +375 -0
- package/server/lib/spaces/places/memory-place.js +103 -0
- package/server/lib/spaces/places/packed.docker.live.test.js +77 -0
- package/server/lib/spaces/places/registry.js +43 -0
- package/server/lib/spaces/places/registry.test.js +50 -0
- package/server/lib/spaces/places/server.docker.live.test.js +225 -0
- package/server/lib/spaces/run-command.js +261 -0
- package/server/lib/spaces/run-command.test.js +396 -0
- package/server/lib/spaces/space-server.js +190 -0
- package/server/lib/spaces/space-server.test.js +335 -0
- package/server/lib/spaces/tools-filler.js +82 -0
- package/server/lib/spaces/tools-filler.test.js +148 -0
- package/server/lib/spaces/tools-pack.js +49 -0
- package/server/lib/spaces/tools-pack.test.js +57 -0
- package/server/lib/spaces/tools.js +138 -0
- package/server/lib/spaces/tools.test.js +153 -0
- package/server/lib/tts/openai-sdk.js +11 -0
- package/server/lib/tts/service.js +7 -5
- package/server/lib/tts/stt.js +3 -2
- package/server/lib/tunnels/DOCUMENTATION.md +2 -0
- package/server/lib/tunnels/routes.js +7 -0
- package/server/lib/tunnels/routes.test.js +55 -0
- package/server/lib/ui-auth/ui-auth.js +38 -2
- package/server/lib/ui-auth/ui-auth.test.js +42 -0
- package/server/lib/ui-auth/ui-passkeys.js +12 -10
- package/server/lib/walkthrough/DOCUMENTATION.md +10 -1
- package/server/lib/walkthrough/pull-request.js +61 -8
- package/server/lib/walkthrough/pull-request.test.js +54 -1
- package/server/lib/walkthrough/routes.js +20 -0
- package/server/lib/walkthrough/routes.test.js +19 -0
- package/server/opencode-proxy.test.js +190 -166
- package/server/opencode-worktree-readiness.test.js +5 -4
- package/dist/assets/DiffView-B2CD9wC8.js +0 -4
- package/dist/assets/FilesView-C4vMFclc.js +0 -13
- package/dist/assets/LinearIssuesView-EzGNCGrd.js +0 -1
- package/dist/assets/MarkdownImageGallery-B2YUDFaB.js +0 -1
- package/dist/assets/MarkdownRendererImpl-B4ikIzR3.js +0 -12
- package/dist/assets/OnboardingScreen-CQHBSTEi.js +0 -1
- package/dist/assets/PlanView-D3x-pTkZ.js +0 -5
- package/dist/assets/PluginPane-DEcgDQ9h.js +0 -3
- package/dist/assets/SettingsView-CSinuH-1.js +0 -29
- package/dist/assets/SettingsWindow-B5CHoJFZ.js +0 -1
- package/dist/assets/SubmoduleDiffSummary-oxeACCWs.js +0 -1
- package/dist/assets/ToolOutputDialog-tLNWmfcW.js +0 -15
- package/dist/assets/ToolPartDiffPreview-CflzmK-x.js +0 -8
- package/dist/assets/appThemeRegistry-DLicw4dH.js +0 -1
- package/dist/assets/de-Bro6_zyl.js +0 -10
- package/dist/assets/es-Bb9XQZvd.js +0 -16
- package/dist/assets/fr-Dgfzn-IL.js +0 -14
- package/dist/assets/index-CtQNmqUL.js +0 -1
- package/dist/assets/index-DqaMWNrC.css +0 -1
- package/dist/assets/index-Dqc6USl3.js +0 -40
- package/dist/assets/ja-BYsakGFh.js +0 -16
- package/dist/assets/ko-BSaQz9aN.js +0 -16
- package/dist/assets/main-B62_pjL5.js +0 -2
- package/dist/assets/main-BEdcOI2P.js +0 -896
- package/dist/assets/miniChat-LT8Rqofr.js +0 -2
- package/dist/assets/mobile-CqarEynh.js +0 -2
- package/dist/assets/pl-OIviPdoj.js +0 -16
- package/dist/assets/pt-BR-Cltg-E6s.js +0 -16
- package/dist/assets/renderElectronMiniChatApp-COyLTtZx.js +0 -2
- package/dist/assets/renderMobileApp-BC1MHg6g.js +0 -25
- package/dist/assets/runtimeEndpointReset-DdrazOVH.js +0 -25
- package/dist/assets/scheduledTasksApi-B2SWMGEU.js +0 -1
- package/dist/assets/tr-CQI-KPre.js +0 -7
- package/dist/assets/uk-CqW7R4DA.js +0 -16
- package/dist/assets/useAppFontEffects-ne84JBVL.js +0 -7333
- package/dist/assets/useRootScrollLock-Cpf1qoGp.js +0 -2
- package/dist/assets/vendor-cron-parser-CUcP7OfX.js +0 -2
- package/dist/assets/vendor-opencode-sdk-BJ73u-bH.js +0 -7
- package/dist/assets/zh-CN-CVZG_o_J.js +0 -16
- package/dist/assets/zh-TW-D5ZxzCZp.js +0 -16
- package/server/lib/opencode/mcp-oauth-callback.test.js +0 -111
- package/server/lib/routing/feature-flag.js +0 -11
- package/server/lib/small-model/call.js +0 -851
- package/server/lib/small-model/call.test.js +0 -1292
- package/server/lib/small-model/catalog.js +0 -13
- package/server/lib/small-model/resolve.js +0 -131
- package/server/lib/small-model/resolve.test.js +0 -197
- package/server/lib/small-model/runtime-providers.js +0 -164
- package/server/lib/small-model/runtime-providers.test.js +0 -118
- package/server/lib/system-prompt/DOCUMENTATION.md +0 -36
- package/server/lib/system-prompt/runtime.js +0 -49
- package/server/lib/system-prompt/runtime.test.js +0 -76
- package/server/lib/walkthrough/reproduce-2607.test.js +0 -148
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { buildAppliedResponse } from './config-mutation-response.js';
|
|
2
2
|
|
|
3
3
|
export const registerConfigEntityRoutes = (app, dependencies) => {
|
|
4
4
|
const {
|
|
@@ -6,10 +6,12 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
|
|
|
6
6
|
resolveOptionalProjectDirectory,
|
|
7
7
|
getAgentSources,
|
|
8
8
|
getAgentConfig,
|
|
9
|
+
getAgentPermissions,
|
|
9
10
|
createAgent,
|
|
10
11
|
updateAgent,
|
|
11
12
|
deleteAgent,
|
|
12
13
|
getCommandSources,
|
|
14
|
+
getCommandConfig,
|
|
13
15
|
createCommand,
|
|
14
16
|
updateCommand,
|
|
15
17
|
deleteCommand,
|
|
@@ -29,10 +31,11 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
|
|
|
29
31
|
// Persist to disk immediately; OpenCode restart is deferred to an explicit
|
|
30
32
|
// Apply & Restart so settings edits do not interrupt live sessions.
|
|
31
33
|
const completeMcpMutation = async (res, action, name, applyChange) => {
|
|
32
|
-
applyChange();
|
|
34
|
+
const result = applyChange();
|
|
33
35
|
const past = action === 'delete' ? 'deleted' : `${action}d`;
|
|
34
|
-
return res.json(
|
|
35
|
-
`MCP server "${name}" ${past}
|
|
36
|
+
return res.json(buildAppliedResponse(
|
|
37
|
+
`MCP server "${name}" ${past}.`,
|
|
38
|
+
result,
|
|
36
39
|
));
|
|
37
40
|
};
|
|
38
41
|
|
|
@@ -77,6 +80,20 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
|
|
|
77
80
|
}
|
|
78
81
|
});
|
|
79
82
|
|
|
83
|
+
app.get('/api/config/agents/:name/permissions', async (req, res) => {
|
|
84
|
+
try {
|
|
85
|
+
const agentName = req.params.name;
|
|
86
|
+
const { directory, error } = await resolveProjectDirectory(req);
|
|
87
|
+
if (!directory) {
|
|
88
|
+
return res.status(400).json({ error });
|
|
89
|
+
}
|
|
90
|
+
res.json(getAgentPermissions(agentName, directory));
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.error('Failed to get agent permissions:', error);
|
|
93
|
+
res.status(500).json({ error: 'Failed to get agent permissions' });
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
80
97
|
app.post('/api/config/agents/:name', async (req, res) => {
|
|
81
98
|
try {
|
|
82
99
|
const agentName = req.params.name;
|
|
@@ -90,9 +107,10 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
|
|
|
90
107
|
console.log('[Server] Config received:', JSON.stringify(config, null, 2));
|
|
91
108
|
console.log('[Server] Scope:', scope, 'Working directory:', directory);
|
|
92
109
|
|
|
93
|
-
createAgent(agentName, config, directory, scope);
|
|
94
|
-
res.json(
|
|
95
|
-
`Agent ${agentName} created successfully
|
|
110
|
+
const created = createAgent(agentName, config, directory, scope);
|
|
111
|
+
res.json(buildAppliedResponse(
|
|
112
|
+
`Agent ${agentName} created successfully.`,
|
|
113
|
+
created,
|
|
96
114
|
));
|
|
97
115
|
} catch (error) {
|
|
98
116
|
console.error('Failed to create agent:', error);
|
|
@@ -113,12 +131,13 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
|
|
|
113
131
|
console.log('[Server] Updates:', JSON.stringify(updates, null, 2));
|
|
114
132
|
console.log('[Server] Working directory:', directory);
|
|
115
133
|
|
|
116
|
-
updateAgent(agentName, updates, directory);
|
|
134
|
+
const updated = updateAgent(agentName, updates, directory);
|
|
117
135
|
|
|
118
136
|
console.log(`[Server] Agent ${agentName} updated successfully`);
|
|
119
137
|
|
|
120
|
-
res.json(
|
|
121
|
-
`Agent ${agentName} updated successfully
|
|
138
|
+
res.json(buildAppliedResponse(
|
|
139
|
+
`Agent ${agentName} updated successfully.`,
|
|
140
|
+
updated,
|
|
122
141
|
));
|
|
123
142
|
} catch (error) {
|
|
124
143
|
console.error('[Server] Failed to update agent:', error);
|
|
@@ -137,8 +156,8 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
|
|
|
137
156
|
|
|
138
157
|
const scope = req.body?.scope;
|
|
139
158
|
deleteAgent(agentName, directory, scope);
|
|
140
|
-
res.json(
|
|
141
|
-
`Agent ${agentName} deleted successfully
|
|
159
|
+
res.json(buildAppliedResponse(
|
|
160
|
+
`Agent ${agentName} deleted successfully.`,
|
|
142
161
|
));
|
|
143
162
|
} catch (error) {
|
|
144
163
|
console.error('Failed to delete agent:', error);
|
|
@@ -188,9 +207,7 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
|
|
|
188
207
|
}
|
|
189
208
|
console.log(`[API:POST /api/config/mcp] Creating MCP server: ${name}`);
|
|
190
209
|
|
|
191
|
-
await completeMcpMutation(res, 'create', name, () =>
|
|
192
|
-
createMcpConfig(name, config, directory, scope);
|
|
193
|
-
});
|
|
210
|
+
await completeMcpMutation(res, 'create', name, () => createMcpConfig(name, config, directory, scope));
|
|
194
211
|
} catch (error) {
|
|
195
212
|
console.error('[API:POST /api/config/mcp/:name] Failed:', error);
|
|
196
213
|
res.status(500).json({ error: error.message || 'Failed to create MCP server' });
|
|
@@ -207,9 +224,7 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
|
|
|
207
224
|
}
|
|
208
225
|
console.log(`[API:PATCH /api/config/mcp] Updating MCP server: ${name}`);
|
|
209
226
|
|
|
210
|
-
await completeMcpMutation(res, 'update', name, () =>
|
|
211
|
-
updateMcpConfig(name, updates, directory);
|
|
212
|
-
});
|
|
227
|
+
await completeMcpMutation(res, 'update', name, () => updateMcpConfig(name, updates, directory));
|
|
213
228
|
} catch (error) {
|
|
214
229
|
console.error('[API:PATCH /api/config/mcp/:name] Failed:', error);
|
|
215
230
|
if (error?.message === `MCP server "${req.params.name}" not found`) {
|
|
@@ -228,9 +243,7 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
|
|
|
228
243
|
}
|
|
229
244
|
console.log(`[API:DELETE /api/config/mcp] Deleting MCP server: ${name}`);
|
|
230
245
|
|
|
231
|
-
await completeMcpMutation(res, 'delete', name, () =>
|
|
232
|
-
deleteMcpConfig(name, directory);
|
|
233
|
-
});
|
|
246
|
+
await completeMcpMutation(res, 'delete', name, () => deleteMcpConfig(name, directory));
|
|
234
247
|
} catch (error) {
|
|
235
248
|
console.error('[API:DELETE /api/config/mcp/:name] Failed:', error);
|
|
236
249
|
res.status(500).json({ error: error.message || 'Failed to delete MCP server' });
|
|
@@ -262,6 +275,20 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
|
|
|
262
275
|
}
|
|
263
276
|
});
|
|
264
277
|
|
|
278
|
+
app.get('/api/config/commands/:name/config', async (req, res) => {
|
|
279
|
+
try {
|
|
280
|
+
const commandName = req.params.name;
|
|
281
|
+
const { directory, error } = await resolveProjectDirectory(req);
|
|
282
|
+
if (!directory) {
|
|
283
|
+
return res.status(400).json({ error });
|
|
284
|
+
}
|
|
285
|
+
res.json(getCommandConfig(commandName, directory));
|
|
286
|
+
} catch (error) {
|
|
287
|
+
console.error('Failed to get command config:', error);
|
|
288
|
+
res.status(500).json({ error: 'Failed to get command configuration' });
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
|
|
265
292
|
app.post('/api/config/commands/:name', async (req, res) => {
|
|
266
293
|
try {
|
|
267
294
|
const commandName = req.params.name;
|
|
@@ -275,9 +302,10 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
|
|
|
275
302
|
console.log('[Server] Config received:', JSON.stringify(config, null, 2));
|
|
276
303
|
console.log('[Server] Scope:', scope, 'Working directory:', directory);
|
|
277
304
|
|
|
278
|
-
createCommand(commandName, config, directory, scope);
|
|
279
|
-
res.json(
|
|
280
|
-
`Command ${commandName} created successfully
|
|
305
|
+
const created = createCommand(commandName, config, directory, scope);
|
|
306
|
+
res.json(buildAppliedResponse(
|
|
307
|
+
`Command ${commandName} created successfully.`,
|
|
308
|
+
created,
|
|
281
309
|
));
|
|
282
310
|
} catch (error) {
|
|
283
311
|
console.error('Failed to create command:', error);
|
|
@@ -298,12 +326,13 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
|
|
|
298
326
|
console.log('[Server] Updates:', JSON.stringify(updates, null, 2));
|
|
299
327
|
console.log('[Server] Working directory:', directory);
|
|
300
328
|
|
|
301
|
-
updateCommand(commandName, updates, directory);
|
|
329
|
+
const updated = updateCommand(commandName, updates, directory);
|
|
302
330
|
|
|
303
331
|
console.log(`[Server] Command ${commandName} updated successfully`);
|
|
304
332
|
|
|
305
|
-
res.json(
|
|
306
|
-
`Command ${commandName} updated successfully
|
|
333
|
+
res.json(buildAppliedResponse(
|
|
334
|
+
`Command ${commandName} updated successfully.`,
|
|
335
|
+
updated,
|
|
307
336
|
));
|
|
308
337
|
} catch (error) {
|
|
309
338
|
console.error('[Server] Failed to update command:', error);
|
|
@@ -321,8 +350,8 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
|
|
|
321
350
|
}
|
|
322
351
|
|
|
323
352
|
deleteCommand(commandName, directory);
|
|
324
|
-
res.json(
|
|
325
|
-
`Command ${commandName} deleted successfully
|
|
353
|
+
res.json(buildAppliedResponse(
|
|
354
|
+
`Command ${commandName} deleted successfully.`,
|
|
326
355
|
));
|
|
327
356
|
} catch (error) {
|
|
328
357
|
console.error('Failed to delete command:', error);
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { afterAll, beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
// The path constants are frozen when the modules load, so point
|
|
7
|
+
// XDG_CONFIG_HOME at a scratch directory BEFORE importing them. Nothing in this
|
|
8
|
+
// file may touch the real ~/.config/opencode.
|
|
9
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'openchamber-entity-v2-'));
|
|
10
|
+
process.env.XDG_CONFIG_HOME = path.join(root, 'xdg');
|
|
11
|
+
delete process.env.OPENCODE_CONFIG;
|
|
12
|
+
|
|
13
|
+
const { parseMdFile } = await import('./shared.js');
|
|
14
|
+
const { getAgentConfig, getAgentPermissions, updateAgent, createAgent } = await import('./agents.js');
|
|
15
|
+
const { getCommandConfig, updateCommand } = await import('./commands.js');
|
|
16
|
+
const { getMcpConfig, listMcpConfigs, updateMcpConfig } = await import('./mcp.js');
|
|
17
|
+
|
|
18
|
+
let projectDir;
|
|
19
|
+
|
|
20
|
+
function write(relativePath, content) {
|
|
21
|
+
const filePath = path.join(projectDir, relativePath);
|
|
22
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
23
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
24
|
+
return filePath;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function readJson(filePath) {
|
|
28
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe('entity modules speak OpenCode 2 shapes', () => {
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
projectDir = fs.mkdtempSync(path.join(root, 'project-'));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
afterAll(() => {
|
|
37
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
38
|
+
delete process.env.XDG_CONFIG_HOME;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('reads a v1 agent markdown file and answers the v2 shape', () => {
|
|
42
|
+
write('.opencode/agent/reviewer.md', [
|
|
43
|
+
'---',
|
|
44
|
+
'description: Reviewer',
|
|
45
|
+
'model: anthropic/claude-sonnet-4-5',
|
|
46
|
+
'variant: high',
|
|
47
|
+
'temperature: 0.4',
|
|
48
|
+
'maxSteps: 9',
|
|
49
|
+
'permission:',
|
|
50
|
+
' bash:',
|
|
51
|
+
' "git push *": ask',
|
|
52
|
+
' edit: deny',
|
|
53
|
+
'---',
|
|
54
|
+
'',
|
|
55
|
+
'Review for correctness.',
|
|
56
|
+
].join('\n'));
|
|
57
|
+
|
|
58
|
+
const result = getAgentConfig('reviewer', projectDir);
|
|
59
|
+
expect(result.source).toBe('md');
|
|
60
|
+
expect(result.legacy).toBe(true);
|
|
61
|
+
expect(result.config).toEqual({
|
|
62
|
+
description: 'Reviewer',
|
|
63
|
+
model: 'anthropic/claude-sonnet-4-5#high',
|
|
64
|
+
steps: 9,
|
|
65
|
+
system: 'Review for correctness.',
|
|
66
|
+
request: { body: { temperature: 0.4 } },
|
|
67
|
+
permissions: [
|
|
68
|
+
{ action: 'shell', resource: 'git push *', effect: 'ask' },
|
|
69
|
+
{ action: 'edit', resource: '*', effect: 'deny' },
|
|
70
|
+
],
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('rewrites a v1 agent file in place, at its own path, in v2 shape', () => {
|
|
75
|
+
const agentPath = write('.opencode/agent/reviewer.md', [
|
|
76
|
+
'---',
|
|
77
|
+
'description: Reviewer',
|
|
78
|
+
'temperature: 0.4',
|
|
79
|
+
'permission:',
|
|
80
|
+
' bash: ask',
|
|
81
|
+
'---',
|
|
82
|
+
'',
|
|
83
|
+
'Old prompt.',
|
|
84
|
+
].join('\n'));
|
|
85
|
+
|
|
86
|
+
const result = updateAgent('reviewer', { description: 'Careful reviewer' }, projectDir);
|
|
87
|
+
|
|
88
|
+
// Same file: OpenChamber never moves an entity between directories.
|
|
89
|
+
expect(result.path).toBe(agentPath);
|
|
90
|
+
expect(fs.existsSync(path.join(projectDir, '.opencode', 'agents', 'reviewer.md'))).toBe(false);
|
|
91
|
+
|
|
92
|
+
const parsed = parseMdFile(agentPath);
|
|
93
|
+
expect(parsed.frontmatter).toEqual({
|
|
94
|
+
description: 'Careful reviewer',
|
|
95
|
+
request: { body: { temperature: 0.4 } },
|
|
96
|
+
permissions: [{ action: 'shell', resource: '*', effect: 'ask' }],
|
|
97
|
+
});
|
|
98
|
+
expect(parsed.body).toBe('Old prompt.');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('writes the permission rule array when the client sends a v1 map', () => {
|
|
102
|
+
write('.opencode/agents/reviewer.md', '---\ndescription: Reviewer\n---\n\nPrompt.\n');
|
|
103
|
+
|
|
104
|
+
updateAgent('reviewer', { permission: { task: 'deny', write: 'allow' } }, projectDir);
|
|
105
|
+
|
|
106
|
+
const parsed = parseMdFile(path.join(projectDir, '.opencode', 'agents', 'reviewer.md'));
|
|
107
|
+
expect(parsed.frontmatter.permissions).toEqual([
|
|
108
|
+
{ action: 'subagent', resource: '*', effect: 'deny' },
|
|
109
|
+
{ action: 'edit', resource: '*', effect: 'allow' },
|
|
110
|
+
]);
|
|
111
|
+
expect(parsed.frontmatter.permission).toBeUndefined();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('creates a new agent in the v2 agents directory', () => {
|
|
115
|
+
createAgent('fresh', { description: 'Fresh', system: 'Do the thing.' }, projectDir, 'project');
|
|
116
|
+
|
|
117
|
+
const created = path.join(projectDir, '.opencode', 'agents', 'fresh.md');
|
|
118
|
+
expect(fs.existsSync(created)).toBe(true);
|
|
119
|
+
expect(parseMdFile(created)).toEqual({
|
|
120
|
+
frontmatter: { description: 'Fresh' },
|
|
121
|
+
body: 'Do the thing.',
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('moves a v1 JSON agent entry into the agents section of the same file', () => {
|
|
126
|
+
const configPath = write('opencode.json', JSON.stringify({
|
|
127
|
+
agent: {
|
|
128
|
+
reviewer: { prompt: 'Old', permission: { bash: 'ask' } },
|
|
129
|
+
other: { prompt: 'Untouched' },
|
|
130
|
+
},
|
|
131
|
+
}, null, 2));
|
|
132
|
+
|
|
133
|
+
updateAgent('reviewer', { system: 'New' }, projectDir);
|
|
134
|
+
|
|
135
|
+
const written = readJson(configPath);
|
|
136
|
+
expect(written.agent).toEqual({ other: { prompt: 'Untouched' } });
|
|
137
|
+
expect(written.agents.reviewer).toEqual({
|
|
138
|
+
system: 'New',
|
|
139
|
+
permissions: [{ action: 'shell', resource: '*', effect: 'ask' }],
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('clears v1 fields at the v2 location they moved to', () => {
|
|
144
|
+
const configPath = write('opencode.json', JSON.stringify({
|
|
145
|
+
agent: { build: { variant: 'fast', temperature: 0.3, top_p: 0.8, mode: 'subagent' } },
|
|
146
|
+
}, null, 2));
|
|
147
|
+
|
|
148
|
+
// The client still names the v1 fields, but on a v2 entity `variant` is the
|
|
149
|
+
// `#` suffix on `model` and `temperature`/`top_p` live in `request.body`.
|
|
150
|
+
updateAgent('build', { variant: null, temperature: null, top_p: null }, projectDir);
|
|
151
|
+
|
|
152
|
+
const written = readJson(configPath);
|
|
153
|
+
expect(written.agent).toBeUndefined();
|
|
154
|
+
expect(written.agents.build).toEqual({ mode: 'subagent' });
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('drops only the variant when a model is set', () => {
|
|
158
|
+
write('.opencode/agents/reviewer.md', '---\nmodel: anthropic/claude-sonnet-4-5#high\n---\n\nPrompt.\n');
|
|
159
|
+
|
|
160
|
+
updateAgent('reviewer', { variant: null }, projectDir);
|
|
161
|
+
|
|
162
|
+
const parsed = parseMdFile(path.join(projectDir, '.opencode', 'agents', 'reviewer.md'));
|
|
163
|
+
expect(parsed.frontmatter.model).toBe('anthropic/claude-sonnet-4-5');
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('reports global and agent rules in evaluation order', () => {
|
|
167
|
+
write('opencode.json', JSON.stringify({
|
|
168
|
+
permission: { edit: 'allow' },
|
|
169
|
+
tools: { websearch: false },
|
|
170
|
+
}, null, 2));
|
|
171
|
+
write('.opencode/agents/reviewer.md', [
|
|
172
|
+
'---',
|
|
173
|
+
'permissions:',
|
|
174
|
+
' - action: edit',
|
|
175
|
+
' resource: "*"',
|
|
176
|
+
' effect: deny',
|
|
177
|
+
'---',
|
|
178
|
+
'',
|
|
179
|
+
'Prompt.',
|
|
180
|
+
].join('\n'));
|
|
181
|
+
|
|
182
|
+
const result = getAgentPermissions('reviewer', projectDir);
|
|
183
|
+
expect(result.agent).toEqual([{ action: 'edit', resource: '*', effect: 'deny' }]);
|
|
184
|
+
expect(result.effective).toEqual([
|
|
185
|
+
{ action: 'websearch', resource: '*', effect: 'deny', source: 'global' },
|
|
186
|
+
{ action: 'edit', resource: '*', effect: 'allow', source: 'global' },
|
|
187
|
+
{ action: 'edit', resource: '*', effect: 'deny', source: 'agent' },
|
|
188
|
+
]);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('reads a v1 command file and rewrites it in place as subagent', () => {
|
|
192
|
+
const commandPath = write('.opencode/command/review.md', [
|
|
193
|
+
'---',
|
|
194
|
+
'description: Review',
|
|
195
|
+
'model: anthropic/claude-sonnet-4-5',
|
|
196
|
+
'variant: high',
|
|
197
|
+
'subtask: true',
|
|
198
|
+
'---',
|
|
199
|
+
'',
|
|
200
|
+
'Review the current changes.',
|
|
201
|
+
].join('\n'));
|
|
202
|
+
|
|
203
|
+
expect(getCommandConfig('review', projectDir).config).toEqual({
|
|
204
|
+
description: 'Review',
|
|
205
|
+
model: 'anthropic/claude-sonnet-4-5#high',
|
|
206
|
+
subagent: true,
|
|
207
|
+
template: 'Review the current changes.',
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const result = updateCommand('review', { description: 'Review changes' }, projectDir);
|
|
211
|
+
expect(result.path).toBe(commandPath);
|
|
212
|
+
expect(parseMdFile(commandPath).frontmatter).toEqual({
|
|
213
|
+
description: 'Review changes',
|
|
214
|
+
model: 'anthropic/claude-sonnet-4-5#high',
|
|
215
|
+
subagent: true,
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('rewrites a v1 theme color as the hex OpenCode migrates it to', () => {
|
|
220
|
+
const agentPath = write('.opencode/agent/tinted.md', [
|
|
221
|
+
'---',
|
|
222
|
+
'description: Tinted',
|
|
223
|
+
'color: primary',
|
|
224
|
+
'---',
|
|
225
|
+
'',
|
|
226
|
+
'Prompt.',
|
|
227
|
+
].join('\n'));
|
|
228
|
+
|
|
229
|
+
updateAgent('tinted', { description: 'Still tinted' }, projectDir);
|
|
230
|
+
|
|
231
|
+
// `primary` is not a v2 color; the native decoder would skip the whole file.
|
|
232
|
+
expect(parseMdFile(agentPath).frontmatter).toEqual({ description: 'Still tinted', color: '#aaaaaa' });
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('answers the project v1 override when the user file already holds the v2 spelling', () => {
|
|
236
|
+
const userConfigPath = path.join(process.env.XDG_CONFIG_HOME, 'opencode', 'opencode.json');
|
|
237
|
+
fs.mkdirSync(path.dirname(userConfigPath), { recursive: true });
|
|
238
|
+
fs.writeFileSync(userConfigPath, JSON.stringify({
|
|
239
|
+
mcp: { servers: { docs: { type: 'remote', url: 'https://global.example.com/mcp' } } },
|
|
240
|
+
}, null, 2), 'utf8');
|
|
241
|
+
const projectConfigPath = write('.opencode/opencode.json', JSON.stringify({
|
|
242
|
+
mcp: { docs: { type: 'remote', url: 'https://project.example.com/mcp', timeout: 5000 } },
|
|
243
|
+
}, null, 2));
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
expect(getMcpConfig('docs', projectDir)).toEqual(expect.objectContaining({
|
|
247
|
+
url: 'https://project.example.com/mcp',
|
|
248
|
+
scope: 'project',
|
|
249
|
+
legacy: true,
|
|
250
|
+
}));
|
|
251
|
+
expect(listMcpConfigs(projectDir).find((entry) => entry.name === 'docs').url).toBe('https://project.example.com/mcp');
|
|
252
|
+
|
|
253
|
+
updateMcpConfig('docs', { timeout: { catalog: 9000, execution: 9000 } }, projectDir);
|
|
254
|
+
|
|
255
|
+
expect(readJson(projectConfigPath).mcp).toEqual({
|
|
256
|
+
servers: { docs: { type: 'remote', url: 'https://project.example.com/mcp', timeout: { catalog: 9000, execution: 9000 } } },
|
|
257
|
+
});
|
|
258
|
+
expect(readJson(userConfigPath).mcp.servers.docs.url).toBe('https://global.example.com/mcp');
|
|
259
|
+
} finally {
|
|
260
|
+
fs.rmSync(userConfigPath, { force: true });
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('reads a v1 mcp entry and rewrites it under mcp.servers in the same file', () => {
|
|
265
|
+
const configPath = write('opencode.json', JSON.stringify({
|
|
266
|
+
mcp: {
|
|
267
|
+
playwright: { type: 'local', command: ['npx', '@playwright/mcp'], enabled: true, timeout: 30000 },
|
|
268
|
+
},
|
|
269
|
+
}, null, 2));
|
|
270
|
+
|
|
271
|
+
expect(getMcpConfig('playwright', projectDir)).toEqual(expect.objectContaining({
|
|
272
|
+
name: 'playwright',
|
|
273
|
+
type: 'local',
|
|
274
|
+
disabled: false,
|
|
275
|
+
timeout: { catalog: 30000, execution: 30000 },
|
|
276
|
+
legacy: true,
|
|
277
|
+
}));
|
|
278
|
+
|
|
279
|
+
updateMcpConfig('playwright', { disabled: true }, projectDir);
|
|
280
|
+
|
|
281
|
+
const written = readJson(configPath);
|
|
282
|
+
expect(written.mcp.playwright).toBeUndefined();
|
|
283
|
+
expect(written.mcp.servers.playwright).toEqual({
|
|
284
|
+
type: 'local',
|
|
285
|
+
command: ['npx', '@playwright/mcp'],
|
|
286
|
+
disabled: true,
|
|
287
|
+
timeout: { catalog: 30000, execution: 30000 },
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
});
|
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared response shapes for OpenCode config mutations.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* OpenCode 2 watches every config source it reads — agents, commands, skills,
|
|
5
|
+
* MCP servers, providers, instructions and the plugin list — and rebuilds them
|
|
6
|
+
* when a file changes. A mutation that lands on disk is therefore already live
|
|
7
|
+
* by the time the route answers, so config mutations report plain success.
|
|
8
|
+
*
|
|
9
|
+
* Only changes OpenChamber cannot hand to a running process still ask for a
|
|
10
|
+
* restart: the OpenCode binary, its port, and switching between managed and
|
|
11
|
+
* external OpenCode. Those keep `buildExternalManualRestartResponse` and the
|
|
12
|
+
* manual `POST /api/config/reload`.
|
|
7
13
|
*/
|
|
8
14
|
|
|
9
|
-
|
|
15
|
+
/**
|
|
16
|
+
* `details` carries the file the mutation landed in (`{ path, scope, source }`).
|
|
17
|
+
* OpenChamber never moves an entity between files, so the caller can show the
|
|
18
|
+
* user exactly which config file changed — including a v1 file that was
|
|
19
|
+
* rewritten in place in v2 shape.
|
|
20
|
+
*/
|
|
21
|
+
export function buildAppliedResponse(message, details) {
|
|
10
22
|
return {
|
|
11
23
|
success: true,
|
|
12
|
-
requiresReload: false,
|
|
13
|
-
requiresRestart: true,
|
|
14
|
-
restartDeferred: true,
|
|
15
24
|
message,
|
|
25
|
+
...(details && typeof details === 'object' ? details : {}),
|
|
16
26
|
};
|
|
17
27
|
}
|
|
18
28
|
|
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test';
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
buildAppliedResponse,
|
|
5
5
|
buildExternalManualRestartResponse,
|
|
6
6
|
} from './config-mutation-response.js';
|
|
7
7
|
|
|
8
8
|
describe('config mutation response helpers', () => {
|
|
9
|
-
test('
|
|
10
|
-
expect(
|
|
9
|
+
test('buildAppliedResponse reports plain success with no restart flags', () => {
|
|
10
|
+
expect(buildAppliedResponse('Agent saved.')).toEqual({
|
|
11
11
|
success: true,
|
|
12
|
-
|
|
13
|
-
requiresRestart: true,
|
|
14
|
-
restartDeferred: true,
|
|
15
|
-
message: 'Saved. Restart OpenCode to apply.',
|
|
12
|
+
message: 'Agent saved.',
|
|
16
13
|
});
|
|
17
14
|
});
|
|
18
15
|
|
|
@@ -38,7 +38,7 @@ describe('OpenCode global config paths', () => {
|
|
|
38
38
|
expect(AGENT_DIR).toBe(path.join(configDir, 'agents'));
|
|
39
39
|
expect(COMMAND_DIR).toBe(path.join(configDir, 'commands'));
|
|
40
40
|
expect(SKILL_DIR).toBe(path.join(configDir, 'skills'));
|
|
41
|
-
expect(CONFIG_FILE).toBe(path.join(configDir, '
|
|
41
|
+
expect(CONFIG_FILE).toBe(path.join(configDir, 'opencode.json'));
|
|
42
42
|
|
|
43
43
|
fs.rmSync(xdgConfigHome, { recursive: true, force: true });
|
|
44
44
|
});
|
|
@@ -70,7 +70,7 @@ describe('OpenCode global config paths', () => {
|
|
|
70
70
|
expect(fs.existsSync(path.join(configDir, 'commands', 'global-command.md'))).toBe(true);
|
|
71
71
|
expect(fs.existsSync(path.join(configDir, 'skills', 'global-skill', 'SKILL.md'))).toBe(true);
|
|
72
72
|
expect(fs.existsSync(path.join(configDir, 'snippet', 'global-snippet.md'))).toBe(true);
|
|
73
|
-
expect(JSON.parse(fs.readFileSync(path.join(configDir, '
|
|
73
|
+
expect(JSON.parse(fs.readFileSync(path.join(configDir, 'opencode.json'), 'utf8')).plugins).toEqual(['global-plugin']);
|
|
74
74
|
|
|
75
75
|
agents.createAgent('project-agent', { description: 'Project', prompt: 'Project prompt' }, projectDir, 'project');
|
|
76
76
|
commands.createCommand('project-command', { description: 'Project', template: 'Project template' }, projectDir, 'project');
|