agent-tower 0.7.4 → 0.7.5-beta.5
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/dist/app.d.ts.map +1 -1
- package/dist/app.js +6 -1
- package/dist/app.js.map +1 -1
- package/dist/core/container.d.ts +2 -0
- package/dist/core/container.d.ts.map +1 -1
- package/dist/core/container.js +25 -0
- package/dist/core/container.js.map +1 -1
- package/dist/executors/__tests__/base.executor.test.js +19 -0
- package/dist/executors/__tests__/base.executor.test.js.map +1 -1
- package/dist/executors/__tests__/claude-code-mcp.test.d.ts +2 -0
- package/dist/executors/__tests__/claude-code-mcp.test.d.ts.map +1 -0
- package/dist/executors/__tests__/claude-code-mcp.test.js +211 -0
- package/dist/executors/__tests__/claude-code-mcp.test.js.map +1 -0
- package/dist/executors/__tests__/codex-mcp-windows.test.d.ts +2 -0
- package/dist/executors/__tests__/codex-mcp-windows.test.d.ts.map +1 -0
- package/dist/executors/__tests__/codex-mcp-windows.test.js +128 -0
- package/dist/executors/__tests__/codex-mcp-windows.test.js.map +1 -0
- package/dist/executors/__tests__/codex.executor.test.js +394 -1
- package/dist/executors/__tests__/codex.executor.test.js.map +1 -1
- package/dist/executors/base.executor.d.ts.map +1 -1
- package/dist/executors/base.executor.js +13 -0
- package/dist/executors/base.executor.js.map +1 -1
- package/dist/executors/claude-code.executor.d.ts +1 -0
- package/dist/executors/claude-code.executor.d.ts.map +1 -1
- package/dist/executors/claude-code.executor.js +52 -3
- package/dist/executors/claude-code.executor.js.map +1 -1
- package/dist/executors/codex.executor.d.ts +6 -6
- package/dist/executors/codex.executor.d.ts.map +1 -1
- package/dist/executors/codex.executor.js +118 -32
- package/dist/executors/codex.executor.js.map +1 -1
- package/dist/git/worktree.manager.d.ts.map +1 -1
- package/dist/git/worktree.manager.js +5 -2
- package/dist/git/worktree.manager.js.map +1 -1
- package/dist/mcp/http-client.d.ts +8 -1
- package/dist/mcp/http-client.d.ts.map +1 -1
- package/dist/mcp/http-client.js +6 -0
- package/dist/mcp/http-client.js.map +1 -1
- package/dist/mcp/tools/__tests__/workspace-services.test.js +34 -1
- package/dist/mcp/tools/__tests__/workspace-services.test.js.map +1 -1
- package/dist/mcp/tools/tasks.js +1 -1
- package/dist/mcp/tools/tasks.js.map +1 -1
- package/dist/mcp/tools/workspace-services.d.ts.map +1 -1
- package/dist/mcp/tools/workspace-services.js +39 -2
- package/dist/mcp/tools/workspace-services.js.map +1 -1
- package/dist/mcp/types.d.ts +20 -0
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/mcp/types.js +12 -0
- package/dist/mcp/types.js.map +1 -1
- package/dist/middleware/__tests__/preview-fallback.test.d.ts +2 -0
- package/dist/middleware/__tests__/preview-fallback.test.d.ts.map +1 -0
- package/dist/middleware/__tests__/preview-fallback.test.js +93 -0
- package/dist/middleware/__tests__/preview-fallback.test.js.map +1 -0
- package/dist/middleware/preview-fallback.d.ts +5 -0
- package/dist/middleware/preview-fallback.d.ts.map +1 -0
- package/dist/middleware/preview-fallback.js +91 -0
- package/dist/middleware/preview-fallback.js.map +1 -0
- package/dist/output/__tests__/claude-code-parser-continuation.test.d.ts +2 -0
- package/dist/output/__tests__/claude-code-parser-continuation.test.d.ts.map +1 -0
- package/dist/output/__tests__/claude-code-parser-continuation.test.js +89 -0
- package/dist/output/__tests__/claude-code-parser-continuation.test.js.map +1 -0
- package/dist/output/__tests__/claude-code-parser-lifecycle.test.js +4 -1
- package/dist/output/__tests__/claude-code-parser-lifecycle.test.js.map +1 -1
- package/dist/output/claude-code-parser.d.ts +36 -1
- package/dist/output/claude-code-parser.d.ts.map +1 -1
- package/dist/output/claude-code-parser.js +149 -18
- package/dist/output/claude-code-parser.js.map +1 -1
- package/dist/output/types.d.ts +2 -0
- package/dist/output/types.d.ts.map +1 -1
- package/dist/output/types.js.map +1 -1
- package/dist/prompts/workspace-background-service-policy.d.ts +6 -2
- package/dist/prompts/workspace-background-service-policy.d.ts.map +1 -1
- package/dist/prompts/workspace-background-service-policy.js +12 -3
- package/dist/prompts/workspace-background-service-policy.js.map +1 -1
- package/dist/prompts/workspace-background-service-policy.test.js +11 -0
- package/dist/prompts/workspace-background-service-policy.test.js.map +1 -1
- package/dist/routes/__tests__/previews.integration.test.js +117 -6
- package/dist/routes/__tests__/previews.integration.test.js.map +1 -1
- package/dist/routes/__tests__/previews.test.js +223 -194
- package/dist/routes/__tests__/previews.test.js.map +1 -1
- package/dist/routes/__tests__/task-handoff.test.js +25 -16
- package/dist/routes/__tests__/task-handoff.test.js.map +1 -1
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +3 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/previews.d.ts +11 -9
- package/dist/routes/previews.d.ts.map +1 -1
- package/dist/routes/previews.js +137 -516
- package/dist/routes/previews.js.map +1 -1
- package/dist/routes/session-wakeups.d.ts +7 -0
- package/dist/routes/session-wakeups.d.ts.map +1 -0
- package/dist/routes/session-wakeups.js +86 -0
- package/dist/routes/session-wakeups.js.map +1 -0
- package/dist/routes/tasks.d.ts.map +1 -1
- package/dist/routes/tasks.js +3 -1
- package/dist/routes/tasks.js.map +1 -1
- package/dist/runtime/__tests__/acp-process-manager.test.js +65 -0
- package/dist/runtime/__tests__/acp-process-manager.test.js.map +1 -1
- package/dist/runtime/__tests__/claude-cli-driver.test.js +96 -2
- package/dist/runtime/__tests__/claude-cli-driver.test.js.map +1 -1
- package/dist/runtime/__tests__/claude-run-lifecycle.test.d.ts +2 -0
- package/dist/runtime/__tests__/claude-run-lifecycle.test.d.ts.map +1 -0
- package/dist/runtime/__tests__/claude-run-lifecycle.test.js +88 -0
- package/dist/runtime/__tests__/claude-run-lifecycle.test.js.map +1 -0
- package/dist/runtime/__tests__/cli-driver-lifecycle.test.js +14 -7
- package/dist/runtime/__tests__/cli-driver-lifecycle.test.js.map +1 -1
- package/dist/runtime/acp/agents/pi-coding-agent.d.ts.map +1 -1
- package/dist/runtime/acp/agents/pi-coding-agent.js +3 -0
- package/dist/runtime/acp/agents/pi-coding-agent.js.map +1 -1
- package/dist/runtime/claude-run-lifecycle.d.ts +77 -0
- package/dist/runtime/claude-run-lifecycle.d.ts.map +1 -0
- package/dist/runtime/claude-run-lifecycle.js +147 -0
- package/dist/runtime/claude-run-lifecycle.js.map +1 -0
- package/dist/runtime/cli-driver.d.ts.map +1 -1
- package/dist/runtime/cli-driver.js +89 -3
- package/dist/runtime/cli-driver.js.map +1 -1
- package/dist/runtime/contracts.d.ts +6 -1
- package/dist/runtime/contracts.d.ts.map +1 -1
- package/dist/runtime/process-scope/__tests__/process-scope.test.d.ts +2 -0
- package/dist/runtime/process-scope/__tests__/process-scope.test.d.ts.map +1 -0
- package/dist/runtime/process-scope/__tests__/process-scope.test.js +156 -0
- package/dist/runtime/process-scope/__tests__/process-scope.test.js.map +1 -0
- package/dist/runtime/process-scope/process-scope.d.ts +46 -0
- package/dist/runtime/process-scope/process-scope.d.ts.map +1 -0
- package/dist/runtime/process-scope/process-scope.js +68 -0
- package/dist/runtime/process-scope/process-scope.js.map +1 -0
- package/dist/runtime/process-scope/scope-helper.d.ts +28 -0
- package/dist/runtime/process-scope/scope-helper.d.ts.map +1 -0
- package/dist/runtime/process-scope/scope-helper.js +78 -0
- package/dist/runtime/process-scope/scope-helper.js.map +1 -0
- package/dist/runtime/runtime-coordinator.d.ts.map +1 -1
- package/dist/runtime/runtime-coordinator.js +6 -0
- package/dist/runtime/runtime-coordinator.js.map +1 -1
- package/dist/services/__tests__/preview-runtime-manager.test.js +13 -112
- package/dist/services/__tests__/preview-runtime-manager.test.js.map +1 -1
- package/dist/services/__tests__/preview.service.test.js +13 -14
- package/dist/services/__tests__/preview.service.test.js.map +1 -1
- package/dist/services/__tests__/session-manager.lifecycle.test.js +147 -1
- package/dist/services/__tests__/session-manager.lifecycle.test.js.map +1 -1
- package/dist/services/__tests__/session-wakeup.service.test.d.ts +2 -0
- package/dist/services/__tests__/session-wakeup.service.test.d.ts.map +1 -0
- package/dist/services/__tests__/session-wakeup.service.test.js +173 -0
- package/dist/services/__tests__/session-wakeup.service.test.js.map +1 -0
- package/dist/services/__tests__/terminal-manager.test.js +129 -51
- package/dist/services/__tests__/terminal-manager.test.js.map +1 -1
- package/dist/services/__tests__/workspace.service.test.js +31 -0
- package/dist/services/__tests__/workspace.service.test.js.map +1 -1
- package/dist/services/message-delivery.service.d.ts +3 -2
- package/dist/services/message-delivery.service.d.ts.map +1 -1
- package/dist/services/message-delivery.service.js +11 -7
- package/dist/services/message-delivery.service.js.map +1 -1
- package/dist/services/preview-bridge.d.ts +50 -0
- package/dist/services/preview-bridge.d.ts.map +1 -0
- package/dist/services/preview-bridge.js +537 -0
- package/dist/services/preview-bridge.js.map +1 -0
- package/dist/services/preview-cookies.d.ts +28 -0
- package/dist/services/preview-cookies.d.ts.map +1 -0
- package/dist/services/preview-cookies.js +133 -0
- package/dist/services/preview-cookies.js.map +1 -0
- package/dist/services/preview-proxy.d.ts +71 -0
- package/dist/services/preview-proxy.d.ts.map +1 -0
- package/dist/services/preview-proxy.js +462 -0
- package/dist/services/preview-proxy.js.map +1 -0
- package/dist/services/preview-runtime-manager.d.ts +11 -25
- package/dist/services/preview-runtime-manager.d.ts.map +1 -1
- package/dist/services/preview-runtime-manager.js +46 -363
- package/dist/services/preview-runtime-manager.js.map +1 -1
- package/dist/services/preview.service.d.ts.map +1 -1
- package/dist/services/preview.service.js +11 -9
- package/dist/services/preview.service.js.map +1 -1
- package/dist/services/project.service.d.ts +13 -13
- package/dist/services/session-manager.d.ts +2 -0
- package/dist/services/session-manager.d.ts.map +1 -1
- package/dist/services/session-manager.js +4 -2
- package/dist/services/session-manager.js.map +1 -1
- package/dist/services/session-wakeup.service.d.ts +105 -0
- package/dist/services/session-wakeup.service.d.ts.map +1 -0
- package/dist/services/session-wakeup.service.js +319 -0
- package/dist/services/session-wakeup.service.js.map +1 -0
- package/dist/services/task-handoff.service.d.ts +3 -2
- package/dist/services/task-handoff.service.d.ts.map +1 -1
- package/dist/services/task-handoff.service.js +7 -6
- package/dist/services/task-handoff.service.js.map +1 -1
- package/dist/services/team-room-delivery.d.ts +4 -4
- package/dist/services/terminal-manager.d.ts +16 -0
- package/dist/services/terminal-manager.d.ts.map +1 -1
- package/dist/services/terminal-manager.js +64 -7
- package/dist/services/terminal-manager.js.map +1 -1
- package/dist/services/workspace-background-service.service.d.ts +15 -1
- package/dist/services/workspace-background-service.service.d.ts.map +1 -1
- package/dist/services/workspace-background-service.service.js +41 -2
- package/dist/services/workspace-background-service.service.js.map +1 -1
- package/dist/services/workspace.service.d.ts.map +1 -1
- package/dist/services/workspace.service.js +2 -1
- package/dist/services/workspace.service.js.map +1 -1
- package/dist/utils/database-schema.d.ts.map +1 -1
- package/dist/utils/database-schema.js +9 -4
- package/dist/utils/database-schema.js.map +1 -1
- package/dist/utils/database-schema.test.js +21 -0
- package/dist/utils/database-schema.test.js.map +1 -1
- package/dist/utils/owned-child-process.d.ts +32 -3
- package/dist/utils/owned-child-process.d.ts.map +1 -1
- package/dist/utils/owned-child-process.js +138 -1
- package/dist/utils/owned-child-process.js.map +1 -1
- package/dist/web/assets/{AgentDemoPage-D9bIiALx.js → AgentDemoPage-D6clLNZx.js} +1 -1
- package/dist/web/assets/{AgentEnvironmentSettingsPage-BE0RC6fR.js → AgentEnvironmentSettingsPage-O1hlpsgp.js} +1 -1
- package/dist/web/assets/AttachmentPreview-hD3g_T_y.js +2 -0
- package/dist/web/assets/{BranchSelect-Kv3M1YhZ.js → BranchSelect-BkuXnEG2.js} +1 -1
- package/dist/web/assets/{BrandLogo-fR2lCwEo.js → BrandLogo-BSwQa2SF.js} +1 -1
- package/dist/web/assets/{ConversationPage-BZiMHI9T.js → ConversationPage-DvWjqFF2.js} +1 -1
- package/dist/web/assets/CreateTaskInput-Bric0GeW.js +1 -0
- package/dist/web/assets/{DemoPage-DwpThe16.js → DemoPage-Cwy3EmOm.js} +1 -1
- package/dist/web/assets/{DesignSystemPage-DH9_cygJ.js → DesignSystemPage-C1HrGwpV.js} +1 -1
- package/dist/web/assets/{ExecutionPermissionSelector-C-akTOVg.js → ExecutionPermissionSelector-BgrxbvBB.js} +1 -1
- package/dist/web/assets/{GeneralSettingsPage-v2w1eofz.js → GeneralSettingsPage-qdeAdBkW.js} +1 -1
- package/dist/web/assets/{LoadingPreviewPage-pgk3PWM9.js → LoadingPreviewPage-qeZ8nuiQ.js} +1 -1
- package/dist/web/assets/{McpSettingsPage-w1DOlY5V.js → McpSettingsPage-Bo-auo0X.js} +1 -1
- package/dist/web/assets/{MemberAvatar-CJtylxwu.js → MemberAvatar-DBab_9EO.js} +1 -1
- package/dist/web/assets/{MobileTaskDetail-D5Czlr2O.js → MobileTaskDetail-Dm64ie99.js} +2 -2
- package/dist/web/assets/{NotificationSettingsPage-CkLg4WvT.js → NotificationSettingsPage-BVJpo76k.js} +1 -1
- package/dist/web/assets/{ProfileSettingsPage-B3yzbY1V.js → ProfileSettingsPage-CbSRYGcp.js} +1 -1
- package/dist/web/assets/{ProjectKanbanPage-CuGSLvrU.js → ProjectKanbanPage-Cp3rdFik.js} +6 -6
- package/dist/web/assets/{ProjectSettingsPage-DoklOfna.js → ProjectSettingsPage-DLs5-Gk3.js} +1 -1
- package/dist/web/assets/{ProviderSettingsPage-Cm0hbX5n.js → ProviderSettingsPage-BnBcgAMQ.js} +1 -1
- package/dist/web/assets/{SettingsMasterDetail-CGCUalkW.js → SettingsMasterDetail-fu-_yejj.js} +1 -1
- package/dist/web/assets/{TaskDetail-BiZbuXcE.js → TaskDetail-C2_Fg59e.js} +1 -1
- package/dist/web/assets/TaskHandoffPanel-3Y0gcjyN.js +70 -0
- package/dist/web/assets/{TeamSettingsPage-CyppRTaY.js → TeamSettingsPage-CAjitaBe.js} +1 -1
- package/dist/web/assets/{TranslationSettingsPage-BoQXEZmn.js → TranslationSettingsPage-DKD6Uw2F.js} +1 -1
- package/dist/web/assets/{arc-5NPBSbiL.js → arc-BiuUFTzU.js} +1 -1
- package/dist/web/assets/{architectureDiagram-3BPJPVTR-CqPT-IsX.js → architectureDiagram-3BPJPVTR-Dcwk905A.js} +1 -1
- package/dist/web/assets/{at-sign-Cf39WHCg.js → at-sign-CZAKOH3i.js} +1 -1
- package/dist/web/assets/{blockDiagram-GPEHLZMM-BN-1z9KN.js → blockDiagram-GPEHLZMM-Cfc5JDN-.js} +1 -1
- package/dist/web/assets/{c4Diagram-AAUBKEIU-CkeLbmQp.js → c4Diagram-AAUBKEIU-B4v5PUdt.js} +1 -1
- package/dist/web/assets/channel-DThSYkmR.js +1 -0
- package/dist/web/assets/check-C3vuSPKz.js +1 -0
- package/dist/web/assets/{chevron-down-Ct7FZbKm.js → chevron-down-CtAk9uZw.js} +1 -1
- package/dist/web/assets/{chevron-up-ARFlXMyO.js → chevron-up-DAc3NX1J.js} +1 -1
- package/dist/web/assets/{chip-BV_1-tIb.js → chip-CDUNxQW2.js} +1 -1
- package/dist/web/assets/{chunk-2J33WTMH-CWR7tRuT.js → chunk-2J33WTMH-BYRdUeyt.js} +1 -1
- package/dist/web/assets/{chunk-4BX2VUAB-CAulp4D5.js → chunk-4BX2VUAB-3si_kPmV.js} +1 -1
- package/dist/web/assets/{chunk-55IACEB6-DqkKXsbD.js → chunk-55IACEB6-JiMm9u33.js} +1 -1
- package/dist/web/assets/{chunk-727SXJPM-B19m45Sq.js → chunk-727SXJPM-CMCFIUyF.js} +1 -1
- package/dist/web/assets/{chunk-AQP2D5EJ-CuODwkza.js → chunk-AQP2D5EJ-CzvnZjH5.js} +1 -1
- package/dist/web/assets/{chunk-FMBD7UC4-CbUAjtCT.js → chunk-FMBD7UC4-hsabZkPl.js} +1 -1
- package/dist/web/assets/{chunk-ND2GUHAM-C54VxArl.js → chunk-ND2GUHAM-D2o-Reeo.js} +1 -1
- package/dist/web/assets/{chunk-QZHKN3VN-D54Uw5fD.js → chunk-QZHKN3VN-ASJk-E7d.js} +1 -1
- package/dist/web/assets/classDiagram-4FO5ZUOK-CjJqgIpK.js +1 -0
- package/dist/web/assets/classDiagram-v2-Q7XG4LA2-CjJqgIpK.js +1 -0
- package/dist/web/assets/{code-block-OCS4YCEC-CtAySD1h.js → code-block-OCS4YCEC-5bkFBowa.js} +1 -1
- package/dist/web/assets/{confirm-dialog-CCjHVFTv.js → confirm-dialog-Bs80kXB9.js} +1 -1
- package/dist/web/assets/{content-DZozZbBD.js → content-DAccroCT.js} +1 -1
- package/dist/web/assets/{copy-C-jvx2et.js → copy-D1XK3OqK.js} +1 -1
- package/dist/web/assets/{cose-bilkent-S5V4N54A-BaB2EVcU.js → cose-bilkent-S5V4N54A-C52UKnOd.js} +1 -1
- package/dist/web/assets/{cssMode-wd7JR1S4.js → cssMode-BFq_c5xU.js} +1 -1
- package/dist/web/assets/{dagre-BM42HDAG-C2j1Y8pO.js → dagre-BM42HDAG-CeEute93.js} +1 -1
- package/dist/web/assets/{diagram-2AECGRRQ-CF-EvpV9.js → diagram-2AECGRRQ-CEcq4W4S.js} +1 -1
- package/dist/web/assets/{diagram-5GNKFQAL-rBNso835.js → diagram-5GNKFQAL-DqQe7Rr7.js} +1 -1
- package/dist/web/assets/{diagram-KO2AKTUF-6NW-oos1.js → diagram-KO2AKTUF-C1KVZphS.js} +1 -1
- package/dist/web/assets/{diagram-LMA3HP47-DTfRxRLT.js → diagram-LMA3HP47-D1BL7GQh.js} +1 -1
- package/dist/web/assets/{diagram-OG6HWLK6-CS4Ae-hD.js → diagram-OG6HWLK6-ngyXooqC.js} +1 -1
- package/dist/web/assets/{dialog-DV4wfpNU.js → dialog-XkZ8wMQa.js} +1 -1
- package/dist/web/assets/{erDiagram-TEJ5UH35-Dw9QQctN.js → erDiagram-TEJ5UH35-D1zIFVqE.js} +1 -1
- package/dist/web/assets/{file-Btui_I8Z.js → file-CXCll7Pm.js} +1 -1
- package/dist/web/assets/{flowDiagram-I6XJVG4X-BGZ9q86S.js → flowDiagram-I6XJVG4X-BZu49qcj.js} +1 -1
- package/dist/web/assets/{folder-picker-DiR-8Qdl.js → folder-picker-CVkaEJEL.js} +1 -1
- package/dist/web/assets/{freemarker2-yVWXzD3l.js → freemarker2-CmhadAeB.js} +1 -1
- package/dist/web/assets/{ganttDiagram-6RSMTGT7-CkGun0no.js → ganttDiagram-6RSMTGT7-eJ8yBxfS.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-PVQCEYII-Bz3oiYvi.js → gitGraphDiagram-PVQCEYII-DpeUUyH_.js} +1 -1
- package/dist/web/assets/{globe-DAFqpAcW.js → globe-WnxN1JzY.js} +1 -1
- package/dist/web/assets/{handlebars-BPf0z6eN.js → handlebars-DtjmH1qR.js} +1 -1
- package/dist/web/assets/{html-HCTCjfUg.js → html-DOYAlzlD.js} +1 -1
- package/dist/web/assets/{htmlMode-BDkbsknt.js → htmlMode-ORxd7KRN.js} +1 -1
- package/dist/web/assets/{index-BMcLxIvZ.js → index-6mINSZng.js} +1 -1
- package/dist/web/assets/{index-BPqjxXmH.js → index-BrjvxKaf.js} +1 -1
- package/dist/web/assets/{index-B8xoIIa6.js → index-CqIyOiVN.js} +4 -4
- package/dist/web/assets/{index-DgUtx9rV.js → index-D9w1zMaz.js} +1 -1
- package/dist/web/assets/{index-DkSPjZm4.js → index-DHDgbH3t.js} +1 -1
- package/dist/web/assets/{index-BszkzNS4.js → index-Dz9SKopM.js} +13 -13
- package/dist/web/assets/{index-GuczxPxb.js → index-eIXz5Hbk.js} +1 -1
- package/dist/web/assets/{index-DuM6iAxG.js → index-iOIxdF6J.js} +1 -1
- package/dist/web/assets/{index-lZLy5rkr.js → index-wbMiSmsY.js} +1 -1
- package/dist/web/assets/{infoDiagram-5YYISTIA-DYFufUYC.js → infoDiagram-5YYISTIA-BiVrC__7.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-YF4QCWOH-IOkrKRwu.js → ishikawaDiagram-YF4QCWOH-B1kPjO1J.js} +1 -1
- package/dist/web/assets/javascript-CHlWHrtz.js +1 -0
- package/dist/web/assets/{journeyDiagram-JHISSGLW-Btl3yxne.js → journeyDiagram-JHISSGLW-DoTAPPAQ.js} +1 -1
- package/dist/web/assets/{jsonMode-DJPdvFcw.js → jsonMode-BkeQ_C6g.js} +1 -1
- package/dist/web/assets/{kanban-definition-UN3LZRKU-CDryhPbA.js → kanban-definition-UN3LZRKU-CBRmLcce.js} +1 -1
- package/dist/web/assets/{kbd-8RRRm9jd.js → kbd-CTfS0G5-.js} +1 -1
- package/dist/web/assets/{key-round-wojrCrDP.js → key-round-B7gTfpDR.js} +1 -1
- package/dist/web/assets/{layers-BH_LL4b8.js → layers-DE0xXedq.js} +1 -1
- package/dist/web/assets/{linear-BLuwKHze.js → linear-VLiA8Vcw.js} +1 -1
- package/dist/web/assets/{link-jWQOSQOQ.js → link-BSE_nRDT.js} +1 -1
- package/dist/web/assets/{liquid-1y-xg637.js → liquid-BZeg6p1K.js} +1 -1
- package/dist/web/assets/{lspLanguageFeatures-cYkILdCn.js → lspLanguageFeatures-CuFQ2_f9.js} +1 -1
- package/dist/web/assets/{mdx-CRg4Q6aP.js → mdx-C7xLyFFX.js} +1 -1
- package/dist/web/assets/{menu-CMqbaDni.js → menu-Dp4RDxvP.js} +1 -1
- package/dist/web/assets/{mermaid-NOHMQCX5-CYeAhOl-.js → mermaid-NOHMQCX5-C2enCJxR.js} +62 -62
- package/dist/web/assets/{message-square-DykJ2lWP.js → message-square-4lyHjyHe.js} +1 -1
- package/dist/web/assets/{mindmap-definition-RKZ34NQL-B5wtw1Rv.js → mindmap-definition-RKZ34NQL-C1OOtqma.js} +1 -1
- package/dist/web/assets/{modal-BwOwTzij.js → modal-BGglpgrR.js} +1 -1
- package/dist/web/assets/{monaco-CcHqpOKe.js → monaco-B-6VmJO0.js} +3 -3
- package/dist/web/assets/{nav-row-BmtdNLxR.js → nav-row-DaR8yP7p.js} +1 -1
- package/dist/web/assets/{paperclip-C4t49vX2.js → paperclip-DORF2rEM.js} +1 -1
- package/dist/web/assets/{pencil-BQ5_raiB.js → pencil-BG033B74.js} +1 -1
- package/dist/web/assets/{people-Dr0v8HK0.js → people-1qyKeYq6.js} +1 -1
- package/dist/web/assets/{pieDiagram-4H26LBE5-BZ3VoaNI.js → pieDiagram-4H26LBE5-BYYFVqOS.js} +1 -1
- package/dist/web/assets/{popover-CTqvKqTR.js → popover-CvjMbMb4.js} +1 -1
- package/dist/web/assets/{python-Huq_acyd.js → python-TTfnYnjF.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-W4KKPZXB-C6ArHmnG.js → quadrantDiagram-W4KKPZXB-DTllM2j9.js} +1 -1
- package/dist/web/assets/{razor-Clc87Ivb.js → razor-BFVXAHVd.js} +1 -1
- package/dist/web/assets/{requirementDiagram-4Y6WPE33-DiLzBRQP.js → requirementDiagram-4Y6WPE33-DeJDRYUP.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-5OEKKPKP-QlYYd7IZ.js → sankeyDiagram-5OEKKPKP-DzTvEKv8.js} +1 -1
- package/dist/web/assets/{search-CMIAPUEt.js → search-DDRcBORc.js} +1 -1
- package/dist/web/assets/{select-DqB7YF38.js → select-CXC17bJx.js} +1 -1
- package/dist/web/assets/{select-Bg7NI8i0.js → select-CzXL3ZbG.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-3UESZ5HK-COKEtqc0.js → sequenceDiagram-3UESZ5HK-CIYrA1ww.js} +1 -1
- package/dist/web/assets/{server-D7knl8H9.js → server-KqeWHfgr.js} +1 -1
- package/dist/web/assets/{stateDiagram-AJRCARHV-1RZZZdOe.js → stateDiagram-AJRCARHV-Dg_UXkEq.js} +1 -1
- package/dist/web/assets/stateDiagram-v2-BHNVJYJU-Lc4r0bC3.js +1 -0
- package/dist/web/assets/{textarea-BCY2J2ca.js → textarea-Cmbv5l35.js} +1 -1
- package/dist/web/assets/{timeline-definition-PNZ67QCA-IJl1LY6J.js → timeline-definition-PNZ67QCA-CfSPMqxF.js} +1 -1
- package/dist/web/assets/{toggles-C4LmZwyo.js → toggles-t5v4QKn6.js} +1 -1
- package/dist/web/assets/{tooltip-Cirpotu3.js → tooltip-Cp8tovqH.js} +1 -1
- package/dist/web/assets/{track-Cv7h6oaE.js → track-C1rUV49K.js} +1 -1
- package/dist/web/assets/{trash-2-BEOQ1ICJ.js → trash-2-Ca2ken6q.js} +1 -1
- package/dist/web/assets/{tsMode-CHIbCBQc.js → tsMode-DBbiXlyu.js} +1 -1
- package/dist/web/assets/{typescript-CScj0bbb.js → typescript-jv_3P9x_.js} +1 -1
- package/dist/web/assets/{upload-tLVD0xMT.js → upload-CgGjN1FH.js} +1 -1
- package/dist/web/assets/{use-profiles-TkoP4Yw9.js → use-profiles-BdL-U8GZ.js} +1 -1
- package/dist/web/assets/{use-providers-Bz6YNiYj.js → use-providers-B6Mj5qeG.js} +1 -1
- package/dist/web/assets/{use-translation-settings-CCpQNS30.js → use-translation-settings-FIP674t9.js} +1 -1
- package/dist/web/assets/{useNormalizedLogs-Bstb98cF.js → useNormalizedLogs-FpVMUHeq.js} +1 -1
- package/dist/web/assets/useTokenUsage-Ay3Yy0_V.js +1 -0
- package/dist/web/assets/{vennDiagram-CIIHVFJN-DzVayy4U.js → vennDiagram-CIIHVFJN-DKREAIRT.js} +1 -1
- package/dist/web/assets/{wardley-L42UT6IY-CULSKh55.js → wardley-L42UT6IY-BMpDotAz.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-YWT4CUSO-C5_h3lAA.js → wardleyDiagram-YWT4CUSO-DAPccQsF.js} +1 -1
- package/dist/web/assets/{xml-XuycSwh6.js → xml-DnxIQwRS.js} +1 -1
- package/dist/web/assets/{xychartDiagram-2RQKCTM6-F65naXcR.js → xychartDiagram-2RQKCTM6-o06g8vo8.js} +1 -1
- package/dist/web/assets/{yaml-BWm159lw.js → yaml-BV5yQTRd.js} +1 -1
- package/dist/web/index.html +1 -1
- package/node_modules/@agent-tower/shared/dist/log-adapter.d.ts +4 -0
- package/node_modules/@agent-tower/shared/dist/log-adapter.d.ts.map +1 -1
- package/node_modules/@agent-tower/shared/dist/log-adapter.js +1 -0
- package/node_modules/@agent-tower/shared/dist/log-adapter.js.map +1 -1
- package/node_modules/@agent-tower/shared/dist/translation.d.ts +4 -1
- package/node_modules/@agent-tower/shared/dist/translation.d.ts.map +1 -1
- package/node_modules/@agent-tower/shared/dist/translation.js +4 -1
- package/node_modules/@agent-tower/shared/dist/translation.js.map +1 -1
- package/node_modules/@agent-tower/shared/dist/types.d.ts +38 -2
- package/node_modules/@agent-tower/shared/dist/types.d.ts.map +1 -1
- package/node_modules/@agent-tower/shared/dist/types.js.map +1 -1
- package/package.json +2 -1
- package/prisma/migrations/20261106000000_add_session_wakeups/migration.sql +28 -0
- package/prisma/schema.prisma +28 -0
- package/scripts/postinstall.js +23 -0
- package/dist/web/assets/AttachmentPreview-TPd4gdfb.js +0 -2
- package/dist/web/assets/CreateTaskInput-TcZpjBe-.js +0 -1
- package/dist/web/assets/TaskHandoffPanel-CJahUKNo.js +0 -70
- package/dist/web/assets/channel-CDkuQ_7m.js +0 -1
- package/dist/web/assets/check-yNWhPvMH.js +0 -1
- package/dist/web/assets/classDiagram-4FO5ZUOK-eHY73FGu.js +0 -1
- package/dist/web/assets/classDiagram-v2-Q7XG4LA2-eHY73FGu.js +0 -1
- package/dist/web/assets/javascript-DKmxrNeR.js +0 -1
- package/dist/web/assets/stateDiagram-v2-BHNVJYJU-CCLmVs34.js +0 -1
- package/dist/web/assets/useTokenUsage-D7SEwXb0.js +0 -1
|
@@ -1,82 +1,30 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomBytes, timingSafeEqual } from 'node:crypto';
|
|
2
2
|
import http from 'node:http';
|
|
3
3
|
import https from 'node:https';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { stopCloudflaredTunnel, trackCloudflaredTunnel, waitForTunnelBinary } from './cloudflared-process.js';
|
|
4
|
+
import { AccessAuthService } from './access-auth.service.js';
|
|
5
|
+
import { buildPreviewBridgeScript, injectPreviewBridge } from './preview-bridge.js';
|
|
6
|
+
import { filterTargetCookieHeader, parseCookieHeader, previewCookieScopeName, rewriteTargetCookie, } from './preview-cookies.js';
|
|
7
|
+
import { classifyPreviewRequest, firstHeaderValue, requestProtocol } from './preview-proxy.js';
|
|
9
8
|
import { writeErrorLog } from '../utils/error-log.js';
|
|
9
|
+
export { rewriteTargetCookie } from './preview-cookies.js';
|
|
10
|
+
/**
|
|
11
|
+
* Dedicated-origin preview gateways.
|
|
12
|
+
*
|
|
13
|
+
* Each workspace preview opened from a browser that can reach extra ports on
|
|
14
|
+
* the Agent Tower host gets its own root-path HTTP server. The previewed app
|
|
15
|
+
* then owns the whole URL space (`/login`, `/assets`, SPA history, cookies)
|
|
16
|
+
* without any rewriting. Browsers that cannot reach those ports use the
|
|
17
|
+
* same-origin `/view/:workspaceId/` proxy instead (see preview-proxy.ts).
|
|
18
|
+
*/
|
|
10
19
|
export const PREVIEW_GATEWAY_TOKEN_PARAM = '__agent_tower_preview_token';
|
|
11
|
-
export const PREVIEW_BRIDGE_PATH = '/__agent_tower_preview_bridge.js';
|
|
12
20
|
export const PREVIEW_HEARTBEAT_PATH = '/__agent_tower_preview_heartbeat';
|
|
13
21
|
const DEFAULT_IDLE_TTL_MS = 10 * 60 * 1000;
|
|
14
22
|
const DEFAULT_LEASE_TTL_MS = 3 * 60 * 1000;
|
|
15
23
|
const DEFAULT_SWEEP_INTERVAL_MS = 30 * 1000;
|
|
16
|
-
const TUNNEL_STARTUP_TIMEOUT_MS = 30 * 1000;
|
|
17
24
|
const PREVIEW_COOKIE_MAX_AGE_SECONDS = 7 * 24 * 60 * 60;
|
|
18
|
-
const QUICK_TUNNEL_OPTIONS = { '--no-autoupdate': true };
|
|
19
|
-
const PREVIEW_BRIDGE_SCRIPT = `(() => {
|
|
20
|
-
const previewSource = 'agent-tower-preview';
|
|
21
|
-
const hostSource = 'agent-tower-preview-host';
|
|
22
|
-
const loopbackHosts = new Set(['localhost', '127.0.0.1', '::1', '[::1]', '0.0.0.0']);
|
|
23
|
-
const post = (message) => {
|
|
24
|
-
if (window.parent !== window) window.parent.postMessage({ source: previewSource, ...message }, '*');
|
|
25
|
-
};
|
|
26
|
-
const sendLocation = () => post({ type: 'location', href: window.location.href, title: document.title });
|
|
27
|
-
const scheduleLocation = () => window.setTimeout(sendLocation, 0);
|
|
28
|
-
|
|
29
|
-
const nativePushState = History.prototype.pushState;
|
|
30
|
-
const nativeReplaceState = History.prototype.replaceState;
|
|
31
|
-
History.prototype.pushState = function(...args) {
|
|
32
|
-
const result = nativePushState.apply(this, args);
|
|
33
|
-
scheduleLocation();
|
|
34
|
-
return result;
|
|
35
|
-
};
|
|
36
|
-
History.prototype.replaceState = function(...args) {
|
|
37
|
-
const result = nativeReplaceState.apply(this, args);
|
|
38
|
-
scheduleLocation();
|
|
39
|
-
return result;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
window.addEventListener('popstate', scheduleLocation);
|
|
43
|
-
window.addEventListener('hashchange', scheduleLocation);
|
|
44
|
-
window.addEventListener('pageshow', scheduleLocation);
|
|
45
|
-
document.addEventListener('click', (event) => {
|
|
46
|
-
if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
47
|
-
const anchor = event.target instanceof Element ? event.target.closest('a[href]') : null;
|
|
48
|
-
if (!anchor || anchor.hasAttribute('download')) return;
|
|
49
|
-
let url;
|
|
50
|
-
try { url = new URL(anchor.href, window.location.href); } catch { return; }
|
|
51
|
-
if (!loopbackHosts.has(url.hostname.toLowerCase()) || url.origin === window.location.origin) return;
|
|
52
|
-
event.preventDefault();
|
|
53
|
-
post({ type: 'navigate-loopback', href: url.toString(), newTab: anchor.target === '_blank' });
|
|
54
|
-
}, true);
|
|
55
|
-
|
|
56
|
-
window.addEventListener('message', (event) => {
|
|
57
|
-
const message = event.data;
|
|
58
|
-
if (!message || message.source !== hostSource) return;
|
|
59
|
-
if (message.action === 'back') window.history.back();
|
|
60
|
-
else if (message.action === 'forward') window.history.forward();
|
|
61
|
-
else if (message.action === 'reload') window.location.reload();
|
|
62
|
-
else if (message.action === 'stop') window.stop();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
const heartbeat = () => fetch(${JSON.stringify(PREVIEW_HEARTBEAT_PATH)}, {
|
|
66
|
-
credentials: 'include',
|
|
67
|
-
cache: 'no-store',
|
|
68
|
-
}).catch(() => {});
|
|
69
|
-
window.setInterval(heartbeat, 30000);
|
|
70
|
-
heartbeat();
|
|
71
|
-
sendLocation();
|
|
72
|
-
})();`;
|
|
73
25
|
function nowIso(value) {
|
|
74
26
|
return new Date(value).toISOString();
|
|
75
27
|
}
|
|
76
|
-
function cookieNameForWorkspace(workspaceId) {
|
|
77
|
-
const suffix = createHash('sha256').update(workspaceId).digest('hex').slice(0, 16);
|
|
78
|
-
return `agent-tower-preview-${suffix}`;
|
|
79
|
-
}
|
|
80
28
|
function newSecret() {
|
|
81
29
|
return randomBytes(32).toString('base64url');
|
|
82
30
|
}
|
|
@@ -85,46 +33,6 @@ function safeEqual(left, right) {
|
|
|
85
33
|
const b = Buffer.from(right);
|
|
86
34
|
return a.length === b.length && timingSafeEqual(a, b);
|
|
87
35
|
}
|
|
88
|
-
function parseCookies(header) {
|
|
89
|
-
const cookies = new Map();
|
|
90
|
-
if (!header)
|
|
91
|
-
return cookies;
|
|
92
|
-
for (const part of header.split(';')) {
|
|
93
|
-
const [rawName, ...rawValue] = part.trim().split('=');
|
|
94
|
-
if (!rawName)
|
|
95
|
-
continue;
|
|
96
|
-
try {
|
|
97
|
-
cookies.set(rawName, decodeURIComponent(rawValue.join('=')));
|
|
98
|
-
}
|
|
99
|
-
catch {
|
|
100
|
-
cookies.set(rawName, rawValue.join('='));
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return cookies;
|
|
104
|
-
}
|
|
105
|
-
function firstHeaderValue(value) {
|
|
106
|
-
return Array.isArray(value) ? value[0] : value;
|
|
107
|
-
}
|
|
108
|
-
function requestProtocol(headers) {
|
|
109
|
-
const forwarded = firstHeaderValue(headers['x-forwarded-proto']);
|
|
110
|
-
if (forwarded?.split(',')[0]?.trim().toLowerCase() === 'https')
|
|
111
|
-
return 'https';
|
|
112
|
-
const cfVisitor = firstHeaderValue(headers['cf-visitor']);
|
|
113
|
-
if (cfVisitor) {
|
|
114
|
-
try {
|
|
115
|
-
if (JSON.parse(cfVisitor).scheme === 'https')
|
|
116
|
-
return 'https';
|
|
117
|
-
}
|
|
118
|
-
catch {
|
|
119
|
-
// Ignore malformed Cloudflare metadata.
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
return 'http';
|
|
123
|
-
}
|
|
124
|
-
function isRemoteGatewayRequest(headers) {
|
|
125
|
-
return Boolean(headers['cf-connecting-ip'] || headers['cf-ray'])
|
|
126
|
-
|| requestProtocol(headers) === 'https';
|
|
127
|
-
}
|
|
128
36
|
function requestOrigin(headers) {
|
|
129
37
|
const host = firstHeaderValue(headers.host)
|
|
130
38
|
|| firstHeaderValue(headers['x-forwarded-host'])?.split(',')[0]?.trim()
|
|
@@ -152,47 +60,6 @@ function removeQueryParameter(rawUrl, parameter) {
|
|
|
152
60
|
url.searchParams.delete(parameter);
|
|
153
61
|
return `${url.pathname}${url.search}${url.hash}`;
|
|
154
62
|
}
|
|
155
|
-
function scopedTargetCookieName(runtimeCookieName, targetCookieName) {
|
|
156
|
-
return `${runtimeCookieName}-target-${Buffer.from(targetCookieName).toString('base64url')}`;
|
|
157
|
-
}
|
|
158
|
-
function isInternalCookieName(name) {
|
|
159
|
-
return name === TUNNEL_SESSION_COOKIE_NAME || isAccessAuthCookieName(name);
|
|
160
|
-
}
|
|
161
|
-
function unscopedTargetCookieName(runtimeCookieName, cookieName) {
|
|
162
|
-
const prefix = `${runtimeCookieName}-target-`;
|
|
163
|
-
if (!cookieName.startsWith(prefix))
|
|
164
|
-
return null;
|
|
165
|
-
try {
|
|
166
|
-
const targetCookieName = Buffer.from(cookieName.slice(prefix.length), 'base64url').toString('utf8');
|
|
167
|
-
return isInternalCookieName(targetCookieName) ? targetCookieName : null;
|
|
168
|
-
}
|
|
169
|
-
catch {
|
|
170
|
-
return null;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
function filterCookieHeader(cookieHeader, runtimeCookieName) {
|
|
174
|
-
if (!cookieHeader)
|
|
175
|
-
return undefined;
|
|
176
|
-
const cookies = [];
|
|
177
|
-
for (const rawPart of cookieHeader.split(';')) {
|
|
178
|
-
const part = rawPart.trim();
|
|
179
|
-
if (!part)
|
|
180
|
-
continue;
|
|
181
|
-
const separator = part.indexOf('=');
|
|
182
|
-
const name = separator === -1 ? part : part.slice(0, separator);
|
|
183
|
-
if (!name || name === runtimeCookieName)
|
|
184
|
-
continue;
|
|
185
|
-
const targetName = unscopedTargetCookieName(runtimeCookieName, name);
|
|
186
|
-
if (targetName) {
|
|
187
|
-
cookies.push(`${targetName}${separator === -1 ? '' : part.slice(separator)}`);
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
if (isInternalCookieName(name))
|
|
191
|
-
continue;
|
|
192
|
-
cookies.push(part);
|
|
193
|
-
}
|
|
194
|
-
return cookies.length > 0 ? cookies.join('; ') : undefined;
|
|
195
|
-
}
|
|
196
63
|
function joinTargetPath(target, requestUrl) {
|
|
197
64
|
const parsed = new URL(requestUrl, 'http://preview.local');
|
|
198
65
|
const suffix = parsed.pathname.startsWith('/') ? parsed.pathname : `/${parsed.pathname}`;
|
|
@@ -214,7 +81,7 @@ function rewriteRequestHeaders(headers, runtime, targetPath) {
|
|
|
214
81
|
if (key.toLowerCase().startsWith('cf-'))
|
|
215
82
|
delete next[key];
|
|
216
83
|
}
|
|
217
|
-
const cookie =
|
|
84
|
+
const cookie = filterTargetCookieHeader(headers.cookie, runtime.cookieName);
|
|
218
85
|
if (cookie)
|
|
219
86
|
next.cookie = cookie;
|
|
220
87
|
else
|
|
@@ -247,41 +114,6 @@ function rewriteLocation(value, runtime, requestHeaders) {
|
|
|
247
114
|
return value;
|
|
248
115
|
}
|
|
249
116
|
}
|
|
250
|
-
export function rewriteTargetCookie(value, target, runtimeCookieName, secureGateway) {
|
|
251
|
-
let next = value.replace(/;\s*domain=[^;]*/gi, '');
|
|
252
|
-
const separator = next.indexOf('=');
|
|
253
|
-
if (separator > 0) {
|
|
254
|
-
const cookieName = next.slice(0, separator).trim();
|
|
255
|
-
if (isInternalCookieName(cookieName)) {
|
|
256
|
-
next = `${scopedTargetCookieName(runtimeCookieName, cookieName)}${next.slice(separator)}`;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
if (target.basePath && /;\s*path=/i.test(next)) {
|
|
260
|
-
next = next.replace(/;\s*path=([^;]*)/i, (_match, rawPath) => {
|
|
261
|
-
const path = rawPath.trim();
|
|
262
|
-
if (path === target.basePath)
|
|
263
|
-
return '; Path=/';
|
|
264
|
-
if (path.startsWith(`${target.basePath}/`)) {
|
|
265
|
-
return `; Path=${path.slice(target.basePath.length) || '/'}`;
|
|
266
|
-
}
|
|
267
|
-
return `; Path=${path || '/'}`;
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
if (secureGateway) {
|
|
271
|
-
next = next
|
|
272
|
-
.replace(/;\s*secure(?=;|$)/gi, '')
|
|
273
|
-
.replace(/;\s*samesite=[^;]*/gi, '')
|
|
274
|
-
.replace(/;\s*partitioned(?=;|$)/gi, '');
|
|
275
|
-
return `${next}; Secure; SameSite=None; Partitioned`;
|
|
276
|
-
}
|
|
277
|
-
next = next
|
|
278
|
-
.replace(/;\s*secure(?=;|$)/gi, '')
|
|
279
|
-
.replace(/;\s*partitioned(?=;|$)/gi, '');
|
|
280
|
-
if (/;\s*samesite=none(?=;|$)/i.test(next)) {
|
|
281
|
-
next = next.replace(/;\s*samesite=none(?=;|$)/i, '; SameSite=Lax');
|
|
282
|
-
}
|
|
283
|
-
return next;
|
|
284
|
-
}
|
|
285
117
|
function rewriteResponseHeaders(headers, runtime, requestHeaders, injectBridge) {
|
|
286
118
|
const next = {};
|
|
287
119
|
for (const [key, value] of Object.entries(headers)) {
|
|
@@ -313,14 +145,16 @@ function isHtmlResponse(headers) {
|
|
|
313
145
|
const contentType = firstHeaderValue(headers['content-type'])?.toLowerCase() ?? '';
|
|
314
146
|
return contentType.includes('text/html');
|
|
315
147
|
}
|
|
316
|
-
function
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
148
|
+
function gatewayBridgeTag(runtime) {
|
|
149
|
+
return buildPreviewBridgeScript({
|
|
150
|
+
mode: 'gateway',
|
|
151
|
+
prefix: '',
|
|
152
|
+
workspaceId: runtime.workspaceId,
|
|
153
|
+
targetOrigin: runtime.target.origin,
|
|
154
|
+
targetBasePath: runtime.target.basePath,
|
|
155
|
+
serviceWorker: false,
|
|
156
|
+
heartbeatPath: PREVIEW_HEARTBEAT_PATH,
|
|
157
|
+
});
|
|
324
158
|
}
|
|
325
159
|
function writeUnauthorized(response) {
|
|
326
160
|
response.writeHead(401, {
|
|
@@ -381,18 +215,12 @@ export class PreviewRuntimeManager {
|
|
|
381
215
|
leaseTtlMs;
|
|
382
216
|
listenHost;
|
|
383
217
|
now;
|
|
384
|
-
createTunnel;
|
|
385
|
-
ensureTunnelBinary;
|
|
386
218
|
sweepTimer;
|
|
387
219
|
constructor(options = {}) {
|
|
388
220
|
this.idleTtlMs = options.idleTtlMs ?? DEFAULT_IDLE_TTL_MS;
|
|
389
221
|
this.leaseTtlMs = options.leaseTtlMs ?? DEFAULT_LEASE_TTL_MS;
|
|
390
222
|
this.listenHost = options.listenHost ?? process.env.AGENT_TOWER_HOST ?? '0.0.0.0';
|
|
391
223
|
this.now = options.now ?? (() => Date.now());
|
|
392
|
-
this.createTunnel = options.createTunnel
|
|
393
|
-
?? ((targetUrl) => Tunnel.quick(targetUrl, QUICK_TUNNEL_OPTIONS));
|
|
394
|
-
this.ensureTunnelBinary = options.ensureTunnelBinary
|
|
395
|
-
?? (options.createTunnel ? async () => { } : ensureCloudflaredBinary);
|
|
396
224
|
this.sweepTimer = setInterval(() => {
|
|
397
225
|
void this.sweep().catch((error) => {
|
|
398
226
|
writeErrorLog({ level: 'error', source: 'preview.cleanup', message: 'Failed to reclaim preview runtime', error });
|
|
@@ -400,25 +228,18 @@ export class PreviewRuntimeManager {
|
|
|
400
228
|
}, options.sweepIntervalMs ?? DEFAULT_SWEEP_INTERVAL_MS);
|
|
401
229
|
this.sweepTimer.unref?.();
|
|
402
230
|
}
|
|
403
|
-
async acquire(workspaceId, target,
|
|
231
|
+
async acquire(workspaceId, target, localHostname) {
|
|
404
232
|
const runtime = await this.ensureRuntime(workspaceId, target);
|
|
405
233
|
this.assertRuntimeActive(runtime);
|
|
406
234
|
const now = this.now();
|
|
407
235
|
const lease = {
|
|
408
236
|
id: randomBytes(16).toString('base64url'),
|
|
409
|
-
mode,
|
|
410
237
|
localHostname,
|
|
411
238
|
expiresAtMs: now + this.leaseTtlMs,
|
|
412
239
|
};
|
|
413
240
|
runtime.leases.set(lease.id, lease);
|
|
414
241
|
runtime.lastActivityAtMs = now;
|
|
415
|
-
|
|
416
|
-
runtime.tunnelLastActivityAtMs = now;
|
|
417
|
-
const viewBaseUrl = mode === 'remote'
|
|
418
|
-
? await this.ensureTunnel(runtime)
|
|
419
|
-
: `http://${formatHostname(localHostname)}:${runtime.port}`;
|
|
420
|
-
this.assertRuntimeActive(runtime);
|
|
421
|
-
return this.toSession(runtime, lease, viewBaseUrl);
|
|
242
|
+
return this.toSession(runtime, lease);
|
|
422
243
|
}
|
|
423
244
|
async heartbeat(workspaceId, leaseId) {
|
|
424
245
|
const runtime = this.runtimes.get(workspaceId);
|
|
@@ -428,13 +249,8 @@ export class PreviewRuntimeManager {
|
|
|
428
249
|
const now = this.now();
|
|
429
250
|
lease.expiresAtMs = now + this.leaseTtlMs;
|
|
430
251
|
runtime.lastActivityAtMs = now;
|
|
431
|
-
if (lease.mode === 'remote')
|
|
432
|
-
runtime.tunnelLastActivityAtMs = now;
|
|
433
|
-
const viewBaseUrl = lease.mode === 'remote'
|
|
434
|
-
? await this.ensureTunnel(runtime)
|
|
435
|
-
: `http://${formatHostname(lease.localHostname)}:${runtime.port}`;
|
|
436
252
|
this.assertRuntimeActive(runtime);
|
|
437
|
-
return this.toSession(runtime, lease
|
|
253
|
+
return this.toSession(runtime, lease);
|
|
438
254
|
}
|
|
439
255
|
release(workspaceId, leaseId) {
|
|
440
256
|
this.runtimes.get(workspaceId)?.leases.delete(leaseId);
|
|
@@ -443,7 +259,7 @@ export class PreviewRuntimeManager {
|
|
|
443
259
|
const runtime = this.runtimes.get(workspaceId);
|
|
444
260
|
if (!runtime)
|
|
445
261
|
return;
|
|
446
|
-
await
|
|
262
|
+
await closeServer(runtime);
|
|
447
263
|
if (this.runtimes.get(workspaceId) === runtime)
|
|
448
264
|
this.runtimes.delete(workspaceId);
|
|
449
265
|
}
|
|
@@ -455,13 +271,12 @@ export class PreviewRuntimeManager {
|
|
|
455
271
|
if (failure)
|
|
456
272
|
throw failure.reason;
|
|
457
273
|
}
|
|
458
|
-
toSession(runtime, lease
|
|
274
|
+
toSession(runtime, lease) {
|
|
459
275
|
return {
|
|
460
276
|
id: lease.id,
|
|
461
277
|
workspaceId: runtime.workspaceId,
|
|
462
278
|
target: runtime.target.target,
|
|
463
|
-
|
|
464
|
-
viewBaseUrl,
|
|
279
|
+
viewBaseUrl: `http://${formatHostname(lease.localHostname)}:${runtime.port}`,
|
|
465
280
|
expiresAt: nowIso(lease.expiresAtMs),
|
|
466
281
|
};
|
|
467
282
|
}
|
|
@@ -492,19 +307,13 @@ export class PreviewRuntimeManager {
|
|
|
492
307
|
server,
|
|
493
308
|
port: 0,
|
|
494
309
|
ready: Promise.resolve(0),
|
|
495
|
-
cookieName:
|
|
310
|
+
cookieName: previewCookieScopeName(workspaceId),
|
|
496
311
|
accessSecret: newSecret(),
|
|
497
312
|
accessGeneration: AccessAuthService.getSessionSecretGeneration(),
|
|
498
313
|
leases: new Map(),
|
|
499
314
|
lastActivityAtMs: this.now(),
|
|
500
|
-
tunnelLastActivityAtMs: this.now(),
|
|
501
315
|
activeConnections: 0,
|
|
502
|
-
activeRemoteConnections: 0,
|
|
503
316
|
sockets: new Set(),
|
|
504
|
-
tunnel: null,
|
|
505
|
-
tunnelUrl: null,
|
|
506
|
-
tunnelStartPromise: null,
|
|
507
|
-
tunnelStartController: null,
|
|
508
317
|
stopped: false,
|
|
509
318
|
});
|
|
510
319
|
server.on('connection', (socket) => {
|
|
@@ -526,7 +335,7 @@ export class PreviewRuntimeManager {
|
|
|
526
335
|
return runtime;
|
|
527
336
|
}
|
|
528
337
|
catch (error) {
|
|
529
|
-
await
|
|
338
|
+
await closeServer(runtime);
|
|
530
339
|
if (this.runtimes.get(workspaceId) === runtime)
|
|
531
340
|
this.runtimes.delete(workspaceId);
|
|
532
341
|
throw error;
|
|
@@ -541,14 +350,11 @@ export class PreviewRuntimeManager {
|
|
|
541
350
|
}
|
|
542
351
|
hasGatewayCookie(runtime, request) {
|
|
543
352
|
this.rotateAccessSecretIfNeeded(runtime);
|
|
544
|
-
const candidate =
|
|
353
|
+
const candidate = parseCookieHeader(request.headers.cookie).get(runtime.cookieName);
|
|
545
354
|
return Boolean(candidate && safeEqual(candidate, runtime.accessSecret));
|
|
546
355
|
}
|
|
547
|
-
touchRuntime(runtime
|
|
548
|
-
|
|
549
|
-
runtime.lastActivityAtMs = now;
|
|
550
|
-
if (isRemoteGatewayRequest(headers))
|
|
551
|
-
runtime.tunnelLastActivityAtMs = now;
|
|
356
|
+
touchRuntime(runtime) {
|
|
357
|
+
runtime.lastActivityAtMs = this.now();
|
|
552
358
|
}
|
|
553
359
|
async authorizeBootstrap(runtime, request, response) {
|
|
554
360
|
const parsed = new URL(request.url ?? '/', 'http://preview.local');
|
|
@@ -557,7 +363,7 @@ export class PreviewRuntimeManager {
|
|
|
557
363
|
return false;
|
|
558
364
|
}
|
|
559
365
|
this.rotateAccessSecretIfNeeded(runtime);
|
|
560
|
-
this.touchRuntime(runtime
|
|
366
|
+
this.touchRuntime(runtime);
|
|
561
367
|
response.setHeader('set-cookie', serializeGatewayCookie(runtime, request.headers));
|
|
562
368
|
if (request.method === 'GET' || request.method === 'HEAD') {
|
|
563
369
|
response.writeHead(302, {
|
|
@@ -585,15 +391,7 @@ export class PreviewRuntimeManager {
|
|
|
585
391
|
writeUnauthorized(response);
|
|
586
392
|
return;
|
|
587
393
|
}
|
|
588
|
-
this.touchRuntime(runtime
|
|
589
|
-
if (parsed.pathname === PREVIEW_BRIDGE_PATH) {
|
|
590
|
-
response.writeHead(200, {
|
|
591
|
-
'content-type': 'application/javascript; charset=utf-8',
|
|
592
|
-
'cache-control': 'no-store',
|
|
593
|
-
});
|
|
594
|
-
response.end(PREVIEW_BRIDGE_SCRIPT);
|
|
595
|
-
return;
|
|
596
|
-
}
|
|
394
|
+
this.touchRuntime(runtime);
|
|
597
395
|
if (parsed.pathname === PREVIEW_HEARTBEAT_PATH) {
|
|
598
396
|
response.writeHead(204, { 'cache-control': 'no-store' });
|
|
599
397
|
response.end();
|
|
@@ -604,6 +402,7 @@ export class PreviewRuntimeManager {
|
|
|
604
402
|
async proxyHttpRequest(runtime, request, response) {
|
|
605
403
|
const targetUrl = new URL(runtime.target.origin);
|
|
606
404
|
const targetPath = joinTargetPath(runtime.target, request.url ?? '/');
|
|
405
|
+
const context = classifyPreviewRequest(request.method ?? 'GET', request.headers, new URL(request.url ?? '/', 'http://preview.local').search);
|
|
607
406
|
const options = {
|
|
608
407
|
protocol: targetUrl.protocol,
|
|
609
408
|
hostname: targetUrl.hostname,
|
|
@@ -619,6 +418,7 @@ export class PreviewRuntimeManager {
|
|
|
619
418
|
const injectBridge = request.method !== 'HEAD'
|
|
620
419
|
&& proxyResponse.statusCode !== 204
|
|
621
420
|
&& proxyResponse.statusCode !== 304
|
|
421
|
+
&& context.document
|
|
622
422
|
&& isHtmlResponse(proxyResponse.headers);
|
|
623
423
|
const headers = rewriteResponseHeaders(proxyResponse.headers, runtime, request.headers, injectBridge);
|
|
624
424
|
response.statusCode = proxyResponse.statusCode ?? 502;
|
|
@@ -640,7 +440,7 @@ export class PreviewRuntimeManager {
|
|
|
640
440
|
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
641
441
|
});
|
|
642
442
|
proxyResponse.once('end', () => {
|
|
643
|
-
response.end(
|
|
443
|
+
response.end(injectPreviewBridge(Buffer.concat(chunks), gatewayBridgeTag(runtime)));
|
|
644
444
|
resolve();
|
|
645
445
|
});
|
|
646
446
|
proxyResponse.once('error', () => {
|
|
@@ -663,8 +463,7 @@ export class PreviewRuntimeManager {
|
|
|
663
463
|
writeUpgradeError(socket, 401, 'Unauthorized');
|
|
664
464
|
return;
|
|
665
465
|
}
|
|
666
|
-
this.touchRuntime(runtime
|
|
667
|
-
const remoteConnection = isRemoteGatewayRequest(request.headers);
|
|
466
|
+
this.touchRuntime(runtime);
|
|
668
467
|
const targetUrl = new URL(runtime.target.origin);
|
|
669
468
|
const targetPath = joinTargetPath(runtime.target, request.url ?? '/');
|
|
670
469
|
const headers = rewriteRequestHeaders(request.headers, runtime, targetPath);
|
|
@@ -683,24 +482,15 @@ export class PreviewRuntimeManager {
|
|
|
683
482
|
const proxyRequest = transport.request(options);
|
|
684
483
|
proxyRequest.once('upgrade', (proxyResponse, proxySocket, proxyHead) => {
|
|
685
484
|
runtime.activeConnections += 1;
|
|
686
|
-
if (remoteConnection)
|
|
687
|
-
runtime.activeRemoteConnections += 1;
|
|
688
485
|
runtime.sockets.add(proxySocket);
|
|
689
486
|
const close = () => {
|
|
690
487
|
runtime.activeConnections = Math.max(0, runtime.activeConnections - 1);
|
|
691
|
-
if (remoteConnection) {
|
|
692
|
-
runtime.activeRemoteConnections = Math.max(0, runtime.activeRemoteConnections - 1);
|
|
693
|
-
}
|
|
694
488
|
runtime.sockets.delete(proxySocket);
|
|
695
|
-
this.touchRuntime(runtime
|
|
489
|
+
this.touchRuntime(runtime);
|
|
696
490
|
};
|
|
697
491
|
proxySocket.once('close', close);
|
|
698
|
-
proxySocket.on('data', () =>
|
|
699
|
-
|
|
700
|
-
});
|
|
701
|
-
socket.on('data', () => {
|
|
702
|
-
this.touchRuntime(runtime, request.headers);
|
|
703
|
-
});
|
|
492
|
+
proxySocket.on('data', () => this.touchRuntime(runtime));
|
|
493
|
+
socket.on('data', () => this.touchRuntime(runtime));
|
|
704
494
|
const headerLines = [
|
|
705
495
|
`HTTP/1.1 ${proxyResponse.statusCode} ${proxyResponse.statusMessage}`,
|
|
706
496
|
...Object.entries(proxyResponse.headers).flatMap(([key, value]) => {
|
|
@@ -730,88 +520,6 @@ export class PreviewRuntimeManager {
|
|
|
730
520
|
throw new Error('Preview runtime was invalidated');
|
|
731
521
|
}
|
|
732
522
|
}
|
|
733
|
-
async ensureTunnel(runtime) {
|
|
734
|
-
this.assertRuntimeActive(runtime);
|
|
735
|
-
if (runtime.tunnel && runtime.tunnelUrl)
|
|
736
|
-
return runtime.tunnelUrl;
|
|
737
|
-
if (runtime.tunnelStartPromise)
|
|
738
|
-
return runtime.tunnelStartPromise;
|
|
739
|
-
if (runtime.tunnel)
|
|
740
|
-
throw new Error('Previous preview tunnel cleanup must finish before starting');
|
|
741
|
-
const controller = new AbortController();
|
|
742
|
-
runtime.tunnelStartController = controller;
|
|
743
|
-
let startedTunnel = null;
|
|
744
|
-
const startPromise = (async () => {
|
|
745
|
-
await waitForTunnelBinary(this.ensureTunnelBinary(), controller.signal);
|
|
746
|
-
this.assertRuntimeActive(runtime);
|
|
747
|
-
return new Promise((resolve, reject) => {
|
|
748
|
-
const tunnel = this.createTunnel(`http://127.0.0.1:${runtime.port}`);
|
|
749
|
-
startedTunnel = tunnel;
|
|
750
|
-
trackCloudflaredTunnel(tunnel);
|
|
751
|
-
runtime.tunnel = tunnel;
|
|
752
|
-
let settled = false;
|
|
753
|
-
const timer = setTimeout(() => settle(reject, new Error('Preview tunnel startup timed out')), TUNNEL_STARTUP_TIMEOUT_MS);
|
|
754
|
-
const cleanup = () => {
|
|
755
|
-
clearTimeout(timer);
|
|
756
|
-
tunnel.off('url', onUrl);
|
|
757
|
-
tunnel.off('error', onError);
|
|
758
|
-
tunnel.off('exit', onExitBeforeReady);
|
|
759
|
-
controller.signal.removeEventListener('abort', onAbort);
|
|
760
|
-
};
|
|
761
|
-
const settle = (done, value) => {
|
|
762
|
-
if (settled)
|
|
763
|
-
return;
|
|
764
|
-
settled = true;
|
|
765
|
-
cleanup();
|
|
766
|
-
done(value);
|
|
767
|
-
};
|
|
768
|
-
const onAbort = () => settle(reject, controller.signal.reason);
|
|
769
|
-
const onUrl = (url) => {
|
|
770
|
-
runtime.tunnelUrl = url.replace(/\/$/, '');
|
|
771
|
-
settle(resolve, runtime.tunnelUrl);
|
|
772
|
-
};
|
|
773
|
-
const onError = (error) => settle(reject, error);
|
|
774
|
-
const onExitBeforeReady = (code, signal) => {
|
|
775
|
-
settle(reject, new Error(`Preview tunnel exited before ready (${code ?? signal ?? 'unknown'})`));
|
|
776
|
-
};
|
|
777
|
-
controller.signal.addEventListener('abort', onAbort, { once: true });
|
|
778
|
-
tunnel.once('url', onUrl);
|
|
779
|
-
tunnel.once('error', onError);
|
|
780
|
-
tunnel.once('exit', onExitBeforeReady);
|
|
781
|
-
tunnel.on('error', () => {
|
|
782
|
-
// The session heartbeat will recreate a failed tunnel when needed.
|
|
783
|
-
});
|
|
784
|
-
tunnel.on('exit', () => {
|
|
785
|
-
if (runtime.tunnel !== tunnel)
|
|
786
|
-
return;
|
|
787
|
-
runtime.tunnel = null;
|
|
788
|
-
runtime.tunnelUrl = null;
|
|
789
|
-
});
|
|
790
|
-
});
|
|
791
|
-
})();
|
|
792
|
-
runtime.tunnelStartPromise = startPromise;
|
|
793
|
-
try {
|
|
794
|
-
const url = await startPromise;
|
|
795
|
-
controller.signal.throwIfAborted();
|
|
796
|
-
this.assertRuntimeActive(runtime);
|
|
797
|
-
return url;
|
|
798
|
-
}
|
|
799
|
-
catch (error) {
|
|
800
|
-
if (startedTunnel) {
|
|
801
|
-
await stopCloudflaredTunnel(startedTunnel);
|
|
802
|
-
if (runtime.tunnel === startedTunnel)
|
|
803
|
-
runtime.tunnel = null;
|
|
804
|
-
}
|
|
805
|
-
runtime.tunnelUrl = null;
|
|
806
|
-
throw error;
|
|
807
|
-
}
|
|
808
|
-
finally {
|
|
809
|
-
if (runtime.tunnelStartPromise === startPromise) {
|
|
810
|
-
runtime.tunnelStartPromise = null;
|
|
811
|
-
runtime.tunnelStartController = null;
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
523
|
async sweep() {
|
|
816
524
|
const now = this.now();
|
|
817
525
|
const expired = [];
|
|
@@ -820,13 +528,6 @@ export class PreviewRuntimeManager {
|
|
|
820
528
|
if (lease.expiresAtMs <= now)
|
|
821
529
|
runtime.leases.delete(leaseId);
|
|
822
530
|
}
|
|
823
|
-
const hasRemoteLease = [...runtime.leases.values()].some((lease) => lease.mode === 'remote');
|
|
824
|
-
if (runtime.tunnel
|
|
825
|
-
&& !hasRemoteLease
|
|
826
|
-
&& runtime.activeRemoteConnections === 0
|
|
827
|
-
&& now - runtime.tunnelLastActivityAtMs >= this.idleTtlMs) {
|
|
828
|
-
await this.stopTunnel(runtime);
|
|
829
|
-
}
|
|
830
531
|
if (runtime.leases.size === 0
|
|
831
532
|
&& runtime.activeConnections === 0
|
|
832
533
|
&& now - runtime.lastActivityAtMs >= this.idleTtlMs) {
|
|
@@ -839,23 +540,5 @@ export class PreviewRuntimeManager {
|
|
|
839
540
|
await this.invalidate(runtime.workspaceId);
|
|
840
541
|
}));
|
|
841
542
|
}
|
|
842
|
-
async stopRuntime(runtime) {
|
|
843
|
-
runtime.stopped = true;
|
|
844
|
-
const results = await Promise.allSettled([this.stopTunnel(runtime), closeServer(runtime)]);
|
|
845
|
-
const failure = results.find((result) => result.status === 'rejected');
|
|
846
|
-
if (failure)
|
|
847
|
-
throw failure.reason;
|
|
848
|
-
}
|
|
849
|
-
async stopTunnel(runtime) {
|
|
850
|
-
runtime.tunnelStartController?.abort(new Error('Preview tunnel startup cancelled'));
|
|
851
|
-
runtime.tunnelUrl = null;
|
|
852
|
-
await runtime.tunnelStartPromise?.catch(() => undefined);
|
|
853
|
-
const tunnel = runtime.tunnel;
|
|
854
|
-
if (tunnel) {
|
|
855
|
-
await stopCloudflaredTunnel(tunnel);
|
|
856
|
-
if (runtime.tunnel === tunnel)
|
|
857
|
-
runtime.tunnel = null;
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
543
|
}
|
|
861
544
|
//# sourceMappingURL=preview-runtime-manager.js.map
|