@vantran-se/openchamber-web 2.1.1 → 2.2.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 +32 -24
- package/bin/cli.js +1 -3
- package/bin/cli.test.js +40 -16
- package/bin/lib/DOCUMENTATION.md +6 -5
- package/bin/lib/cli-args.js +6 -1
- package/bin/lib/cli-args.test.js +34 -0
- package/bin/lib/cli-lifecycle.js +0 -1
- package/bin/lib/cli-network.js +8 -2
- package/bin/lib/cli-process.js +1 -1
- package/bin/lib/cli-settings-accessors.test.js +21 -13
- package/bin/lib/cli-startup.js +17 -11
- package/bin/lib/cli-startup.test.js +92 -2
- package/bin/lib/commands-connect-url.js +8 -1
- package/bin/lib/commands-schedule.js +8 -2
- package/bin/lib/commands-serve.js +15 -21
- package/bin/lib/commands-update.js +12 -24
- package/bin/lib/commands-update.test.js +14 -46
- package/dist/assets/DiffView-Cv9MSXl1.js +4 -0
- package/dist/assets/FilePreviewCommentMenu-D4kLseHj.js +4 -0
- package/dist/assets/FilesView-BVoD8I2x.js +14 -0
- package/dist/assets/{GitView-DzuzhIPx.js → GitView-Bzb8p-ew.js} +4 -4
- package/dist/assets/GuestSurfacePane-Crdbvsxo.js +1 -0
- package/dist/assets/InProgressOperationBanner-CwMjNQgk.js +2 -0
- package/dist/assets/{JsonTreeView-B_EPRjzy.js → JsonTreeView-DRKHZfsn.js} +1 -1
- package/dist/assets/MarkdownImageGallery-DZtqT1Zn.js +1 -0
- package/dist/assets/MarkdownRendererImpl-CqG7HCNd.js +10 -0
- package/dist/assets/OnboardingScreen-DMIxwYK5.js +1 -0
- package/dist/assets/{PatchDiff-6yh1csza.js → PatchDiff-CUcCg1jk.js} +1 -1
- package/dist/assets/PierreDiffViewer-CnnR-dnD.js +228 -0
- package/dist/assets/PlanView-RCBadcY7.js +5 -0
- package/dist/assets/SettingsView-CjBjZFRO.js +30 -0
- package/dist/assets/SettingsWindow-DyM7QuhI.js +1 -0
- package/dist/assets/SubmoduleDiffSummary-BWIfgMED.js +1 -0
- package/dist/assets/ToolOutputDialog-Q0P3FuAi.js +15 -0
- package/dist/assets/ToolPartDiffPreview-p7qR9FpU.js +8 -0
- package/dist/assets/{VirtualizedFileDiff-BTJVRkyo.js → VirtualizedFileDiff-CFpjL8Ns.js} +3 -3
- package/dist/assets/{Virtualizer-xCEYz34p.js → Virtualizer-C0IsclZn.js} +1 -1
- package/dist/assets/WalkthroughView-DI31zD02.js +4 -0
- package/dist/assets/appThemeRegistry-B9gkFq9i.js +1 -0
- package/dist/assets/{computeVirtualFileMetrics-CmPe29DM.js → computeVirtualFileMetrics-C2WM6JC2.js} +2 -2
- package/dist/assets/de-YKfk5MmC.js +14 -0
- package/dist/assets/es-_gwT0kr-.js +20 -0
- package/dist/assets/fileTreeStatus-D438yi8M.js +1 -0
- package/dist/assets/fr-DqzvXmIm.js +18 -0
- package/dist/assets/index-BLaHKdtx.js +1 -0
- package/dist/assets/index-BkMXRrVQ.css +1 -0
- package/dist/assets/index-DxNa7xYB.js +65 -0
- package/dist/assets/initializeSharedPreferences-Bck5EAWP.js +1 -0
- package/dist/assets/{isDiffPlainText-CQmDCnEv.js → isDiffPlainText-Dgbw8VKk.js} +1 -1
- package/dist/assets/{isFilePlainText-CYBUzW4I.js → isFilePlainText-_ZS5UYRj.js} +1 -1
- package/dist/assets/ja-B-GFYhQE.js +20 -0
- package/dist/assets/ko-mzksfqDC.js +20 -0
- package/dist/assets/main-C5Xx8dn_.js +2 -0
- package/dist/assets/main-CfBSQM1H.js +990 -0
- package/dist/assets/markdownCore-BmWwW1ll.js +7 -0
- package/dist/assets/miniChat-mnKwy4vQ.js +2 -0
- package/dist/assets/mobile-DeEQd0wZ.js +2 -0
- package/dist/assets/nl-CWQr6NSq.js +20 -0
- package/dist/assets/{parseDiffFromFile-Cy0PNxun.js → parseDiffFromFile-BUgn-T9p.js} +2 -2
- package/dist/assets/patchFileDiff-Dt8LhgBh.js +13 -0
- package/dist/assets/pl-UmRcDUX2.js +20 -0
- package/dist/assets/pt-BR-BSLybvVF.js +20 -0
- package/dist/assets/relocateRestoredSession-B5E9xb3T.js +1 -0
- package/dist/assets/renderElectronMiniChatApp-C0a9A5_J.js +2 -0
- package/dist/assets/renderMobileApp-BNncg3Pk.js +25 -0
- package/dist/assets/runtimeEndpointReset-CZ5aafpl.js +62 -0
- package/dist/assets/{selectionMarkdown-TkYk5h9l.js → selectionMarkdown-CUN3wUld.js} +9 -9
- package/dist/assets/{sessionOwnership-Bs1xbRfB.js → sessionOwnership-BSuxCEhb.js} +1 -1
- package/dist/assets/shikiHighlight-DHDx-6eM.js +1 -0
- package/dist/assets/space-apply-DM_2pRW7.js +1 -0
- package/dist/assets/space-close-Bpomt8z0.js +1 -0
- package/dist/assets/{surface-B6n7wJtt.js → surface-C-jQaGnL.js} +1 -1
- package/dist/assets/{textMateThemeFromAppTheme-CDlKffWz.js → textMateThemeFromAppTheme-DlsTphbY.js} +1 -1
- package/dist/assets/tr-gaTrX_HK.js +11 -0
- package/dist/assets/uk-CnJF6ykl.js +20 -0
- package/dist/assets/{useAppFontEffects-DCqCVGlT.js → useAppFontEffects-DGlvOqP_.js} +194 -233
- package/dist/assets/useInlineCommentController-kMN4Wg_D.js +2 -0
- package/dist/assets/{useRootScrollLock-BowwS8kq.js → useRootScrollLock-DMPpvpNP.js} +2 -2
- package/dist/assets/{vendor-base-ui-DX96QhTP.js → vendor-base-ui-Det7U81h.js} +1 -1
- package/dist/assets/{vendor-codemirror-autocomplete-DW1FQz9_.js → vendor-codemirror-autocomplete-BSHQHbKu.js} +1 -1
- package/dist/assets/{vendor-codemirror-commands-DpmhalwN.js → vendor-codemirror-commands-C4G_dPWe.js} +1 -1
- package/dist/assets/{vendor-codemirror-lang-angular-BqExbN1M.js → vendor-codemirror-lang-angular-B6LbBGHB.js} +1 -1
- package/dist/assets/{vendor-codemirror-lang-cpp-DZA36HfS.js → vendor-codemirror-lang-cpp-BWWXBx7W.js} +1 -1
- package/dist/assets/{vendor-codemirror-lang-css-B07blnnZ.js → vendor-codemirror-lang-css-Cayfb29q.js} +1 -1
- package/dist/assets/{vendor-codemirror-lang-go-BM8K9D1-.js → vendor-codemirror-lang-go-BoKlCNKw.js} +1 -1
- package/dist/assets/{vendor-codemirror-lang-html-DaH9adfE.js → vendor-codemirror-lang-html-BQWerHsJ.js} +1 -1
- package/dist/assets/{vendor-codemirror-lang-java-CsdC0viz.js → vendor-codemirror-lang-java-DWX7Fq_K.js} +1 -1
- package/dist/assets/{vendor-codemirror-lang-javascript-CpGYXGuo.js → vendor-codemirror-lang-javascript-DbpEcZM0.js} +2 -2
- package/dist/assets/{vendor-codemirror-lang-jinja-BBeGzXw5.js → vendor-codemirror-lang-jinja-D1JIJRA7.js} +1 -1
- package/dist/assets/{vendor-codemirror-lang-json-58T70Hqs.js → vendor-codemirror-lang-json-BzgckzmB.js} +1 -1
- package/dist/assets/{vendor-codemirror-lang-less-BI-xCA59.js → vendor-codemirror-lang-less-B9EpDXFi.js} +1 -1
- package/dist/assets/{vendor-codemirror-lang-liquid-zEcMa-zy.js → vendor-codemirror-lang-liquid-9U7kRbSf.js} +1 -1
- package/dist/assets/{vendor-codemirror-lang-markdown-BLKnlLP4.js → vendor-codemirror-lang-markdown-D2TEBIvZ.js} +1 -1
- package/dist/assets/{vendor-codemirror-lang-php-9f35M83-.js → vendor-codemirror-lang-php-BT6sDVzE.js} +1 -1
- package/dist/assets/vendor-codemirror-lang-python-jAMtYiA9.js +3 -0
- package/dist/assets/{vendor-codemirror-lang-rust-MNDL_jZQ.js → vendor-codemirror-lang-rust-CVO56EiL.js} +1 -1
- package/dist/assets/{vendor-codemirror-lang-sass-p9epsSNm.js → vendor-codemirror-lang-sass-DZlOoUWu.js} +1 -1
- package/dist/assets/{vendor-codemirror-lang-sql-Bqzh0EXX.js → vendor-codemirror-lang-sql-fZ6t1DPw.js} +1 -1
- package/dist/assets/{vendor-codemirror-lang-vue-BnNUWV-p.js → vendor-codemirror-lang-vue-DxbqrLiE.js} +1 -1
- package/dist/assets/{vendor-codemirror-lang-wast-C2a-oLmk.js → vendor-codemirror-lang-wast-DyU1Gt-I.js} +1 -1
- package/dist/assets/vendor-codemirror-lang-xml-C9a_K5_p.js +1 -0
- package/dist/assets/{vendor-codemirror-lang-yaml-C0sjswB0.js → vendor-codemirror-lang-yaml-Cptq8l0e.js} +1 -1
- package/dist/assets/{vendor-codemirror-language-ByBn5zIW.js → vendor-codemirror-language-DufXoQvE.js} +2 -2
- package/dist/assets/vendor-codemirror-language-data-CtwSWKtX.js +2 -0
- package/dist/assets/{vendor-codemirror-search-ib-sHD5W.js → vendor-codemirror-search-CvXM_pZg.js} +1 -1
- package/dist/assets/{vendor-codemirror-state-G3UuR-be.js → vendor-codemirror-state-Dek9sMtw.js} +1 -1
- package/dist/assets/{vendor-codemirror-view-Bniqp7p5.js → vendor-codemirror-view-1e0FQ6Wn.js} +1 -1
- package/dist/assets/vendor-framer-motion-DxDVhOLc.js +9 -0
- package/dist/assets/vendor-katex-CuQvj9-L.js +257 -0
- package/dist/assets/vendor-katex-DdZNUVNG.css +1 -0
- package/dist/assets/{vendor-motion-dom-DcCgeG69.js → vendor-motion-dom-HXPOMxb7.js} +1 -1
- package/dist/assets/{vendor-opencode-client-DHzhmLwV.js → vendor-opencode-client-BmFcUkbQ.js} +1 -1
- package/dist/assets/{vendor-replit-codemirror-vim-DkzqxN2H.js → vendor-replit-codemirror-vim-BlJRn-cW.js} +2 -2
- package/dist/assets/{vendor-tanstack-react-virtual-H_JmXOFV.js → vendor-tanstack-react-virtual-CvuLsk7D.js} +1 -1
- package/dist/assets/{vendor-tanstack-virtual-core-Dxc8hSTB.js → vendor-tanstack-virtual-core-DPaNcJcT.js} +1 -1
- package/dist/assets/zh-CN-t2vigSB1.js +20 -0
- package/dist/assets/zh-TW-jAgqBokB.js +20 -0
- package/dist/index.html +6 -6
- package/dist/mini-chat.html +6 -6
- package/dist/mobile.html +6 -6
- package/package.json +8 -5
- package/server/index.js +80 -36
- package/server/lib/agent-memory/actions.js +1 -6
- package/server/lib/agent-memory/routes.js +1 -2
- package/server/lib/agent-memory/runtime.js +1 -8
- package/server/lib/agent-tool/DOCUMENTATION.md +7 -3
- package/server/lib/agent-tool/runtime.js +3 -6
- package/server/lib/agent-tool/runtime.test.js +0 -2
- package/server/lib/browser-control/DOCUMENTATION.md +6 -0
- package/server/lib/changelog/update-notes.js +1 -1
- package/server/lib/context-obligatory/runtime.js +1 -2
- package/server/lib/dev-tunnel/DOCUMENTATION.md +8 -0
- package/server/lib/dev-tunnel/client.js +21 -10
- package/server/lib/dev-tunnel/tunnel.test.js +34 -0
- package/server/lib/dispatch-results/runtime.js +1 -1
- package/server/lib/enterprise-mode.d.ts +1 -0
- package/server/lib/enterprise-mode.js +13 -2
- package/server/lib/enterprise-mode.test.js +21 -0
- package/server/lib/event-stream/DOCUMENTATION.md +2 -2
- package/server/lib/event-stream/index.js +1 -3
- package/server/lib/event-stream/translate-v2.js +1 -9
- package/server/lib/event-stream/translate-v2.test.js +0 -11
- package/server/lib/fs/DOCUMENTATION.md +5 -2
- package/server/lib/fs/routes.js +62 -15
- package/server/lib/fs/routes.test.js +278 -48
- package/server/lib/fs/workspace-file-names.js +104 -0
- package/server/lib/fs/workspace-file-names.test.js +62 -0
- package/server/lib/git/DOCUMENTATION.md +17 -5
- package/server/lib/git/contributor-provenance-storage.js +9 -6
- package/server/lib/git/contributor-provenance-storage.test.js +29 -0
- package/server/lib/git/credential-broker.js +1 -1
- package/server/lib/git/credential-resolver.js +1 -4
- package/server/lib/git/credentials.js +1 -0
- package/server/lib/git/discovery-endpoint.js +1 -1
- package/server/lib/git/identity-provisioning.js +2 -1
- package/server/lib/git/identity-storage.js +3 -4
- package/server/lib/git/lfs-discovery.js +14 -14
- package/server/lib/git/lfs-discovery.test.js +16 -6
- package/server/lib/git/network-operation-plan.js +50 -62
- package/server/lib/git/network-operation-plan.test.js +103 -1
- package/server/lib/git/network-operation-registry.js +4 -6
- package/server/lib/git/network-operation-storage.js +4 -6
- package/server/lib/git/network-operations.js +12 -4
- package/server/lib/git/network-operations.test.js +19 -6
- package/server/lib/git/redaction.js +2 -1
- package/server/lib/git/repository-credential-runtime.js +5 -3
- package/server/lib/git/repository-credential-runtime.test.js +22 -0
- package/server/lib/git/routes.js +31 -137
- package/server/lib/git/routes.test.js +2 -2
- package/server/lib/git/service.js +143 -27
- package/server/lib/git/service.test.js +215 -1
- package/server/lib/git/ssh-credential-storage.js +1 -4
- package/server/lib/git/submodule-discovery.js +1 -1
- package/server/lib/git/worktree-bootstrap-storage.js +3 -4
- package/server/lib/git/worktree-bootstrap-storage.test.js +15 -0
- package/server/lib/git/worktree-creation.js +194 -0
- package/server/lib/git/worktree-creation.test.js +155 -0
- package/server/lib/github/DOCUMENTATION.md +11 -6
- package/server/lib/github/auth.js +2 -4
- package/server/lib/github/device-flow.js +2 -2
- package/server/lib/github/gh-cli-credential.js +1 -0
- package/server/lib/github/octokit.js +31 -0
- package/server/lib/github/pr-status.js +35 -18
- package/server/lib/github/pr-status.test.js +271 -3
- package/server/lib/github/reference-search.js +102 -13
- package/server/lib/github/reference-search.test.js +48 -7
- package/server/lib/github/routes.auth.test.js +18 -0
- package/server/lib/github/routes.js +360 -6
- package/server/lib/github/routes.mutations.test.js +239 -0
- package/server/lib/github/routes.test.js +134 -2
- package/server/lib/gitlab/DOCUMENTATION.md +5 -1
- package/server/lib/gitlab/auth-storage.test.js +1 -0
- package/server/lib/gitlab/mappers.js +30 -1
- package/server/lib/gitlab/resources.js +230 -6
- package/server/lib/gitlab/resources.test.js +179 -0
- package/server/lib/gitlab/routes.js +200 -6
- package/server/lib/gitlab/routes.test.js +186 -2
- package/server/lib/gitlab/validation.js +1 -5
- package/server/lib/guests/DOCUMENTATION.md +1 -1
- package/server/lib/guests/oauth.js +2 -2
- package/server/lib/guests/service.js +1 -1
- package/server/lib/guests/sockets.js +1 -1
- package/server/lib/injected-env.d.ts +8 -0
- package/server/lib/injected-env.js +50 -0
- package/server/lib/injected-env.test.js +56 -0
- package/server/lib/linear/DOCUMENTATION.md +5 -6
- package/server/lib/linear/issues.js +62 -4
- package/server/lib/linear/issues.test.js +68 -0
- package/server/lib/linear/oauth.js +5 -5
- package/server/lib/linear/parse.js +2 -9
- package/server/lib/message-queue/DOCUMENTATION.md +23 -1
- package/server/lib/message-queue/runtime.js +85 -12
- package/server/lib/message-queue/runtime.test.js +172 -2
- package/server/lib/notifications/apns-runtime.js +5 -6
- package/server/lib/notifications/apns-runtime.test.js +20 -4
- package/server/lib/openchamber-control/DOCUMENTATION.md +19 -1
- package/server/lib/openchamber-control/actions.js +2 -1
- package/server/lib/openchamber-control/service.js +119 -20
- package/server/lib/openchamber-control/service.test.js +166 -0
- package/server/lib/openchamber-sessions/archive-store.js +1 -6
- package/server/lib/openchamber-sessions/routes.js +47 -12
- package/server/lib/openchamber-sessions/routes.test.js +65 -2
- package/server/lib/openchamber-sessions/session-metadata-store.js +1 -6
- package/server/lib/opencode/DOCUMENTATION.md +22 -8
- package/server/lib/opencode/agents.js +62 -0
- package/server/lib/opencode/auth-state-runtime.js +3 -1
- package/server/lib/opencode/auth.d.ts +1 -1
- package/server/lib/opencode/auth.js +8 -0
- package/server/lib/opencode/auth.test.js +31 -0
- package/server/lib/opencode/config-entity-routes.js +16 -0
- package/server/lib/opencode/config-entity-v2.test.js +16 -1
- package/server/lib/opencode/config-paths.test.js +2 -2
- package/server/lib/opencode/config-v2.js +2 -4
- package/server/lib/opencode/feature-routes-runtime.js +89 -49
- package/server/lib/opencode/lifecycle.js +41 -21
- package/server/lib/opencode/lifecycle.test.js +50 -2
- package/server/lib/opencode/model-discovery.js +1 -1
- package/server/lib/opencode/model-discovery.test.js +17 -0
- package/server/lib/opencode/models-metadata.js +0 -2
- package/server/lib/opencode/openchamber-routes.test.js +3 -3
- package/server/lib/opencode/plugins.js +6 -9
- package/server/lib/opencode/provider-source-route.test.js +101 -3
- package/server/lib/opencode/providers-disabled.test.js +59 -0
- package/server/lib/opencode/providers.js +34 -0
- package/server/lib/opencode/proxy.js +11 -3
- package/server/lib/opencode/routes.js +40 -2
- package/server/lib/opencode/settings-helpers.js +27 -1
- package/server/lib/opencode/settings-helpers.test.js +19 -7
- package/server/lib/opencode/settings-registry.json +27 -16
- package/server/lib/opencode/settings-runtime.test.js +18 -0
- package/server/lib/opencode/shared-service-runtime.js +23 -104
- package/server/lib/opencode/shared.js +77 -46
- package/server/lib/opencode/shared.test.js +12 -12
- package/server/lib/opencode/shutdown-runtime.test.js +2 -3
- package/server/lib/opencode/skill-routes.js +123 -38
- package/server/lib/opencode/skill-routes.test.js +201 -0
- package/server/lib/opencode/skills.js +81 -34
- package/server/lib/opencode/skills.test.js +176 -21
- package/server/lib/opencode/startup-pipeline-runtime.js +6 -3
- package/server/lib/opencode/startup-pipeline-runtime.test.js +2 -1
- package/server/lib/opencode/watcher.test.js +0 -54
- package/server/lib/outbound-proxy.js +52 -0
- package/server/lib/outbound-proxy.test.js +55 -0
- package/server/lib/package-manager.js +23 -17
- package/server/lib/package-manager.test.js +5 -18
- package/server/lib/permission-auto-accept/DOCUMENTATION.md +1 -1
- package/server/lib/permission-auto-accept/runtime.js +19 -47
- package/server/lib/permission-auto-accept/runtime.test.js +2 -2
- package/server/lib/project-context/DOCUMENTATION.md +7 -1
- package/server/lib/project-context/routes.js +1 -2
- package/server/lib/project-context/runtime.js +12 -14
- package/server/lib/project-context/runtime.test.js +78 -2
- package/server/lib/projects/project-config.js +30 -15
- package/server/lib/projects/project-config.test.js +30 -0
- package/server/lib/projects/project-setup.js +1 -2
- package/server/lib/projects/routes.js +1 -2
- package/server/lib/quota/DOCUMENTATION.md +35 -7
- package/server/lib/quota/index.js +1 -0
- package/server/lib/quota/providers/claude/index.js +1 -1
- package/server/lib/quota/providers/cline-pass.js +1 -1
- package/server/lib/quota/providers/exe-dev.js +1 -1
- package/server/lib/quota/providers/google/index.js +1 -1
- package/server/lib/quota/providers/hyper.js +1 -1
- package/server/lib/quota/providers/kimi.js +116 -36
- package/server/lib/quota/providers/kimi.test.js +128 -3
- package/server/lib/quota/providers/opencode-go.js +144 -21
- package/server/lib/quota/providers/opencode-go.test.js +229 -6
- package/server/lib/quota/providers/openrouter.js +1 -1
- package/server/lib/quota/providers/zenmux.js +1 -1
- package/server/lib/quota/utils/auth.js +1 -0
- package/server/lib/quota/utils/transformers.js +2 -6
- package/server/lib/relay/DOCUMENTATION.md +18 -3
- package/server/lib/relay/e2ee.js +7 -7
- package/server/lib/relay/host-client.js +84 -13
- package/server/lib/relay/host-client.test.js +201 -1
- package/server/lib/relay/identity.js +6 -29
- package/server/lib/relay/identity.test.js +18 -28
- package/server/lib/relay/key-store.js +141 -0
- package/server/lib/relay/key-store.test.js +138 -0
- package/server/lib/relay/service.js +8 -8
- package/server/lib/relay/service.test.js +10 -5
- package/server/lib/relay/signing-key.js +13 -36
- package/server/lib/relay/tunnel-codec.js +10 -10
- package/server/lib/route-module-exports.test.js +22 -0
- package/server/lib/routing/DOCUMENTATION.md +23 -3
- package/server/lib/routing/cache.js +87 -0
- package/server/lib/routing/defaults.js +11 -0
- package/server/lib/routing/history.js +12 -4
- package/server/lib/routing/runtime.js +39 -3
- package/server/lib/routing/runtime.test.js +81 -2
- package/server/lib/routing/store.js +12 -1
- package/server/lib/routing/store.test.js +13 -0
- package/server/lib/scheduled-tasks/DOCUMENTATION.md +59 -1
- package/server/lib/scheduled-tasks/existing-session.js +119 -0
- package/server/lib/scheduled-tasks/existing-session.test.js +350 -0
- package/server/lib/scheduled-tasks/loops.js +1 -8
- package/server/lib/scheduled-tasks/routes.js +8 -8
- package/server/lib/scheduled-tasks/runtime.js +103 -13
- package/server/lib/scheduled-tasks/runtime.test.js +48 -0
- package/server/lib/scheduled-tasks/service.js +33 -11
- package/server/lib/scheduled-tasks/service.test.js +128 -2
- package/server/lib/scheduled-tasks/session-defaults.js +27 -0
- package/server/lib/scheduled-tasks/session-defaults.test.js +19 -0
- package/server/lib/session-assist/context.js +1 -0
- package/server/lib/session-folders/routes.js +2 -2
- package/server/lib/session-goal/objectives.js +1 -1
- package/server/lib/session-knowledge/routes.js +2 -2
- package/server/lib/session-knowledge/runtime.js +1 -2
- package/server/lib/session-work/DOCUMENTATION.md +39 -14
- package/server/lib/session-work/questions.js +31 -2
- package/server/lib/session-work/runtime.js +22 -13
- package/server/lib/session-work/runtime.test.js +43 -26
- package/server/lib/session-work/state.js +10 -4
- package/server/lib/session-work/state.test.js +8 -1
- package/server/lib/shared/DOCUMENTATION.md +89 -0
- package/server/lib/shared/guards.js +53 -0
- package/server/lib/shared/guards.test.js +112 -0
- package/server/lib/skills-catalog/DOCUMENTATION.md +10 -8
- package/server/lib/skills-catalog/cache.js +3 -2
- package/server/lib/skills-catalog/curated-sources.js +5 -1
- package/server/lib/skills-catalog/git.js +107 -20
- package/server/lib/skills-catalog/git.test.js +78 -0
- package/server/lib/skills-catalog/install.js +21 -12
- package/server/lib/skills-catalog/scan.js +25 -15
- package/server/lib/skills-catalog/source.js +26 -1
- package/server/lib/skills-catalog/source.test.js +27 -0
- package/server/lib/source-control/DOCUMENTATION.md +2 -1
- package/server/lib/source-control/audit-storage.js +7 -6
- package/server/lib/source-control/audit-storage.test.js +11 -0
- package/server/lib/source-control/binding-service.js +2 -5
- package/server/lib/source-control/change-request-worktree.js +90 -0
- package/server/lib/source-control/change-request-worktree.test.js +121 -0
- package/server/lib/source-control/mutation-executor.js +1 -1
- package/server/lib/source-control/mutation-storage.js +11 -5
- package/server/lib/source-control/provider-instance.js +1 -2
- package/server/lib/source-control/repository-identity.js +1 -1
- package/server/lib/source-control/routes.js +14 -2
- package/server/lib/spaces/DOCUMENTATION.md +42 -17
- package/server/lib/spaces/command-stream.test.js +15 -0
- package/server/lib/spaces/dispatcher.js +7 -0
- package/server/lib/spaces/dispatcher.test.js +17 -0
- package/server/lib/spaces/hardening.js +2 -2
- package/server/lib/spaces/hardening.test.js +1 -1
- package/server/lib/spaces/host.js +2 -0
- package/server/lib/spaces/journey.js +66 -5
- package/server/lib/spaces/journey.test.js +95 -4
- package/server/lib/spaces/layout.js +1 -1
- package/server/lib/spaces/places/docker-disk.js +38 -14
- package/server/lib/spaces/places/docker-disk.test.js +80 -1
- package/server/lib/spaces/places/docker.js +35 -11
- package/server/lib/spaces/places/fake-docker.js +22 -3
- package/server/lib/spaces/places/memory-place.js +3 -0
- package/server/lib/spaces/places/packed.docker.live.test.js +1 -1
- package/server/lib/spaces/routes.js +5 -0
- package/server/lib/spaces/routes.test.js +4 -2
- package/server/lib/spaces/run-command.js +9 -2
- package/server/lib/spaces/tools.js +4 -5
- package/server/lib/spaces/tools.test.js +2 -2
- package/server/lib/terminal/runtime.js +18 -6
- package/server/lib/terminal/runtime.test.js +66 -0
- package/server/lib/tts/index.js +3 -0
- package/server/lib/tts/service.js +4 -2
- package/server/lib/walkthrough/DOCUMENTATION.md +3 -1
- package/server/lib/walkthrough/index.js +2 -3
- package/server/lib/walkthrough/pr-source.test.js +22 -0
- package/server/lib/walkthrough/sources.js +6 -1
- package/server/opencode-proxy.test.js +127 -0
- package/bin/lib/commands-startup.test.js +0 -15
- package/dist/assets/CodeMirrorCommentWidgets-CVDSjcrP.js +0 -1
- package/dist/assets/DiffView-Ct7vL3fD.js +0 -4
- package/dist/assets/FilesView-DT0uEYgT.js +0 -17
- package/dist/assets/GuestSurfacePane-DVvk1VPu.js +0 -1
- package/dist/assets/LinearIssuesView-C10MwA9h.js +0 -1
- package/dist/assets/MarkdownImageGallery-BbN_K_3d.js +0 -1
- package/dist/assets/MarkdownRendererImpl-BLt2Tkk9.js +0 -10
- package/dist/assets/OnboardingScreen-Bgp5A3WB.js +0 -1
- package/dist/assets/PierreDiffViewer-CWJLj-S-.js +0 -210
- package/dist/assets/PlanView-DsRWnsOI.js +0 -5
- package/dist/assets/PublishDialog-CCeKloRu.js +0 -2
- package/dist/assets/SettingsView-BuPPsNPp.js +0 -30
- package/dist/assets/SettingsWindow-CM5fL8Ot.js +0 -1
- package/dist/assets/SubmoduleDiffSummary-CfsMcfiQ.js +0 -1
- package/dist/assets/ToolOutputDialog-mZrfSofz.js +0 -15
- package/dist/assets/ToolPartDiffPreview-BUGCRYEL.js +0 -8
- package/dist/assets/WalkthroughView-ClBo7PPd.js +0 -4
- package/dist/assets/appThemeRegistry-CxzMxR-H.js +0 -1
- package/dist/assets/de-CBla4hjd.js +0 -14
- package/dist/assets/es-BmnZtxTD.js +0 -20
- package/dist/assets/fileTreeStatus-BSHFIGEg.js +0 -1
- package/dist/assets/fr-OVKIBUZO.js +0 -18
- package/dist/assets/index-CKCFKmjF.js +0 -1
- package/dist/assets/index-Dbx4V4GN.css +0 -1
- package/dist/assets/index-vQ1Pg1cr.js +0 -61
- package/dist/assets/ja-6v-yr-gE.js +0 -20
- package/dist/assets/ko-I_ZLvgQ9.js +0 -20
- package/dist/assets/main-CmO4dRtR.js +0 -2
- package/dist/assets/main-b9vqhlYM.js +0 -999
- package/dist/assets/markdownCore-BPEZRQfP.js +0 -7
- package/dist/assets/miniChat-B5Oi7B-6.js +0 -2
- package/dist/assets/mobile-CVtShif9.js +0 -2
- package/dist/assets/nl-DcvIJf46.js +0 -20
- package/dist/assets/patchFileDiff-CW3wCH1O.js +0 -13
- package/dist/assets/pl-JxCUl0sd.js +0 -20
- package/dist/assets/pt-BR-L89mjja2.js +0 -20
- package/dist/assets/relocateRestoredSession-DYh_9oBa.js +0 -1
- package/dist/assets/renderElectronMiniChatApp-BD-1JHSg.js +0 -2
- package/dist/assets/renderMobileApp-7s5InXt1.js +0 -25
- package/dist/assets/runtimeEndpointReset-DqLJC6L1.js +0 -53
- package/dist/assets/shikiHighlight-CXgZnvHh.js +0 -1
- package/dist/assets/space-apply-1trGVIl8.js +0 -1
- package/dist/assets/space-close-y7j-NuBv.js +0 -1
- package/dist/assets/tr-ka5J1flt.js +0 -11
- package/dist/assets/uk-PyGe6Q_u.js +0 -20
- package/dist/assets/useInlineCommentController-CrjSGCpi.js +0 -2
- package/dist/assets/vendor-codemirror-lang-python-B7ClrMIT.js +0 -3
- package/dist/assets/vendor-codemirror-lang-xml-C8tGnS64.js +0 -1
- package/dist/assets/vendor-codemirror-language-data-q2bKO1EM.js +0 -2
- package/dist/assets/vendor-framer-motion-Bq3iTZ6r.js +0 -1
- package/dist/assets/vendor-katex-CHD9BdDO.js +0 -257
- package/dist/assets/vendor-katex-Cp579UoJ.css +0 -1
- package/dist/assets/zh-CN-B0P2tybJ.js +0 -20
- package/dist/assets/zh-TW-Bs9C5cN8.js +0 -20
|
@@ -36,6 +36,37 @@ describe('projectCredentialEntries', () => {
|
|
|
36
36
|
custom: { type: 'api', key: 'k', metadata: { region: 'eu' } },
|
|
37
37
|
});
|
|
38
38
|
});
|
|
39
|
+
|
|
40
|
+
it('keeps only the Console server and organization from OpenCode Console metadata', () => {
|
|
41
|
+
const projected = projectCredentialEntries([
|
|
42
|
+
entry('opencode', true, {
|
|
43
|
+
type: 'oauth',
|
|
44
|
+
methodID: 'console',
|
|
45
|
+
access: 'console-access',
|
|
46
|
+
refresh: 'console-refresh',
|
|
47
|
+
expires: 42,
|
|
48
|
+
metadata: {
|
|
49
|
+
server: 'https://opencode.ai/console',
|
|
50
|
+
orgID: 'org_TESTORG123',
|
|
51
|
+
accountID: 'acc_console',
|
|
52
|
+
email: 'person@example.com',
|
|
53
|
+
orgName: 'Example',
|
|
54
|
+
},
|
|
55
|
+
}),
|
|
56
|
+
]);
|
|
57
|
+
|
|
58
|
+
expect(projected).toEqual({
|
|
59
|
+
opencode: {
|
|
60
|
+
type: 'oauth',
|
|
61
|
+
access: 'console-access',
|
|
62
|
+
refresh: 'console-refresh',
|
|
63
|
+
expires: 42,
|
|
64
|
+
accountId: 'acc_console',
|
|
65
|
+
server: 'https://opencode.ai/console',
|
|
66
|
+
orgID: 'org_TESTORG123',
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
});
|
|
39
70
|
});
|
|
40
71
|
|
|
41
72
|
describe('projectEnvironmentKeys', () => {
|
|
@@ -19,6 +19,7 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
|
|
|
19
19
|
getAgentSources,
|
|
20
20
|
getAgentConfig,
|
|
21
21
|
getAgentPermissions,
|
|
22
|
+
listDisabledAgents,
|
|
22
23
|
createAgent,
|
|
23
24
|
updateAgent,
|
|
24
25
|
deleteAgent,
|
|
@@ -58,6 +59,21 @@ export const registerConfigEntityRoutes = (app, dependencies) => {
|
|
|
58
59
|
return next();
|
|
59
60
|
});
|
|
60
61
|
|
|
62
|
+
// Its own path: under /api/config/agents/ it would shadow an agent named
|
|
63
|
+
// "disabled".
|
|
64
|
+
app.get('/api/config/disabled-agents', async (req, res) => {
|
|
65
|
+
try {
|
|
66
|
+
const { directory, error } = await resolveProjectDirectory(req);
|
|
67
|
+
if (!directory) {
|
|
68
|
+
return res.status(400).json({ error });
|
|
69
|
+
}
|
|
70
|
+
res.json({ agents: listDisabledAgents(directory) });
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error('Failed to list disabled agents:', error);
|
|
73
|
+
res.status(500).json({ error: 'Failed to list disabled agents' });
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
61
77
|
app.get('/api/config/agents/:name', async (req, res) => {
|
|
62
78
|
try {
|
|
63
79
|
const agentName = req.params.name;
|
|
@@ -11,7 +11,7 @@ process.env.XDG_CONFIG_HOME = path.join(root, 'xdg');
|
|
|
11
11
|
delete process.env.OPENCODE_CONFIG;
|
|
12
12
|
|
|
13
13
|
const { parseMdFile } = await import('./shared.js');
|
|
14
|
-
const { getAgentConfig, getAgentPermissions, updateAgent, createAgent } = await import('./agents.js');
|
|
14
|
+
const { getAgentConfig, getAgentPermissions, updateAgent, createAgent, listDisabledAgents } = await import('./agents.js');
|
|
15
15
|
const { getCommandConfig, updateCommand } = await import('./commands.js');
|
|
16
16
|
const { getMcpConfig, listMcpConfigs, updateMcpConfig } = await import('./mcp.js');
|
|
17
17
|
|
|
@@ -314,4 +314,19 @@ describe('entity modules speak OpenCode 2 shapes', () => {
|
|
|
314
314
|
timeout: { catalog: 30000, execution: 30000 },
|
|
315
315
|
});
|
|
316
316
|
});
|
|
317
|
+
|
|
318
|
+
it('lists agents switched off in files and config sections, and forgets them once enabled', () => {
|
|
319
|
+
write('.opencode/agents/incident.md', ['---', 'description: Incident response', 'disabled: true', '---', '', 'Handle incidents.'].join('\n'));
|
|
320
|
+
write('.opencode/agents/ops/oncall.md', ['---', 'disable: true', '---', '', 'On call.'].join('\n'));
|
|
321
|
+
write('.opencode/agents/reviewer.md', ['---', 'description: Reviewer', '---', '', 'Review.'].join('\n'));
|
|
322
|
+
write('opencode.json', JSON.stringify({ agents: { plan: { disabled: true }, build: { color: '#112233' } } }));
|
|
323
|
+
|
|
324
|
+
expect(listDisabledAgents(projectDir).map((agent) => agent.name)).toEqual(['incident', 'ops/oncall', 'plan']);
|
|
325
|
+
expect(listDisabledAgents(projectDir)[0]).toMatchObject({ name: 'incident', scope: 'project', description: 'Incident response' });
|
|
326
|
+
|
|
327
|
+
updateAgent('incident', { disabled: null }, projectDir);
|
|
328
|
+
updateAgent('plan', { disabled: null }, projectDir);
|
|
329
|
+
expect(listDisabledAgents(projectDir).map((agent) => agent.name)).toEqual(['ops/oncall']);
|
|
330
|
+
expect(getAgentConfig('incident', projectDir).config.description).toBe('Incident response');
|
|
331
|
+
});
|
|
317
332
|
});
|
|
@@ -61,7 +61,7 @@ describe('OpenCode global config paths', () => {
|
|
|
61
61
|
const [, agents, commands, skills, snippets, plugins] = await loadOpenCodeModules();
|
|
62
62
|
agents.createAgent('global-agent', { description: 'Global', prompt: 'Global prompt' }, projectDir, 'user');
|
|
63
63
|
commands.createCommand('global-command', { description: 'Global', template: 'Global template' }, projectDir, 'user');
|
|
64
|
-
skills.createSkill('global-skill', { description: 'Global', instructions: 'Global instructions' }, projectDir, 'user');
|
|
64
|
+
await skills.createSkill('global-skill', { description: 'Global', instructions: 'Global instructions' }, projectDir, 'user');
|
|
65
65
|
snippets.createSnippet('global-snippet', { content: 'Global snippet' }, projectDir, 'global');
|
|
66
66
|
plugins.createPluginEntry({ spec: 'global-plugin', scope: 'user' }, projectDir);
|
|
67
67
|
|
|
@@ -74,7 +74,7 @@ describe('OpenCode global config paths', () => {
|
|
|
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');
|
|
77
|
-
skills.createSkill('project-skill', { description: 'Project', instructions: 'Project instructions' }, projectDir, 'project');
|
|
77
|
+
await skills.createSkill('project-skill', { description: 'Project', instructions: 'Project instructions' }, projectDir, 'project');
|
|
78
78
|
|
|
79
79
|
expect(fs.existsSync(path.join(projectDir, '.opencode', 'agents', 'project-agent.md'))).toBe(true);
|
|
80
80
|
expect(fs.existsSync(path.join(projectDir, '.opencode', 'commands', 'project-command.md'))).toBe(true);
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* Reference: OpenCode `packages/core/src/config/normalize.ts`,
|
|
12
12
|
* `packages/core/src/v1/config/migrate.ts`, and `services/www` migrate-v1 docs.
|
|
13
13
|
*/
|
|
14
|
+
import { isRecord } from '../shared/guards.js';
|
|
14
15
|
|
|
15
16
|
// ============== SECTIONS ==============
|
|
16
17
|
|
|
@@ -18,16 +19,13 @@
|
|
|
18
19
|
* Config section keys per entity. `v2` is what OpenChamber writes; `v1` is the
|
|
19
20
|
* legacy key OpenCode still decodes and OpenChamber still reads.
|
|
20
21
|
*/
|
|
22
|
+
|
|
21
23
|
const SECTIONS = {
|
|
22
24
|
agents: { v2: 'agents', v1: 'agent' },
|
|
23
25
|
commands: { v2: 'commands', v1: 'command' },
|
|
24
26
|
providers: { v2: 'providers', v1: 'provider' },
|
|
25
27
|
};
|
|
26
28
|
|
|
27
|
-
function isRecord(value) {
|
|
28
|
-
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
29
|
function sectionKeys(kind) {
|
|
32
30
|
const section = SECTIONS[kind];
|
|
33
31
|
if (!section) throw new Error(`Unknown config section: ${kind}`);
|
|
@@ -39,7 +39,7 @@ import { getNpmInfo, clearCache as clearNpmCache } from './npm-registry.js';
|
|
|
39
39
|
import { parseNpmSpec, parsePathSpec, isExactSemver } from './plugin-spec.js';
|
|
40
40
|
import { registerOpenCodeRoutes } from './routes.js';
|
|
41
41
|
import { getProviderSources, removeProviderConfig, upsertProviderConfig } from './providers.js';
|
|
42
|
-
import { getAgentSources, getAgentConfig, getAgentPermissions, createAgent, updateAgent, deleteAgent } from './agents.js';
|
|
42
|
+
import { getAgentSources, getAgentConfig, getAgentPermissions, createAgent, updateAgent, deleteAgent, listDisabledAgents } from './agents.js';
|
|
43
43
|
import { getCommandSources, getCommandConfig, createCommand, updateCommand, deleteCommand } from './commands.js';
|
|
44
44
|
import { listMcpConfigs, getMcpConfig, createMcpConfig, updateMcpConfig, deleteMcpConfig } from './mcp.js';
|
|
45
45
|
import { listSnippets, getSnippet, createSnippet, updateSnippet, deleteSnippet, expandSnippets } from './snippets.js';
|
|
@@ -66,6 +66,7 @@ import { installSkillsFromRepository } from '../skills-catalog/install.js';
|
|
|
66
66
|
import { fetchGitHubRepoMetas } from '../skills-catalog/github-meta.js';
|
|
67
67
|
import crypto from 'node:crypto';
|
|
68
68
|
import { getGitHubAuthByAccountId } from '../github/auth.js';
|
|
69
|
+
import { getGitHubCliCredential } from '../github/octokit.js';
|
|
69
70
|
import { createSourceControlAuthStore } from '../gitlab/auth-storage.js';
|
|
70
71
|
import { refreshGitLabAccessToken, resolveGitLabClientId } from '../gitlab/device-flow.js';
|
|
71
72
|
import { createGitCredentialResolver, createHttpsCredentialReference, parseGitCredentialReference } from '../git/credential-resolver.js';
|
|
@@ -110,6 +111,7 @@ export const createFeatureRoutesRuntime = (dependencies) => {
|
|
|
110
111
|
let walkthroughBindingService = null;
|
|
111
112
|
let gitRepositoryCredentialRuntime = null;
|
|
112
113
|
let networkOperations = null;
|
|
114
|
+
let gitWorktreeCreation = null;
|
|
113
115
|
const getWalkthroughService = async () => {
|
|
114
116
|
if (!walkthroughService) {
|
|
115
117
|
const [service, pullRequest] = await Promise.all([
|
|
@@ -171,6 +173,30 @@ export const createFeatureRoutesRuntime = (dependencies) => {
|
|
|
171
173
|
});
|
|
172
174
|
};
|
|
173
175
|
|
|
176
|
+
/**
|
|
177
|
+
* A worktree from a pull or merge request given by number, the same kind
|
|
178
|
+
* the New worktree dialog makes: the server reads the change request with
|
|
179
|
+
* the project's own account and creates the worktree through the Git
|
|
180
|
+
* routes' contributor pipeline. `name` and `branchName` default to the
|
|
181
|
+
* change request's head branch.
|
|
182
|
+
*/
|
|
183
|
+
const createChangeRequestWorktree = async ({ directory, number, name, branchName }) => {
|
|
184
|
+
if (!(gitWorktreeCreation?.createWorktree instanceof Function)
|
|
185
|
+
|| !(walkthroughBindingService?.resolveChangeRequestWorktreeSource instanceof Function)) {
|
|
186
|
+
throw Object.assign(new Error('Worktrees from pull requests are unavailable'), {
|
|
187
|
+
code: 'RUNTIME_UNSUPPORTED', status: 501,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
const { headBranch, sourceRequest } = await walkthroughBindingService.resolveChangeRequestWorktreeSource({ directory, number });
|
|
191
|
+
const branch = branchName || headBranch;
|
|
192
|
+
return gitWorktreeCreation.createWorktree(directory, {
|
|
193
|
+
mode: 'existing',
|
|
194
|
+
branchName: branch,
|
|
195
|
+
worktreeName: name || branch,
|
|
196
|
+
changeRequestSource: sourceRequest,
|
|
197
|
+
});
|
|
198
|
+
};
|
|
199
|
+
|
|
174
200
|
const registerRoutes = async (app, routeDependencies) => {
|
|
175
201
|
const {
|
|
176
202
|
messageSearchRuntime,
|
|
@@ -326,6 +352,7 @@ export const createFeatureRoutesRuntime = (dependencies) => {
|
|
|
326
352
|
getAgentSources,
|
|
327
353
|
getAgentConfig,
|
|
328
354
|
getAgentPermissions,
|
|
355
|
+
listDisabledAgents,
|
|
329
356
|
createAgent,
|
|
330
357
|
updateAgent,
|
|
331
358
|
deleteAgent,
|
|
@@ -370,44 +397,6 @@ export const createFeatureRoutesRuntime = (dependencies) => {
|
|
|
370
397
|
|
|
371
398
|
const { getProfiles, getProfile, getGlobalIdentity, resolveRepositoryGitPaths } = await import('../git/index.js');
|
|
372
399
|
|
|
373
|
-
registerSkillRoutes(app, {
|
|
374
|
-
fs,
|
|
375
|
-
path,
|
|
376
|
-
os,
|
|
377
|
-
resolveProjectDirectory,
|
|
378
|
-
resolveOptionalProjectDirectory,
|
|
379
|
-
readSettingsFromDisk,
|
|
380
|
-
sanitizeSkillCatalogs,
|
|
381
|
-
isUnsafeSkillRelativePath,
|
|
382
|
-
refreshOpenCodeAfterConfigChange,
|
|
383
|
-
clientReloadDelayMs,
|
|
384
|
-
buildOpenCodeUrl,
|
|
385
|
-
getOpenCodeAuthHeaders,
|
|
386
|
-
getOpenCodePort,
|
|
387
|
-
getSkillSources,
|
|
388
|
-
discoverSkills,
|
|
389
|
-
mergeDiscoveredSkills,
|
|
390
|
-
createSkill,
|
|
391
|
-
updateSkill,
|
|
392
|
-
deleteSkill,
|
|
393
|
-
renameSkill,
|
|
394
|
-
isManagedSkillPath,
|
|
395
|
-
readSkillSupportingFile,
|
|
396
|
-
writeSkillSupportingFile,
|
|
397
|
-
deleteSkillSupportingFile,
|
|
398
|
-
SKILL_SCOPE,
|
|
399
|
-
SKILL_DIR,
|
|
400
|
-
getCuratedSkillsSources,
|
|
401
|
-
getCacheKey,
|
|
402
|
-
scanWithCache,
|
|
403
|
-
parseSkillRepoSource,
|
|
404
|
-
scanSkillsRepository,
|
|
405
|
-
installSkillsFromRepository,
|
|
406
|
-
fetchGitHubRepoMetas,
|
|
407
|
-
getProfiles,
|
|
408
|
-
getProfile,
|
|
409
|
-
});
|
|
410
|
-
|
|
411
400
|
registerQuotaRoutes(app, { getQuotaProviders });
|
|
412
401
|
registerSmallModelRoutes(app, { getSmallModelService });
|
|
413
402
|
registerSessionGoalRoutes(app);
|
|
@@ -419,12 +408,23 @@ export const createFeatureRoutesRuntime = (dependencies) => {
|
|
|
419
408
|
origin, refreshToken, clientId: await resolveGitLabClientId(origin, readSettingsFromDisk),
|
|
420
409
|
}),
|
|
421
410
|
});
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
411
|
+
// A GitHub account is either one OpenChamber stores or the gh CLI login
|
|
412
|
+
// the user switched to; reads already accept both, so Git transport
|
|
413
|
+
// (fork PR worktrees, clones) must too.
|
|
414
|
+
const resolveSourceControlAccount = async ({ provider, instance, accountId, credentialRevision }) => {
|
|
415
|
+
if (provider !== 'github') return gitlabAuthStore.readAccount(instance, accountId, credentialRevision);
|
|
416
|
+
return await getGitHubAuthByAccountId(accountId, credentialRevision)
|
|
417
|
+
?? getGitHubCliCredential(accountId, credentialRevision);
|
|
418
|
+
};
|
|
425
419
|
const resolveTransportRepository = createPrivateRepositoryIdentityResolver({
|
|
426
420
|
getTransportRevision: (directory) => readEffectiveGitTransportRevision(directory, { gitBinary }),
|
|
427
421
|
});
|
|
422
|
+
const contributorProvenance = createContributorProvenanceStore({
|
|
423
|
+
filePath: path.join(openchamberDataDir, 'git-contributor-provenance.json'),
|
|
424
|
+
resolveRepositoryIdentity: resolveTransportRepository,
|
|
425
|
+
resolveGitPaths: resolveRepositoryGitPaths,
|
|
426
|
+
fsImpl: fsPromises,
|
|
427
|
+
});
|
|
428
428
|
const sourceControlAuditStore = createSourceControlAuditStore({
|
|
429
429
|
filePath: path.join(openchamberDataDir, 'source-control-audit.json'),
|
|
430
430
|
fsImpl: fsPromises,
|
|
@@ -479,6 +479,11 @@ export const createFeatureRoutesRuntime = (dependencies) => {
|
|
|
479
479
|
});
|
|
480
480
|
return inspection.requirements.find((entry) => entry.kind === kind && entry.path === checkoutPath) ?? null;
|
|
481
481
|
},
|
|
482
|
+
github: {
|
|
483
|
+
// A worktree made from a fork PR has no upstream; its provenance says
|
|
484
|
+
// which remote the branch came from, so PR status can find the PR.
|
|
485
|
+
readContributorProvenance: contributorProvenance.read,
|
|
486
|
+
},
|
|
482
487
|
gitlab: {
|
|
483
488
|
configRoot: openchamberDataDir,
|
|
484
489
|
store: gitlabAuthStore,
|
|
@@ -558,12 +563,6 @@ export const createFeatureRoutesRuntime = (dependencies) => {
|
|
|
558
563
|
if (!profile) throw new Error('Git identity profile is unavailable');
|
|
559
564
|
return profile;
|
|
560
565
|
};
|
|
561
|
-
const contributorProvenance = createContributorProvenanceStore({
|
|
562
|
-
filePath: path.join(openchamberDataDir, 'git-contributor-provenance.json'),
|
|
563
|
-
resolveRepositoryIdentity: resolveTransportRepository,
|
|
564
|
-
resolveGitPaths: resolveRepositoryGitPaths,
|
|
565
|
-
fsImpl: fsPromises,
|
|
566
|
-
});
|
|
567
566
|
const networkOperationStore = createGitNetworkOperationStore({
|
|
568
567
|
filePath: path.join(openchamberDataDir, 'git-network-operations.json'),
|
|
569
568
|
fsImpl: fsPromises,
|
|
@@ -579,6 +578,46 @@ export const createFeatureRoutesRuntime = (dependencies) => {
|
|
|
579
578
|
fsImpl: fsPromises,
|
|
580
579
|
snapshotRoot: path.join(openchamberDataDir, 'git-ssh-operation-keys'),
|
|
581
580
|
});
|
|
581
|
+
registerSkillRoutes(app, {
|
|
582
|
+
fs,
|
|
583
|
+
path,
|
|
584
|
+
os,
|
|
585
|
+
resolveProjectDirectory,
|
|
586
|
+
resolveOptionalProjectDirectory,
|
|
587
|
+
readSettingsFromDisk,
|
|
588
|
+
sanitizeSkillCatalogs,
|
|
589
|
+
isUnsafeSkillRelativePath,
|
|
590
|
+
refreshOpenCodeAfterConfigChange,
|
|
591
|
+
clientReloadDelayMs,
|
|
592
|
+
buildOpenCodeUrl,
|
|
593
|
+
getOpenCodeAuthHeaders,
|
|
594
|
+
getOpenCodePort,
|
|
595
|
+
getSkillSources,
|
|
596
|
+
discoverSkills,
|
|
597
|
+
mergeDiscoveredSkills,
|
|
598
|
+
createSkill,
|
|
599
|
+
updateSkill,
|
|
600
|
+
deleteSkill,
|
|
601
|
+
renameSkill,
|
|
602
|
+
isManagedSkillPath,
|
|
603
|
+
readSkillSupportingFile,
|
|
604
|
+
writeSkillSupportingFile,
|
|
605
|
+
deleteSkillSupportingFile,
|
|
606
|
+
SKILL_SCOPE,
|
|
607
|
+
SKILL_DIR,
|
|
608
|
+
getCuratedSkillsSources,
|
|
609
|
+
getCacheKey,
|
|
610
|
+
scanWithCache,
|
|
611
|
+
parseSkillRepoSource,
|
|
612
|
+
scanSkillsRepository,
|
|
613
|
+
installSkillsFromRepository,
|
|
614
|
+
fetchGitHubRepoMetas,
|
|
615
|
+
getProfiles,
|
|
616
|
+
getProfile,
|
|
617
|
+
createHttpsCredentialReference,
|
|
618
|
+
resolveSourceControlAccount,
|
|
619
|
+
credentialResolver: gitCredentialResolver,
|
|
620
|
+
});
|
|
582
621
|
gitRepositoryCredentialRuntime = createGitRepositoryCredentialRuntime({
|
|
583
622
|
readBinding: (directory) => walkthroughBindingService.get(directory),
|
|
584
623
|
credentialResolver: gitCredentialResolver,
|
|
@@ -612,7 +651,7 @@ export const createFeatureRoutesRuntime = (dependencies) => {
|
|
|
612
651
|
});
|
|
613
652
|
await registerBuiltInGuests({ persistPath: extensionsPersistPath(openchamberDataDir), root: routeDependencies.builtInExtensionsDir });
|
|
614
653
|
registerGuestRoutes(app, { openchamberDataDir, openchamberVersion, resolveGitBinaryForSpawn, resolveOptionalProjectDirectory, getSmallModelService, onGuestDeactivated, surfaceViewerHeaders });
|
|
615
|
-
registerGitRoutes(app, {
|
|
654
|
+
gitWorktreeCreation = registerGitRoutes(app, {
|
|
616
655
|
// Identities for accounts connected before identities carried one are
|
|
617
656
|
// made the first time identities are listed: a moment someone asked for,
|
|
618
657
|
// not startup, where a development restart can kill a process holding a
|
|
@@ -689,6 +728,7 @@ export const createFeatureRoutesRuntime = (dependencies) => {
|
|
|
689
728
|
return {
|
|
690
729
|
registerRoutes,
|
|
691
730
|
hydrateBoundCheckout,
|
|
731
|
+
createChangeRequestWorktree,
|
|
692
732
|
/** Writes the Git credential helper's endpoint file; a no-op until the Git routes are registered. */
|
|
693
733
|
publishRepositoryCredentialEndpoint: async () => { await gitRepositoryCredentialRuntime?.publish(); },
|
|
694
734
|
};
|
|
@@ -3,6 +3,7 @@ import { spawn, spawnSync } from 'node:child_process';
|
|
|
3
3
|
import { accessSync, constants as fsConstants, statSync } from 'node:fs';
|
|
4
4
|
import net from 'node:net';
|
|
5
5
|
import { stripAppImageArgv0Leak, stripAppImageLauncherEnv } from '../inherited-env.js';
|
|
6
|
+
import { assignInjectedEnv } from '../injected-env.js';
|
|
6
7
|
import { registerManagedProcess, unregisterManagedProcess, reapOrphanedProcesses } from './managed-process-registry.js';
|
|
7
8
|
import { applyProviderEnvAliases } from './provider-env-aliases.js';
|
|
8
9
|
import { overlayEnvironment } from '../environment/variables.js';
|
|
@@ -137,12 +138,21 @@ export const createOpenCodeLifecycleControls = ({
|
|
|
137
138
|
return Promise.reject(new Error('Shared OpenCode recovery is not configured'));
|
|
138
139
|
}
|
|
139
140
|
if (!sharedRecoveryPromise) {
|
|
140
|
-
|
|
141
|
-
.then(() =>
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
141
|
+
const recovery = Promise.resolve(recoverShared())
|
|
142
|
+
.then(() => onSharedRecovered());
|
|
143
|
+
sharedRecoveryPromise = recovery;
|
|
144
|
+
void recovery.then(
|
|
145
|
+
() => {
|
|
146
|
+
if (sharedRecoveryPromise === recovery) {
|
|
147
|
+
sharedRecoveryPromise = null;
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
() => {
|
|
151
|
+
if (sharedRecoveryPromise === recovery) {
|
|
152
|
+
sharedRecoveryPromise = null;
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
);
|
|
146
156
|
}
|
|
147
157
|
return sharedRecoveryPromise;
|
|
148
158
|
};
|
|
@@ -269,6 +279,10 @@ export const createOpenCodeLifecycleRuntime = (deps) => {
|
|
|
269
279
|
getActiveSessionCount = () => 0,
|
|
270
280
|
reapManagedOrphanedProcesses = reapOrphanedProcesses,
|
|
271
281
|
getWarmupDirectories = async () => [],
|
|
282
|
+
// Scopes server-side reads while no project directory is usable (the
|
|
283
|
+
// managed chats root). Without it they name no directory, and OpenCode
|
|
284
|
+
// starts a location over its own working directory, the whole home.
|
|
285
|
+
noProjectDirectory = null,
|
|
272
286
|
onOpenCodeRestarted = null,
|
|
273
287
|
managedStartupTimeoutMs = 30_000,
|
|
274
288
|
now = Date.now,
|
|
@@ -899,16 +913,20 @@ export const createOpenCodeLifecycleRuntime = (deps) => {
|
|
|
899
913
|
// in front of the managed PATH) and under everything OpenChamber itself
|
|
900
914
|
// sets for OpenCode.
|
|
901
915
|
const inheritedEnv = overlayEnvironment({ ...shellEnv, ...process.env, PATH: envPath }, getUserEnvironment());
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
916
|
+
// What OpenChamber sets for OpenCode is recorded as injected, so a shell
|
|
917
|
+
// the agent opens can tell it from the user's own variables.
|
|
918
|
+
const processEnv = stripAppImageLauncherEnv(stripAppImageArgv0Leak(applyProviderEnvAliases(assignInjectedEnv(
|
|
919
|
+
{ ...inheritedEnv },
|
|
920
|
+
{
|
|
921
|
+
...managedOpenCodeEnv,
|
|
922
|
+
PATH: inheritedEnv.PATH,
|
|
923
|
+
// OpenCode 2 reads OPENCODE_PASSWORD before the legacy name, so a
|
|
924
|
+
// user's own OPENCODE_PASSWORD would otherwise win and every request
|
|
925
|
+
// we send with openCodePassword would get 401.
|
|
926
|
+
OPENCODE_PASSWORD: openCodePassword,
|
|
927
|
+
OPENCODE_SERVER_PASSWORD: openCodePassword,
|
|
928
|
+
},
|
|
929
|
+
))));
|
|
912
930
|
managedProcessEnv = processEnv;
|
|
913
931
|
|
|
914
932
|
let serverInstance;
|
|
@@ -1416,8 +1434,9 @@ export const createOpenCodeLifecycleRuntime = (deps) => {
|
|
|
1416
1434
|
const usable = selectUsableDirectory(warmedOpenCodeDirectories);
|
|
1417
1435
|
if (usable) defaultOpenCodeDirectory = usable;
|
|
1418
1436
|
} catch {
|
|
1419
|
-
// Best-effort: the caller falls back to
|
|
1420
|
-
// for this read and the refresh can
|
|
1437
|
+
// Best-effort: the caller falls back to the no-project directory or
|
|
1438
|
+
// OpenCode's working directory for this read, and the refresh can
|
|
1439
|
+
// succeed next time.
|
|
1421
1440
|
} finally {
|
|
1422
1441
|
defaultDirectoryRefresh = null;
|
|
1423
1442
|
}
|
|
@@ -1426,8 +1445,9 @@ export const createOpenCodeLifecycleRuntime = (deps) => {
|
|
|
1426
1445
|
};
|
|
1427
1446
|
|
|
1428
1447
|
// The warmed directory only while it is still usable; otherwise another
|
|
1429
|
-
// usable directory (from this or a refreshed pass),
|
|
1430
|
-
// sends no directory and OpenCode uses
|
|
1448
|
+
// usable directory (from this or a refreshed pass), else the no-project
|
|
1449
|
+
// directory, else null so the caller sends no directory and OpenCode uses
|
|
1450
|
+
// its own working directory.
|
|
1431
1451
|
const getValidatedDefaultDirectory = () => {
|
|
1432
1452
|
if (isUsableDirectory(defaultOpenCodeDirectory)) return defaultOpenCodeDirectory;
|
|
1433
1453
|
const alternate = selectUsableDirectory(warmedOpenCodeDirectories);
|
|
@@ -1440,7 +1460,7 @@ export const createOpenCodeLifecycleRuntime = (deps) => {
|
|
|
1440
1460
|
defaultDirectoryRefreshAt = checkedAt;
|
|
1441
1461
|
void refreshDefaultDirectories();
|
|
1442
1462
|
}
|
|
1443
|
-
return null;
|
|
1463
|
+
return isUsableDirectory(noProjectDirectory) ? noProjectDirectory : null;
|
|
1444
1464
|
};
|
|
1445
1465
|
|
|
1446
1466
|
// OpenCode initializes each project directory lazily on its first
|
|
@@ -4,6 +4,8 @@ import os from 'node:os';
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
6
6
|
|
|
7
|
+
import { injectedEnvKeys } from '../injected-env.js';
|
|
8
|
+
|
|
7
9
|
const spawnMock = vi.fn();
|
|
8
10
|
const spawnSyncMock = vi.fn();
|
|
9
11
|
const recordStartupPerformanceMock = vi.fn();
|
|
@@ -581,6 +583,45 @@ describe('OpenCode lifecycle', () => {
|
|
|
581
583
|
await fs.rm(root, { recursive: true, force: true });
|
|
582
584
|
});
|
|
583
585
|
|
|
586
|
+
it('scopes default reads to the chats root while no project directory is usable', async () => {
|
|
587
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'oc-no-project-'));
|
|
588
|
+
const chats = path.join(root, 'chats');
|
|
589
|
+
const project = path.join(root, 'project');
|
|
590
|
+
await fs.mkdir(chats);
|
|
591
|
+
await fs.mkdir(project);
|
|
592
|
+
let warmup = [];
|
|
593
|
+
globalThis.fetch = vi.fn(async () => ({
|
|
594
|
+
ok: true,
|
|
595
|
+
json: async () => ({ version: '2.0.20', pid: 1, urls: [], paths: { tmp: '/tmp' } }),
|
|
596
|
+
}));
|
|
597
|
+
const runtime = createRuntime({
|
|
598
|
+
env: {
|
|
599
|
+
ENV_CONFIGURED_OPENCODE_PORT: 45678,
|
|
600
|
+
ENV_CONFIGURED_OPENCODE_HOST: null,
|
|
601
|
+
ENV_EFFECTIVE_PORT: 45678,
|
|
602
|
+
ENV_CONFIGURED_OPENCODE_HOSTNAME: '127.0.0.1',
|
|
603
|
+
ENV_SKIP_OPENCODE_START: true,
|
|
604
|
+
},
|
|
605
|
+
reapManagedOrphanedProcesses: vi.fn(async () => ({ reaped: 0 })),
|
|
606
|
+
getWarmupDirectories: vi.fn(async () => warmup),
|
|
607
|
+
noProjectDirectory: chats,
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
await runtime.bootstrapOpenCodeAtStartup();
|
|
611
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
612
|
+
// No project: a read without a directory would start OpenCode over the
|
|
613
|
+
// whole home, its working directory.
|
|
614
|
+
expect(runtime.getDefaultOpenCodeDirectory()).toBe(chats);
|
|
615
|
+
expect(globalThis.fetch.mock.calls.filter(([url]) => String(url).endsWith('/api/location'))).toEqual([]);
|
|
616
|
+
|
|
617
|
+
// A project opened later takes over once the warmup source names it.
|
|
618
|
+
warmup = [project];
|
|
619
|
+
await runtime.bootstrapOpenCodeAtStartup();
|
|
620
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
621
|
+
expect(runtime.getDefaultOpenCodeDirectory()).toBe(project);
|
|
622
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
623
|
+
});
|
|
624
|
+
|
|
584
625
|
it('advances to a currently-valid warmup directory when the cached default goes stale', async () => {
|
|
585
626
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'oc-replace-'));
|
|
586
627
|
const stale = path.join(root, 'gone');
|
|
@@ -776,12 +817,12 @@ describe('OpenCode lifecycle', () => {
|
|
|
776
817
|
}
|
|
777
818
|
|
|
778
819
|
expect(close).not.toHaveBeenCalled();
|
|
779
|
-
expect(warn).toHaveBeenCalledTimes(
|
|
820
|
+
expect(warn).toHaveBeenCalledTimes(4);
|
|
780
821
|
|
|
781
822
|
now += 15_000;
|
|
782
823
|
await runtime.triggerHealthCheck();
|
|
783
824
|
|
|
784
|
-
expect(warn).toHaveBeenCalledTimes(
|
|
825
|
+
expect(warn).toHaveBeenCalledTimes(5);
|
|
785
826
|
expect(warn).toHaveBeenLastCalledWith(expect.stringContaining('(2/20)'));
|
|
786
827
|
warn.mockRestore();
|
|
787
828
|
});
|
|
@@ -1212,6 +1253,13 @@ describe('OpenCode lifecycle', () => {
|
|
|
1212
1253
|
expect(options.env.OPENCODE_PASSWORD).toBe('password');
|
|
1213
1254
|
expect(options.env.OPENCHAMBER_AGENT_TOOL_TOKEN).toBe('ephemeral');
|
|
1214
1255
|
expect(runtime.getManagedOpenCodeProcessEnv().DATABASE_URL).toBe('postgres://db');
|
|
1256
|
+
// The replaced values are recorded as OpenChamber's; the user's are not.
|
|
1257
|
+
const injected = injectedEnvKeys(options.env);
|
|
1258
|
+
for (const key of ['OPENCODE_PASSWORD', 'OPENCODE_SERVER_PASSWORD', 'OPENCHAMBER_AGENT_TOOL_TOKEN']) {
|
|
1259
|
+
expect(injected.has(key)).toBe(true);
|
|
1260
|
+
}
|
|
1261
|
+
expect(injected.has('DATABASE_URL')).toBe(false);
|
|
1262
|
+
expect(injected.has('PATH')).toBe(false);
|
|
1215
1263
|
|
|
1216
1264
|
await server.close();
|
|
1217
1265
|
});
|
|
@@ -319,7 +319,7 @@ export async function discoverProviderModels(input, options = {}) {
|
|
|
319
319
|
const storedApiKey = isSameEndpoint(options.storedBaseURL, body.baseURL)
|
|
320
320
|
? optionalString(options.storedApiKey)
|
|
321
321
|
: null;
|
|
322
|
-
const apiKey = requestedApiKey ?? storedApiKey;
|
|
322
|
+
const apiKey = requestedApiKey ?? resolveApiKey(storedApiKey, env);
|
|
323
323
|
const response = await (options.fetch ?? fetch)(endpoint, {
|
|
324
324
|
method: 'GET',
|
|
325
325
|
headers: buildHeaders(body.headers, apiKey, env),
|
|
@@ -89,6 +89,23 @@ describe('provider model discovery', () => {
|
|
|
89
89
|
expect(requests).toEqual(['Bearer stored-key', 'Bearer replacement-key']);
|
|
90
90
|
});
|
|
91
91
|
|
|
92
|
+
test('resolves environment variables in stored keys', async () => {
|
|
93
|
+
const requests = [];
|
|
94
|
+
const fetchMock = async (_url, init) => {
|
|
95
|
+
requests.push(init.headers.Authorization);
|
|
96
|
+
return new Response(JSON.stringify({ data: [{ id: 'local' }] }), { status: 200 });
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
await discoverProviderModels({ baseURL: 'https://provider.test', enrich: false }, {
|
|
100
|
+
fetch: fetchMock,
|
|
101
|
+
storedApiKey: '{env:STORED_KEY}',
|
|
102
|
+
storedBaseURL: 'https://provider.test',
|
|
103
|
+
env: { STORED_KEY: 'resolved-secret' },
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
expect(requests).toEqual(['Bearer resolved-secret']);
|
|
107
|
+
});
|
|
108
|
+
|
|
92
109
|
test('never sends the stored key to a base URL other than the saved one', async () => {
|
|
93
110
|
const requests = [];
|
|
94
111
|
const fetchMock = async (_url, init) => {
|
|
@@ -83,7 +83,7 @@ beforeEach(() => {
|
|
|
83
83
|
packageManager.detectPackageManagerDetails.mockReturnValue({
|
|
84
84
|
packageManager: 'npm',
|
|
85
85
|
});
|
|
86
|
-
packageManager.getUpdateCommand.mockReturnValue('npm install -g @openchamber
|
|
86
|
+
packageManager.getUpdateCommand.mockReturnValue('npm install -g @vantran-se/openchamber-web@latest');
|
|
87
87
|
});
|
|
88
88
|
|
|
89
89
|
afterEach(() => {
|
|
@@ -308,7 +308,7 @@ describe('OpenChamber foreground update route', () => {
|
|
|
308
308
|
'--setenv=PATH=/home/syu/.npm-global/bin:/usr/bin:/bin',
|
|
309
309
|
'/bin/sh',
|
|
310
310
|
'-c',
|
|
311
|
-
"set -eu\nnpm install -g @openchamber
|
|
311
|
+
"set -eu\nnpm install -g @vantran-se/openchamber-web@latest\nsystemctl --user restart 'openchamber@wsl.service'",
|
|
312
312
|
], {
|
|
313
313
|
encoding: 'utf8',
|
|
314
314
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
@@ -480,7 +480,7 @@ describe('OpenChamber web update route on Windows', () => {
|
|
|
480
480
|
// The UI password is never part of the script or its log.
|
|
481
481
|
expect(script).not.toContain('pa%');
|
|
482
482
|
// A .cmd shim (npm, pnpm, yarn) must be `call`ed or the script ends there.
|
|
483
|
-
expect(lines).toContain('call npm install -g @openchamber
|
|
483
|
+
expect(lines).toContain('call npm install -g @vantran-se/openchamber-web@latest');
|
|
484
484
|
expect(lines).toContain('ping -n 3 127.0.0.1 >nul');
|
|
485
485
|
expect(lines.some((line) => line.startsWith('timeout '))).toBe(false);
|
|
486
486
|
expect(lines).toContain('if %ERRORLEVEL% EQU 0 (');
|