@aiworker/web 1.22.0 → 1.22.2
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 +4 -4
- package/bin/cli.js +7 -4
- package/bin/cli.test.js +165 -73
- package/bin/lib/cli-api-target.js +2 -2
- package/bin/lib/cli-args.js +119 -119
- package/bin/lib/cli-lifecycle.js +9 -9
- package/bin/lib/cli-network.js +3 -3
- package/bin/lib/cli-paths.js +3 -3
- package/bin/lib/cli-ports.js +2 -2
- package/bin/lib/cli-process.js +4 -4
- package/bin/lib/cli-startup.js +64 -15
- package/bin/lib/cli-tunnel-profiles.js +1 -1
- package/bin/lib/cli-tunnel-utils.js +2 -2
- package/bin/lib/commands-connect-url.js +19 -19
- package/bin/lib/commands-lifecycle.js +27 -27
- package/bin/lib/commands-logs.js +5 -5
- package/bin/lib/commands-models.js +1 -1
- package/bin/lib/commands-projects.js +1 -1
- package/bin/lib/commands-schedule.js +1 -1
- package/bin/lib/commands-serve.js +29 -29
- package/bin/lib/commands-session.js +1 -1
- package/bin/lib/commands-startup.js +45 -10
- package/bin/lib/commands-status.js +1 -1
- package/bin/lib/commands-tunnel.js +40 -40
- package/bin/lib/commands-update.js +1 -1
- package/bin/lib/commands-update.test.js +6 -6
- package/dist/assets/{CodeMirrorCommentWidgets-B3u7UIcE.js → CodeMirrorCommentWidgets-CSICpWQD.js} +1 -1
- package/dist/assets/{DiffView-DluiRSla.js → DiffView-hDBKesA_.js} +3 -3
- package/dist/assets/DirtyBranchSwitchDialog-C22HdWj4.js +1 -0
- package/dist/assets/FilesView-CdWzeOp9.js +17 -0
- package/dist/assets/GitView-VQQkYuUw.js +6 -0
- package/dist/assets/{JsonTreeView-B0BdZEHG.js → JsonTreeView-6lrhwD3q.js} +1 -1
- package/dist/assets/LinearIssuesView-Ba6Q9569.js +1 -0
- package/dist/assets/{MarkdownImageGallery-ClOvuZxC.js → MarkdownImageGallery-Czv_mhvS.js} +1 -1
- package/dist/assets/MarkdownRendererImpl-BrMcswiy.js +12 -0
- package/dist/assets/{OnboardingScreen-D2DG7K6b.js → OnboardingScreen-ChxQoAkx.js} +1 -1
- package/dist/assets/{PatchDiff-DvfqDql7.js → PatchDiff-BzcZPlOq.js} +1 -1
- package/dist/assets/{PierreDiffViewer-B7m3f0d7.js → PierreDiffViewer-BchBtZlp.js} +2 -2
- package/dist/assets/PlanView-DyDt_nZ_.js +5 -0
- package/dist/assets/SettingsView-BKf5xBsP.js +28 -0
- package/dist/assets/{SettingsWindow-i45U1Cy1.js → SettingsWindow-DDQYaKWQ.js} +1 -1
- package/dist/assets/{ToolOutputDialog-BTwyh5OF.js → ToolOutputDialog-iYF8Y2bz.js} +6 -6
- package/dist/assets/ToolPartDiffPreview-CdjbOVMM.js +8 -0
- package/dist/assets/{VirtualizedFileDiff-DD8lDVej.js → VirtualizedFileDiff-fFFm2QgH.js} +1 -1
- package/dist/assets/{WalkthroughView-egQgxel3.js → WalkthroughView-DOeCsMju.js} +2 -2
- package/dist/assets/{computeVirtualFileMetrics-BMIRgMHv.js → computeVirtualFileMetrics-CzgLum_K.js} +2 -2
- package/dist/assets/{contextPanelEmbeddedChat-C3fjDbdg.js → contextPanelEmbeddedChat-C0ZLAepL.js} +1 -1
- package/dist/assets/{de-C1P6X5Rs.js → de-B5JaA3SA.js} +6 -5
- package/dist/assets/{es-CDgaF1_t.js → es-EPIH88ii.js} +6 -5
- package/dist/assets/{fr-Bgxqvvz_.js → fr-CAMfxUdZ.js} +6 -5
- package/dist/assets/{index-CODwDsU_.js → index-Bl3LSbjn.js} +1 -1
- package/dist/assets/index-DF5kMP-i.js +39 -0
- package/dist/assets/index-Dl3xbAB_.css +1 -0
- package/dist/assets/{isDiffPlainText-BqayB8d3.js → isDiffPlainText-Dx-dB_8l.js} +1 -1
- package/dist/assets/{isFilePlainText-QfoiKJNY.js → isFilePlainText-CWFPwmnU.js} +1 -1
- package/dist/assets/{ja-BDRrEcNP.js → ja-CifOE1VN.js} +7 -6
- package/dist/assets/{ko-Ba8sQV79.js → ko-BBDyAyYQ.js} +7 -6
- package/dist/assets/{main-C5DVOxc6.js → main-DnBAoktx.js} +2 -2
- package/dist/assets/main-MhSaOZoB.js +930 -0
- package/dist/assets/{markdownCore-CZwzz5gw.js → markdownCore-BrBwOOMn.js} +2 -2
- package/dist/assets/{miniChat-DUJ4irZc.js → miniChat-BKyvLseN.js} +2 -2
- package/dist/assets/{mobile-CcaxB0n4.js → mobile-DKjeFgMN.js} +2 -2
- package/dist/assets/{patchFileDiff-yNfWXN4E.js → patchFileDiff-o8ms1me8.js} +1 -1
- package/dist/assets/pl-DgEHYfFM.js +16 -0
- package/dist/assets/{pt-BR-DIs6ElwV.js → pt-BR-DuWHojUw.js} +6 -5
- package/dist/assets/{renderElectronMiniChatApp-BcKLHXKe.js → renderElectronMiniChatApp-CDgBwwCS.js} +2 -2
- package/dist/assets/renderMobileApp-DJIKIK1r.js +25 -0
- package/dist/assets/runtimeEndpointReset-CYJ9O_xX.js +17 -0
- package/dist/assets/{scheduledTasksApi-DLW0mkQK.js → scheduledTasksApi-D8yamh1b.js} +1 -1
- package/dist/assets/{shikiHighlight-DYOWuY65.js → shikiHighlight-TEQCmzW8.js} +1 -1
- package/dist/assets/tr-D5m2yIWL.js +7 -0
- package/dist/assets/{uk-ZrCNjM7Y.js → uk-D_q60oog.js} +6 -5
- package/dist/assets/{useAppFontEffects-CjrntdL0.js → useAppFontEffects-D7sNpuL2.js} +161 -190
- package/dist/assets/useInlineCommentController-Brz_cLcQ.js +2 -0
- package/dist/assets/{useRootScrollLock-BHYPkrLv.js → useRootScrollLock-lXEnWlaU.js} +1 -1
- package/dist/assets/{vendor-codemirror-language-data-DFZik9R2.js → vendor-codemirror-language-data-Dp3KMRQG.js} +2 -2
- package/dist/assets/vendor-zod-CK21ihK-.js +39 -0
- package/dist/assets/{web-components-Bhg83tjf.js → web-components-_mNEMjFY.js} +1 -1
- package/dist/assets/{zh-CN-iv_AV4EC.js → zh-CN-D8QvsVeg.js} +6 -5
- package/dist/assets/{zh-TW-CzEUk38j.js → zh-TW-B9t7rQQO.js} +6 -5
- package/dist/index.html +22 -22
- package/dist/mini-chat.html +7 -7
- package/dist/mobile.html +5 -5
- package/package.json +3 -3
- package/server/index.js +118 -85
- package/server/lib/agent-memory/actions.js +2 -2
- package/server/lib/agent-memory/feature-flag.js +1 -1
- package/server/lib/agent-memory/feature-flag.test.js +8 -8
- package/server/lib/agent-memory/routes.js +2 -2
- package/server/lib/agent-tool/runtime.js +37 -54
- package/server/lib/agent-tool/runtime.test.js +59 -59
- package/server/lib/browser-control/broker.js +3 -3
- package/server/lib/browser-control/broker.test.js +1 -1
- package/server/lib/changelog/update-notes.js +82 -0
- package/server/lib/changelog/update-notes.test.js +59 -0
- package/server/lib/client-auth/remote-clients.test.js +5 -5
- package/server/lib/context-obligatory/runtime.js +7 -7
- package/server/lib/context-obligatory/runtime.test.js +5 -5
- package/server/lib/dev-servers/parse.js +2 -2
- package/server/lib/dev-servers/parse.test.js +2 -2
- package/server/lib/dev-servers/routes.js +1 -1
- package/server/lib/dev-tunnel/DOCUMENTATION.md +13 -4
- package/server/lib/dev-tunnel/client.js +1 -1
- package/server/lib/dev-tunnel/runtime.js +5 -5
- package/server/lib/dev-tunnel/tunnel.test.js +21 -1
- package/server/lib/dictation/local/model-catalog.js +1 -1
- package/server/lib/dictation/local/worker-process.js +2 -2
- package/server/lib/event-stream/directory-ws-bridge.js +1 -1
- package/server/lib/event-stream/global-ws-bridge.js +1 -1
- package/server/lib/event-stream/protocol.test.js +4 -4
- package/server/lib/event-stream/runtime.test.js +6 -6
- package/server/lib/fs/DOCUMENTATION.md +2 -0
- package/server/lib/fs/routes.js +37 -26
- package/server/lib/fs/routes.test.js +98 -21
- package/server/lib/git/DOCUMENTATION.md +3 -1
- package/server/lib/git/identity-storage.js +1 -1
- package/server/lib/git/issue-2746-longpaths.test.js +4 -4
- package/server/lib/git/routes.js +17 -1
- package/server/lib/git/service.js +116 -26
- package/server/lib/git/service.test.js +174 -3
- package/server/lib/github/auth.js +8 -8
- package/server/lib/github/pr-status.js +1 -1
- package/server/lib/github/routes.js +2 -2
- package/server/lib/inherited-env.js +1 -1
- package/server/lib/inherited-env.test.js +2 -2
- package/server/lib/linear/auth.js +8 -8
- package/server/lib/linear/auth.test.js +24 -24
- package/server/lib/linear/issues.test.js +6 -6
- package/server/lib/linear/mapping.test.js +4 -4
- package/server/lib/linear/oauth.js +2 -2
- package/server/lib/linear/oauth.test.js +14 -14
- package/server/lib/linear/routes.js +5 -5
- package/server/lib/linear/routes.test.js +16 -16
- package/server/lib/linear/status-runtime.test.js +9 -9
- package/server/lib/linear/status.js +1 -1
- package/server/lib/linear/status.test.js +15 -15
- package/server/lib/linear/teams.test.js +4 -4
- package/server/lib/magic-prompts/routes.js +2 -2
- package/server/lib/mcp-reconnect/DOCUMENTATION.md +55 -0
- package/server/lib/mcp-reconnect/runtime.js +122 -0
- package/server/lib/mcp-reconnect/runtime.test.js +140 -0
- package/server/lib/message-queue/DOCUMENTATION.md +158 -0
- package/server/lib/message-queue/runtime.js +870 -0
- package/server/lib/message-queue/runtime.test.js +482 -0
- package/server/lib/notifications/apns-runtime.js +17 -17
- package/server/lib/notifications/apns-runtime.test.js +11 -11
- package/server/lib/notifications/emitter-runtime.js +1 -1
- package/server/lib/notifications/emitter-runtime.test.js +1 -1
- package/server/lib/notifications/push-runtime.js +7 -7
- package/server/lib/notifications/routes.js +1 -1
- package/server/lib/notifications/runtime.js +1 -1
- package/server/lib/openchamber-control/action-resolution.test.js +10 -10
- package/server/lib/openchamber-control/actions.js +24 -24
- package/server/lib/openchamber-control/error.js +4 -4
- package/server/lib/openchamber-control/routes.js +2 -2
- package/server/lib/openchamber-control/routes.test.js +5 -5
- package/server/lib/openchamber-control/screenshots.js +1 -1
- package/server/lib/openchamber-control/screenshots.test.js +1 -1
- package/server/lib/openchamber-control/service.js +50 -50
- package/server/lib/openchamber-control/service.test.js +5 -5
- package/server/lib/openchamber-sessions/routes.js +124 -22
- package/server/lib/openchamber-sessions/routes.test.js +99 -11
- package/server/lib/opencode/DOCUMENTATION.md +13 -5
- package/server/lib/opencode/auth.js +1 -1
- package/server/lib/opencode/bootstrap-runtime.js +6 -6
- package/server/lib/opencode/cli-options.js +8 -8
- package/server/lib/opencode/config-paths.test.js +116 -0
- package/server/lib/opencode/core-routes.js +8 -7
- package/server/lib/opencode/core-routes.test.js +11 -11
- package/server/lib/opencode/env-config.js +5 -5
- package/server/lib/opencode/env-config.test.js +7 -7
- package/server/lib/opencode/env-runtime.js +10 -10
- package/server/lib/opencode/env-runtime.test.js +18 -18
- package/server/lib/opencode/feature-routes-runtime.js +17 -12
- package/server/lib/opencode/hmr-state-runtime.js +2 -2
- package/server/lib/opencode/hmr-state-runtime.test.js +1 -1
- package/server/lib/opencode/input-history-scope.js +14 -0
- package/server/lib/opencode/lifecycle.js +8 -8
- package/server/lib/opencode/lifecycle.test.js +4 -4
- package/server/lib/opencode/managed-plugin-config.js +35 -0
- package/server/lib/opencode/managed-process-registry.js +3 -3
- package/server/lib/opencode/mcp-oauth-callback.test.js +2 -2
- package/server/lib/opencode/openchamber-routes.js +15 -15
- package/server/lib/opencode/openchamber-routes.test.js +11 -11
- package/server/lib/opencode/plugins.js +2 -2
- package/server/lib/opencode/plugins.test.js +1 -1
- package/server/lib/opencode/project-icon-routes.js +2 -2
- package/server/lib/opencode/project-icon-routes.test.js +1 -1
- package/server/lib/opencode/providers.js +44 -2
- package/server/lib/opencode/providers.test.js +131 -4
- package/server/lib/opencode/proxy.js +2 -2
- package/server/lib/opencode/pwa-manifest-routes.js +1 -1
- package/server/lib/opencode/restart-session-recovery.test.js +1 -1
- package/server/lib/opencode/routes-upgrade.test.js +4 -4
- package/server/lib/opencode/routes.js +14 -14
- package/server/lib/opencode/server-startup-runtime.js +6 -6
- package/server/lib/opencode/session-runtime.js +3 -3
- package/server/lib/opencode/session-runtime.test.js +6 -6
- package/server/lib/opencode/settings-helpers.js +31 -3
- package/server/lib/opencode/settings-helpers.test.js +189 -12
- package/server/lib/opencode/settings-runtime.js +2 -2
- package/server/lib/opencode/settings-runtime.test.js +29 -0
- package/server/lib/opencode/shared.js +5 -2
- package/server/lib/opencode/shared.test.js +7 -7
- package/server/lib/opencode/shutdown-runtime.js +2 -0
- package/server/lib/opencode/skill-routes.js +2 -1
- package/server/lib/opencode/snippets.js +1 -2
- package/server/lib/opencode/startup-performance.js +1 -1
- package/server/lib/opencode/startup-performance.test.js +6 -6
- package/server/lib/opencode/static-routes-runtime.js +8 -8
- package/server/lib/opencode/static-routes-runtime.test.js +7 -7
- package/server/lib/opencode/upgrade-capability.js +2 -2
- package/server/lib/opencode/upgrade-capability.test.js +3 -3
- package/server/lib/package-manager.js +17 -42
- package/server/lib/package-manager.test.js +19 -19
- package/server/lib/permission-auto-accept/runtime.js +1 -1
- package/server/lib/project-context/routes.js +1 -1
- package/server/lib/projects/project-config.js +2 -2
- package/server/lib/quota/DOCUMENTATION.md +9 -1
- package/server/lib/quota/credentials/providers.js +4 -0
- package/server/lib/quota/credentials/store.js +4 -4
- package/server/lib/quota/credentials/store.test.js +8 -8
- package/server/lib/quota/index.js +1 -0
- package/server/lib/quota/providers/exe-dev.js +75 -0
- package/server/lib/quota/providers/exe-dev.test.js +47 -0
- package/server/lib/quota/providers/hyper.js +118 -0
- package/server/lib/quota/providers/hyper.test.js +126 -0
- package/server/lib/quota/providers/index.js +15 -0
- package/server/lib/quota/providers/ollama-cloud.js +1 -1
- package/server/lib/quota/providers/opencode-go.js +2 -1
- package/server/lib/quota/providers/opencode-go.test.js +9 -5
- package/server/lib/quota/providers/xai.js +1 -1
- package/server/lib/quota/routes.js +2 -0
- package/server/lib/quota/utils/auth.js +1 -1
- package/server/lib/relay/DOCUMENTATION.md +1 -1
- package/server/lib/relay/host-client.js +1 -1
- package/server/lib/relay/host-client.test.js +9 -1
- package/server/lib/relay/host-lock.js +1 -1
- package/server/lib/relay/service.js +5 -5
- package/server/lib/relay/tunnel-host.js +3 -1
- package/server/lib/relay/tunnel-host.test.js +9 -1
- package/server/lib/scheduled-tasks/issue-2710-double-execution.test.js +2 -2
- package/server/lib/scheduled-tasks/routes.js +5 -5
- package/server/lib/scheduled-tasks/service.js +25 -25
- package/server/lib/scheduled-tasks/service.test.js +1 -1
- package/server/lib/security/bind-host.js +4 -4
- package/server/lib/security/bind-host.test.js +1 -1
- package/server/lib/security/request-security.js +11 -5
- package/server/lib/security/request-security.test.js +14 -14
- package/server/lib/session-assist/runtime.js +10 -9
- package/server/lib/session-folders/routes.js +2 -2
- package/server/lib/session-folders/routes.test.js +8 -8
- package/server/lib/session-goal/DOCUMENTATION.md +20 -7
- package/server/lib/session-goal/create.js +4 -2
- package/server/lib/session-goal/create.test.js +1 -1
- package/server/lib/session-goal/objectives.js +4 -4
- package/server/lib/session-goal/routes.js +1 -1
- package/server/lib/session-goal/runtime.js +88 -22
- package/server/lib/session-goal/runtime.test.js +428 -2
- package/server/lib/session-knowledge/runtime.js +11 -11
- package/server/lib/session-knowledge/runtime.test.js +5 -5
- package/server/lib/skills-catalog/DOCUMENTATION.md +1 -1
- package/server/lib/skills-catalog/cache.test.js +2 -2
- package/server/lib/skills-catalog/disk-cache.js +5 -5
- package/server/lib/skills-catalog/github-meta.test.js +2 -2
- package/server/lib/skills-catalog/install.js +3 -2
- package/server/lib/small-model/DOCUMENTATION.md +4 -0
- package/server/lib/small-model/call.js +9 -3
- package/server/lib/small-model/call.test.js +29 -6
- package/server/lib/small-model/index.js +8 -7
- package/server/lib/small-model/index.test.js +1 -1
- package/server/lib/small-model/resolve.js +1 -1
- package/server/lib/small-model/resolve.test.js +1 -1
- package/server/lib/small-model/routes.js +2 -1
- package/server/lib/system-prompt/runtime.js +3 -22
- package/server/lib/system-prompt/runtime.test.js +3 -3
- package/server/lib/terminal/DOCUMENTATION.md +11 -7
- package/server/lib/terminal/runtime.js +180 -26
- package/server/lib/terminal/runtime.test.js +554 -27
- package/server/lib/terminal/shells.js +20 -3
- package/server/lib/terminal/shells.test.js +33 -2
- package/server/lib/tts/base-url.js +3 -3
- package/server/lib/tts/routes.test.js +20 -20
- package/server/lib/ui-auth/ui-auth.js +12 -9
- package/server/lib/ui-auth/ui-auth.test.js +19 -3
- package/server/lib/ui-auth/ui-passkeys.js +7 -7
- package/server/lib/walkthrough/index.js +1 -0
- package/server/lib/walkthrough/jobs.test.js +1 -1
- package/server/lib/walkthrough/language.test.js +1 -1
- package/server/lib/walkthrough/model-settings.js +3 -3
- package/server/lib/walkthrough/model-settings.test.js +1 -1
- package/server/lib/walkthrough/pull-request.test.js +5 -5
- package/server/lib/walkthrough/reproduce-2607.test.js +2 -2
- package/server/lib/walkthrough/routes.test.js +26 -5
- package/server/lib/walkthrough/store.js +3 -3
- package/server/lib/walkthrough/store.test.js +1 -1
- package/server/opencode-proxy.test.js +2 -2
- package/server/proxy-headers.js +1 -1
- package/server/sse-routes.test.js +4 -4
- package/README.md.bak +0 -250
- package/bin/lib/DOCUMENTATION.md.bak +0 -163
- package/dist/assets/CommitSection-SGBCAW0w.js +0 -1
- package/dist/assets/FilesView-Bdq8ppva.js +0 -17
- package/dist/assets/GitView-Znn7VJcP.js +0 -6
- package/dist/assets/LinearIssuesView-jP-hXyac.js +0 -1
- package/dist/assets/MarkdownRendererImpl-Dt9mjTRN.js +0 -12
- package/dist/assets/PlanView-B0F3BtPZ.js +0 -5
- package/dist/assets/SettingsView-BBW-S-gh.js +0 -28
- package/dist/assets/ToolPartDiffPreview-CUNe28U1.js +0 -8
- package/dist/assets/index-C57jT0Sa.css +0 -1
- package/dist/assets/index-DW_E3rnL.js +0 -7
- package/dist/assets/main-Bv5Pkngh.js +0 -928
- package/dist/assets/pl-Dioz29BI.js +0 -15
- package/dist/assets/renderMobileApp-D9yQJy0L.js +0 -25
- package/dist/assets/runtimeEndpointReset-CjhvxLH0.js +0 -16
- package/dist/assets/tr-DFnMlPxY.js +0 -6
- package/dist/assets/useInlineCommentController-CDEuSlPH.js +0 -2
- package/dist/assets/vendor-zod-BLfBSgBh.js +0 -39
- package/server/index.d.ts.bak +0 -39
- package/server/lib/agent-tool/DOCUMENTATION.md.bak +0 -119
- package/server/lib/browser-control/DOCUMENTATION.md.bak +0 -50
- package/server/lib/context-obligatory/DOCUMENTATION.md.bak +0 -19
- package/server/lib/dev-tunnel/DOCUMENTATION.md.bak +0 -57
- package/server/lib/dictation/DOCUMENTATION.md.bak +0 -100
- package/server/lib/event-stream/DOCUMENTATION.md.bak +0 -62
- package/server/lib/fs/DOCUMENTATION.md.bak +0 -47
- package/server/lib/git/DOCUMENTATION.md.bak +0 -193
- package/server/lib/github/DOCUMENTATION.md.bak +0 -183
- package/server/lib/linear/DOCUMENTATION.md.bak +0 -97
- package/server/lib/linear/OAUTH-BROKER-HANDOFF.md.bak +0 -91
- package/server/lib/markdown-image-grants/DOCUMENTATION.md.bak +0 -40
- package/server/lib/notifications/APNS.md.bak +0 -135
- package/server/lib/notifications/DOCUMENTATION.md.bak +0 -138
- package/server/lib/openchamber-control/DOCUMENTATION.md.bak +0 -64
- package/server/lib/opencode/DOCUMENTATION.md.bak +0 -431
- package/server/lib/opencode/managed-process-registry.d.ts.bak +0 -13
- package/server/lib/opencode/provider-env-aliases.d.ts.bak +0 -3
- package/server/lib/permission-auto-accept/DOCUMENTATION.md.bak +0 -34
- package/server/lib/project-context/DOCUMENTATION.md.bak +0 -160
- package/server/lib/quota/DOCUMENTATION.md.bak +0 -123
- package/server/lib/relay/DOCUMENTATION.md.bak +0 -100
- package/server/lib/scheduled-tasks/DOCUMENTATION.md.bak +0 -182
- package/server/lib/session-assist/DOCUMENTATION.md.bak +0 -66
- package/server/lib/session-goal/DOCUMENTATION.md.bak +0 -213
- package/server/lib/session-knowledge/DOCUMENTATION.md.bak +0 -89
- package/server/lib/skills-catalog/DOCUMENTATION.md.bak +0 -151
- package/server/lib/small-model/DOCUMENTATION.md.bak +0 -212
- package/server/lib/system-prompt/DOCUMENTATION.md.bak +0 -36
- package/server/lib/terminal/DOCUMENTATION.md.bak +0 -50
- package/server/lib/text/DOCUMENTATION.md.bak +0 -35
- package/server/lib/tts/DOCUMENTATION.md.bak +0 -147
- package/server/lib/tunnels/DOCUMENTATION.md.bak +0 -21
- package/server/lib/ui-auth/DOCUMENTATION.md.bak +0 -44
- package/server/lib/walkthrough/DOCUMENTATION.md.bak +0 -411
|
@@ -5,10 +5,10 @@ function resolveSystemdServiceUnit(environment) {
|
|
|
5
5
|
return null;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
const configuredUnit = typeof environment.
|
|
9
|
-
? environment.
|
|
8
|
+
const configuredUnit = typeof environment.AIWORKER_SYSTEMD_UNIT === 'string'
|
|
9
|
+
? environment.AIWORKER_SYSTEMD_UNIT.trim()
|
|
10
10
|
: '';
|
|
11
|
-
const unit = configuredUnit || '
|
|
11
|
+
const unit = configuredUnit || 'aiworker.service';
|
|
12
12
|
return SYSTEMD_SERVICE_UNIT_PATTERN.test(unit) ? unit : null;
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -16,14 +16,14 @@ function quotePosixShell(value) {
|
|
|
16
16
|
return `'${String(value).replace(/'/g, "'\\''")}'`;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export const
|
|
19
|
+
export const registerAiWorkerRoutes = (app, dependencies) => {
|
|
20
20
|
const {
|
|
21
21
|
fs,
|
|
22
22
|
path,
|
|
23
23
|
process,
|
|
24
24
|
server,
|
|
25
25
|
__dirname,
|
|
26
|
-
|
|
26
|
+
aiworkerDataDir,
|
|
27
27
|
modelsDevApiUrl,
|
|
28
28
|
modelsMetadataCacheTtl,
|
|
29
29
|
readSettingsFromDiskMigrated,
|
|
@@ -119,7 +119,7 @@ export const registerOpenChamberRoutes = (app, dependencies) => {
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
const currentPort = server.address()?.port || 3000;
|
|
122
|
-
const instanceFilePath = path.join(
|
|
122
|
+
const instanceFilePath = path.join(aiworkerDataDir, 'run', `openchamber-${currentPort}.json`);
|
|
123
123
|
let storedOptions = { port: currentPort, daemon: true };
|
|
124
124
|
try {
|
|
125
125
|
const content = await fs.promises.readFile(instanceFilePath, 'utf8');
|
|
@@ -133,7 +133,7 @@ export const registerOpenChamberRoutes = (app, dependencies) => {
|
|
|
133
133
|
if (isForegroundService) {
|
|
134
134
|
if (!systemdServiceUnit) {
|
|
135
135
|
return res.status(409).json({
|
|
136
|
-
error: 'Foreground servers must be updated by their service manager. Set
|
|
136
|
+
error: 'Foreground servers must be updated by their service manager. Set AIWORKER_SYSTEMD_UNIT when running under systemd, or run aiworker update and restart the service.',
|
|
137
137
|
});
|
|
138
138
|
}
|
|
139
139
|
|
|
@@ -168,7 +168,7 @@ export const registerOpenChamberRoutes = (app, dependencies) => {
|
|
|
168
168
|
|
|
169
169
|
return res.json({
|
|
170
170
|
success: true,
|
|
171
|
-
message: 'Update queued;
|
|
171
|
+
message: 'Update queued; AiWorker will restart after installation completes',
|
|
172
172
|
version: updateInfo.version,
|
|
173
173
|
packageManager: pm,
|
|
174
174
|
autoRestart: true,
|
|
@@ -194,7 +194,7 @@ export const registerOpenChamberRoutes = (app, dependencies) => {
|
|
|
194
194
|
String(storedOptions.port),
|
|
195
195
|
];
|
|
196
196
|
let restartCmdPrimary = restartParts.join(' ');
|
|
197
|
-
let restartCmdFallback = `
|
|
197
|
+
let restartCmdFallback = `aiworker serve --port ${storedOptions.port}`;
|
|
198
198
|
if (storedOptions.host) {
|
|
199
199
|
if (isWindows) {
|
|
200
200
|
const escapedHost = storedOptions.host.replace(/"/g, '""');
|
|
@@ -222,10 +222,10 @@ export const registerOpenChamberRoutes = (app, dependencies) => {
|
|
|
222
222
|
restartCmdFallback += ' --api-only';
|
|
223
223
|
}
|
|
224
224
|
const restartCmd = isForegroundService ? '' : `(${restartCmdPrimary}) || (${restartCmdFallback})`;
|
|
225
|
-
const updateLogPath = path.join(
|
|
225
|
+
const updateLogPath = path.join(aiworkerDataDir, 'update-install.log');
|
|
226
226
|
const logPreamble = [
|
|
227
227
|
'',
|
|
228
|
-
`===
|
|
228
|
+
`=== AiWorker update ${new Date().toISOString()} ===`,
|
|
229
229
|
`currentVersion=${updateInfo.currentVersion || 'unknown'}`,
|
|
230
230
|
`targetVersion=${updateInfo.version || 'unknown'}`,
|
|
231
231
|
`packageManager=${pm}`,
|
|
@@ -262,8 +262,8 @@ export const registerOpenChamberRoutes = (app, dependencies) => {
|
|
|
262
262
|
timeout /t 2 /nobreak >nul
|
|
263
263
|
${updateCmd}
|
|
264
264
|
if %ERRORLEVEL% EQU 0 (
|
|
265
|
-
echo Update successful, restarting
|
|
266
|
-
${restartCmd || 'echo Service manager will restart
|
|
265
|
+
echo Update successful, restarting AiWorker...
|
|
266
|
+
${restartCmd || 'echo Service manager will restart AiWorker.'}
|
|
267
267
|
) else (
|
|
268
268
|
echo Update failed
|
|
269
269
|
exit /b 1
|
|
@@ -274,8 +274,8 @@ export const registerOpenChamberRoutes = (app, dependencies) => {
|
|
|
274
274
|
sleep 2
|
|
275
275
|
${updateCmd}
|
|
276
276
|
if [ $? -eq 0 ]; then
|
|
277
|
-
echo "Update successful, restarting
|
|
278
|
-
${restartCmd || 'echo "Service manager will restart
|
|
277
|
+
echo "Update successful, restarting AiWorker..."
|
|
278
|
+
${restartCmd || 'echo "Service manager will restart AiWorker."'}
|
|
279
279
|
else
|
|
280
280
|
echo "Update failed"
|
|
281
281
|
exit 1
|
|
@@ -16,7 +16,7 @@ vi.mock('../package-manager.js', () => ({
|
|
|
16
16
|
|
|
17
17
|
const childProcess = await import('child_process');
|
|
18
18
|
const packageManager = await import('../package-manager.js');
|
|
19
|
-
const {
|
|
19
|
+
const { registerAiWorkerRoutes } = await import('./openchamber-routes.js');
|
|
20
20
|
|
|
21
21
|
const createApp = ({ environment = {}, storedOptions = {} } = {}) => {
|
|
22
22
|
const app = express();
|
|
@@ -41,7 +41,7 @@ const createApp = ({ environment = {}, storedOptions = {} } = {}) => {
|
|
|
41
41
|
address: () => ({ port: 7897 }),
|
|
42
42
|
},
|
|
43
43
|
__dirname: '/opt/openchamber/server',
|
|
44
|
-
|
|
44
|
+
aiworkerDataDir: '/tmp/openchamber',
|
|
45
45
|
modelsDevApiUrl: 'https://models.example.test',
|
|
46
46
|
modelsMetadataCacheTtl: 0,
|
|
47
47
|
readSettingsFromDiskMigrated: vi.fn(),
|
|
@@ -49,7 +49,7 @@ const createApp = ({ environment = {}, storedOptions = {} } = {}) => {
|
|
|
49
49
|
getCachedZenModels: vi.fn(),
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
registerAiWorkerRoutes(app, dependencies);
|
|
53
53
|
return { app, dependencies };
|
|
54
54
|
};
|
|
55
55
|
|
|
@@ -61,7 +61,7 @@ beforeEach(() => {
|
|
|
61
61
|
packageManager.detectPackageManagerDetails.mockReturnValue({
|
|
62
62
|
packageManager: 'npm',
|
|
63
63
|
});
|
|
64
|
-
packageManager.getUpdateCommand.mockReturnValue('npm install -g @
|
|
64
|
+
packageManager.getUpdateCommand.mockReturnValue('npm install -g @aiworker/web@latest');
|
|
65
65
|
});
|
|
66
66
|
|
|
67
67
|
afterEach(() => {
|
|
@@ -69,14 +69,14 @@ afterEach(() => {
|
|
|
69
69
|
vi.clearAllMocks();
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
-
describe('
|
|
72
|
+
describe('AiWorker foreground update route', () => {
|
|
73
73
|
it('rejects a foreground update when the server is not owned by systemd', async () => {
|
|
74
74
|
const { app } = createApp();
|
|
75
75
|
|
|
76
76
|
await request(app)
|
|
77
77
|
.post('/api/openchamber/update-install')
|
|
78
78
|
.expect(409, {
|
|
79
|
-
error: 'Foreground servers must be updated by their service manager. Set
|
|
79
|
+
error: 'Foreground servers must be updated by their service manager. Set AIWORKER_SYSTEMD_UNIT when running under systemd, or run aiworker update and restart the service.',
|
|
80
80
|
});
|
|
81
81
|
|
|
82
82
|
expect(childProcess.spawnSync).not.toHaveBeenCalled();
|
|
@@ -86,14 +86,14 @@ describe('OpenChamber foreground update route', () => {
|
|
|
86
86
|
const { app } = createApp({
|
|
87
87
|
environment: {
|
|
88
88
|
INVOCATION_ID: 'systemd-invocation',
|
|
89
|
-
|
|
89
|
+
AIWORKER_SYSTEMD_UNIT: 'aiworker.service; rm -rf /',
|
|
90
90
|
},
|
|
91
91
|
});
|
|
92
92
|
|
|
93
93
|
await request(app)
|
|
94
94
|
.post('/api/openchamber/update-install')
|
|
95
95
|
.expect(409, {
|
|
96
|
-
error: 'Foreground servers must be updated by their service manager. Set
|
|
96
|
+
error: 'Foreground servers must be updated by their service manager. Set AIWORKER_SYSTEMD_UNIT when running under systemd, or run aiworker update and restart the service.',
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
expect(childProcess.spawnSync).not.toHaveBeenCalled();
|
|
@@ -105,7 +105,7 @@ describe('OpenChamber foreground update route', () => {
|
|
|
105
105
|
const { app } = createApp({
|
|
106
106
|
environment: {
|
|
107
107
|
INVOCATION_ID: 'systemd-invocation',
|
|
108
|
-
|
|
108
|
+
AIWORKER_SYSTEMD_UNIT: 'aiworker@wsl.service',
|
|
109
109
|
PATH: '/home/syu/.npm-global/bin:/usr/bin:/bin',
|
|
110
110
|
},
|
|
111
111
|
});
|
|
@@ -114,7 +114,7 @@ describe('OpenChamber foreground update route', () => {
|
|
|
114
114
|
.post('/api/openchamber/update-install')
|
|
115
115
|
.expect(200, {
|
|
116
116
|
success: true,
|
|
117
|
-
message: 'Update queued;
|
|
117
|
+
message: 'Update queued; AiWorker will restart after installation completes',
|
|
118
118
|
version: '1.17.1',
|
|
119
119
|
packageManager: 'npm',
|
|
120
120
|
autoRestart: true,
|
|
@@ -131,7 +131,7 @@ describe('OpenChamber foreground update route', () => {
|
|
|
131
131
|
'--setenv=PATH=/home/syu/.npm-global/bin:/usr/bin:/bin',
|
|
132
132
|
'/bin/sh',
|
|
133
133
|
'-c',
|
|
134
|
-
"set -eu\nnpm install -g @
|
|
134
|
+
"set -eu\nnpm install -g @aiworker/web@latest\nsystemctl --user restart 'aiworker@wsl.service'",
|
|
135
135
|
], {
|
|
136
136
|
encoding: 'utf8',
|
|
137
137
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
|
-
import os from 'os';
|
|
3
2
|
import path from 'path';
|
|
4
3
|
import {
|
|
5
4
|
AGENT_SCOPE,
|
|
5
|
+
OPENCODE_CONFIG_DIR,
|
|
6
6
|
readConfigFile,
|
|
7
7
|
readConfigLayer,
|
|
8
8
|
writeConfig,
|
|
@@ -71,7 +71,7 @@ function getActiveOpencodeConfigDir() {
|
|
|
71
71
|
if (customConfigPath) {
|
|
72
72
|
return path.dirname(path.resolve(customConfigPath));
|
|
73
73
|
}
|
|
74
|
-
return
|
|
74
|
+
return OPENCODE_CONFIG_DIR;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
function getActiveUserConfigPaths() {
|
|
@@ -136,7 +136,7 @@ describe('opencode plugins data layer', () => {
|
|
|
136
136
|
|
|
137
137
|
expect(plugins.listPluginEntries(projectDir).map((entry) => entry.spec)).toEqual(['user-plugin']);
|
|
138
138
|
expect(fs.readFileSync(projectFile, 'utf8')).toBe(partialProject);
|
|
139
|
-
expect(fs.existsSync(`${projectFile}.
|
|
139
|
+
expect(fs.existsSync(`${projectFile}.aiworker.backup`)).toBe(false);
|
|
140
140
|
});
|
|
141
141
|
|
|
142
142
|
test('lists entries from user and project layers with scopes and parsed kinds', () => {
|
|
@@ -3,7 +3,7 @@ export const registerProjectIconRoutes = (app, dependencies) => {
|
|
|
3
3
|
fsPromises,
|
|
4
4
|
path,
|
|
5
5
|
crypto,
|
|
6
|
-
|
|
6
|
+
aiworkerDataDir,
|
|
7
7
|
sanitizeProjects,
|
|
8
8
|
readSettingsFromDiskMigrated,
|
|
9
9
|
persistSettings,
|
|
@@ -12,7 +12,7 @@ export const registerProjectIconRoutes = (app, dependencies) => {
|
|
|
12
12
|
resolveGitBinaryForSpawn,
|
|
13
13
|
} = dependencies;
|
|
14
14
|
|
|
15
|
-
const projectIconsDirPath = path.join(
|
|
15
|
+
const projectIconsDirPath = path.join(aiworkerDataDir, 'project-icons');
|
|
16
16
|
const projectIconMimeToExtension = {
|
|
17
17
|
'image/png': 'png',
|
|
18
18
|
'image/jpeg': 'jpg',
|
|
@@ -79,7 +79,7 @@ describe('project icon routes', () => {
|
|
|
79
79
|
fsPromises,
|
|
80
80
|
path,
|
|
81
81
|
crypto,
|
|
82
|
-
|
|
82
|
+
aiworkerDataDir: '/tmp/openchamber-test',
|
|
83
83
|
sanitizeProjects: (projects) => projects,
|
|
84
84
|
readSettingsFromDiskMigrated: async () => ({
|
|
85
85
|
projects: [{
|
|
@@ -152,6 +152,37 @@ function validateCustomProviderConfig(providerId, config, options = {}) {
|
|
|
152
152
|
return { ok: true, value: { providerId, config: normalized } };
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
function mergeCustomProviderConfig(existingValue, normalizedConfig) {
|
|
156
|
+
const existing = isPlainObject(existingValue) ? existingValue : {};
|
|
157
|
+
const existingOptions = isPlainObject(existing.options) ? existing.options : {};
|
|
158
|
+
const normalizedOptions = isPlainObject(normalizedConfig.options) ? normalizedConfig.options : {};
|
|
159
|
+
const mergedOptions = { ...existingOptions, ...normalizedOptions };
|
|
160
|
+
if (!Object.prototype.hasOwnProperty.call(normalizedOptions, 'headers')) {
|
|
161
|
+
delete mergedOptions.headers;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const existingModels = isPlainObject(existing.models) ? existing.models : {};
|
|
165
|
+
const normalizedModels = isPlainObject(normalizedConfig.models) ? normalizedConfig.models : {};
|
|
166
|
+
const mergedModels = Object.fromEntries(
|
|
167
|
+
Object.entries(normalizedModels).map(([modelId, normalizedModel]) => {
|
|
168
|
+
const existingModel = isPlainObject(existingModels[modelId]) ? existingModels[modelId] : {};
|
|
169
|
+
const nextModel = isPlainObject(normalizedModel) ? normalizedModel : {};
|
|
170
|
+
return [modelId, { ...existingModel, ...nextModel }];
|
|
171
|
+
}),
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
const merged = {
|
|
175
|
+
...existing,
|
|
176
|
+
...normalizedConfig,
|
|
177
|
+
options: mergedOptions,
|
|
178
|
+
models: mergedModels,
|
|
179
|
+
};
|
|
180
|
+
if (!Object.prototype.hasOwnProperty.call(normalizedConfig, 'env')) {
|
|
181
|
+
delete merged.env;
|
|
182
|
+
}
|
|
183
|
+
return merged;
|
|
184
|
+
}
|
|
185
|
+
|
|
155
186
|
/**
|
|
156
187
|
* Persist (create or update) a custom provider block in OpenCode user/project/custom config.
|
|
157
188
|
* Does not write secrets — API keys remain in auth.json via the OpenCode auth API.
|
|
@@ -183,8 +214,19 @@ function upsertProviderConfig(providerId, config, workingDirectory, scope = 'use
|
|
|
183
214
|
|
|
184
215
|
const targetConfig = getConfigForPath(layers, targetPath);
|
|
185
216
|
const providerConfig = isPlainObject(targetConfig.provider) ? { ...targetConfig.provider } : {};
|
|
186
|
-
|
|
217
|
+
const providersAlias = isPlainObject(targetConfig.providers) ? { ...targetConfig.providers } : {};
|
|
218
|
+
const existingProvider = providerConfig[validated.value.providerId] ?? providersAlias[validated.value.providerId];
|
|
219
|
+
const mergedConfig = mergeCustomProviderConfig(existingProvider, validated.value.config);
|
|
220
|
+
providerConfig[validated.value.providerId] = mergedConfig;
|
|
187
221
|
targetConfig.provider = providerConfig;
|
|
222
|
+
if (Object.prototype.hasOwnProperty.call(providersAlias, validated.value.providerId)) {
|
|
223
|
+
delete providersAlias[validated.value.providerId];
|
|
224
|
+
if (Object.keys(providersAlias).length === 0) {
|
|
225
|
+
delete targetConfig.providers;
|
|
226
|
+
} else {
|
|
227
|
+
targetConfig.providers = providersAlias;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
188
230
|
|
|
189
231
|
if (Array.isArray(targetConfig.disabled_providers)) {
|
|
190
232
|
targetConfig.disabled_providers = targetConfig.disabled_providers.filter(
|
|
@@ -198,7 +240,7 @@ function upsertProviderConfig(providerId, config, workingDirectory, scope = 'use
|
|
|
198
240
|
return {
|
|
199
241
|
providerId: validated.value.providerId,
|
|
200
242
|
path: writePath,
|
|
201
|
-
config:
|
|
243
|
+
config: mergedConfig,
|
|
202
244
|
};
|
|
203
245
|
}
|
|
204
246
|
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
getProviderSources,
|
|
10
10
|
removeProviderConfig,
|
|
11
11
|
} from './providers.js';
|
|
12
|
+
import { OPENCODE_CONFIG_DIR } from './shared.js';
|
|
12
13
|
|
|
13
14
|
let projectDir;
|
|
14
15
|
|
|
@@ -162,6 +163,132 @@ describe('custom provider config persistence', () => {
|
|
|
162
163
|
expect(written.disabled_providers).toEqual(['other']);
|
|
163
164
|
});
|
|
164
165
|
|
|
166
|
+
test('upsertProviderConfig preserves unmanaged provider and model metadata', () => {
|
|
167
|
+
const configPath = path.join(projectDir, 'opencode.json');
|
|
168
|
+
writeJson(configPath, {
|
|
169
|
+
provider: {
|
|
170
|
+
'campus-llm': {
|
|
171
|
+
npm: '@ai-sdk/openai-compatible',
|
|
172
|
+
name: 'Old',
|
|
173
|
+
customProviderField: { owner: 'user' },
|
|
174
|
+
env: ['OLD_KEY'],
|
|
175
|
+
options: {
|
|
176
|
+
baseURL: 'https://old.example.edu/v1',
|
|
177
|
+
headers: { 'X-Old': '1' },
|
|
178
|
+
timeout: 45_000,
|
|
179
|
+
},
|
|
180
|
+
models: {
|
|
181
|
+
retained: {
|
|
182
|
+
name: 'Old retained name',
|
|
183
|
+
reasoning: true,
|
|
184
|
+
attachment: true,
|
|
185
|
+
tool_call: true,
|
|
186
|
+
modalities: { input: ['text', 'image', 'pdf'], output: ['text'] },
|
|
187
|
+
limit: { context: 1_050_000, input: 922_000, output: 128_000 },
|
|
188
|
+
options: { instructions: 'Keep this instruction' },
|
|
189
|
+
variants: {
|
|
190
|
+
low: { reasoningEffort: 'low' },
|
|
191
|
+
high: { reasoningEffort: 'high' },
|
|
192
|
+
},
|
|
193
|
+
customModelField: { source: 'manual' },
|
|
194
|
+
},
|
|
195
|
+
removed: {
|
|
196
|
+
name: 'Remove me',
|
|
197
|
+
reasoning: true,
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
upsertProviderConfig('campus-llm', {
|
|
205
|
+
name: 'Campus LLM',
|
|
206
|
+
options: { baseURL: 'https://new.example.edu/v1' },
|
|
207
|
+
models: {
|
|
208
|
+
retained: { name: 'Retained model' },
|
|
209
|
+
added: { name: 'Added model' },
|
|
210
|
+
},
|
|
211
|
+
}, projectDir, 'project', { hasStoredAuth: true });
|
|
212
|
+
|
|
213
|
+
const written = readJson(configPath).provider['campus-llm'];
|
|
214
|
+
expect(written).toEqual({
|
|
215
|
+
npm: '@ai-sdk/openai-compatible',
|
|
216
|
+
name: 'Campus LLM',
|
|
217
|
+
customProviderField: { owner: 'user' },
|
|
218
|
+
options: {
|
|
219
|
+
baseURL: 'https://new.example.edu/v1',
|
|
220
|
+
timeout: 45_000,
|
|
221
|
+
},
|
|
222
|
+
models: {
|
|
223
|
+
retained: {
|
|
224
|
+
name: 'Retained model',
|
|
225
|
+
reasoning: true,
|
|
226
|
+
attachment: true,
|
|
227
|
+
tool_call: true,
|
|
228
|
+
modalities: { input: ['text', 'image', 'pdf'], output: ['text'] },
|
|
229
|
+
limit: { context: 1_050_000, input: 922_000, output: 128_000 },
|
|
230
|
+
options: { instructions: 'Keep this instruction' },
|
|
231
|
+
variants: {
|
|
232
|
+
low: { reasoningEffort: 'low' },
|
|
233
|
+
high: { reasoningEffort: 'high' },
|
|
234
|
+
},
|
|
235
|
+
customModelField: { source: 'manual' },
|
|
236
|
+
},
|
|
237
|
+
added: { name: 'Added model' },
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test('upsertProviderConfig preserves metadata while migrating the legacy providers alias', () => {
|
|
243
|
+
const configPath = path.join(projectDir, 'opencode.json');
|
|
244
|
+
writeJson(configPath, {
|
|
245
|
+
providers: {
|
|
246
|
+
legacy: {
|
|
247
|
+
name: 'Legacy provider',
|
|
248
|
+
options: { baseURL: 'https://old.example.com/v1', timeout: 30_000 },
|
|
249
|
+
models: { model: { name: 'Old model', reasoning: true } },
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
upsertProviderConfig('legacy', {
|
|
255
|
+
name: 'Updated provider',
|
|
256
|
+
options: { baseURL: 'https://new.example.com/v1' },
|
|
257
|
+
models: { model: { name: 'Updated model' } },
|
|
258
|
+
}, projectDir, 'project', { hasStoredAuth: true });
|
|
259
|
+
|
|
260
|
+
const written = readJson(configPath);
|
|
261
|
+
expect(written.providers).toBeUndefined();
|
|
262
|
+
expect(written.provider.legacy).toEqual({
|
|
263
|
+
npm: '@ai-sdk/openai-compatible',
|
|
264
|
+
name: 'Updated provider',
|
|
265
|
+
options: { baseURL: 'https://new.example.com/v1', timeout: 30_000 },
|
|
266
|
+
models: { model: { name: 'Updated model', reasoning: true } },
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test('migrating one legacy providers entry keeps the other legacy entries', () => {
|
|
271
|
+
const configPath = path.join(projectDir, 'opencode.json');
|
|
272
|
+
writeJson(configPath, {
|
|
273
|
+
providers: {
|
|
274
|
+
legacy: { name: 'Legacy provider', options: { baseURL: 'https://old.example.com/v1' }, models: { model: { name: 'Old model' } } },
|
|
275
|
+
untouched: { name: 'Untouched', options: { baseURL: 'https://other.example.com/v1' }, models: { model: { name: 'Other model' } } },
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
upsertProviderConfig('legacy', {
|
|
280
|
+
name: 'Updated provider',
|
|
281
|
+
options: { baseURL: 'https://new.example.com/v1' },
|
|
282
|
+
models: { model: { name: 'Updated model' } },
|
|
283
|
+
}, projectDir, 'project', { hasStoredAuth: true });
|
|
284
|
+
|
|
285
|
+
const written = readJson(configPath);
|
|
286
|
+
expect(written.providers).toEqual({
|
|
287
|
+
untouched: { name: 'Untouched', options: { baseURL: 'https://other.example.com/v1' }, models: { model: { name: 'Other model' } } },
|
|
288
|
+
});
|
|
289
|
+
expect(written.provider.legacy.name).toBe('Updated provider');
|
|
290
|
+
});
|
|
291
|
+
|
|
165
292
|
test('upsert then remove restores absence', () => {
|
|
166
293
|
upsertProviderConfig('temp-provider', {
|
|
167
294
|
name: 'Temp',
|
|
@@ -230,8 +357,8 @@ describe('custom provider config persistence', () => {
|
|
|
230
357
|
expect(sources.sources.custom.exists).toBe(false);
|
|
231
358
|
|
|
232
359
|
for (const userPath of [
|
|
233
|
-
path.join(
|
|
234
|
-
path.join(
|
|
360
|
+
path.join(OPENCODE_CONFIG_DIR, 'opencode.json'),
|
|
361
|
+
path.join(OPENCODE_CONFIG_DIR, 'config.json'),
|
|
235
362
|
]) {
|
|
236
363
|
if (!fs.existsSync(userPath)) continue;
|
|
237
364
|
const userConfig = readJson(userPath);
|
|
@@ -269,8 +396,8 @@ describe('custom provider config persistence', () => {
|
|
|
269
396
|
expect(sources.sources.project.exists).toBe(false);
|
|
270
397
|
|
|
271
398
|
for (const userPath of [
|
|
272
|
-
path.join(
|
|
273
|
-
path.join(
|
|
399
|
+
path.join(OPENCODE_CONFIG_DIR, 'opencode.json'),
|
|
400
|
+
path.join(OPENCODE_CONFIG_DIR, 'config.json'),
|
|
274
401
|
]) {
|
|
275
402
|
if (!fs.existsSync(userPath)) continue;
|
|
276
403
|
const userConfig = readJson(userPath);
|
|
@@ -407,7 +407,7 @@ export const registerOpenCodeProxy = (app, deps) => {
|
|
|
407
407
|
};
|
|
408
408
|
|
|
409
409
|
const PROXY_REQUEST_TIMEOUT_MS = normalizeProxyTimeout(LONG_REQUEST_TIMEOUT_MS);
|
|
410
|
-
const PROXY_TIMEOUT_MARKER = Symbol('
|
|
410
|
+
const PROXY_TIMEOUT_MARKER = Symbol('aiworkerProxyTimedOut');
|
|
411
411
|
|
|
412
412
|
// A provider OAuth callback blocks upstream for as long as the user takes to
|
|
413
413
|
// sign in in their browser (device-code polling, or a loopback redirect), so
|
|
@@ -793,7 +793,7 @@ export const registerOpenCodeProxy = (app, deps) => {
|
|
|
793
793
|
return null;
|
|
794
794
|
});
|
|
795
795
|
|
|
796
|
-
const settingsPath = path.join(os.homedir(), '.config', '
|
|
796
|
+
const settingsPath = path.join(os.homedir(), '.config', 'aiworker', 'settings.json');
|
|
797
797
|
let projectDirs = [];
|
|
798
798
|
try {
|
|
799
799
|
const settingsRaw = fs.readFileSync(settingsPath, 'utf8');
|
|
@@ -45,7 +45,7 @@ describe('managed OpenCode restart session recovery', () => {
|
|
|
45
45
|
expect(sessionRuntime.getActiveSessionCount()).toBe(0);
|
|
46
46
|
expect(Object.values(sessionRuntime.getSessionStateSnapshot()).map((state) => state.status))
|
|
47
47
|
.toEqual(['idle', 'idle', 'idle']);
|
|
48
|
-
expect(events.filter((event) => event.type === '
|
|
48
|
+
expect(events.filter((event) => event.type === 'aiworker:session-status')).toHaveLength(3);
|
|
49
49
|
expect(events.filter((event) => event.type === 'session.error')).toHaveLength(3);
|
|
50
50
|
expect(broadcastUiNotification).toHaveBeenCalledOnce();
|
|
51
51
|
expect(broadcastUiNotification).toHaveBeenCalledWith(expect.objectContaining({
|
|
@@ -22,7 +22,7 @@ const createApp = (overrides = {}) => {
|
|
|
22
22
|
const dependencies = {
|
|
23
23
|
getOpenCodeUpgradeCapability: () => ({
|
|
24
24
|
supported: false,
|
|
25
|
-
manager: '
|
|
25
|
+
manager: 'aiworker',
|
|
26
26
|
reason: 'bundled',
|
|
27
27
|
}),
|
|
28
28
|
buildOpenCodeUrl: (pathname) => `http://127.0.0.1:4096${pathname}`,
|
|
@@ -44,8 +44,8 @@ describe('OpenCode upgrade routes', () => {
|
|
|
44
44
|
.send({})
|
|
45
45
|
.expect(409, {
|
|
46
46
|
success: false,
|
|
47
|
-
code: '
|
|
48
|
-
error: 'OpenCode is bundled with
|
|
47
|
+
code: 'OPENCODE_UPGRADE_MANAGED_BY_AIWORKER',
|
|
48
|
+
error: 'OpenCode is bundled with AiWorker Desktop and updates with the app.',
|
|
49
49
|
});
|
|
50
50
|
|
|
51
51
|
expect(globalThis.fetch).not.toHaveBeenCalled();
|
|
@@ -68,7 +68,7 @@ describe('OpenCode upgrade routes', () => {
|
|
|
68
68
|
latestVersion: null,
|
|
69
69
|
upgrade: {
|
|
70
70
|
supported: false,
|
|
71
|
-
manager: '
|
|
71
|
+
manager: 'aiworker',
|
|
72
72
|
reason: 'bundled',
|
|
73
73
|
},
|
|
74
74
|
});
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
2
|
import { createProjectIdFromPath } from '../projects/project-id.js';
|
|
3
3
|
import fs from 'fs';
|
|
4
|
-
import os from 'os';
|
|
5
4
|
import path from 'path';
|
|
6
5
|
import {
|
|
7
6
|
buildDeferredRestartResponse,
|
|
8
7
|
} from './config-mutation-response.js';
|
|
9
8
|
import { getClaudeCliAuthStatus } from './claude-cli-auth.js';
|
|
9
|
+
import { OPENCODE_CONFIG_DIR } from './shared.js';
|
|
10
10
|
|
|
11
11
|
export const registerOpenCodeRoutes = (app, dependencies) => {
|
|
12
12
|
const {
|
|
@@ -57,7 +57,7 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
|
|
|
57
57
|
|
|
58
58
|
// Self-contained page for the OAuth return leg: the system browser has no UI
|
|
59
59
|
// session, so it cannot load the SPA behind the auth gate — everything it
|
|
60
|
-
// needs ships inline. `
|
|
60
|
+
// needs ships inline. `aiworker://focus/mcp-auth` raises the desktop app;
|
|
61
61
|
// the link stays visible because some browsers only follow custom-protocol
|
|
62
62
|
// URLs from a user gesture.
|
|
63
63
|
const renderMcpOAuthCallbackPage = ({ title, message, desktopReturn }) => `<!doctype html>
|
|
@@ -65,7 +65,7 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
|
|
|
65
65
|
<head>
|
|
66
66
|
<meta charset="utf-8">
|
|
67
67
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
68
|
-
<title>${escapeHtml(title)} —
|
|
68
|
+
<title>${escapeHtml(title)} — AiWorker</title>
|
|
69
69
|
<style>
|
|
70
70
|
:root { color-scheme: light dark; }
|
|
71
71
|
body { margin: 0; min-height: 100vh; display: flex; align-items: center; justify-content: center;
|
|
@@ -82,8 +82,8 @@ export const registerOpenCodeRoutes = (app, dependencies) => {
|
|
|
82
82
|
<main>
|
|
83
83
|
<h1>${escapeHtml(title)}</h1>
|
|
84
84
|
<p>${escapeHtml(message)}</p>
|
|
85
|
-
${desktopReturn ? `<a class="return" href="
|
|
86
|
-
<script>window.location.href = '
|
|
85
|
+
${desktopReturn ? `<a class="return" href="aiworker://focus/mcp-auth">Return to AiWorker</a>
|
|
86
|
+
<script>window.location.href = 'aiworker://focus/mcp-auth';</script>` : ''}
|
|
87
87
|
</main>
|
|
88
88
|
</body>
|
|
89
89
|
</html>`;
|
|
@@ -238,11 +238,11 @@ ${desktopReturn ? `<a class="return" href="openchamber://focus/mcp-auth">Return
|
|
|
238
238
|
return res.status(409).json({
|
|
239
239
|
success: false,
|
|
240
240
|
code: capability.reason === 'bundled'
|
|
241
|
-
? '
|
|
241
|
+
? 'OPENCODE_UPGRADE_MANAGED_BY_AIWORKER'
|
|
242
242
|
: 'OPENCODE_UPGRADE_UNSUPPORTED',
|
|
243
243
|
error: capability.reason === 'bundled'
|
|
244
|
-
? 'OpenCode is bundled with
|
|
245
|
-
: 'This OpenCode runtime cannot be upgraded by
|
|
244
|
+
? 'OpenCode is bundled with AiWorker Desktop and updates with the app.'
|
|
245
|
+
: 'This OpenCode runtime cannot be upgraded by AiWorker.',
|
|
246
246
|
});
|
|
247
247
|
}
|
|
248
248
|
if (openCodeUpgradePromise) {
|
|
@@ -513,7 +513,7 @@ ${desktopReturn ? `<a class="return" href="openchamber://focus/mcp-auth">Return
|
|
|
513
513
|
// Browser return leg of the MCP OAuth flow, completed entirely server-side.
|
|
514
514
|
//
|
|
515
515
|
// The provider redirects the SYSTEM browser here, and that browser has no
|
|
516
|
-
//
|
|
516
|
+
// AiWorker UI session — the SPA route this path used to land on sits
|
|
517
517
|
// behind the client-side auth gate, so the user saw a login page instead of
|
|
518
518
|
// a finished authorization. No session can be required on this path.
|
|
519
519
|
//
|
|
@@ -562,7 +562,7 @@ ${desktopReturn ? `<a class="return" href="openchamber://focus/mcp-auth">Return
|
|
|
562
562
|
if (!context?.name) {
|
|
563
563
|
return finish(400, {
|
|
564
564
|
title: 'Authorization Failed',
|
|
565
|
-
message: 'This authorization session has expired or is unknown to the running app. Return to
|
|
565
|
+
message: 'This authorization session has expired or is unknown to the running app. Return to AiWorker and click Authorize again.',
|
|
566
566
|
});
|
|
567
567
|
}
|
|
568
568
|
|
|
@@ -583,7 +583,7 @@ ${desktopReturn ? `<a class="return" href="openchamber://focus/mcp-auth">Return
|
|
|
583
583
|
}
|
|
584
584
|
return finish(200, {
|
|
585
585
|
title: 'Authorization Complete',
|
|
586
|
-
message: 'You can close this tab and return to
|
|
586
|
+
message: 'You can close this tab and return to AiWorker.',
|
|
587
587
|
});
|
|
588
588
|
} catch (error) {
|
|
589
589
|
return finish(502, {
|
|
@@ -807,7 +807,7 @@ ${desktopReturn ? `<a class="return" href="openchamber://focus/mcp-auth">Return
|
|
|
807
807
|
});
|
|
808
808
|
|
|
809
809
|
// Behavior / Global AGENTS.md endpoints
|
|
810
|
-
const AGENTS_MD_PATH = path.join(
|
|
810
|
+
const AGENTS_MD_PATH = path.join(OPENCODE_CONFIG_DIR, 'AGENTS.md');
|
|
811
811
|
const MAX_BEHAVIOR_PROMPT_SIZE = 1024 * 1024; // 1 MB
|
|
812
812
|
|
|
813
813
|
app.get('/api/behavior/agents-md', async (_req, res) => {
|
|
@@ -815,10 +815,10 @@ ${desktopReturn ? `<a class="return" href="openchamber://focus/mcp-auth">Return
|
|
|
815
815
|
try {
|
|
816
816
|
await fs.promises.access(AGENTS_MD_PATH);
|
|
817
817
|
} catch {
|
|
818
|
-
return res.json({ content: '', exists: false });
|
|
818
|
+
return res.json({ content: '', exists: false, path: AGENTS_MD_PATH });
|
|
819
819
|
}
|
|
820
820
|
const content = await fs.promises.readFile(AGENTS_MD_PATH, 'utf8');
|
|
821
|
-
return res.json({ content, exists: true });
|
|
821
|
+
return res.json({ content, exists: true, path: AGENTS_MD_PATH });
|
|
822
822
|
} catch (error) {
|
|
823
823
|
console.error('Failed to read AGENTS.md:', error);
|
|
824
824
|
return res.status(500).json({ error: 'Failed to read AGENTS.md' });
|