@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,62 +0,0 @@
|
|
|
1
|
-
# Event Stream Module Documentation
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
This module contains the AiWorker message-stream WebSocket protocol and runtime bridge. It keeps the browser-facing WebSocket transport separate from the upstream OpenCode SSE transport.
|
|
5
|
-
|
|
6
|
-
## Entrypoints and structure
|
|
7
|
-
- `packages/web/server/lib/event-stream/index.js`: public entrypoint re-exporting protocol and runtime helpers.
|
|
8
|
-
- `packages/web/server/lib/event-stream/global-hub.js`: shared global upstream SSE hub for server-side subscribers and browser WS fan-out.
|
|
9
|
-
- `packages/web/server/lib/event-stream/global-ws-bridge.js`: browser-facing global WS bridge that subscribes clients to the shared global hub.
|
|
10
|
-
- `packages/web/server/lib/event-stream/directory-ws-bridge.js`: browser-facing per-directory WS bridge that owns one scoped upstream reader per connection.
|
|
11
|
-
- `packages/web/server/lib/event-stream/protocol.js`: path constants, SSE envelope parsing, and WebSocket frame serialization helpers.
|
|
12
|
-
- `packages/web/server/lib/event-stream/upstream-reader.js`: reusable upstream SSE reader with event-id tracking, stall recovery, and reconnect handling.
|
|
13
|
-
- `packages/web/server/lib/event-stream/runtime.js`: thin WebSocket server runtime for upgrade handling and path dispatch to the global/directory bridges.
|
|
14
|
-
- `packages/web/server/lib/event-stream/protocol.test.js`: unit tests for protocol helpers.
|
|
15
|
-
- `packages/web/server/lib/event-stream/upstream-reader.test.js`: unit tests for upstream SSE reader behavior.
|
|
16
|
-
- `packages/web/server/lib/event-stream/runtime.test.js`: unit tests for runtime-side broadcaster behavior.
|
|
17
|
-
|
|
18
|
-
## Public exports
|
|
19
|
-
|
|
20
|
-
### Protocol helpers
|
|
21
|
-
- `MESSAGE_STREAM_GLOBAL_WS_PATH`: `/api/global/event/ws`
|
|
22
|
-
- `MESSAGE_STREAM_DIRECTORY_WS_PATH`: `/api/event/ws`
|
|
23
|
-
- `MESSAGE_STREAM_WS_HEARTBEAT_INTERVAL_MS`: heartbeat interval for browser-facing WS connections.
|
|
24
|
-
- `parseSseEventEnvelope(block)`: parses an SSE block into `{ eventId, directory, payload }`.
|
|
25
|
-
- `sendMessageStreamWsFrame(socket, payload)`: serializes and sends a JSON WS frame.
|
|
26
|
-
- `sendMessageStreamWsEvent(socket, payload, options)`: sends an event frame with optional `eventId` and `directory`.
|
|
27
|
-
|
|
28
|
-
### Runtime helpers
|
|
29
|
-
- `createGlobalMessageStreamHub(...)`: creates a shared `/global/event` upstream SSE hub with event/status subscribers and bounded event-id replay.
|
|
30
|
-
- `createGlobalUiEventBroadcaster({ sseClients, wsClients, writeSseEvent })`: returns a broadcaster that fans out the same synthetic UI event to SSE and WS clients.
|
|
31
|
-
- `createMessageStreamWsRuntime(...)`: mounts the message-stream WS server, upgrade handler, and SSE-to-WS bridge onto the web HTTP server.
|
|
32
|
-
|
|
33
|
-
### Upstream reader helpers
|
|
34
|
-
- `DEFAULT_UPSTREAM_STALL_TIMEOUT_MS`: default idle timeout before an attached upstream SSE fetch is aborted for reconnect.
|
|
35
|
-
- `DEFAULT_UPSTREAM_RECONNECT_DELAY_MS`: default delay between upstream reconnect attempts.
|
|
36
|
-
- `createUpstreamSseReader(...)`: creates a start/stop reader for OpenCode SSE streams. The reader parses SSE blocks, tracks the latest `Last-Event-ID`, reconnects after closed or stalled upstream streams, and reports events through callbacks.
|
|
37
|
-
|
|
38
|
-
## Runtime behavior
|
|
39
|
-
- Browser clients connect to the WS endpoints above.
|
|
40
|
-
- AiWorker still fetches OpenCode upstream event streams over SSE.
|
|
41
|
-
- The web server creates one shared global message-stream hub. OpenCode watcher side effects and global WS clients subscribe to that hub, so there is one upstream `/global/event` SSE reader for both server-side processing and browser fan-out.
|
|
42
|
-
- The global hub keeps a bounded replay buffer keyed by SSE `eventId` so reconnecting browser clients can receive buffered events after their requested `Last-Event-ID`.
|
|
43
|
-
- Directory WS clients still attach one upstream `/event?directory=...` SSE reader per connection because directory streams are scoped.
|
|
44
|
-
- If an upstream SSE stream stalls after the browser WS is already ready, the reader aborts that upstream fetch and reconnects upstream with `Last-Event-ID`, keeping the browser WS alive when recovery is fast.
|
|
45
|
-
- When the shared global upstream reconnects after it was previously ready, the global WS bridge sends a fresh `ready` frame to already-ready browser clients. The browser treats this as a reconnect edge and can run scoped state repair without requiring the browser WS to close.
|
|
46
|
-
- Health checks are reserved for initial upstream connect failures and explicit upstream-unavailable responses, not for ordinary stall recovery on an already-established stream.
|
|
47
|
-
- Global synthetic events such as `aiworker:session-status`, `aiworker:session-activity`, `aiworker:notification`, and `aiworker:heartbeat` are preserved on the WS path, but heartbeat frames are emitted only while an upstream SSE stream is actively attached.
|
|
48
|
-
- Global UI broadcasts are fan-out capable across both SSE and WS clients.
|
|
49
|
-
- The reusable upstream reader centralizes SSE fetch/parsing/reconnect behavior for the WS runtime and OpenCode watcher. Additional event consumers should move to it only with parity tests for their lifecycle and error semantics.
|
|
50
|
-
- Browser transport concerns live in the WS bridge modules; server-side global stream ownership lives in `global-hub.js`.
|
|
51
|
-
|
|
52
|
-
## Notes for contributors
|
|
53
|
-
- Keep protocol helpers pure and small so they can be unit tested without spinning up a server.
|
|
54
|
-
- Keep `runtime.js` focused on WebSocket upgrade and endpoint dispatch. Put global browser-client lifecycle in `global-ws-bridge.js`, directory stream lifecycle in `directory-ws-bridge.js`, and upstream stream sharing in `global-hub.js`.
|
|
55
|
-
- Do not change upstream OpenCode transport assumptions here; OpenCode remains SSE-based.
|
|
56
|
-
- Keep global replay bounded; do not turn it into an unbounded event log.
|
|
57
|
-
|
|
58
|
-
## Testing
|
|
59
|
-
- Run `bun test packages/web/server/lib/event-stream/protocol.test.js`
|
|
60
|
-
- Run `bun test packages/web/server/lib/event-stream/upstream-reader.test.js`
|
|
61
|
-
- Run `bun test packages/web/server/lib/event-stream/runtime.test.js`
|
|
62
|
-
- Run repo validation before finalizing: `bun run type-check`, `bun run lint`, `bun run build`
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# FS Module Documentation
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
Own filesystem API behavior for the web server runtime, including workspace-bound file operations, directory listing, reveal, and background command execution jobs.
|
|
5
|
-
|
|
6
|
-
## Entrypoints and structure
|
|
7
|
-
- `packages/web/server/lib/fs/routes.js`: route registration and runtime-owned state for `/api/fs/*` endpoints.
|
|
8
|
-
- `packages/web/server/lib/fs/search.js`: fuzzy filesystem search runtime used by non-FS routes (for example project icon discovery).
|
|
9
|
-
|
|
10
|
-
## Public exports
|
|
11
|
-
- `registerFsRoutes(app, dependencies)` from `routes.js`
|
|
12
|
-
- Registers all filesystem routes:
|
|
13
|
-
- `GET /api/fs/home`
|
|
14
|
-
- `POST /api/fs/mkdir`
|
|
15
|
-
- `GET /api/fs/read`
|
|
16
|
-
- `GET /api/fs/raw`
|
|
17
|
-
- `GET /api/fs/serve/:path(*)`
|
|
18
|
-
- `POST /api/fs/write`
|
|
19
|
-
- `POST /api/fs/upload`
|
|
20
|
-
- `POST /api/fs/delete`
|
|
21
|
-
- `POST /api/fs/rename`
|
|
22
|
-
- `POST /api/fs/reveal`
|
|
23
|
-
- `POST /api/fs/exec`
|
|
24
|
-
- `GET /api/fs/exec/:jobId`
|
|
25
|
-
- `GET /api/fs/list`
|
|
26
|
-
- `GET /api/fs/git-dirs` — shallow nested git repository discovery for the
|
|
27
|
-
Git tab (depth- and visit-capped readdir walk; `.git` directory, file, or
|
|
28
|
-
symlink marks a repository boundary; junk directories and symlinks are
|
|
29
|
-
never descended into)
|
|
30
|
-
- Owns exec job queue state (`execJobs`) and lifecycle/TTL pruning.
|
|
31
|
-
- Enforces workspace boundary checks with active project + worktree fallback support.
|
|
32
|
-
- The active project directory is validated with `fs.realpath`, so when the project root is itself a symlink the workspace base no longer matches the paths the client sends. Workspace resolution therefore retries against the raw directory the client requested (`requestedDirectory` from `resolveProjectDirectory`) before falling back to worktree roots. Symlinks are still resolved afterwards, and write/exec routes keep their canonical containment check against the resolved base.
|
|
33
|
-
- `createFsSearchRuntime({ fsPromises, path, spawn, resolveGitBinaryForSpawn })` from `search.js`
|
|
34
|
-
- Returns `{ searchFilesystemFiles(rootPath, options) }`.
|
|
35
|
-
- Supports fuzzy matching, hidden-file handling, and optional `git check-ignore` filtering.
|
|
36
|
-
|
|
37
|
-
## Composition contract with `index.js`
|
|
38
|
-
- `index.js` provides composition-time dependencies only (platform primitives + callbacks such as `resolveProjectDirectory`, `normalizeDirectoryPath`, and `buildAugmentedPath`).
|
|
39
|
-
- `index.js` no longer owns FS route handlers or FS exec job state.
|
|
40
|
-
|
|
41
|
-
## Notes for contributors
|
|
42
|
-
- Keep filesystem policy (workspace root checks, error mapping, exec timeout behavior) inside this module, not in the composition root.
|
|
43
|
-
- Filesystem `EPERM`/`EACCES` failures use the stable `reason: "os-permission"` response marker. Policy denials such as workspace-boundary or missing-grant failures must not use that marker because a native folder picker cannot remediate them.
|
|
44
|
-
- Read-only routes authorize the requested path against the workspace before resolving symlinks. A symlink reached through the workspace may therefore target a file outside it, while a directly requested outside path still requires an exact-path grant. Write routes keep canonical-target boundary checks.
|
|
45
|
-
- If adding new `/api/fs/*` endpoints, add them in `routes.js` and extend this document.
|
|
46
|
-
- `GET /api/fs/list` may resolve symlinks with `realpath` to read directory contents, but the response `path` and each entry `path` must stay in the caller's requested path space (`path.join(requestedPath, name)`). Returning real paths breaks file-tree expansion for directories reached through workspace symlinks.
|
|
47
|
-
- `POST /api/fs/upload` accepts one `application/octet-stream` body with `path` and optional `overwrite=true` query parameters. The body streams into a same-directory temp file with a 100 MiB default cap configurable through `AIWORKER_FS_UPLOAD_MAX_BYTES`; failed and oversized uploads clean up that temp file. New files commit through an atomic no-replace link, existing files return `409` unless overwrite is explicit, directory targets are rejected, and the destination parent resolves before writing so uploads cannot escape through workspace symlinks.
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
# Git Module Documentation
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
This module provides Git repository operations for the web server runtime, including repository management, branch/worktree operations, status/diff queries, commit handling, and merge/rebase workflows.
|
|
5
|
-
|
|
6
|
-
## Entrypoints and structure
|
|
7
|
-
- `packages/web/server/lib/git/`: Git module directory containing all Git-related functionality.
|
|
8
|
-
- `index.js`: Public API entry point imported by `packages/web/server/index.js`.
|
|
9
|
-
- `routes.js`: Express route registration for `/api/git/*` endpoints.
|
|
10
|
-
- `service.js`: Core Git operations (repository, branch, worktree, commit, merge/rebase, status/diff, log).
|
|
11
|
-
- `credentials.js`: Git credentials management.
|
|
12
|
-
- `identity-storage.js`: Git identity (user.name, user.email) storage.
|
|
13
|
-
|
|
14
|
-
## Public API
|
|
15
|
-
|
|
16
|
-
The following functions are exported and used by the web server:
|
|
17
|
-
|
|
18
|
-
### Repository Operations
|
|
19
|
-
- `isGitRepository(directory)`: Check if a directory is a Git repository.
|
|
20
|
-
- `getGlobalIdentity()`: Get global Git user.name, user.email, and core.sshCommand.
|
|
21
|
-
- `getCurrentIdentity(directory)`: Get local Git identity (fallback to global if not set locally).
|
|
22
|
-
- `hasLocalIdentity(directory)`: Check if local Git identity is configured.
|
|
23
|
-
- `setLocalIdentity(directory, profile)`: Set local Git identity (userName, userEmail, authType, sshKey/host).
|
|
24
|
-
- `getRemoteUrl(directory, remoteName)`: Get URL for a specific remote.
|
|
25
|
-
|
|
26
|
-
### Status and Diff Operations
|
|
27
|
-
- `getStatus(directory)`: Get comprehensive Git status including current branch, tracking, ahead/behind, file changes, diff stats, merge/rebase state.
|
|
28
|
-
- `getDiff(directory, { path, staged, contextLines })`: Get diff output for files or entire working tree. Untracked symbolic links are represented as link entries without following their targets.
|
|
29
|
-
- `getRangeDiff(directory, { base, head, path, contextLines })`: Get diff between two refs. Uses three-dot `base...head` semantics, so work merged into `head` from `base` is excluded and only the branch's own changes are returned. Prefers `origin/<base>` when that remote-tracking ref exists, so a stale local base branch does not resurface already-merged commits. Exposed as `GET /api/git/range-diff` (`path` optional; omit it for the whole range).
|
|
30
|
-
- `getRangeFiles(directory, { base, head })`: Get list of changed files between two refs.
|
|
31
|
-
- `getFileDiff(directory, { path, staged })`: Get original and modified file contents for a single file (handles images as data URLs and symbolic links as their link-target text).
|
|
32
|
-
- `listUntrackedPaths(directory)`: List individual untracked file paths honoring ignore rules. Much cheaper than `getStatus` when that is all a caller needs. Deliberately not `--directory`: collapsed directory entries end in a slash and are rejected by the per-file diff helpers, so a caller would silently lose every file inside a new directory.
|
|
33
|
-
- `getUntrackedDiffs(directory, filePaths, { concurrency, contextLines })`: Diffs for untracked files against an empty tree. Resolves the repository context once instead of per file (`getDiff` re-resolves every call, costing an extra `rev-parse` each time) and bounds how many diff processes run at once. Returns one entry per input path in order; unreadable paths yield `''` rather than failing the batch.
|
|
34
|
-
- `collectDiffs(directory, files)`: Collect diff output for multiple files.
|
|
35
|
-
- `revertFile(directory, filePath, options)`: Revert a file. Default scope `all` discards staged and working-tree changes; scope `working` discards only unstaged/working-tree changes.
|
|
36
|
-
- `stageFile(directory, filePath)`: Add one file path to the index.
|
|
37
|
-
- `unstageFile(directory, filePath)`: Remove one file path from the index while preserving working-tree content.
|
|
38
|
-
- `applyHunk(directory, filePath, options)`: Apply a single-hunk patch via `git apply`. `options.action` is `stage` (`git apply --cached`), `unstage` (`git apply --cached --reverse`), or `discard` (`git apply --reverse` in the working tree). The patch is written to a temp file; a `--check` runs first so a stale hunk fails with a clear "refresh and try again" error instead of a partial mutation. The patch target path must match the requested file.
|
|
39
|
-
|
|
40
|
-
### Branch Operations
|
|
41
|
-
- `getBranches(directory)`: Get list of local and remote branches (filtered to active remote branches).
|
|
42
|
-
- `createBranch(directory, branchName, options)`: Create and checkout a new branch.
|
|
43
|
-
- `checkoutBranch(directory, branchName)`: Checkout an existing branch. A remote-tracking name (`origin/main`, or the `remotes/`-prefixed form) resolves to the local branch of that name, created with `--track` when it does not exist yet, because the branch selector offers remote branches as places to work rather than commits to inspect — a literal checkout of the remote ref would detach HEAD. A local branch whose own name looks like a remote ref wins over that resolution, and anything unresolvable is checked out as requested. The returned `branch` is the branch that was actually checked out, which callers should report instead of the requested name.
|
|
44
|
-
- `deleteBranch(directory, branch, options)`: Delete a branch (supports force flag).
|
|
45
|
-
- `renameBranch(directory, oldName, newName)`: Rename a branch and preserve upstream tracking.
|
|
46
|
-
- `getRemotes(directory)`: Get list of configured remotes.
|
|
47
|
-
|
|
48
|
-
### Worktree Operations
|
|
49
|
-
- `getWorktrees(directory)`: List all git worktrees for a repository.
|
|
50
|
-
- `validateWorktreeCreate(directory, input)`: Validate worktree creation parameters (mode, branchName, startRef, upstream config).
|
|
51
|
-
- `createWorktree(directory, input)`: Create a new worktree (supports 'new' and 'existing' modes, upstream setup). After populating the worktree, the repository's `post-checkout` hook runs once with git's standard arguments (null ref as previous HEAD, the checked-out HEAD, and flag `1`) from the worktree directory, mirroring `git worktree add` without `--no-checkout`; a missing or non-executable hook is skipped and a failing hook is logged as a warning, never failing worktree creation or the session bootstrap.
|
|
52
|
-
- `removeWorktree(directory, input)`: Remove a worktree (optionally delete local branch).
|
|
53
|
-
- `isLinkedWorktree(directory)`: Check if directory is a linked worktree (not primary).
|
|
54
|
-
|
|
55
|
-
### Worktree creation from a GitHub pull request
|
|
56
|
-
The UI provisions `pr-<owner>` via `ensureRemoteName`/`ensureRemoteUrl`
|
|
57
|
-
(HTTPS clone URL preferred over SSH) and checks out
|
|
58
|
-
`remotes/pr-<owner>/<head>`. A missing head URL or unreachable fork fails with
|
|
59
|
-
a clear error before a worktree is kept. If upstream fetch fails during
|
|
60
|
-
bootstrap, tracking is left unset rather than writing `branch.*.remote` /
|
|
61
|
-
`branch.*.merge` for a ref that was never fetched.
|
|
62
|
-
|
|
63
|
-
### Commit and Remote Operations
|
|
64
|
-
- `commit(directory, message, options)`: Create a commit from the current index. `options.stageFiles` may be provided with `options.files` by older callers to stage only selected unstaged rows before committing, but the shared Git panel now stages/unstages explicitly before commit.
|
|
65
|
-
- `pull(directory, options)`: Pull changes from remote.
|
|
66
|
-
- `push(directory, options)`: Push changes to remote (auto-sets upstream if needed).
|
|
67
|
-
- `fetch(directory, options)`: Fetch changes from remote.
|
|
68
|
-
- `removeRemote(directory, options)`: Remove a configured remote (except `origin`).
|
|
69
|
-
- `deleteRemoteBranch(directory, options)`: Delete a remote branch.
|
|
70
|
-
|
|
71
|
-
### Log Operations
|
|
72
|
-
- `getLog(directory, options)`: Get commit history with stats (supports maxCount, from, to, file filters).
|
|
73
|
-
- `getCommitFiles(directory, commitHash)`: Get file changes for a specific commit.
|
|
74
|
-
- `getCommitFileDiff(directory, hash, filePath, isBinary)`: Get before/after content for a specific file in a commit. Returns `{ original, modified, isBinary }`. Runs `git show <hash>^:<path>` and `git show <hash>:<path>` in parallel; returns empty strings on failure (added/deleted/root-commit edge cases).
|
|
75
|
-
|
|
76
|
-
### Merge and Rebase Operations
|
|
77
|
-
- `rebase(directory, options)`: Start a rebase onto a target branch.
|
|
78
|
-
- `abortRebase(directory)`: Abort an in-progress rebase.
|
|
79
|
-
- `continueRebase(directory)`: Continue a rebase after conflict resolution.
|
|
80
|
-
- `merge(directory, options)`: Merge a branch into current branch.
|
|
81
|
-
- `abortMerge(directory)`: Abort an in-progress merge.
|
|
82
|
-
- `continueMerge(directory)`: Continue a merge after conflict resolution.
|
|
83
|
-
- `getConflictDetails(directory)`: Get detailed conflict information including operation type, unmerged files, and diff.
|
|
84
|
-
|
|
85
|
-
### Stash Operations
|
|
86
|
-
- `listStashes(directory)`: List stash entries with ref, message, relative time, and hash.
|
|
87
|
-
- `countStashFiles(directory, refs)`: Batch-count changed files for stash refs with bounded concurrency.
|
|
88
|
-
- `stashPush(directory, options)`: Stash changes, always including untracked files, with optional message.
|
|
89
|
-
- `stashApply(directory, options)`: Apply a stash by ref without removing it.
|
|
90
|
-
- `stashPop(directory, options)`: Apply a stash by ref and drop it only after a successful apply.
|
|
91
|
-
- `stashDrop(directory, options)`: Drop a stash by ref.
|
|
92
|
-
|
|
93
|
-
## Internal Helpers
|
|
94
|
-
|
|
95
|
-
The following functions are internal helpers used by exported functions:
|
|
96
|
-
- `buildSshCommand(sshKeyPath)`: Build SSH command string for git config.
|
|
97
|
-
- `buildGitEnv()`: Build Git environment with SSH_AUTH_SOCK resolution.
|
|
98
|
-
- `createGit(directory)`: Create simple-git instance with environment.
|
|
99
|
-
- `normalizeDirectoryPath(value)`: Normalize directory paths (supports ~ expansion).
|
|
100
|
-
- `cleanBranchName(branch)`: Remove refs/heads/ or refs/ prefixes.
|
|
101
|
-
- `parseWorktreePorcelain(raw)`: Parse `git worktree list --porcelain` output.
|
|
102
|
-
- `resolveWorktreeProjectContext(directory)`: Resolve project context (projectID, primaryWorktree, worktreeRoot).
|
|
103
|
-
- `resolveCandidateDirectory(...)`: Generate unique worktree directory candidates.
|
|
104
|
-
- `resolveBranchForExistingMode(...)`: Resolve branch for existing-mode worktree creation.
|
|
105
|
-
- `applyUpstreamConfiguration(...)`: Set upstream tracking for new branches.
|
|
106
|
-
- `runPostCheckoutHook(directory)`: Invoke the worktree's `post-checkout` hook after population, because `git worktree add --no-checkout` and the bootstrap's `git reset --hard` never run git hooks. Runs with git's standard arguments and the worktree as cwd; skips missing/non-executable hooks and never throws on hook failure.
|
|
107
|
-
- And various other internal helpers for Git command execution and parsing.
|
|
108
|
-
|
|
109
|
-
## Response Contracts
|
|
110
|
-
|
|
111
|
-
### Status Response
|
|
112
|
-
- `current`: Current branch name.
|
|
113
|
-
- `tracking`: Upstream branch (e.g., 'origin/main').
|
|
114
|
-
- `ahead`: Number of commits ahead of upstream.
|
|
115
|
-
- `behind`: Number of commits behind upstream.
|
|
116
|
-
- `upstreamComparison`: Optional comparison against `upstream/<current-branch>`, with `{ remote, branch, ahead, behind }`.
|
|
117
|
-
- `files`: Array of file objects with `path`, `index`, `working_dir` status codes.
|
|
118
|
-
- `isClean`: Boolean indicating if working tree is clean.
|
|
119
|
-
- `diffStats`: Object mapping file paths to `{ insertions, deletions }`.
|
|
120
|
-
- `mergeInProgress`: Object with `{ head, message }` if merge in progress.
|
|
121
|
-
- `rebaseInProgress`: Object with `{ headName, onto }` if rebase in progress.
|
|
122
|
-
|
|
123
|
-
### Branches Response
|
|
124
|
-
- `all`: Local branches plus every branch each reachable remote reports via `ls-remote --heads`, formatted as `remotes/<remote>/<branch>`. This is a union: local remote-tracking refs deleted on the remote are pruned, and branches that exist on the remote without a local tracking ref (never fetched) are still included, so a freshly pushed branch appears without requiring a fetch. A remote that fails to answer keeps its locally known branches in the list: "we could not ask" must not be reported as "these branches are gone", because callers use this list to decide whether a base branch exists at all.
|
|
125
|
-
- `current`: Current branch name.
|
|
126
|
-
- `branches`: Per-branch detail keyed by branch name, as reported by `git branch`. Remote-only entries in `all` — branches `ls-remote` reported that were never fetched — have **no** entry here, because `git branch` never saw them. Consumers must treat a missing detail entry as normal and read the name from `all`.
|
|
127
|
-
- Never-fetched remote-only branches also have no local ref, so any operation that resolves one locally has to account for that: `checkoutBranch` fetches the single branch (`git fetch <remote> <branch>`) before creating the tracking branch, and the range helpers (`getRangeDiff`, `getRangeFiles`) reject an unresolvable ref with `Ref "<ref>" is not available locally. Fetch it before comparing.` instead of surfacing git's "ambiguous argument".
|
|
128
|
-
- `defaultBranches`: Each remote's default branch, keyed by remote name. Read from the local `remotes/<name>/HEAD` symbolic ref; for a remote that has none — clone writes it, a hand-added remote may not — the remote itself is asked once with `ls-remote --symref`. A remote that answers neither is absent rather than guessed, and consumers fall back to conventional branch names. Omitted entirely by runtimes that do not provide this Git metadata.
|
|
129
|
-
|
|
130
|
-
### Runtime availability of range diffs
|
|
131
|
-
- `GET /api/git/range-diff` is served by the AiWorker web server, so it is available to web, desktop, and mobile clients. The shared `GitAPI.getGitRangeDiff` is therefore optional: web supplies the HTTP implementation, and VS Code does not implement it because the extension host serves Git through its own bridge rather than these routes. Features built on range diffs (currently the AI diff walkthrough) are not offered in VS Code.
|
|
132
|
-
|
|
133
|
-
### Staged and unstaged change handling
|
|
134
|
-
- `status.files` exposes both `index` and `working_dir` codes. Shared UI uses these as separate scopes: staged rows are derived from non-empty `index` statuses, while unstaged rows are derived from `working_dir` statuses and untracked files.
|
|
135
|
-
- A file with both staged and unstaged changes can appear in both UI sections. Staged rows request diffs with `staged: true`; unstaged rows request normal working-tree diffs.
|
|
136
|
-
- The shared Git panel exposes explicit staging actions. Unstaged rows use `stageFile`, staged rows use `unstageFile`, and commits operate on the current staged index.
|
|
137
|
-
- `stageFiles` remains supported for callers that need to stage a selected unstaged subset as part of commit. In that mode the server temporarily unstages unrelated index entries, stages `stageFiles`, commits from the index, then restores temporarily unstaged entries.
|
|
138
|
-
### Worktree Create/Remove Response
|
|
139
|
-
- `head`: HEAD commit SHA.
|
|
140
|
-
- `name`: Worktree name.
|
|
141
|
-
- `branch`: Local branch name.
|
|
142
|
-
- `path`: Absolute path to worktree directory.
|
|
143
|
-
- `directoryCreated`: Present when create returned after the target directory exists while background Git/bootstrap work continues.
|
|
144
|
-
- `bootstrapStatus`: Background setup state. The legacy `status` remains `pending`, `ready`, or `failed`, while `phase` reports `directory-created`, `git-ready`, or `setup-ready`. Fast create starts at `pending`/`directory-created`; population and upstream Git completion advances to `pending`/`git-ready` before setup/start scripts; completed setup is `ready`/`setup-ready`. A missing in-memory state falls back to `ready`/`setup-ready`; clients continue to accept legacy status responses that omit `phase`.
|
|
145
|
-
- Fast-create background failures remove OpenCode sandbox metadata for directories that never became Git worktrees, and remove the pre-created directory only if it is still empty. User-created files are never recursively deleted by this cleanup.
|
|
146
|
-
- Worktree removal waits for any active create/bootstrap task for that directory before deleting it, preventing a background Git or setup task from restoring removed state or racing filesystem cleanup.
|
|
147
|
-
- Worktree bootstrap retries transient `index.lock` conflicts. If the lock remains byte-for-byte and metadata-identical across the retry window, it is treated as stale, removed, and population continues automatically; changing locks are left untouched and reported as failures.
|
|
148
|
-
- Worktree population enables Git `core.longpaths` (local repo config plus `-c core.longpaths=true` on `git reset --hard`) so deeply nested checkouts under the managed data-dir worktree root do not fail on Windows MAX_PATH with "Filename too long". Path-component limits that the filesystem itself rejects still fail bootstrap, with a clearer path-length guidance message.
|
|
149
|
-
|
|
150
|
-
### Log Response
|
|
151
|
-
- `all`: Array of commit objects with hash, date, message, author info, stats.
|
|
152
|
-
- `latest`: Latest commit object or null.
|
|
153
|
-
- `total`: Total number of commits.
|
|
154
|
-
|
|
155
|
-
## Notes for Contributors
|
|
156
|
-
|
|
157
|
-
### Adding a New Git Operation
|
|
158
|
-
1. Add the function to `packages/web/server/lib/git/service.js`.
|
|
159
|
-
2. Export the function if it's part of the public API.
|
|
160
|
-
3. Use `createGit(directory)` to get a simple-git instance with the correct environment. `directory` is required (`baseDir`); never omit it so commands cannot inherit `process.cwd()`.
|
|
161
|
-
4. Use `runGitCommand(cwd, args)` for direct git command execution with better error handling.
|
|
162
|
-
5. Use `runGitCommandOrThrow(cwd, args, fallbackMessage)` for commands that must succeed.
|
|
163
|
-
6. Return consistent error messages; use `parseGitErrorText(error)` to extract meaningful git errors.
|
|
164
|
-
7. Update this file with the new function in the appropriate API section.
|
|
165
|
-
|
|
166
|
-
### SSH Key Handling
|
|
167
|
-
- SSH keys are escaped and validated via `escapeSshKeyPath` to prevent command injection.
|
|
168
|
-
- On Windows, paths are converted to MSYS format (`C:/path` → `/c/path`).
|
|
169
|
-
- SSH_AUTH_SOCK is automatically resolved via `resolveSshAuthSock` (checks GPG agent, gpgconf).
|
|
170
|
-
|
|
171
|
-
### Working directory (simple-git)
|
|
172
|
-
- Repository operations always pass an explicit `baseDir` (the opened project/directory path) into simple-git. Omitting `baseDir` would default to `process.cwd()`, which breaks when the server was launched from a neutral directory (e.g. `$HOME`) while the opened project lives elsewhere.
|
|
173
|
-
- Global identity reads use the user home directory as `baseDir` (they do not need a repository).
|
|
174
|
-
- A `GitError` / non-repository result from status or check must not abort project/session enumeration: routes return a soft non-repo payload and log a warning.
|
|
175
|
-
|
|
176
|
-
### Worktree Naming
|
|
177
|
-
- Worktree names are slugified via `slugWorktreeName`.
|
|
178
|
-
- Random names use adjectives/nouns from `OPENCODE_ADJECTIVES` and `OPENCODE_NOUNS` lists.
|
|
179
|
-
- Branches created for new worktrees use `aiworker/<worktree-name>` pattern.
|
|
180
|
-
|
|
181
|
-
### Cross-Platform Considerations
|
|
182
|
-
- Use `normalizeDirectoryPath` for all directory inputs to handle `~` and path separators.
|
|
183
|
-
- Use `canonicalPath` for path comparisons to handle case-insensitive filesystems (Windows).
|
|
184
|
-
- Windows Git commands use MSYS/MinGW paths; avoid direct Windows paths in git commands.
|
|
185
|
-
|
|
186
|
-
### Error Handling
|
|
187
|
-
- All exported functions should throw errors with descriptive messages.
|
|
188
|
-
- Use `console.error` for logging Git operation failures.
|
|
189
|
-
- Return structured objects for operations that need partial success reporting (e.g., merge/rebase conflicts).
|
|
190
|
-
|
|
191
|
-
### Testing
|
|
192
|
-
- Run `bun run type-check`, `bun run lint`, and `bun run build` before finalizing changes.
|
|
193
|
-
- Consider edge cases: non-Git directories, missing remotes, conflict states, concurrent worktree operations.
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
# GitHub Module Documentation
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
|
|
5
|
-
- This module owns GitHub auth, Octokit access, repo resolution, and Pull Request status resolution for AiWorker.
|
|
6
|
-
- From user perspective, this is the layer that lets the app know which PR belongs to a local branch and keeps that UI feeling current.
|
|
7
|
-
|
|
8
|
-
## Entrypoints and structure
|
|
9
|
-
|
|
10
|
-
- `packages/web/server/lib/github/index.js`: public server entrypoint. `routes.js` loads it lazily with `await import('./index.js')` and destructures the handler it needs, so a re-export removed from here breaks a route at request time rather than at build time. Static "unused export" reports do not see these consumers.
|
|
11
|
-
- `packages/web/server/lib/github/routes.js`: Express route registration for `/api/github/*` endpoints.
|
|
12
|
-
- `packages/web/server/lib/github/auth.js`: auth storage, multi-account support, client id, scope config.
|
|
13
|
-
- `packages/web/server/lib/github/device-flow.js`: OAuth device flow.
|
|
14
|
-
- `packages/web/server/lib/github/octokit.js`: Octokit factory for the current auth.
|
|
15
|
-
- `packages/web/server/lib/github/repo/index.js`: remote URL parsing and directory-to-repo resolution.
|
|
16
|
-
- `packages/web/server/lib/github/pr-status.js`: PR lookup across remotes, forks, and upstreams.
|
|
17
|
-
- `packages/web/server/index.js`: API route layer that calls this module.
|
|
18
|
-
- `packages/web/src/api/github.ts`: web client wrapper for GitHub endpoints.
|
|
19
|
-
|
|
20
|
-
## Public exports
|
|
21
|
-
|
|
22
|
-
### Auth
|
|
23
|
-
|
|
24
|
-
- `getGitHubAuth()`: current auth entry.
|
|
25
|
-
- `getGitHubAuthAccounts()`: all configured accounts.
|
|
26
|
-
- `setGitHubAuth({ accessToken, scope, tokenType, user, accountId })`: save or update account.
|
|
27
|
-
- `activateGitHubAuth(accountId)`: switch active account.
|
|
28
|
-
- `clearGitHubAuth()`: clear current account.
|
|
29
|
-
- `getGitHubClientId()`: resolve client id.
|
|
30
|
-
- `getGitHubScopes()`: resolve scopes.
|
|
31
|
-
- `GITHUB_AUTH_FILE`: auth file path.
|
|
32
|
-
|
|
33
|
-
### Device flow
|
|
34
|
-
|
|
35
|
-
- `startDeviceFlow({ clientId, scope })`: request device code.
|
|
36
|
-
- `exchangeDeviceCode({ clientId, deviceCode })`: poll for access token.
|
|
37
|
-
|
|
38
|
-
### Octokit
|
|
39
|
-
|
|
40
|
-
- `getOctokitOrNull()`: current Octokit or `null`.
|
|
41
|
-
|
|
42
|
-
### Repo
|
|
43
|
-
|
|
44
|
-
- `parseGitHubRemoteUrl(raw)`: parse SSH or HTTPS remote URL into `{ owner, repo, url }`.
|
|
45
|
-
- `resolveGitHubRepoFromDirectory(directory, remoteName)`: resolve GitHub repo from a local git remote.
|
|
46
|
-
|
|
47
|
-
## Auth storage and config
|
|
48
|
-
|
|
49
|
-
- Auth storage: `~/.config/openchamber/github-auth.json`
|
|
50
|
-
- Writes are atomic and file mode is `0o600`.
|
|
51
|
-
- Client ID resolution order: `AIWORKER_GITHUB_CLIENT_ID` -> `settings.json` -> default.
|
|
52
|
-
- Scope resolution order: `AIWORKER_GITHUB_SCOPES` -> `settings.json` -> default.
|
|
53
|
-
- Account id resolution order: explicit `accountId` -> user login -> user id -> token prefix.
|
|
54
|
-
|
|
55
|
-
## PR integration overview
|
|
56
|
-
|
|
57
|
-
- The UI asks `github.prStatus(directory, branch, remote?)` from `packages/web/src/api/github.ts`.
|
|
58
|
-
- That hits `GET /api/github/pr/status` in `packages/web/server/index.js`.
|
|
59
|
-
- The route calls `resolveGitHubPrStatus(...)` in `packages/web/server/lib/github/pr-status.js`.
|
|
60
|
-
- The resolver finds the most likely repo and PR for a local branch.
|
|
61
|
-
- The route then enriches that result with checks, mergeability, and permission-related fields.
|
|
62
|
-
- The client caches and shares the result between sidebar and Git view.
|
|
63
|
-
|
|
64
|
-
## Consumers of PR data
|
|
65
|
-
|
|
66
|
-
- `packages/ui/src/components/session/SessionSidebar.tsx` reads all PR entries and maps them to `directory::branch`.
|
|
67
|
-
- `packages/ui/src/components/session/sidebar/SessionGroupSection.tsx` renders the compact badge, PR number, title, checks summary, and GitHub link.
|
|
68
|
-
- `packages/ui/src/components/views/git/PullRequestSection.tsx` uses the same shared entry for the full PR workflow.
|
|
69
|
-
- `packages/ui/src/components/ui/MemoryDebugPanel.tsx` reads request counters for debugging.
|
|
70
|
-
|
|
71
|
-
## How PR resolution works
|
|
72
|
-
|
|
73
|
-
- It reads local git status and remotes first.
|
|
74
|
-
- It ranks remotes in this order: explicit remote, tracking remote, `origin`, `upstream`, then the rest.
|
|
75
|
-
- It resolves those remotes into GitHub repos.
|
|
76
|
-
- It expands each repo through `parent` and `source` so PRs in upstream repos can still be found.
|
|
77
|
-
- It skips PR lookup when the current branch matches that repo's default branch.
|
|
78
|
-
- It first searches for **open** PRs by likely source owner plus exact head branch.
|
|
79
|
-
- If that fails, it falls back to broader GitHub search for open PRs on the branch name.
|
|
80
|
-
- An **open PR from any candidate repo always wins** over a closed/merged one, so a merged fork PR can never hide an open upstream PR for the same head.
|
|
81
|
-
- Only when no target has an open PR does it return the branch's newest closed/merged PR, as history.
|
|
82
|
-
- History is looked up **only for the ranked-first remote and the branch's own name** — the repo it actually pushes to. Live status is worth searching the whole fork network for; history is not, and asking every target for it multiplies serial GitHub calls until the route hits its `12s` resolve timeout and returns no status at all.
|
|
83
|
-
- The history answer is remembered per repo+branch so discovery polls do not re-query it: a found closed/merged record for `6h`, and "no history yet" for `10m`. A found record only changes if a second PR appears on the same head, and while that one is open the open-PR path wins without ever reading this cache.
|
|
84
|
-
- Creating, merging, or closing a PR invalidates both the shared repo pull list and that remembered history.
|
|
85
|
-
- The route skips the checks summary and the merge-permission lookup for a closed/merged PR: neither is actionable, and both cost extra GitHub calls.
|
|
86
|
-
- `403` and `404` during repo lookups are treated as expected gaps, not hard errors.
|
|
87
|
-
|
|
88
|
-
## Shared client state model
|
|
89
|
-
|
|
90
|
-
- Client key is effectively `directory::branch`.
|
|
91
|
-
- One entry stores last known status, loading state, error, timestamps, watcher count, identity, and resolved remote.
|
|
92
|
-
- Requests are deduplicated by branch signature, not by component instance.
|
|
93
|
-
- This keeps sidebar and Git view aligned and avoids duplicated fetches.
|
|
94
|
-
|
|
95
|
-
## Persistence
|
|
96
|
-
|
|
97
|
-
- PR state is persisted in local storage under `aiworker.github-pr-status`.
|
|
98
|
-
- Persisted fields include status, timestamps, identity, and resolved remote.
|
|
99
|
-
- Runtime-only details are not persisted.
|
|
100
|
-
- Persisted entries expire after 12 hours.
|
|
101
|
-
- On reload, users get last known state first, then background refresh resumes.
|
|
102
|
-
|
|
103
|
-
## Polling and refresh model
|
|
104
|
-
|
|
105
|
-
- There are two layers: entry-level polling in `useGitHubPrStatusStore` and repo scanning in `useGitHubPrBackgroundTracking`.
|
|
106
|
-
- Entry-level polling decides when a known branch should revalidate PR state.
|
|
107
|
-
- Background tracking decides which directories and branches should even be watched.
|
|
108
|
-
|
|
109
|
-
## Entry-level polling rules
|
|
110
|
-
|
|
111
|
-
- Start watching -> immediate refresh.
|
|
112
|
-
- If no PR is found yet -> retry after `2s` and `5s`.
|
|
113
|
-
- Still no PR -> discovery refresh every `5m`.
|
|
114
|
-
- Open PR with pending checks -> refresh about every `1m`.
|
|
115
|
-
- Open PR with non-pending checks -> refresh about every `5m`.
|
|
116
|
-
- Open PR without a stable checks signal -> refresh about every `2m`.
|
|
117
|
-
- Closed or merged PR -> discovery refresh every `5m` (do not permanently stop polling).
|
|
118
|
-
- Hidden tab -> skip polling.
|
|
119
|
-
- Non-forced refreshes use a `90s` TTL.
|
|
120
|
-
- Failed non-forced attempts also observe the `90s` TTL so transient server or rate-limit failures cannot retry on every sidebar update. Forced user/action refreshes bypass this guard.
|
|
121
|
-
|
|
122
|
-
## Persistence notes for terminal PRs
|
|
123
|
-
|
|
124
|
-
- Closed/merged branch associations are persisted like open ones, so a reload still shows that the branch's PR was merged.
|
|
125
|
-
- Hydrate resets `lastDiscoveryPollAt` for them, so restored history revalidates on the first watcher tick instead of waiting out a discovery interval.
|
|
126
|
-
|
|
127
|
-
## Background tracking rules
|
|
128
|
-
|
|
129
|
-
- Track up to `50` likely directories.
|
|
130
|
-
- Sources are current directory, projects, worktrees, active sessions, and archived sessions.
|
|
131
|
-
- Active directory branch TTL is `15s`.
|
|
132
|
-
- Background directory branch TTL is `2m`.
|
|
133
|
-
- Background scan wakes every `15s`, but only fetches directories whose TTL expired.
|
|
134
|
-
- Each scan reads `branch`, `tracking`, `ahead`, and `behind` from git status.
|
|
135
|
-
- If any of those branch signals change, that branch's PR status refreshes immediately.
|
|
136
|
-
- After that, one more delayed refresh runs after `5s` to catch GitHub eventual consistency.
|
|
137
|
-
|
|
138
|
-
## UI refresh triggers
|
|
139
|
-
|
|
140
|
-
- App or tab becomes visible.
|
|
141
|
-
- Window regains focus.
|
|
142
|
-
- Current branch changes.
|
|
143
|
-
- Tracking branch changes.
|
|
144
|
-
- Ahead or behind changes.
|
|
145
|
-
- User selects a different remote in Git view.
|
|
146
|
-
- GitHub auth state changes.
|
|
147
|
-
|
|
148
|
-
## Action-based refreshes in Git view
|
|
149
|
-
|
|
150
|
-
- After `Create PR` -> refresh now, then after `2s` and `5s`.
|
|
151
|
-
- After `Merge PR` -> refresh now, then after `2s` and `5s`.
|
|
152
|
-
- After `Mark ready for review` -> refresh now, then after `2s` and `5s`.
|
|
153
|
-
- After `Update PR` -> refresh now, then after `2s` and `5s`.
|
|
154
|
-
|
|
155
|
-
## Sidebar behavior
|
|
156
|
-
|
|
157
|
-
- Sidebar shows only compact PR state.
|
|
158
|
-
- Aggregation is by `directory::branch`, so multiple sessions on one branch share one signal.
|
|
159
|
-
- If multiple entries exist, sidebar keeps the strongest visible PR state.
|
|
160
|
-
- Visual state is based on PR health, not merge permissions.
|
|
161
|
-
|
|
162
|
-
## Git view behavior
|
|
163
|
-
|
|
164
|
-
- Git view watches one branch directly.
|
|
165
|
-
- It supports create, edit, mark ready, and merge.
|
|
166
|
-
- It can probe alternate remotes so fork-heavy setups still find the right PR.
|
|
167
|
-
- It uses the same shared store as the sidebar.
|
|
168
|
-
|
|
169
|
-
## Failure handling
|
|
170
|
-
|
|
171
|
-
- If GitHub is disconnected, API returns `connected: false`.
|
|
172
|
-
- If a repo is private or inaccessible, resolver calls may quietly return no PR.
|
|
173
|
-
- Sidebar stays quiet on missing or inaccessible PR state.
|
|
174
|
-
- Git view is where explicit PR-level problems should be shown.
|
|
175
|
-
|
|
176
|
-
## Notes for contributors
|
|
177
|
-
|
|
178
|
-
- Keep the UI calm. Do not add noisy diagnostics to the sidebar.
|
|
179
|
-
- Prefer shared state over per-component fetches.
|
|
180
|
-
- Prefer event-shaped refreshes over blind frequent polling.
|
|
181
|
-
- Prefer correctness for fork and multi-remote setups over assuming `origin` is enough.
|
|
182
|
-
- Device flow handles GitHub `authorization_pending` at caller level.
|
|
183
|
-
- Repo parser supports `git@github.com:`, `ssh://git@github.com/`, and `https://github.com/`.
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
# Linear Module Documentation
|
|
2
|
-
|
|
3
|
-
## Purpose
|
|
4
|
-
|
|
5
|
-
This module owns Linear OAuth, issue lookup, Linear-team-to-project mapping, issue status updates, and session status comments on Linear issues. Credentials live on the AiWorker server, so web, desktop, and a phone paired to that host share them. You can store more than one Linear workspace; exactly one is current. Issue list, mapping, and new OAuth default to the current workspace. Session status comments use the workspace that started the session. The right-hand context panel lists issues for the current workspace, can switch workspace, filters the list, shows a read-only card, changes status or closes the issue, and starts a session or worktree. Start session stays visible in a footer while the issue card scrolls. The chat picker lists issues and attaches them to a message. New Worktree can also start from a Linear issue in the currently active project. A session started from a Linear issue can post started/completed/failure comments, each with an AiWorker session link. Those comments are opt-in and only appear when this server has a publicly reachable address.
|
|
6
|
-
|
|
7
|
-
VS Code omits Linear (`RuntimeAPIs.linear` is optional). Hide Linear UI when the API is missing.
|
|
8
|
-
|
|
9
|
-
## Entrypoints and structure
|
|
10
|
-
|
|
11
|
-
- `packages/web/server/lib/linear/index.js`: public server entrypoint. `routes.js` loads it lazily with `await import('./index.js')`.
|
|
12
|
-
- `packages/web/server/lib/linear/routes.js`: Express registration for the public callback, `/api/linear/auth/*`, `/api/linear/issues/*`, `/api/linear/mapping`, and `/api/linear/session-status`.
|
|
13
|
-
- `packages/web/server/lib/linear/auth.js`: auth file, client id, scopes, redirect URI.
|
|
14
|
-
- `packages/web/server/lib/linear/oauth.js`: authorization-code + PKCE S256, public callback broker handoff, refresh, revoke.
|
|
15
|
-
- `packages/web/server/lib/linear/client.js`: GraphQL helper, viewer/organization lookup, and access-token refresh. GraphQL errors prefer `extensions.userPresentableMessage` / validation constraints over the generic `Argument Validation Error` label. User-facing Linear errors set `LinearApiError.userError`. Requests send `public-file-urls-expire-in: 3600` so file URLs in issue descriptions and comments are temporarily readable in the panel.
|
|
16
|
-
- `packages/web/server/lib/linear/issues.js`: list/search/get issues, team workflow states, `issueUpdate`, and `commentCreate`. Parses identifiers and Linear URLs. `issueUpdate` resolves identifiers to UUIDs first because Linear's mutation does not accept `ENG-12`. List/get include `state.id`, `priority` (0–4), and labels (`id`, `name`, sanitized hex `color`) so the panel can show them and update status.
|
|
17
|
-
- `packages/web/server/lib/linear/teams.js`: list Linear teams for mapping UI.
|
|
18
|
-
- `packages/web/server/lib/linear/mapping.js`: persist default and per-team AiWorker project paths. Separate from the auth file so disconnect does not wipe maps.
|
|
19
|
-
- `packages/web/server/lib/linear/status.js`: persist per-session started/completed/failure flags and post the matching Linear comment with an open-session URL. Posts nothing unless the user opted in and the session origin is public; `isPublicSessionOrigin` rejects loopback, private LAN, carrier-grade NAT, link-local and single-label hosts. The dedupe file keeps the newest 500 sessions.
|
|
20
|
-
- `packages/web/server/lib/linear/status-runtime.js`: on the OpenCode event hub, first `session.status` idle after started posts completed once; `session.error` (except abort) posts failure once.
|
|
21
|
-
- `packages/web/src/api/linear.ts`: web client wrapper. Electron and hosted/Capacitor mobile reuse it. VS Code omits `linear`.
|
|
22
|
-
|
|
23
|
-
## Public routes
|
|
24
|
-
|
|
25
|
-
- `GET /linear/oauth/callback`: public fallback for an explicitly configured direct redirect URI. The built-in flow uses the stable callback broker instead, because desktop and self-hosted instances may have private or dynamic addresses.
|
|
26
|
-
- `GET /api/linear/auth/status`: connected flag, current user/organization/scope, and `workspaces` (id, name, current, user, authorizedAt). Never returns tokens. A 401 on the current workspace drops that workspace only; if another remains, status returns that one instead of disconnected. Identity refresh does not bump `authorizedAt`.
|
|
27
|
-
- `POST /api/linear/auth/start`: returns `{ authorizationUrl, expiresIn, scope }`. Body may include `origin: "desktop"` so the callback page can raise the desktop window. The authorize URL uses `prompt=consent` so Add workspace can pick a different Linear org. Completing OAuth stores or replaces that org and makes it current.
|
|
28
|
-
- `POST /api/linear/auth/activate`: body `{ organizationId }`. Makes that stored workspace current. 400 if the id is missing, 404 if it is not stored.
|
|
29
|
-
- `DELETE /api/linear/auth`: revokes the current workspace refresh token when present, then drops that workspace only. Other stored workspaces stay. Mapping is kept.
|
|
30
|
-
- `GET /api/linear/issues/list?query=&cursor=&status=&assignee=&teamId=&priority=`: issues from the current workspace. Omitted `status` is incomplete states (same as the chat picker). The panel sends `all`, `backlog`, `todo` (Linear `unstarted`), `started` (In Progress, excluding the In Review name), `inReview` (state name In Review), `completed` (Done), `canceled` (excluding the Duplicate name), or `duplicate` (state type or name Duplicate). `assignee` is `any` (default) or `me`. `teamId` limits the list to that Linear team. `priority` is `all` (default), `none`, `urgent`, `high`, `medium`, or `low`. An identifier or Linear URL returns that issue even if it is completed and ignores the other filters. Each issue includes `state.id` when Linear sends it, plus `priority` (0 none through 4 low) and `labels`. Never returns tokens.
|
|
31
|
-
- `GET /api/linear/issues/get?id=`: one issue by UUID or identifier, including description, comments, team, `state.id`, priority, and labels.
|
|
32
|
-
- `GET /api/linear/issues/states?teamId=`: workflow states for that Linear team (`id`, `name`, `type`, `position`), ordered like Linear's workflow: type (backlog, unstarted, started, completed, canceled) then position. Missing `teamId` is 400. Linear not-found or validation errors are 400 with Linear's presentable message. Disconnected is `{ connected: false }` with HTTP 200.
|
|
33
|
-
- `POST /api/linear/issues/update`: body `{ id, stateId }`. `id` may be a UUID, identifier, or Linear URL; identifiers are resolved before `issueUpdate` because Linear's mutation requires a UUID. Returns the updated issue. Closing an issue is this same call with the team's first `type: completed` state. Missing `id` or `stateId` is 400. Linear validation (for example a non-UUID `stateId`) is 400 with Linear's presentable message. A GraphQL 401 clears that workspace only. Disconnected is `{ connected: false }` with HTTP 200.
|
|
34
|
-
- `GET /api/linear/mapping`: stored default project plus live Linear teams with their mapped paths. Missing file is empty mapping. Malformed file is 500, not empty success. Disconnected is `{ connected: false }` with HTTP 200.
|
|
35
|
-
- `PUT /api/linear/mapping`: replace default project and per-team paths. Body `{ defaultProjectPath, teamProjectPaths }`. Failed write does not touch tokens. Disconnected is `{ connected: false }` and does not save.
|
|
36
|
-
- `GET /api/linear/preferences`: `{ sessionComments }`. `PUT /api/linear/preferences` with body `{ sessionComments: boolean }` replaces it and returns the stored value. A non-boolean body is 400. The preference is server-side because the event hub posts completed/failure without going through the interface.
|
|
37
|
-
- `POST /api/linear/session-status`: post a started/completed/failure comment on the linked Linear issue. Body `{ kind, sessionId, issueIdentifier?, sessionOrigin? }`. `started` requires `issueIdentifier`. `completed` and `failure` reuse the stored issue and open URL from `started`. Each kind posts at most once per session. Answers in this order: disconnected is `{ connected: false }` with HTTP 200; comments turned off is `skipped: 'disabled'`; a `sessionOrigin` nobody else can reach is `skipped: 'origin-not-public'`. `sessionOrigin` must be `http` or `https` with no path, and must resolve to a public host — loopback, private LAN and desktop deep links post no comment at all rather than a link only its author can open. Comment bodies are one markdown link: `[AiWorker session started](url)` so Linear keeps the `?session=` query. The comment carries no issue or session title: it already sits on the issue, and titles routinely contain brackets that would break the link. Invalid body is 400.
|
|
38
|
-
|
|
39
|
-
`POST /api/linear/auth/start`, `PUT /api/linear/mapping`, `POST /api/linear/issues/update`, and `POST /api/linear/session-status` parse JSON on the route (`16kb`). They are not on the `/api` 50mb allowlist.
|
|
40
|
-
|
|
41
|
-
Disconnected list/get/states/update/mapping/session-status return `{ connected: false }` with HTTP 200 so the picker and panel can show an empty state. Missing `id` on get is 400. Missing `teamId` on states is 400.
|
|
42
|
-
|
|
43
|
-
## Auth storage and config
|
|
44
|
-
|
|
45
|
-
- Auth storage: `~/.config/openchamber/linear-auth.json` (or `$AIWORKER_DATA_DIR/linear-auth.json`). Shape is `{ workspaces: [ { accessToken, refreshToken, user, organization, workspaceId, current, authorizedAt, ... } ] }`. `workspaceId` is the Linear organization id, or `user:<id>` when there is no org, or `legacy` for a migrated token with neither. A legacy single-object file is rewritten to this list on read. Reconnecting the same org replaces that slot.
|
|
46
|
-
- Mapping storage: `~/.config/openchamber/linear-mapping.json` (same data dir). Shape is `{ workspaces: { [workspaceId]: { defaultProjectPath, teamProjectPaths } } }`. Reads and writes use the current workspace slice. A legacy flat file is wrapped under the current workspace id on read. Disconnect does not wipe maps. Writes are atomic and file mode is `0o600`.
|
|
47
|
-
- Session status storage: `~/.config/openchamber/linear-session-status.json` (same data dir). Writes are atomic and file mode is `0o600`. Dedupes started/completed/failure per AiWorker session id.
|
|
48
|
-
- Writes are atomic and file mode is `0o600`.
|
|
49
|
-
- Client ID: `AIWORKER_LINEAR_CLIENT_ID` -> `settings.json` `linearClientId` -> baked-in public default.
|
|
50
|
-
- Client secret: `AIWORKER_LINEAR_CLIENT_SECRET` -> `settings.json` `linearClientSecret`. Optional with PKCE. Do not commit a secret.
|
|
51
|
-
- Scopes: `AIWORKER_LINEAR_SCOPES` -> `settings.json` `linearScopes` -> `read,write,comments:create`.
|
|
52
|
-
- Session comments: `settings.json` `linearSessionComments`, boolean, absent means off. Written only through `PUT /api/linear/preferences`.
|
|
53
|
-
- Broker URL: `AIWORKER_LINEAR_BROKER_URL` -> `settings.json` `linearBrokerUrl` -> `https://api.aiworker.dev/v1/oauth/linear`.
|
|
54
|
-
- Redirect URI: `AIWORKER_LINEAR_REDIRECT_URI` -> `settings.json` `linearRedirectUri` -> `<broker-url>/callback`. Setting an explicit redirect URI bypasses the broker for custom/self-hosted OAuth applications.
|
|
55
|
-
|
|
56
|
-
Linear requires an exact callback match. The built-in application registers `https://api.aiworker.dev/v1/oauth/linear/callback`; the broker holds only the short-lived authorization code. The local AiWorker server keeps the claim secret and PKCE verifier, exchanges the code for tokens locally, then acknowledges the handoff. Custom brokers must expose `/start`, `/callback`, `/poll`, and `/complete` with the same contract.
|
|
57
|
-
|
|
58
|
-
## OAuth contract
|
|
59
|
-
|
|
60
|
-
- Authorization code + PKCE S256. Linear has no device flow.
|
|
61
|
-
- The broker stores hashes of OAuth state and a separate claim secret for ten minutes. It never receives the PKCE verifier or Linear tokens. The local status polling path claims a completed broker result and persists tokens on the AiWorker server.
|
|
62
|
-
- Access tokens expire in 24 hours. Refresh tokens rotate; persist the new refresh token from every successful refresh. Concurrent refreshes share one in-flight promise per workspace.
|
|
63
|
-
- `invalid_grant` / 401 on refresh clears that workspace only so a dead token cannot loop. If it was the last workspace, status becomes disconnected.
|
|
64
|
-
- A GraphQL 401 after a valid-looking token also clears that workspace. A network failure while a token is stored does not: status stays connected with the last known user.
|
|
65
|
-
|
|
66
|
-
## Project mapping
|
|
67
|
-
|
|
68
|
-
AiWorker has projects (directories), not accounts or organizations. Mapping is how create-session (picker and the right-hand panel) picks a directory:
|
|
69
|
-
|
|
70
|
-
1. If the issue's Linear team has a project path, use that.
|
|
71
|
-
2. Otherwise use the default project path.
|
|
72
|
-
3. If neither is set, the UI tells the user to map the team in Settings → Integrations. It does not fall back to the currently active project.
|
|
73
|
-
|
|
74
|
-
A worktree started from the panel or picker is created in that mapped project. New Worktree from Git is different: it stays in the currently active project.
|
|
75
|
-
|
|
76
|
-
## Shared UI
|
|
77
|
-
|
|
78
|
-
- `RuntimeAPIs.linear` is optional. Hide Linear settings, the chat picker, and the panel when it is missing (VS Code).
|
|
79
|
-
- Store: `packages/ui/src/stores/useLinearAuthStore.ts`. App start refreshes it from `App.tsx` and `MobileApp.tsx`, not `VSCodeApp`.
|
|
80
|
-
- Settings: first-party section on the Integrations page. Connect opens the authorization URL and polls status until the workspace list or current `authorizedAt` changes, so Add workspace is not treated as done just because a workspace was already connected. When connected, map a default project and optional per-team projects for the current workspace. Other stored workspaces appear in a list with Switch to. Disconnect removes the current workspace only. The panel can also switch the current workspace when more than one is stored.
|
|
81
|
-
- Context panel: desktop/web right-hand rail surface `linear` (`packages/ui/src/components/views/LinearIssuesView.tsx`). Singleton like git/pr. The rail icon is hidden until a Linear workspace is connected; disconnecting while the panel is open closes it. List/search defaults to all issues; the status filter is All, Backlog, To Do, In Progress, In Review, Done, Canceled, and Duplicate, matching the card status order. Identifier/URL still finds completed. Status, assignee, team, and priority filters persist in `useUIStore` so they survive rail switches. Non-default list filters and search tint the filter icon `text-primary`, same as the context rail; one control clears them, not the workspace switch. Changing those filters keeps the previous list until the next page arrives and does not disable the filter row. On a narrow panel search and the filters other than status drop to icons; status keeps its label. The card shows priority and labels. Comments render as an avatar timeline matching the pull request panel, so both context surfaces read alike; comment authors carry `avatarUrl`. The card is read-only except status (`issueUpdate`) and Close (first completed workflow state). Start session stays in a footer while the description and comments scroll. Start session / worktree share `startLinearIssueSession` with the picker. No create-issue, no writing comments, no polling. VS Code and the mobile workspace drawer omit this rail.
|
|
82
|
-
- Chat: composer attach menu "Link Linear Issue" attaches body and comments as `linear-issue` context on the next send. Exclusive with a linked GitHub issue or PR. The attached issue is stored on session metadata (`kind: 'linear'`) so work status can show it. Clicking that work-status row opens the Linear rail when Linear is connected on desktop/web; otherwise the Linear URL. Managed Chats do not offer start-from-issue; those sessions have no project directory.
|
|
83
|
-
- Worktree: New Worktree can start from a Linear issue. It uses the currently active project and does not consult team-to-project mapping. GitHub issue/PR and Linear issue are exclusive on that form.
|
|
84
|
-
- Status comments: off until the user turns them on in Settings -> Integrations -> Linear (`LinearSessionComments.tsx`). When on, create-session and worktree-from-Linear post `started` after the session exists. The event hub posts `completed` on the first idle after that, and `failure` on `session.error` except `MessageAbortedError`. Failed comments must not fail session create. Comment bodies are English (they live on Linear) and are one markdown link named `AiWorker session started` (or completed/failed). Web uses `/?session=<id>` on the current origin; desktop reports the loopback origin its own server listens on, not `openchamber-ui://`. A Linear comment is read by the whole team, so the server posts nothing when that origin is not publicly reachable rather than publishing a link only its author could open. Opening `/?session=` selects that session after the global session list can resolve its directory.
|
|
85
|
-
- Magic prompts: `linear.issue.review.visible` / `.instructions`. Do not reuse the GitHub issue-review templates for Linear.
|
|
86
|
-
|
|
87
|
-
## Notes for contributors
|
|
88
|
-
|
|
89
|
-
The implementation and deployment hand-off for the stable callback broker is
|
|
90
|
-
in [`OAUTH-BROKER-HANDOFF.md`](./OAUTH-BROKER-HANDOFF.md). It records the exact
|
|
91
|
-
Linear redirect URI that must be registered and why the original loopback
|
|
92
|
-
callback could not support packaged desktop or arbitrary self-hosted servers.
|
|
93
|
-
|
|
94
|
-
- Do not log tokens, codes, verifiers, or the client secret.
|
|
95
|
-
- Do not add Linear under Git or as a third-party plugin row.
|
|
96
|
-
- Actor is `user`. Do not enable Linear client-credentials tokens for this flow.
|
|
97
|
-
- One OAuth grant is still one Linear organization. The server stores many grants and keeps one current. Webhooks and inbound Linear issue actions are out of scope until a later change.
|