@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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const TERMINAL_SHELL_IDS = ['bash', 'zsh', 'sh', 'fish', 'pwsh', 'powershell', 'cmd', 'dash', 'ksh', 'nu'];
|
|
2
2
|
const TERMINAL_SHELL_ID_SET = new Set(TERMINAL_SHELL_IDS);
|
|
3
|
+
const isString = (value) => String(value) === value;
|
|
3
4
|
|
|
4
5
|
export const normalizeTerminalShell = (value) => {
|
|
5
6
|
if (typeof value !== 'string') return null;
|
|
@@ -24,6 +25,22 @@ export const getTerminalShellLoginArgs = (executable, platform = process.platfor
|
|
|
24
25
|
return null;
|
|
25
26
|
};
|
|
26
27
|
|
|
28
|
+
export const buildTerminalShellLaunch = (executable, { mode = 'interactive', command = null, loginShell = false, platform = process.platform } = {}) => {
|
|
29
|
+
const loginArgs = loginShell ? getTerminalShellLoginArgs(executable, platform) : [];
|
|
30
|
+
if (!loginArgs) throw new Error(`Terminal shell "${shellIdFromPath(executable) ?? executable}" does not support login mode`);
|
|
31
|
+
|
|
32
|
+
if (mode === 'interactive') return { executable, args: loginArgs };
|
|
33
|
+
|
|
34
|
+
const trimmedCommand = isString(command) ? command.trim() : '';
|
|
35
|
+
if (!trimmedCommand) throw new Error('Terminal command is required');
|
|
36
|
+
|
|
37
|
+
const id = shellIdFromPath(executable);
|
|
38
|
+
if (id === 'nu') return { executable, args: [...loginArgs, '-c', trimmedCommand] };
|
|
39
|
+
if (id === 'pwsh' || id === 'powershell') return { executable, args: [...loginArgs, '-Command', trimmedCommand] };
|
|
40
|
+
if (id === 'cmd') return { executable, args: ['/d', '/s', '/c', trimmedCommand] };
|
|
41
|
+
return { executable, args: [...loginArgs, '-i', '-c', trimmedCommand] };
|
|
42
|
+
};
|
|
43
|
+
|
|
27
44
|
export const createTerminalShellResolver = ({ fs, path, searchPathFor, isExecutable, buildAugmentedPath = () => env.PATH || '', platform = process.platform, env = process.env }) => {
|
|
28
45
|
const resolveExecutable = (candidate) => {
|
|
29
46
|
if (!candidate) return null;
|
|
@@ -35,7 +52,7 @@ export const createTerminalShellResolver = ({ fs, path, searchPathFor, isExecuta
|
|
|
35
52
|
|
|
36
53
|
const defaultCandidates = () => platform === 'win32'
|
|
37
54
|
? [
|
|
38
|
-
env.
|
|
55
|
+
env.AIWORKER_TERMINAL_SHELL,
|
|
39
56
|
env.SHELL,
|
|
40
57
|
env.ComSpec,
|
|
41
58
|
path.join(env.SystemRoot || 'C:\\Windows', 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe'),
|
|
@@ -43,7 +60,7 @@ export const createTerminalShellResolver = ({ fs, path, searchPathFor, isExecuta
|
|
|
43
60
|
'powershell.exe',
|
|
44
61
|
'cmd.exe',
|
|
45
62
|
]
|
|
46
|
-
: [env.
|
|
63
|
+
: [env.AIWORKER_TERMINAL_SHELL, env.SHELL, '/bin/zsh', '/bin/bash', '/bin/sh', 'zsh', 'bash', 'sh'];
|
|
47
64
|
|
|
48
65
|
const resolveCandidates = (candidates) => {
|
|
49
66
|
const seen = new Set();
|
|
@@ -68,7 +85,7 @@ export const createTerminalShellResolver = ({ fs, path, searchPathFor, isExecuta
|
|
|
68
85
|
|
|
69
86
|
const candidates = platform === 'win32'
|
|
70
87
|
? [...defaultCandidates(), ...TERMINAL_SHELL_IDS]
|
|
71
|
-
: [env.
|
|
88
|
+
: [env.AIWORKER_TERMINAL_SHELL, env.SHELL, ...configuredShells, ...TERMINAL_SHELL_IDS, '/bin/zsh', '/bin/bash', '/bin/sh'];
|
|
72
89
|
const autoExecutable = resolveCandidates(defaultCandidates())[0] ?? null;
|
|
73
90
|
const byId = new Map([
|
|
74
91
|
['auto', {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import * as shells from './shells.js';
|
|
4
|
+
|
|
5
|
+
const { createTerminalShellResolver, getTerminalShellLoginArgs } = shells;
|
|
4
6
|
|
|
5
7
|
const createResolver = ({ platform = 'linux', env = {}, augmentedPath = '/augmented/bin', executables = [] } = {}) => {
|
|
6
8
|
const available = new Set(executables);
|
|
@@ -55,7 +57,7 @@ describe('terminal shell resolver', () => {
|
|
|
55
57
|
|
|
56
58
|
it('uses environment overrides before platform defaults for auto', async () => {
|
|
57
59
|
const { resolver } = createResolver({
|
|
58
|
-
env: {
|
|
60
|
+
env: { AIWORKER_TERMINAL_SHELL: '/custom/zsh', SHELL: '/bin/bash' },
|
|
59
61
|
executables: ['/custom/zsh', '/bin/bash'],
|
|
60
62
|
});
|
|
61
63
|
|
|
@@ -70,4 +72,33 @@ describe('terminal shell resolver', () => {
|
|
|
70
72
|
expect(getTerminalShellLoginArgs('C:\\Program Files\\PowerShell\\7\\pwsh.exe', 'win32')).toBeNull();
|
|
71
73
|
expect(getTerminalShellLoginArgs('/bin/dash', 'linux')).toBeNull();
|
|
72
74
|
});
|
|
75
|
+
|
|
76
|
+
it('builds interactive shell launches by shell family', () => {
|
|
77
|
+
const buildLaunch = shells.buildTerminalShellLaunch;
|
|
78
|
+
|
|
79
|
+
expect(buildLaunch('/bin/bash', { mode: 'interactive', loginShell: true, platform: 'linux' })).toEqual({ executable: '/bin/bash', args: ['-l'] });
|
|
80
|
+
expect(buildLaunch('/opt/homebrew/bin/fish', { mode: 'interactive', loginShell: true, platform: 'darwin' })).toEqual({ executable: '/opt/homebrew/bin/fish', args: ['--login'] });
|
|
81
|
+
expect(buildLaunch('/usr/bin/nu', { mode: 'interactive', loginShell: true, platform: 'linux' })).toEqual({ executable: '/usr/bin/nu', args: ['--login'] });
|
|
82
|
+
expect(buildLaunch('/usr/bin/pwsh', { mode: 'interactive', loginShell: true, platform: 'linux' })).toEqual({ executable: '/usr/bin/pwsh', args: ['-Login'] });
|
|
83
|
+
expect(buildLaunch('C:\\Windows\\System32\\cmd.exe', { mode: 'interactive', loginShell: false, platform: 'win32' })).toEqual({ executable: 'C:\\Windows\\System32\\cmd.exe', args: [] });
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('builds command launches by shell family', () => {
|
|
87
|
+
const buildLaunch = shells.buildTerminalShellLaunch;
|
|
88
|
+
|
|
89
|
+
expect(buildLaunch('/bin/zsh', { mode: 'command', command: 'printf ready', loginShell: true, platform: 'linux' })).toEqual({ executable: '/bin/zsh', args: ['-l', '-i', '-c', 'printf ready'] });
|
|
90
|
+
expect(buildLaunch('/opt/homebrew/bin/fish', { mode: 'command', command: 'echo ready', loginShell: true, platform: 'darwin' })).toEqual({ executable: '/opt/homebrew/bin/fish', args: ['--login', '-i', '-c', 'echo ready'] });
|
|
91
|
+
expect(buildLaunch('/usr/bin/nu', { mode: 'command', command: 'ls', loginShell: true, platform: 'linux' })).toEqual({ executable: '/usr/bin/nu', args: ['--login', '-c', 'ls'] });
|
|
92
|
+
expect(buildLaunch('/usr/bin/pwsh', { mode: 'command', command: 'Get-ChildItem', loginShell: true, platform: 'linux' })).toEqual({ executable: '/usr/bin/pwsh', args: ['-Login', '-Command', 'Get-ChildItem'] });
|
|
93
|
+
expect(buildLaunch('C:\\Program Files\\PowerShell\\7\\pwsh.exe', { mode: 'command', command: 'Get-Date', loginShell: false, platform: 'win32' })).toEqual({ executable: 'C:\\Program Files\\PowerShell\\7\\pwsh.exe', args: ['-Command', 'Get-Date'] });
|
|
94
|
+
expect(buildLaunch('C:\\Windows\\System32\\cmd.exe', { mode: 'command', command: 'dir', loginShell: false, platform: 'win32' })).toEqual({ executable: 'C:\\Windows\\System32\\cmd.exe', args: ['/d', '/s', '/c', 'dir'] });
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('rejects unsupported login and command combinations', () => {
|
|
98
|
+
const buildLaunch = shells.buildTerminalShellLaunch;
|
|
99
|
+
|
|
100
|
+
expect(() => buildLaunch('/bin/sh', { mode: 'interactive', loginShell: true, platform: 'linux' })).toThrow('does not support login mode');
|
|
101
|
+
expect(() => buildLaunch('/bin/dash', { mode: 'command', command: 'pwd', loginShell: true, platform: 'linux' })).toThrow('does not support login mode');
|
|
102
|
+
expect(() => buildLaunch('/bin/bash', { mode: 'command', command: '', loginShell: false, platform: 'linux' })).toThrow('Terminal command is required');
|
|
103
|
+
});
|
|
73
104
|
});
|
|
@@ -47,13 +47,13 @@ export const normalizeCustomOpenAIBaseURL = (value) => {
|
|
|
47
47
|
return { error: 'Custom server URL must not include credentials' };
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
const isDesktop = (process.env.
|
|
51
|
-
const envFlagRaw = process.env.
|
|
50
|
+
const isDesktop = (process.env.AIWORKER_RUNTIME || '').trim().toLowerCase() === 'desktop';
|
|
51
|
+
const envFlagRaw = process.env.AIWORKER_ALLOW_REMOTE_OPENAI_COMPAT_URLS;
|
|
52
52
|
const hasExplicitFlag = typeof envFlagRaw === 'string' && envFlagRaw.trim().length > 0;
|
|
53
53
|
const allowRemote = hasExplicitFlag ? isEnvFlagEnabled(envFlagRaw) : isDesktop;
|
|
54
54
|
if (!allowRemote && !isAllowedLocalHost(parsed.hostname)) {
|
|
55
55
|
return {
|
|
56
|
-
error: 'Remote custom server URLs are disabled. Set
|
|
56
|
+
error: 'Remote custom server URLs are disabled. Set AIWORKER_ALLOW_REMOTE_OPENAI_COMPAT_URLS=true to allow this host.',
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -97,44 +97,44 @@ describe('tts routes', () => {
|
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
describe('normalizeCustomOpenAIBaseURL', () => {
|
|
100
|
-
const originalRuntime = process.env.
|
|
101
|
-
const originalAllowRemote = process.env.
|
|
100
|
+
const originalRuntime = process.env.AIWORKER_RUNTIME;
|
|
101
|
+
const originalAllowRemote = process.env.AIWORKER_ALLOW_REMOTE_OPENAI_COMPAT_URLS;
|
|
102
102
|
|
|
103
103
|
afterEach(() => {
|
|
104
104
|
// Restore env vars after each test
|
|
105
105
|
if (originalRuntime === undefined) {
|
|
106
|
-
delete process.env.
|
|
106
|
+
delete process.env.AIWORKER_RUNTIME;
|
|
107
107
|
} else {
|
|
108
|
-
process.env.
|
|
108
|
+
process.env.AIWORKER_RUNTIME = originalRuntime;
|
|
109
109
|
}
|
|
110
110
|
if (originalAllowRemote === undefined) {
|
|
111
|
-
delete process.env.
|
|
111
|
+
delete process.env.AIWORKER_ALLOW_REMOTE_OPENAI_COMPAT_URLS;
|
|
112
112
|
} else {
|
|
113
|
-
process.env.
|
|
113
|
+
process.env.AIWORKER_ALLOW_REMOTE_OPENAI_COMPAT_URLS = originalAllowRemote;
|
|
114
114
|
}
|
|
115
115
|
});
|
|
116
116
|
|
|
117
|
-
it('rejects remote URLs when
|
|
118
|
-
delete process.env.
|
|
119
|
-
delete process.env.
|
|
117
|
+
it('rejects remote URLs when AIWORKER_RUNTIME is not set (web)', () => {
|
|
118
|
+
delete process.env.AIWORKER_RUNTIME;
|
|
119
|
+
delete process.env.AIWORKER_ALLOW_REMOTE_OPENAI_COMPAT_URLS;
|
|
120
120
|
|
|
121
121
|
const result = normalizeCustomOpenAIBaseURL('https://my-tts-server.example.com/v1');
|
|
122
122
|
expect(result.error).toMatch(/Remote custom server URLs are disabled/);
|
|
123
123
|
expect(result.value).toBeUndefined();
|
|
124
124
|
});
|
|
125
125
|
|
|
126
|
-
it('allows remote URLs when
|
|
127
|
-
process.env.
|
|
128
|
-
delete process.env.
|
|
126
|
+
it('allows remote URLs when AIWORKER_RUNTIME is desktop', () => {
|
|
127
|
+
process.env.AIWORKER_RUNTIME = 'desktop';
|
|
128
|
+
delete process.env.AIWORKER_ALLOW_REMOTE_OPENAI_COMPAT_URLS;
|
|
129
129
|
|
|
130
130
|
const result = normalizeCustomOpenAIBaseURL('https://my-tts-server.example.com/v1');
|
|
131
131
|
expect(result.error).toBeUndefined();
|
|
132
132
|
expect(result.value).toBe('https://my-tts-server.example.com/v1');
|
|
133
133
|
});
|
|
134
134
|
|
|
135
|
-
it('allows remote URLs when
|
|
136
|
-
delete process.env.
|
|
137
|
-
process.env.
|
|
135
|
+
it('allows remote URLs when AIWORKER_ALLOW_REMOTE_OPENAI_COMPAT_URLS is true', () => {
|
|
136
|
+
delete process.env.AIWORKER_RUNTIME;
|
|
137
|
+
process.env.AIWORKER_ALLOW_REMOTE_OPENAI_COMPAT_URLS = 'true';
|
|
138
138
|
|
|
139
139
|
const result = normalizeCustomOpenAIBaseURL('https://my-tts-server.example.com/v1');
|
|
140
140
|
expect(result.error).toBeUndefined();
|
|
@@ -142,8 +142,8 @@ describe('normalizeCustomOpenAIBaseURL', () => {
|
|
|
142
142
|
});
|
|
143
143
|
|
|
144
144
|
it('allows localhost URLs regardless of runtime', () => {
|
|
145
|
-
delete process.env.
|
|
146
|
-
delete process.env.
|
|
145
|
+
delete process.env.AIWORKER_RUNTIME;
|
|
146
|
+
delete process.env.AIWORKER_ALLOW_REMOTE_OPENAI_COMPAT_URLS;
|
|
147
147
|
|
|
148
148
|
const result = normalizeCustomOpenAIBaseURL('http://localhost:8880/v1');
|
|
149
149
|
expect(result.error).toBeUndefined();
|
|
@@ -151,15 +151,15 @@ describe('normalizeCustomOpenAIBaseURL', () => {
|
|
|
151
151
|
});
|
|
152
152
|
|
|
153
153
|
it('strips query strings and trailing slashes', () => {
|
|
154
|
-
process.env.
|
|
154
|
+
process.env.AIWORKER_RUNTIME = 'desktop';
|
|
155
155
|
|
|
156
156
|
const result = normalizeCustomOpenAIBaseURL('https://my-server.com/v1/?key=123');
|
|
157
157
|
expect(result.value).toBe('https://my-server.com/v1');
|
|
158
158
|
});
|
|
159
159
|
|
|
160
160
|
it('denies remote URLs on desktop when env var is explicitly false', () => {
|
|
161
|
-
process.env.
|
|
162
|
-
process.env.
|
|
161
|
+
process.env.AIWORKER_RUNTIME = 'desktop';
|
|
162
|
+
process.env.AIWORKER_ALLOW_REMOTE_OPENAI_COMPAT_URLS = 'false';
|
|
163
163
|
|
|
164
164
|
const result = normalizeCustomOpenAIBaseURL('https://my-tts-server.example.com/v1');
|
|
165
165
|
expect(result.error).toMatch(/Remote custom server URLs are disabled/);
|
|
@@ -12,10 +12,10 @@ const URL_AUTH_TOKEN_TTL_MS = 60 * 1000;
|
|
|
12
12
|
const URL_AUTH_TOKEN_PREFIX = 'oc_url_';
|
|
13
13
|
|
|
14
14
|
const RATE_LIMIT_WINDOW_MS = 5 * 60 * 1000;
|
|
15
|
-
const RATE_LIMIT_MAX_ATTEMPTS = Number(process.env.
|
|
15
|
+
const RATE_LIMIT_MAX_ATTEMPTS = Number(process.env.AIWORKER_RATE_LIMIT_MAX_ATTEMPTS) || 10;
|
|
16
16
|
const RATE_LIMIT_LOCKOUT_MS = 15 * 60 * 1000;
|
|
17
17
|
const RATE_LIMIT_CLEANUP_MS = 60 * 60 * 1000;
|
|
18
|
-
const RATE_LIMIT_NO_IP_MAX_ATTEMPTS = Number(process.env.
|
|
18
|
+
const RATE_LIMIT_NO_IP_MAX_ATTEMPTS = Number(process.env.AIWORKER_RATE_LIMIT_NO_IP_MAX_ATTEMPTS) || 3;
|
|
19
19
|
|
|
20
20
|
const loginRateLimiter = new Map();
|
|
21
21
|
let rateLimitCleanupTimer = null;
|
|
@@ -310,6 +310,7 @@ const isUrlAuthWebSocketPath = (pathname) => {
|
|
|
310
310
|
|| pathname === '/api/openchamber/realtime-proxy/ws'
|
|
311
311
|
|| pathname === '/api/terminal/ws'
|
|
312
312
|
|| pathname === '/api/dictation/ws'
|
|
313
|
+
|| pathname === '/api/dev-tunnel'
|
|
313
314
|
|| pathname.startsWith('/api/preview/proxy/');
|
|
314
315
|
};
|
|
315
316
|
|
|
@@ -361,10 +362,10 @@ const normalizePassword = (candidate) => {
|
|
|
361
362
|
|
|
362
363
|
const isTrustedDeviceRequest = (value) => value === true;
|
|
363
364
|
|
|
364
|
-
const
|
|
365
|
-
? path.resolve(process.env.
|
|
366
|
-
: path.join(os.homedir(), '.config', '
|
|
367
|
-
const JWT_SECRET_FILE = path.join(
|
|
365
|
+
const AIWORKER_DATA_DIR = process.env.AIWORKER_DATA_DIR
|
|
366
|
+
? path.resolve(process.env.AIWORKER_DATA_DIR)
|
|
367
|
+
: path.join(os.homedir(), '.config', 'aiworker');
|
|
368
|
+
const JWT_SECRET_FILE = path.join(AIWORKER_DATA_DIR, 'jwt-secret');
|
|
368
369
|
|
|
369
370
|
function getOrCreateJwtSecret() {
|
|
370
371
|
const envSecret = process.env.OPENCODE_JWT_SECRET;
|
|
@@ -382,7 +383,7 @@ function getOrCreateJwtSecret() {
|
|
|
382
383
|
|
|
383
384
|
const secret = crypto.randomBytes(32).toString('hex');
|
|
384
385
|
try {
|
|
385
|
-
fs.mkdirSync(
|
|
386
|
+
fs.mkdirSync(AIWORKER_DATA_DIR, { recursive: true });
|
|
386
387
|
fs.writeFileSync(JWT_SECRET_FILE, secret, { mode: 0o600 });
|
|
387
388
|
console.log('[JWT] Generated and persisted new secret to', JWT_SECRET_FILE);
|
|
388
389
|
} catch (e) {
|
|
@@ -399,7 +400,7 @@ function persistJwtSecret(secret) {
|
|
|
399
400
|
throw error;
|
|
400
401
|
}
|
|
401
402
|
|
|
402
|
-
fs.mkdirSync(
|
|
403
|
+
fs.mkdirSync(AIWORKER_DATA_DIR, { recursive: true });
|
|
403
404
|
fs.writeFileSync(JWT_SECRET_FILE, secret, { mode: 0o600 });
|
|
404
405
|
return new TextEncoder().encode(secret);
|
|
405
406
|
}
|
|
@@ -984,7 +985,9 @@ export const createUiAuth = ({
|
|
|
984
985
|
handlePasskeyList,
|
|
985
986
|
handlePasskeyRevoke,
|
|
986
987
|
handleResetAuth,
|
|
987
|
-
ensureSessionToken:
|
|
988
|
+
ensureSessionToken: (req, _res) => {
|
|
989
|
+
const urlAuth = authenticateUrlAuthToken(req);
|
|
990
|
+
if (urlAuth) return clientSessionToken(urlAuth);
|
|
988
991
|
return resolveAuthenticatedSessionToken(req);
|
|
989
992
|
},
|
|
990
993
|
dispose,
|
|
@@ -4,7 +4,7 @@ import os from 'node:os';
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
|
|
6
6
|
const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'openchamber-ui-auth-test-'));
|
|
7
|
-
process.env.
|
|
7
|
+
process.env.AIWORKER_DATA_DIR = dataDir;
|
|
8
8
|
|
|
9
9
|
afterAll(() => {
|
|
10
10
|
fs.rmSync(dataDir, { recursive: true, force: true });
|
|
@@ -221,6 +221,22 @@ describe('ui auth client credential seam', () => {
|
|
|
221
221
|
};
|
|
222
222
|
expect(await auth.ensureSessionToken(dictationWsReq, null)).toBe('client:device-1');
|
|
223
223
|
|
|
224
|
+
const devTunnelWsReq = {
|
|
225
|
+
method: 'GET',
|
|
226
|
+
path: '/api/dev-tunnel',
|
|
227
|
+
url: `/api/dev-tunnel?port=4322&oc_url_token=${encodeURIComponent(urlToken)}`,
|
|
228
|
+
headers: { upgrade: 'websocket' },
|
|
229
|
+
};
|
|
230
|
+
expect(await auth.ensureSessionToken(devTunnelWsReq, null)).toBe('client:device-1');
|
|
231
|
+
|
|
232
|
+
const devTunnelSubpathWsReq = {
|
|
233
|
+
method: 'GET',
|
|
234
|
+
path: '/api/dev-tunnel/private',
|
|
235
|
+
url: `/api/dev-tunnel/private?port=4322&oc_url_token=${encodeURIComponent(urlToken)}`,
|
|
236
|
+
headers: { upgrade: 'websocket' },
|
|
237
|
+
};
|
|
238
|
+
expect(await auth.ensureSessionToken(devTunnelSubpathWsReq, null)).toBe(null);
|
|
239
|
+
|
|
224
240
|
const dictationHttpReq = {
|
|
225
241
|
method: 'GET',
|
|
226
242
|
path: '/api/dictation/ws',
|
|
@@ -287,7 +303,7 @@ describe('ui auth client credential seam', () => {
|
|
|
287
303
|
body: {
|
|
288
304
|
password: 'secret',
|
|
289
305
|
issueClientToken: true,
|
|
290
|
-
clientLabel: '
|
|
306
|
+
clientLabel: 'AiWorker Desktop',
|
|
291
307
|
},
|
|
292
308
|
};
|
|
293
309
|
const res = createResponse();
|
|
@@ -295,7 +311,7 @@ describe('ui auth client credential seam', () => {
|
|
|
295
311
|
await auth.handleSessionCreate(req, res);
|
|
296
312
|
|
|
297
313
|
expect(res.body.clientToken).toBe('client-token');
|
|
298
|
-
expect(createClientInput.fallbackLabel).toBe('
|
|
314
|
+
expect(createClientInput.fallbackLabel).toBe('AiWorker Desktop');
|
|
299
315
|
const expiresAt = Date.parse(createClientInput.expiresAt);
|
|
300
316
|
expect(expiresAt).toBeGreaterThanOrEqual(before + 122_000);
|
|
301
317
|
expect(expiresAt).toBeLessThanOrEqual(Date.now() + 124_000);
|
|
@@ -11,13 +11,13 @@ import {
|
|
|
11
11
|
|
|
12
12
|
const DEFAULT_STORE_VERSION = 1;
|
|
13
13
|
const DEFAULT_CHALLENGE_TTL_MS = 5 * 60 * 1000;
|
|
14
|
-
const DEFAULT_RP_NAME = '
|
|
14
|
+
const DEFAULT_RP_NAME = 'AiWorker';
|
|
15
15
|
|
|
16
|
-
const
|
|
17
|
-
? path.resolve(process.env.
|
|
18
|
-
: path.join(os.homedir(), '.config', '
|
|
16
|
+
const AIWORKER_DATA_DIR = process.env.AIWORKER_DATA_DIR
|
|
17
|
+
? path.resolve(process.env.AIWORKER_DATA_DIR)
|
|
18
|
+
: path.join(os.homedir(), '.config', 'aiworker');
|
|
19
19
|
|
|
20
|
-
const PASSKEY_STORE_FILE = path.join(
|
|
20
|
+
const PASSKEY_STORE_FILE = path.join(AIWORKER_DATA_DIR, 'ui-passkeys.json');
|
|
21
21
|
|
|
22
22
|
const createUserId = () => crypto.randomBytes(32).toString('base64url');
|
|
23
23
|
|
|
@@ -331,7 +331,7 @@ export const createUiPasskeys = ({
|
|
|
331
331
|
rpID,
|
|
332
332
|
userID,
|
|
333
333
|
userName: 'openchamber-ui',
|
|
334
|
-
userDisplayName: '
|
|
334
|
+
userDisplayName: 'AiWorker UI',
|
|
335
335
|
attestationType: 'none',
|
|
336
336
|
excludeCredentials: getPasskeysForRpId(store, rpID).map((passkey) => ({
|
|
337
337
|
id: passkey.id,
|
|
@@ -471,7 +471,7 @@ export const createUiPasskeys = ({
|
|
|
471
471
|
const passkey = store.passkeys.find((item) => item.id === response?.id);
|
|
472
472
|
|
|
473
473
|
if (!passkey) {
|
|
474
|
-
const error = new Error('That passkey is not registered for this
|
|
474
|
+
const error = new Error('That passkey is not registered for this AiWorker instance');
|
|
475
475
|
error.statusCode = 404;
|
|
476
476
|
throw error;
|
|
477
477
|
}
|
|
@@ -474,6 +474,7 @@ async function runGeneration({ directory, source, repoRoot, key, force, explicit
|
|
|
474
474
|
prompt: options.prompt,
|
|
475
475
|
system: options.system,
|
|
476
476
|
directory,
|
|
477
|
+
sessionID: `openchamber-walkthrough-${cacheKey}`,
|
|
477
478
|
model: `${model.providerID}/${model.modelID}`,
|
|
478
479
|
responseSchema: options.responseSchema,
|
|
479
480
|
onOverflow: 'error',
|
|
@@ -4,7 +4,7 @@ import path from 'path';
|
|
|
4
4
|
import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
5
|
|
|
6
6
|
const TEMP_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), 'walkthrough-jobs-'));
|
|
7
|
-
process.env.
|
|
7
|
+
process.env.AIWORKER_DATA_DIR = TEMP_DATA_DIR;
|
|
8
8
|
|
|
9
9
|
// Mocking git rather than this module's own source loading: fewer of our own
|
|
10
10
|
// seams faked means the test exercises the real digest and prompt path.
|
|
@@ -4,7 +4,7 @@ import path from 'path';
|
|
|
4
4
|
import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
5
|
|
|
6
6
|
const TEMP_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), 'walkthrough-language-'));
|
|
7
|
-
process.env.
|
|
7
|
+
process.env.AIWORKER_DATA_DIR = TEMP_DATA_DIR;
|
|
8
8
|
|
|
9
9
|
vi.mock('../git/service.js', () => ({
|
|
10
10
|
getRepositoryRoot: vi.fn(async () => '/repo'),
|
|
@@ -8,9 +8,9 @@ import path from 'path';
|
|
|
8
8
|
// means the user has to degrade one feature to fix the other.
|
|
9
9
|
|
|
10
10
|
const SETTINGS_FILE = path.join(
|
|
11
|
-
process.env.
|
|
12
|
-
? path.resolve(process.env.
|
|
13
|
-
: path.join(os.homedir(), '.config', '
|
|
11
|
+
process.env.AIWORKER_DATA_DIR
|
|
12
|
+
? path.resolve(process.env.AIWORKER_DATA_DIR)
|
|
13
|
+
: path.join(os.homedir(), '.config', 'aiworker'),
|
|
14
14
|
'settings.json',
|
|
15
15
|
);
|
|
16
16
|
|
|
@@ -4,7 +4,7 @@ import path from 'path';
|
|
|
4
4
|
import { afterAll, beforeEach, describe, expect, it } from 'vitest';
|
|
5
5
|
|
|
6
6
|
const TEMP_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), 'walkthrough-model-settings-'));
|
|
7
|
-
process.env.
|
|
7
|
+
process.env.AIWORKER_DATA_DIR = TEMP_DATA_DIR;
|
|
8
8
|
|
|
9
9
|
const { readWalkthroughModelOverride } = await import('./model-settings.js');
|
|
10
10
|
|
|
@@ -24,8 +24,8 @@ describe('getPullRequestDiff', () => {
|
|
|
24
24
|
// wrapper made every repository look remote-less, which is what this suite
|
|
25
25
|
// exists to prevent.
|
|
26
26
|
resolveGitHubRepoFromDirectory.mockResolvedValue({
|
|
27
|
-
repo: { owner: '
|
|
28
|
-
remoteUrl: 'git@github.com:
|
|
27
|
+
repo: { owner: 'aiworker', repo: 'aiworker' },
|
|
28
|
+
remoteUrl: 'git@github.com:aiworker/openchamber.git',
|
|
29
29
|
});
|
|
30
30
|
});
|
|
31
31
|
|
|
@@ -37,10 +37,10 @@ describe('getPullRequestDiff', () => {
|
|
|
37
37
|
const result = await getPullRequestDiff('/repo', 2122);
|
|
38
38
|
|
|
39
39
|
expect(result.patch).toBe(PATCH);
|
|
40
|
-
expect(result.meta).toEqual({ owner: '
|
|
40
|
+
expect(result.meta).toEqual({ owner: 'aiworker', repo: 'aiworker', number: 2122 });
|
|
41
41
|
expect(request).toHaveBeenCalledWith('GET /repos/{owner}/{repo}/pulls/{pull_number}', {
|
|
42
|
-
owner: '
|
|
43
|
-
repo: '
|
|
42
|
+
owner: 'aiworker',
|
|
43
|
+
repo: 'aiworker',
|
|
44
44
|
pull_number: 2122,
|
|
45
45
|
headers: { accept: 'application/vnd.github.v3.diff' },
|
|
46
46
|
});
|
|
@@ -6,7 +6,7 @@ import express from 'express';
|
|
|
6
6
|
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';
|
|
7
7
|
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
9
|
-
// Regression for https://
|
|
9
|
+
// Regression for https://roweb.cn/roweb/openchamber/issues/2607
|
|
10
10
|
// "[Bug] Why say so?" (walkthrough panel)
|
|
11
11
|
//
|
|
12
12
|
// Before the fix, a walkthrough small model whose provider had no usable login
|
|
@@ -20,7 +20,7 @@ import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';
|
|
|
20
20
|
|
|
21
21
|
const TEMP_HOME = fs.mkdtempSync(path.join(os.tmpdir(), 'oc-home-2607-'));
|
|
22
22
|
process.env.HOME = TEMP_HOME;
|
|
23
|
-
process.env.
|
|
23
|
+
process.env.AIWORKER_DATA_DIR = path.join(TEMP_HOME, '.config', 'aiworker');
|
|
24
24
|
|
|
25
25
|
const CATALOG = {
|
|
26
26
|
deepseek: {
|
|
@@ -15,6 +15,7 @@ describe('walkthrough routes', () => {
|
|
|
15
15
|
let job;
|
|
16
16
|
|
|
17
17
|
let lastArgs;
|
|
18
|
+
let generateCalls = 0;
|
|
18
19
|
|
|
19
20
|
const service = {
|
|
20
21
|
async getWalkthrough(args) {
|
|
@@ -23,6 +24,7 @@ describe('walkthrough routes', () => {
|
|
|
23
24
|
},
|
|
24
25
|
async generateWalkthrough(args) {
|
|
25
26
|
lastArgs = args;
|
|
27
|
+
generateCalls += 1;
|
|
26
28
|
if (job) return job;
|
|
27
29
|
job = new Promise((resolve) => {
|
|
28
30
|
releaseJob = () => resolve({ walkthrough: { title: 'DONE' }, hunks: [], hunkCount: 1 });
|
|
@@ -54,12 +56,27 @@ describe('walkthrough routes', () => {
|
|
|
54
56
|
});
|
|
55
57
|
|
|
56
58
|
afterEach(async () => {
|
|
59
|
+
// A response a failed test never received keeps its keep-alive socket
|
|
60
|
+
// open, and server.close() would wait on it until the hook timeout.
|
|
61
|
+
server.closeAllConnections();
|
|
57
62
|
await new Promise((resolve) => server.close(resolve));
|
|
58
63
|
});
|
|
59
64
|
|
|
65
|
+
// Resolves once the route has asked the service to generate one more time
|
|
66
|
+
// than `seen`. A fixed sleep assumed the request had arrived by then; on a
|
|
67
|
+
// loaded runner it had not, and the step that followed acted on a request
|
|
68
|
+
// the server had not seen yet.
|
|
69
|
+
const untilGenerateCalled = async (seen) => {
|
|
70
|
+
for (let attempt = 0; attempt < 300 && generateCalls <= seen; attempt += 1) {
|
|
71
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
72
|
+
}
|
|
73
|
+
if (generateCalls <= seen) throw new Error('the route never asked the service to generate');
|
|
74
|
+
};
|
|
75
|
+
|
|
60
76
|
it('answers a generation request that nobody interrupted', async () => {
|
|
77
|
+
const seen = generateCalls;
|
|
61
78
|
const pending = generate();
|
|
62
|
-
await
|
|
79
|
+
await untilGenerateCalled(seen);
|
|
63
80
|
releaseJob();
|
|
64
81
|
|
|
65
82
|
const body = await (await pending).json();
|
|
@@ -69,8 +86,9 @@ describe('walkthrough routes', () => {
|
|
|
69
86
|
|
|
70
87
|
it('delivers the result to a client that reconnected after a refresh', async () => {
|
|
71
88
|
const controller = new AbortController();
|
|
89
|
+
const seen = generateCalls;
|
|
72
90
|
generate(controller.signal).catch(() => {});
|
|
73
|
-
await
|
|
91
|
+
await untilGenerateCalled(seen);
|
|
74
92
|
controller.abort();
|
|
75
93
|
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
76
94
|
|
|
@@ -80,8 +98,9 @@ describe('walkthrough routes', () => {
|
|
|
80
98
|
)).json();
|
|
81
99
|
expect(read.generating).toBe(true);
|
|
82
100
|
|
|
101
|
+
const seenBeforeReattach = generateCalls;
|
|
83
102
|
const reattached = generate();
|
|
84
|
-
await
|
|
103
|
+
await untilGenerateCalled(seenBeforeReattach);
|
|
85
104
|
releaseJob();
|
|
86
105
|
|
|
87
106
|
const body = await (await reattached).json();
|
|
@@ -108,12 +127,13 @@ describe('walkthrough routes', () => {
|
|
|
108
127
|
);
|
|
109
128
|
expect(lastArgs.language).toBe('uk');
|
|
110
129
|
|
|
130
|
+
const seen = generateCalls;
|
|
111
131
|
const pending = fetch(`${base}/api/walkthrough/generate`, {
|
|
112
132
|
method: 'POST',
|
|
113
133
|
headers: { 'Content-Type': 'application/json' },
|
|
114
134
|
body: JSON.stringify({ directory: '/repo', source: SOURCE, language: 'ja' }),
|
|
115
135
|
});
|
|
116
|
-
await
|
|
136
|
+
await untilGenerateCalled(seen);
|
|
117
137
|
releaseJob();
|
|
118
138
|
await pending;
|
|
119
139
|
|
|
@@ -129,8 +149,9 @@ describe('walkthrough routes', () => {
|
|
|
129
149
|
});
|
|
130
150
|
|
|
131
151
|
it('cancels through its own endpoint rather than a dropped connection', async () => {
|
|
152
|
+
const seen = generateCalls;
|
|
132
153
|
generate().catch(() => {});
|
|
133
|
-
await
|
|
154
|
+
await untilGenerateCalled(seen);
|
|
134
155
|
|
|
135
156
|
const response = await fetch(`${base}/api/walkthrough/cancel`, {
|
|
136
157
|
method: 'POST',
|
|
@@ -17,9 +17,9 @@ import { PROMPT_VERSION, WALKTHROUGH_VERSION } from './schema.js';
|
|
|
17
17
|
// it written about, and has the code moved since. It is also what feeds the
|
|
18
18
|
// previous walkthrough into a regeneration.
|
|
19
19
|
|
|
20
|
-
const DATA_DIR = process.env.
|
|
21
|
-
? path.resolve(process.env.
|
|
22
|
-
: path.join(os.homedir(), '.config', '
|
|
20
|
+
const DATA_DIR = process.env.AIWORKER_DATA_DIR
|
|
21
|
+
? path.resolve(process.env.AIWORKER_DATA_DIR)
|
|
22
|
+
: path.join(os.homedir(), '.config', 'aiworker');
|
|
23
23
|
|
|
24
24
|
const WALKTHROUGH_DIR = path.join(DATA_DIR, 'walkthroughs');
|
|
25
25
|
const ENTRIES_DIR = path.join(WALKTHROUGH_DIR, 'entries');
|
|
@@ -6,7 +6,7 @@ import { afterAll, beforeEach, describe, expect, it } from 'vitest';
|
|
|
6
6
|
// The store resolves its directory at import time from the environment, so the
|
|
7
7
|
// temp dir has to be in place before the module is loaded.
|
|
8
8
|
const TEMP_ROOT = fs.mkdtempSync(path.join(os.tmpdir(), 'walkthrough-store-'));
|
|
9
|
-
process.env.
|
|
9
|
+
process.env.AIWORKER_DATA_DIR = TEMP_ROOT;
|
|
10
10
|
|
|
11
11
|
const store = await import('./store.js');
|
|
12
12
|
const {
|
|
@@ -381,7 +381,7 @@ describe('OpenCode proxy SSE forwarding', () => {
|
|
|
381
381
|
files: 2,
|
|
382
382
|
diffs: [{ patch: '@@ -1 +1 @@', additions: 5, deletions: 3 }],
|
|
383
383
|
},
|
|
384
|
-
metadata: {
|
|
384
|
+
metadata: { aiworker: { kind: 'review', originalSessionID: 'ses_original' } },
|
|
385
385
|
permission: [{ permission: 'todowrite', action: 'deny', pattern: '*' }],
|
|
386
386
|
revert: { messageID: 'msg_1', partID: 'part_1', snapshot: 'abc123', diff: 'diff --git a/x b/x' },
|
|
387
387
|
},
|
|
@@ -445,7 +445,7 @@ describe('OpenCode proxy SSE forwarding', () => {
|
|
|
445
445
|
cost: 7,
|
|
446
446
|
tokens: { input: 10, output: 20 },
|
|
447
447
|
share: { url: 'https://share.example/ses_1' },
|
|
448
|
-
metadata: {
|
|
448
|
+
metadata: { aiworker: { kind: 'review', originalSessionID: 'ses_original' } },
|
|
449
449
|
project: { id: 'proj_1', worktree: '/repo/app' },
|
|
450
450
|
summary: { additions: 5, deletions: 3, files: 2 },
|
|
451
451
|
revert: { messageID: 'msg_1', partID: 'part_1' },
|
package/server/proxy-headers.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const filteredRequestHeaders = new Set([
|
|
2
|
-
// Client credentials for the
|
|
2
|
+
// Client credentials for the AiWorker server (UI client tokens) must
|
|
3
3
|
// never reach the managed OpenCode upstream — it only accepts its own auth,
|
|
4
4
|
// so a forwarded client bearer turns every upstream response into a 401.
|
|
5
5
|
'authorization',
|
|
@@ -136,7 +136,7 @@ describe('local SSE routes', () => {
|
|
|
136
136
|
expect(res.getHeader('connection')).toBe('keep-alive');
|
|
137
137
|
expect(res.getHeader('x-accel-buffering')).toBe('no');
|
|
138
138
|
expect(res.flushed).toBe(true);
|
|
139
|
-
expect(res.body).toContain('
|
|
139
|
+
expect(res.body).toContain('aiworker:notification-stream-ready');
|
|
140
140
|
expect(clients.has(res)).toBe(true);
|
|
141
141
|
expect(vi.getTimerCount()).toBe(1);
|
|
142
142
|
expect(res.bodyFlushCount).toBe(1);
|
|
@@ -157,12 +157,12 @@ describe('local SSE routes', () => {
|
|
|
157
157
|
}
|
|
158
158
|
});
|
|
159
159
|
|
|
160
|
-
it('serves
|
|
160
|
+
it('serves AiWorker SSE with nginx-safe headers', () => {
|
|
161
161
|
const { app, getRoute } = createRouteRegistry();
|
|
162
162
|
const clients = new Set();
|
|
163
163
|
|
|
164
164
|
registerScheduledTaskRoutes(app, {
|
|
165
|
-
|
|
165
|
+
getAiWorkerEventClients: () => clients,
|
|
166
166
|
writeSseEvent(res, payload) {
|
|
167
167
|
res.write(`data: ${JSON.stringify(payload)}\n\n`);
|
|
168
168
|
},
|
|
@@ -180,7 +180,7 @@ describe('local SSE routes', () => {
|
|
|
180
180
|
expect(res.getHeader('connection')).toBe('keep-alive');
|
|
181
181
|
expect(res.getHeader('x-accel-buffering')).toBe('no');
|
|
182
182
|
expect(res.flushed).toBe(true);
|
|
183
|
-
expect(res.body).toContain('
|
|
183
|
+
expect(res.body).toContain('aiworker:event-stream-ready');
|
|
184
184
|
expect(clients.has(res)).toBe(true);
|
|
185
185
|
|
|
186
186
|
req.emit('close');
|