gitspace 0.2.0-rc.20 → 0.2.0-rc.21
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/package.json +11 -6
- package/.claude/settings.local.json +0 -10
- package/.gitspace/bundle.json +0 -50
- package/.gitspace/events.json +0 -11
- package/.gitspace/processes.json +0 -23
- package/.gitspace/scripts/select/01-status.sh +0 -39
- package/.gitspace/scripts/setup/01-install-deps.sh +0 -12
- package/.gitspace/scripts/setup/02-typecheck.sh +0 -16
- package/AGENTS.md +0 -469
- package/CLAUDE.md +0 -1
- package/bun.lock +0 -794
- package/docs/CONNECTION.md +0 -623
- package/docs/GATEWAY-WORKER.md +0 -319
- package/docs/GETTING-STARTED.md +0 -448
- package/docs/GITSPACE-PLATFORM.md +0 -1819
- package/docs/INFRASTRUCTURE.md +0 -1347
- package/docs/PROTOCOL.md +0 -619
- package/docs/QUICKSTART.md +0 -183
- package/docs/RELAY.md +0 -327
- package/docs/REMOTE-DESIGN.md +0 -554
- package/docs/ROADMAP.md +0 -564
- package/docs/SITE_DOCS_FIGMA_MAKE.md +0 -1176
- package/docs/STACK-DESIGN.md +0 -588
- package/docs/UNIFIED_ARCHITECTURE.md +0 -138
- package/experiments/pty-benchmark.ts +0 -148
- package/experiments/pty-latency.ts +0 -100
- package/experiments/router/client.ts +0 -199
- package/experiments/router/protocol.ts +0 -74
- package/experiments/router/router.ts +0 -217
- package/experiments/router/session.ts +0 -180
- package/experiments/router/test.ts +0 -133
- package/experiments/socket-bandwidth.ts +0 -77
- package/homebrew/gitspace.rb +0 -45
- package/landing-page/ATTRIBUTIONS.md +0 -3
- package/landing-page/README.md +0 -11
- package/landing-page/bun.lock +0 -801
- package/landing-page/guidelines/Guidelines.md +0 -61
- package/landing-page/index.html +0 -37
- package/landing-page/package.json +0 -90
- package/landing-page/postcss.config.mjs +0 -15
- package/landing-page/public/_redirects +0 -1
- package/landing-page/public/favicon.png +0 -0
- package/landing-page/src/app/App.tsx +0 -53
- package/landing-page/src/app/components/figma/ImageWithFallback.tsx +0 -27
- package/landing-page/src/app/components/ui/accordion.tsx +0 -66
- package/landing-page/src/app/components/ui/alert-dialog.tsx +0 -157
- package/landing-page/src/app/components/ui/alert.tsx +0 -66
- package/landing-page/src/app/components/ui/aspect-ratio.tsx +0 -11
- package/landing-page/src/app/components/ui/avatar.tsx +0 -53
- package/landing-page/src/app/components/ui/badge.tsx +0 -46
- package/landing-page/src/app/components/ui/breadcrumb.tsx +0 -109
- package/landing-page/src/app/components/ui/button.tsx +0 -57
- package/landing-page/src/app/components/ui/calendar.tsx +0 -75
- package/landing-page/src/app/components/ui/card.tsx +0 -92
- package/landing-page/src/app/components/ui/carousel.tsx +0 -241
- package/landing-page/src/app/components/ui/chart.tsx +0 -353
- package/landing-page/src/app/components/ui/checkbox.tsx +0 -32
- package/landing-page/src/app/components/ui/collapsible.tsx +0 -33
- package/landing-page/src/app/components/ui/command.tsx +0 -177
- package/landing-page/src/app/components/ui/context-menu.tsx +0 -252
- package/landing-page/src/app/components/ui/dialog.tsx +0 -135
- package/landing-page/src/app/components/ui/drawer.tsx +0 -132
- package/landing-page/src/app/components/ui/dropdown-menu.tsx +0 -257
- package/landing-page/src/app/components/ui/form.tsx +0 -168
- package/landing-page/src/app/components/ui/hover-card.tsx +0 -44
- package/landing-page/src/app/components/ui/input-otp.tsx +0 -77
- package/landing-page/src/app/components/ui/input.tsx +0 -21
- package/landing-page/src/app/components/ui/label.tsx +0 -24
- package/landing-page/src/app/components/ui/menubar.tsx +0 -276
- package/landing-page/src/app/components/ui/navigation-menu.tsx +0 -168
- package/landing-page/src/app/components/ui/pagination.tsx +0 -127
- package/landing-page/src/app/components/ui/popover.tsx +0 -48
- package/landing-page/src/app/components/ui/progress.tsx +0 -31
- package/landing-page/src/app/components/ui/radio-group.tsx +0 -45
- package/landing-page/src/app/components/ui/resizable.tsx +0 -56
- package/landing-page/src/app/components/ui/scroll-area.tsx +0 -58
- package/landing-page/src/app/components/ui/select.tsx +0 -189
- package/landing-page/src/app/components/ui/separator.tsx +0 -28
- package/landing-page/src/app/components/ui/sheet.tsx +0 -139
- package/landing-page/src/app/components/ui/sidebar.tsx +0 -726
- package/landing-page/src/app/components/ui/skeleton.tsx +0 -13
- package/landing-page/src/app/components/ui/slider.tsx +0 -63
- package/landing-page/src/app/components/ui/sonner.tsx +0 -25
- package/landing-page/src/app/components/ui/switch.tsx +0 -31
- package/landing-page/src/app/components/ui/table.tsx +0 -116
- package/landing-page/src/app/components/ui/tabs.tsx +0 -66
- package/landing-page/src/app/components/ui/textarea.tsx +0 -18
- package/landing-page/src/app/components/ui/toggle-group.tsx +0 -73
- package/landing-page/src/app/components/ui/toggle.tsx +0 -47
- package/landing-page/src/app/components/ui/tooltip.tsx +0 -61
- package/landing-page/src/app/components/ui/use-mobile.ts +0 -21
- package/landing-page/src/app/components/ui/utils.ts +0 -6
- package/landing-page/src/components/docs/DocsContent.tsx +0 -801
- package/landing-page/src/components/docs/DocsSidebar.tsx +0 -90
- package/landing-page/src/components/landing/CTA.tsx +0 -59
- package/landing-page/src/components/landing/Comparison.tsx +0 -84
- package/landing-page/src/components/landing/FaultyTerminal.tsx +0 -424
- package/landing-page/src/components/landing/Features.tsx +0 -201
- package/landing-page/src/components/landing/Hero.tsx +0 -142
- package/landing-page/src/components/landing/Pricing.tsx +0 -140
- package/landing-page/src/components/landing/Roadmap.tsx +0 -86
- package/landing-page/src/components/landing/Security.tsx +0 -81
- package/landing-page/src/components/landing/TerminalWindow.tsx +0 -27
- package/landing-page/src/components/landing/UseCases.tsx +0 -55
- package/landing-page/src/components/landing/Workflow.tsx +0 -101
- package/landing-page/src/components/layout/DashboardNavbar.tsx +0 -37
- package/landing-page/src/components/layout/Footer.tsx +0 -55
- package/landing-page/src/components/layout/LandingNavbar.tsx +0 -82
- package/landing-page/src/components/ui/badge.tsx +0 -39
- package/landing-page/src/components/ui/breadcrumb.tsx +0 -115
- package/landing-page/src/components/ui/button.tsx +0 -57
- package/landing-page/src/components/ui/card.tsx +0 -79
- package/landing-page/src/components/ui/mock-terminal.tsx +0 -68
- package/landing-page/src/components/ui/separator.tsx +0 -28
- package/landing-page/src/lib/utils.ts +0 -6
- package/landing-page/src/main.tsx +0 -10
- package/landing-page/src/pages/Dashboard.tsx +0 -133
- package/landing-page/src/pages/DocsPage.tsx +0 -79
- package/landing-page/src/pages/LandingPage.tsx +0 -31
- package/landing-page/src/pages/TerminalView.tsx +0 -106
- package/landing-page/src/styles/fonts.css +0 -0
- package/landing-page/src/styles/index.css +0 -3
- package/landing-page/src/styles/tailwind.css +0 -4
- package/landing-page/src/styles/theme.css +0 -181
- package/landing-page/vite.config.ts +0 -19
- package/scripts/GHOSTTY_TAB_BUG.md +0 -106
- package/scripts/build.ts +0 -298
- package/scripts/migrate-secrets.ts +0 -77
- package/scripts/release.ts +0 -140
- package/scripts/sample-events.ts +0 -263
- package/scripts/test-tabs-minimal.ts +0 -68
- package/scripts/test-tabs-workaround.ts +0 -95
- package/scripts/test-tabs.ts +0 -171
- package/src/__tests__/test-utils.ts +0 -298
- package/src/app/input/__tests__/sessionCommands.test.ts +0 -40
- package/src/app/input/sessionCommands.ts +0 -94
- package/src/app/session/__tests__/useAttachController.test.ts +0 -229
- package/src/app/session/createSessionBackend.bun.ts +0 -76
- package/src/app/session/createSessionBackend.web.ts +0 -104
- package/src/app/session/types.ts +0 -16
- package/src/app/session/useAttachController.ts +0 -220
- package/src/app/session/useProcessActions.ts +0 -201
- package/src/app/session/useSessionClient.ts +0 -35
- package/src/app/session/useWorkspaceDeleteFlow.ts +0 -170
- package/src/app.tui.tsx +0 -2929
- package/src/app.web.tsx +0 -1454
- package/src/commands/__tests__/connect-key.test.ts +0 -10
- package/src/commands/__tests__/events.test.ts +0 -201
- package/src/commands/__tests__/notifications.test.ts +0 -349
- package/src/commands/__tests__/process.test.ts +0 -251
- package/src/commands/__tests__/serve-messages.test.ts +0 -190
- package/src/commands/__tests__/serve-process-hosting.test.ts +0 -63
- package/src/commands/access.ts +0 -298
- package/src/commands/add.ts +0 -455
- package/src/commands/auth.ts +0 -369
- package/src/commands/bundle.ts +0 -232
- package/src/commands/config.ts +0 -242
- package/src/commands/connect-key.ts +0 -1
- package/src/commands/connect.ts +0 -576
- package/src/commands/directory.ts +0 -16
- package/src/commands/events.ts +0 -157
- package/src/commands/host.ts +0 -566
- package/src/commands/identity.ts +0 -184
- package/src/commands/linear.ts +0 -717
- package/src/commands/list.ts +0 -181
- package/src/commands/migrate.ts +0 -52
- package/src/commands/notifications.ts +0 -351
- package/src/commands/process.ts +0 -104
- package/src/commands/relay.ts +0 -315
- package/src/commands/remove.ts +0 -279
- package/src/commands/review.ts +0 -787
- package/src/commands/serve.ts +0 -1946
- package/src/commands/share.ts +0 -451
- package/src/commands/status.ts +0 -125
- package/src/commands/switch.ts +0 -361
- package/src/commands/tmux.ts +0 -317
- package/src/components/DPad.web.tsx +0 -343
- package/src/components/DiffViewer.web.tsx +0 -1192
- package/src/components/Events.tsx +0 -137
- package/src/components/Events.tui.tsx +0 -129
- package/src/components/Events.web.tsx +0 -386
- package/src/components/FloatingControls.web.tsx +0 -112
- package/src/components/FloatingJogWheel.web.tsx +0 -240
- package/src/components/Flow.tsx +0 -458
- package/src/components/Flow.tui.tsx +0 -343
- package/src/components/Flow.web.tsx +0 -442
- package/src/components/Inbox.tsx +0 -448
- package/src/components/Inbox.tui.tsx +0 -262
- package/src/components/Inbox.web.tsx +0 -329
- package/src/components/MachineList.tsx +0 -187
- package/src/components/MachineList.tui.tsx +0 -161
- package/src/components/MachineList.web.tsx +0 -210
- package/src/components/NumPad.web.tsx +0 -270
- package/src/components/ProjectList.tsx +0 -175
- package/src/components/ProjectList.tui.tsx +0 -109
- package/src/components/ProjectList.web.tsx +0 -143
- package/src/components/ProjectOnboardingStep.ts +0 -23
- package/src/components/ProjectOnboardingStep.tui.tsx +0 -88
- package/src/components/ProjectOnboardingStep.web.tsx +0 -59
- package/src/components/RemoteMachineScreen.tui.tsx +0 -690
- package/src/components/ScriptTerminal.tui.tsx +0 -160
- package/src/components/ScriptTerminal.web.tsx +0 -89
- package/src/components/SessionTerminal.tui.tsx +0 -406
- package/src/components/SessionTerminal.web.tsx +0 -467
- package/src/components/SpacesBrowser.tsx +0 -540
- package/src/components/SpacesBrowser.tui.tsx +0 -258
- package/src/components/SpacesBrowser.web.tsx +0 -332
- package/src/components/TerminalControls.web.tsx +0 -464
- package/src/components/ThreadPanel.web.tsx +0 -798
- package/src/components/__tests__/SpacesBrowser.test.ts +0 -541
- package/src/components/__tests__/SpacesBrowser.tui.test.tsx +0 -249
- package/src/components/__tests__/script-terminal-buffer.tui.test.ts +0 -72
- package/src/components/index.ts +0 -105
- package/src/components/review-decision-colors.ts +0 -11
- package/src/components/script-terminal-buffer.tui.ts +0 -37
- package/src/components/session-terminal-page-navigation.ts +0 -48
- package/src/components/terminal-bracketed-paste.tui.test.ts +0 -43
- package/src/components/terminal-bracketed-paste.tui.ts +0 -46
- package/src/core/__tests__/access.test.ts +0 -240
- package/src/core/__tests__/bundle-refresh.test.ts +0 -567
- package/src/core/__tests__/bundle.test.ts +0 -209
- package/src/core/__tests__/github-review.test.ts +0 -781
- package/src/core/__tests__/project-lifecycle.test.ts +0 -137
- package/src/core/__tests__/workspace-lifecycle.test.ts +0 -159
- package/src/core/__tests__/workspace.test.ts +0 -149
- package/src/core/access.ts +0 -277
- package/src/core/bundle-refresh.ts +0 -1064
- package/src/core/bundle.ts +0 -326
- package/src/core/config.ts +0 -405
- package/src/core/git.ts +0 -768
- package/src/core/github-review.ts +0 -761
- package/src/core/github.ts +0 -151
- package/src/core/identity.ts +0 -631
- package/src/core/linear.ts +0 -403
- package/src/core/preferences-service.ts +0 -17
- package/src/core/project-catalog.ts +0 -52
- package/src/core/project-lifecycle.ts +0 -163
- package/src/core/review-executor.ts +0 -316
- package/src/core/review.ts +0 -407
- package/src/core/secret-runtime.ts +0 -167
- package/src/core/shell.ts +0 -117
- package/src/core/trusted-relays.ts +0 -315
- package/src/core/workspace-lifecycle.ts +0 -216
- package/src/core/workspace.ts +0 -363
- package/src/hooks/__tests__/useLocalSession.tui.test.ts +0 -557
- package/src/hooks/index.ts +0 -8
- package/src/hooks/index.tui.ts +0 -32
- package/src/hooks/useDaemonStatus.tui.ts +0 -174
- package/src/hooks/useLocalSession.tui.ts +0 -395
- package/src/hooks/useRelayConnection.web.ts +0 -54
- package/src/hooks/useRemoteMachines.tui.ts +0 -166
- package/src/hooks/useRemoteTerminal.tui.ts +0 -22
- package/src/hooks/useReview.web.ts +0 -248
- package/src/hooks/useTerminal.web.ts +0 -36
- package/src/hooks/useUserActivity.ts +0 -61
- package/src/hooks/useVisualViewport.web.ts +0 -104
- package/src/index.ts +0 -1376
- package/src/lib/events/__tests__/collector-filter.test.ts +0 -105
- package/src/lib/events/__tests__/store-query.test.ts +0 -103
- package/src/lib/events/collector.ts +0 -494
- package/src/lib/events/filters.ts +0 -26
- package/src/lib/events/index.ts +0 -11
- package/src/lib/events/indexer.ts +0 -14
- package/src/lib/events/paths.ts +0 -69
- package/src/lib/events/reader.ts +0 -212
- package/src/lib/events/store.ts +0 -141
- package/src/lib/invite.web.ts +0 -58
- package/src/lib/preferences-service.web.ts +0 -41
- package/src/lib/processes/__tests__/config.test.ts +0 -83
- package/src/lib/processes/__tests__/names.test.ts +0 -125
- package/src/lib/processes/__tests__/schema.test.ts +0 -208
- package/src/lib/processes/__tests__/watchdog.test.ts +0 -210
- package/src/lib/processes/autostart.ts +0 -16
- package/src/lib/processes/config.ts +0 -187
- package/src/lib/processes/control.ts +0 -53
- package/src/lib/processes/editor.ts +0 -32
- package/src/lib/processes/events-config.ts +0 -37
- package/src/lib/processes/index.ts +0 -14
- package/src/lib/processes/instances.ts +0 -20
- package/src/lib/processes/manager.ts +0 -131
- package/src/lib/processes/names.ts +0 -71
- package/src/lib/processes/registry.ts +0 -26
- package/src/lib/processes/runner.ts +0 -211
- package/src/lib/processes/scheduler.ts +0 -17
- package/src/lib/processes/schema.ts +0 -74
- package/src/lib/processes/session-list.ts +0 -15
- package/src/lib/processes/state.ts +0 -82
- package/src/lib/processes/watchdog.test.ts +0 -79
- package/src/lib/processes/watchdog.ts +0 -106
- package/src/lib/remote-session/__tests__/protocol.test.ts +0 -291
- package/src/lib/remote-session/index.ts +0 -7
- package/src/lib/remote-session/protocol.ts +0 -443
- package/src/lib/remote-session/session-handler.ts +0 -1298
- package/src/lib/remote-session/workspace-scanner.ts +0 -161
- package/src/lib/sonner.web.ts +0 -1
- package/src/lib/storage/identity-store.web.ts +0 -94
- package/src/lib/tmux-lite/README.md +0 -81
- package/src/lib/tmux-lite/cli.ts +0 -855
- package/src/lib/tmux-lite/crypto/__tests__/helpers/handshake-runner.ts +0 -349
- package/src/lib/tmux-lite/crypto/__tests__/helpers/mock-relay.ts +0 -291
- package/src/lib/tmux-lite/crypto/__tests__/helpers/test-identities.ts +0 -142
- package/src/lib/tmux-lite/crypto/__tests__/integration/authorization.integration.test.ts +0 -339
- package/src/lib/tmux-lite/crypto/__tests__/integration/e2e-communication.integration.test.ts +0 -477
- package/src/lib/tmux-lite/crypto/__tests__/integration/error-handling.integration.test.ts +0 -499
- package/src/lib/tmux-lite/crypto/__tests__/integration/handshake.integration.test.ts +0 -371
- package/src/lib/tmux-lite/crypto/__tests__/integration/security.integration.test.ts +0 -573
- package/src/lib/tmux-lite/crypto/access-control.test.ts +0 -512
- package/src/lib/tmux-lite/crypto/access-control.ts +0 -320
- package/src/lib/tmux-lite/crypto/frames.test.ts +0 -262
- package/src/lib/tmux-lite/crypto/frames.ts +0 -141
- package/src/lib/tmux-lite/crypto/handshake.ts +0 -894
- package/src/lib/tmux-lite/crypto/identity.test.ts +0 -220
- package/src/lib/tmux-lite/crypto/identity.ts +0 -286
- package/src/lib/tmux-lite/crypto/index.ts +0 -51
- package/src/lib/tmux-lite/crypto/invites.test.ts +0 -381
- package/src/lib/tmux-lite/crypto/invites.ts +0 -215
- package/src/lib/tmux-lite/crypto/keyexchange.ts +0 -435
- package/src/lib/tmux-lite/crypto/keys.test.ts +0 -58
- package/src/lib/tmux-lite/crypto/keys.ts +0 -47
- package/src/lib/tmux-lite/crypto/secretbox.test.ts +0 -169
- package/src/lib/tmux-lite/crypto/secretbox.ts +0 -124
- package/src/lib/tmux-lite/handshake-handler.ts +0 -451
- package/src/lib/tmux-lite/process-run.integration.test.ts +0 -266
- package/src/lib/tmux-lite/protocol.test.ts +0 -307
- package/src/lib/tmux-lite/protocol.ts +0 -291
- package/src/lib/tmux-lite/relay-client.ts +0 -506
- package/src/lib/tmux-lite/server-lifecycle.test.ts +0 -212
- package/src/lib/tmux-lite/server.ts +0 -1412
- package/src/lib/tmux-lite/shell-integration.sh +0 -37
- package/src/lib/tmux-lite/terminal-queries.test.ts +0 -54
- package/src/lib/tmux-lite/terminal-queries.ts +0 -49
- package/src/notifications/__tests__/useNotifications.test.ts +0 -739
- package/src/notifications/index.ts +0 -32
- package/src/notifications/policy.test.ts +0 -424
- package/src/notifications/policy.ts +0 -139
- package/src/notifications/types.ts +0 -82
- package/src/notifications/useNotifications.ts +0 -350
- package/src/pages/ReviewPage.web.tsx +0 -511
- package/src/preferences/index.ts +0 -1
- package/src/preferences/types.ts +0 -9
- package/src/relay/__tests__/e2e-flow.test.ts +0 -1284
- package/src/relay/__tests__/helpers/auth.ts +0 -354
- package/src/relay/__tests__/helpers/ports.ts +0 -51
- package/src/relay/__tests__/protocol-validation.test.ts +0 -265
- package/src/relay/authorization.ts +0 -303
- package/src/relay/embedded-assets.generated.d.ts +0 -15
- package/src/relay/identity.ts +0 -352
- package/src/relay/index.ts +0 -57
- package/src/relay/pipes.test.ts +0 -427
- package/src/relay/pipes.ts +0 -195
- package/src/relay/protocol.ts +0 -804
- package/src/relay/registries.test.ts +0 -437
- package/src/relay/registries.ts +0 -593
- package/src/relay/server.test.ts +0 -1323
- package/src/relay/server.ts +0 -1128
- package/src/relay/signing.ts +0 -238
- package/src/relay/types.ts +0 -69
- package/src/relay-client/__tests__/machine-directory-client.test.ts +0 -152
- package/src/relay-client/__tests__/useMachineDirectory.test.ts +0 -172
- package/src/relay-client/adapters/browser.ts +0 -27
- package/src/relay-client/adapters/node.ts +0 -29
- package/src/relay-client/index.ts +0 -33
- package/src/relay-client/machine-directory-client.ts +0 -244
- package/src/relay-client/useMachineDirectory.ts +0 -175
- package/src/serve/client-session-manager.ts +0 -635
- package/src/serve/daemon.ts +0 -497
- package/src/serve/pty-session.ts +0 -236
- package/src/serve/types.ts +0 -174
- package/src/session/__tests__/backend-manager.test.ts +0 -101
- package/src/session/__tests__/local-session-backend.test.ts +0 -1129
- package/src/session/__tests__/reducer.test.ts +0 -80
- package/src/session/__tests__/remote-session-backend.test.ts +0 -995
- package/src/session/__tests__/session-name.test.ts +0 -35
- package/src/session/__tests__/useBundleRefreshAttachFlow.test.ts +0 -431
- package/src/session/__tests__/useRemoteSessionClient.test.ts +0 -424
- package/src/session/__tests__/workspace-shell-hooks.integration.test.ts +0 -268
- package/src/session/__tests__/workspace-shell-hooks.test.ts +0 -24
- package/src/session/adapters/browser-remote.ts +0 -101
- package/src/session/adapters/node-remote.ts +0 -135
- package/src/session/backend-key.ts +0 -5
- package/src/session/backend-manager.ts +0 -80
- package/src/session/backend.ts +0 -93
- package/src/session/backends/local-session-backend.ts +0 -1119
- package/src/session/backends/remote-session-backend.ts +0 -1378
- package/src/session/crypto/__tests__/web-terminal.test.ts +0 -1158
- package/src/session/crypto/frames.web.ts +0 -205
- package/src/session/crypto/handshake.web.ts +0 -396
- package/src/session/crypto/identity.web.ts +0 -133
- package/src/session/crypto/keyexchange.web.ts +0 -246
- package/src/session/crypto/relay-signing.web.ts +0 -53
- package/src/session/events.ts +0 -38
- package/src/session/index.ts +0 -116
- package/src/session/reducer.ts +0 -274
- package/src/session/selectors.ts +0 -28
- package/src/session/session-name.ts +0 -50
- package/src/session/types.ts +0 -101
- package/src/session/useBundleRefreshAttachFlow.ts +0 -608
- package/src/session/useRemoteSessionClient.ts +0 -424
- package/src/session/useSessionEngine.ts +0 -432
- package/src/session/workspace-shell-hooks.ts +0 -35
- package/src/tui/__tests__/input-text.test.ts +0 -24
- package/src/tui/__tests__/local-terminal-sync.test.ts +0 -82
- package/src/tui/__tests__/session-terminal-page-navigation.test.ts +0 -94
- package/src/tui/app.tsx +0 -2
- package/src/tui/index.ts +0 -18
- package/src/tui/input-text.ts +0 -38
- package/src/tui/local-terminal-sync.ts +0 -41
- package/src/types/bundle-refresh.ts +0 -42
- package/src/types/bundle.ts +0 -130
- package/src/types/config.ts +0 -287
- package/src/types/errors.ts +0 -292
- package/src/types/events.ts +0 -91
- package/src/types/identity.ts +0 -284
- package/src/types/processes.ts +0 -45
- package/src/types/review.ts +0 -349
- package/src/types/script-phase.ts +0 -3
- package/src/types/workspace-fuzzy.ts +0 -49
- package/src/types/workspace.ts +0 -151
- package/src/utils/__tests__/onboarding.test.ts +0 -358
- package/src/utils/__tests__/run-scripts.test.ts +0 -535
- package/src/utils/__tests__/run-workspace-scripts.test.ts +0 -406
- package/src/utils/__tests__/workspace-setup.integration.test.ts +0 -633
- package/src/utils/__tests__/workspace-state.test.ts +0 -78
- package/src/utils/bun-socket-writer.ts +0 -80
- package/src/utils/clipboard.ts +0 -53
- package/src/utils/deps.test.ts +0 -31
- package/src/utils/deps.ts +0 -145
- package/src/utils/device.web.ts +0 -163
- package/src/utils/fuzzy-match.ts +0 -125
- package/src/utils/hostnames.ts +0 -43
- package/src/utils/hunk-header.ts +0 -17
- package/src/utils/id.ts +0 -9
- package/src/utils/logger.ts +0 -127
- package/src/utils/markdown.ts +0 -254
- package/src/utils/normalize-env-key.ts +0 -13
- package/src/utils/onboarding.ts +0 -279
- package/src/utils/prompts.ts +0 -176
- package/src/utils/run-commands.ts +0 -112
- package/src/utils/run-scripts.ts +0 -337
- package/src/utils/run-workspace-scripts.ts +0 -355
- package/src/utils/sanitize.test.ts +0 -149
- package/src/utils/sanitize.ts +0 -162
- package/src/utils/secrets.ts +0 -836
- package/src/utils/shell-escape.ts +0 -40
- package/src/utils/utf8.ts +0 -79
- package/src/utils/workspace-id.ts +0 -55
- package/src/utils/workspace-state.ts +0 -427
- package/src/version.generated.d.ts +0 -2
- package/todo-security.md +0 -92
- package/tsconfig.json +0 -29
- package/web/README.md +0 -73
- package/web/bun.lock +0 -675
- package/web/eslint.config.js +0 -23
- package/web/index.css +0 -249
- package/web/index.html +0 -16
- package/web/main.tsx +0 -10
- package/web/package.json +0 -39
- package/web/public/vite.svg +0 -1
- package/web/tsconfig.app.json +0 -35
- package/web/tsconfig.json +0 -7
- package/web/tsconfig.node.json +0 -26
- package/web/vite.config.ts +0 -39
- package/worker/bun.lock +0 -237
- package/worker/package.json +0 -22
- package/worker/schema.sql +0 -96
- package/worker/src/handlers/auth.ts +0 -451
- package/worker/src/handlers/subdomains.ts +0 -376
- package/worker/src/handlers/user.ts +0 -98
- package/worker/src/index.ts +0 -70
- package/worker/src/middleware/auth.ts +0 -152
- package/worker/src/services/cloudflare.ts +0 -609
- package/worker/src/types.ts +0 -96
- package/worker/tsconfig.json +0 -15
- package/worker/wrangler.toml +0 -26
|
@@ -1,1378 +0,0 @@
|
|
|
1
|
-
import type { Identity, SessionKeys } from '../../types/identity.js';
|
|
2
|
-
import {
|
|
3
|
-
parseRemoteMessage,
|
|
4
|
-
type ApplyBundleRefreshRequest,
|
|
5
|
-
type AttachSessionRequest,
|
|
6
|
-
type BundleRefreshAppliedResponse,
|
|
7
|
-
type BundleRefreshPlanResponse,
|
|
8
|
-
type ClearInboxRequest,
|
|
9
|
-
type ClientToMachineMessage,
|
|
10
|
-
type DeleteWorkspaceRequest,
|
|
11
|
-
type GetEventsRequest,
|
|
12
|
-
type GetInboxRequest,
|
|
13
|
-
type GetBundleRefreshPlanRequest,
|
|
14
|
-
type GetNotificationConfigRequest,
|
|
15
|
-
type KillSessionRequest,
|
|
16
|
-
type ListProjectsRequest,
|
|
17
|
-
type ListSessionsRequest,
|
|
18
|
-
type ListWorkspacesRequest,
|
|
19
|
-
type MachineToClientMessage,
|
|
20
|
-
type MarkInboxReadRequest,
|
|
21
|
-
type ReviewRequest,
|
|
22
|
-
type ReviewResponse,
|
|
23
|
-
type EventsListResponse,
|
|
24
|
-
type ScriptOutputResponse,
|
|
25
|
-
type SessionCtrl,
|
|
26
|
-
type StartProcessRequest,
|
|
27
|
-
type StopProcessRequest,
|
|
28
|
-
type UpdateNotificationConfigRequest,
|
|
29
|
-
} from '../../lib/remote-session/protocol.js';
|
|
30
|
-
import type { BundleRefreshPlan, BundleRefreshSubmission } from '../../types/bundle-refresh.js';
|
|
31
|
-
import type { ReviewOperation, ReviewResult } from '../../types/review.js';
|
|
32
|
-
import type { WideEvent, WideEventFilter } from '../../types/events.js';
|
|
33
|
-
import { findUtf8Boundary } from '../../utils/utf8.js';
|
|
34
|
-
import type { NotificationConfig } from '../../notifications/types.js';
|
|
35
|
-
import {
|
|
36
|
-
ReviewRequestError,
|
|
37
|
-
WorkspaceDeleteError,
|
|
38
|
-
type WorkspaceDeleteErrorCode,
|
|
39
|
-
} from '../../types/errors.js';
|
|
40
|
-
import type {
|
|
41
|
-
AttachSessionParams,
|
|
42
|
-
BackendDescriptor,
|
|
43
|
-
DeleteWorkspaceParams,
|
|
44
|
-
SessionBackend,
|
|
45
|
-
} from '../backend.js';
|
|
46
|
-
import type { BackendEvent } from '../events.js';
|
|
47
|
-
|
|
48
|
-
const DEFAULT_CONTROL_STREAM_ID = 1;
|
|
49
|
-
const DEFAULT_DELETE_WORKSPACE_TIMEOUT_MS = 30000;
|
|
50
|
-
|
|
51
|
-
interface RelayDataMessage {
|
|
52
|
-
type: 'data';
|
|
53
|
-
data: string;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
type RelayConnectMessage =
|
|
57
|
-
| {
|
|
58
|
-
type: 'connect_with_invite';
|
|
59
|
-
inviteId: string;
|
|
60
|
-
clientIdentityId: string;
|
|
61
|
-
}
|
|
62
|
-
| {
|
|
63
|
-
type: 'connect_to_machine';
|
|
64
|
-
machineId: string;
|
|
65
|
-
clientIdentityId: string;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
interface HandshakeEnvelope {
|
|
69
|
-
type: 'handshake';
|
|
70
|
-
phase: string;
|
|
71
|
-
data: unknown;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
type AuthorizationPayload =
|
|
75
|
-
| { type: 'invite'; inviteToken: string }
|
|
76
|
-
| { type: 'access_list' };
|
|
77
|
-
|
|
78
|
-
interface PtyOutputMessage {
|
|
79
|
-
type: 'pty_output';
|
|
80
|
-
data: string;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
interface SessionEventMessage {
|
|
84
|
-
type: 'attach-ready' | 'attached' | 'exited' | 'kicked';
|
|
85
|
-
cols?: number;
|
|
86
|
-
rows?: number;
|
|
87
|
-
code?: number;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
interface PendingEventsChunk {
|
|
91
|
-
workspaceId: string;
|
|
92
|
-
totalChunks: number;
|
|
93
|
-
chunks: Map<number, WideEvent[]>;
|
|
94
|
-
liveEventIds: string[];
|
|
95
|
-
savedEventFilters?: import('../../types/events.js').SavedEventFilter[];
|
|
96
|
-
receivedAtMs: number;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export interface RemoteSessionSocketHandlers {
|
|
100
|
-
onOpen: () => void;
|
|
101
|
-
onClose: () => void;
|
|
102
|
-
onMessage: (data: string) => void;
|
|
103
|
-
onError: (error: Error) => void;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export interface RemoteSessionSocketAdapter<TSocket> {
|
|
107
|
-
setHandlers: (socket: TSocket, handlers: RemoteSessionSocketHandlers) => void;
|
|
108
|
-
clearHandlers: (socket: TSocket) => void;
|
|
109
|
-
send: (socket: TSocket, data: string) => void;
|
|
110
|
-
close: (socket: TSocket) => void;
|
|
111
|
-
getReadyState: (socket: TSocket) => number;
|
|
112
|
-
getOpenReadyStateValue: () => number;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export interface RemoteSessionCryptoAdapter {
|
|
116
|
-
readonly masterStreamId: number;
|
|
117
|
-
readonly controlStreamId?: number;
|
|
118
|
-
createFrame: (streamId: number, data: Uint8Array, key: Uint8Array) => Promise<Uint8Array>;
|
|
119
|
-
openFrame: (
|
|
120
|
-
frame: Uint8Array,
|
|
121
|
-
key: Uint8Array
|
|
122
|
-
) => Promise<{ streamId: number; data: Uint8Array } | null>;
|
|
123
|
-
encodeBase64: (data: Uint8Array) => string;
|
|
124
|
-
decodeBase64: (base64: string) => Uint8Array;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export interface RemoteSessionHandshakeAdapter<THandshakeState, TServerHello, TServerAuth> {
|
|
128
|
-
createClientHello: (machineIdHint?: string) => { state: THandshakeState; message: unknown };
|
|
129
|
-
isServerHello: (data: unknown) => data is TServerHello;
|
|
130
|
-
processServerHello: (
|
|
131
|
-
state: THandshakeState,
|
|
132
|
-
response: TServerHello
|
|
133
|
-
) => THandshakeState | null;
|
|
134
|
-
createClientAuth: (
|
|
135
|
-
state: THandshakeState,
|
|
136
|
-
identity: Identity,
|
|
137
|
-
authorization: AuthorizationPayload
|
|
138
|
-
) => {
|
|
139
|
-
state: THandshakeState;
|
|
140
|
-
message: unknown;
|
|
141
|
-
sessionKeys: SessionKeys;
|
|
142
|
-
};
|
|
143
|
-
isServerAuth: (data: unknown) => data is TServerAuth;
|
|
144
|
-
processServerAuth: (
|
|
145
|
-
state: THandshakeState,
|
|
146
|
-
response: TServerAuth,
|
|
147
|
-
sessionKeys: SessionKeys
|
|
148
|
-
) => {
|
|
149
|
-
peerIdentityId: string;
|
|
150
|
-
authResult?: unknown;
|
|
151
|
-
} | null;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export interface RemoteSessionBackendOptions<TSocket, THandshakeState, TServerHello, TServerAuth> {
|
|
155
|
-
descriptor: BackendDescriptor;
|
|
156
|
-
socket: TSocket;
|
|
157
|
-
socketAdapter: RemoteSessionSocketAdapter<TSocket>;
|
|
158
|
-
identity: Identity;
|
|
159
|
-
machineId: string;
|
|
160
|
-
inviteId?: string;
|
|
161
|
-
inviteToken?: string;
|
|
162
|
-
signer: <T extends object>(message: T, identity: Identity) => T;
|
|
163
|
-
crypto: RemoteSessionCryptoAdapter;
|
|
164
|
-
handshake: RemoteSessionHandshakeAdapter<THandshakeState, TServerHello, TServerAuth>;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const MACHINE_TO_CLIENT_TYPES = new Set<string>([
|
|
168
|
-
'workspace_list',
|
|
169
|
-
'session_list',
|
|
170
|
-
'attached',
|
|
171
|
-
'detached',
|
|
172
|
-
'session_exited',
|
|
173
|
-
'error',
|
|
174
|
-
'project_list',
|
|
175
|
-
'session_killed',
|
|
176
|
-
'workspace_deleted',
|
|
177
|
-
'inbox_list',
|
|
178
|
-
'inbox_cleared',
|
|
179
|
-
'inbox_marked_read',
|
|
180
|
-
'notification_config',
|
|
181
|
-
'notification_config_updated',
|
|
182
|
-
'script_output',
|
|
183
|
-
'bundle_refresh_plan',
|
|
184
|
-
'bundle_refresh_applied',
|
|
185
|
-
'review_response',
|
|
186
|
-
'events_list',
|
|
187
|
-
'process_started',
|
|
188
|
-
'process_stopped',
|
|
189
|
-
]);
|
|
190
|
-
|
|
191
|
-
function isHandshakeEnvelope(value: unknown): value is HandshakeEnvelope {
|
|
192
|
-
if (!value || typeof value !== 'object') {
|
|
193
|
-
return false;
|
|
194
|
-
}
|
|
195
|
-
const envelope = value as Partial<HandshakeEnvelope>;
|
|
196
|
-
return envelope.type === 'handshake' && typeof envelope.phase === 'string';
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function isPtyOutputMessage(value: unknown): value is PtyOutputMessage {
|
|
200
|
-
if (!value || typeof value !== 'object') {
|
|
201
|
-
return false;
|
|
202
|
-
}
|
|
203
|
-
const message = value as Partial<PtyOutputMessage>;
|
|
204
|
-
return message.type === 'pty_output' && typeof message.data === 'string';
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
function isSessionEventMessage(value: unknown): value is SessionEventMessage {
|
|
208
|
-
if (!value || typeof value !== 'object') {
|
|
209
|
-
return false;
|
|
210
|
-
}
|
|
211
|
-
const message = value as Partial<SessionEventMessage>;
|
|
212
|
-
return (
|
|
213
|
-
message.type === 'attach-ready' ||
|
|
214
|
-
message.type === 'attached' ||
|
|
215
|
-
message.type === 'exited' ||
|
|
216
|
-
message.type === 'kicked'
|
|
217
|
-
);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function toMachineMessage(value: unknown): MachineToClientMessage | null {
|
|
221
|
-
if (!value || typeof value !== 'object') {
|
|
222
|
-
return null;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const candidate = value as { type?: string };
|
|
226
|
-
if (!candidate.type || !MACHINE_TO_CLIENT_TYPES.has(candidate.type)) {
|
|
227
|
-
return null;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const parsed = parseRemoteMessage(JSON.stringify(value));
|
|
231
|
-
if (!parsed || !MACHINE_TO_CLIENT_TYPES.has(parsed.type)) {
|
|
232
|
-
return null;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
return parsed as MachineToClientMessage;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
function safeJsonParse(text: string): unknown {
|
|
239
|
-
try {
|
|
240
|
-
return JSON.parse(text);
|
|
241
|
-
} catch {
|
|
242
|
-
return null;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
function concatUint8Array(parts: Uint8Array[]): Uint8Array {
|
|
247
|
-
const total = parts.reduce((sum, part) => sum + part.length, 0);
|
|
248
|
-
const combined = new Uint8Array(total);
|
|
249
|
-
let offset = 0;
|
|
250
|
-
for (const part of parts) {
|
|
251
|
-
combined.set(part, offset);
|
|
252
|
-
offset += part.length;
|
|
253
|
-
}
|
|
254
|
-
return combined;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
function workspaceIdsMatch(expected: string, actual: string | undefined): boolean {
|
|
258
|
-
if (!actual) {
|
|
259
|
-
return false;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
if (expected === actual) {
|
|
263
|
-
return true;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
const expectedSeparator = expected.indexOf(':');
|
|
267
|
-
const actualSeparator = actual.indexOf(':');
|
|
268
|
-
|
|
269
|
-
if (expectedSeparator > 0 && actualSeparator <= 0) {
|
|
270
|
-
return expected.slice(expectedSeparator + 1) === actual;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
if (expectedSeparator <= 0 && actualSeparator > 0) {
|
|
274
|
-
return expected === actual.slice(actualSeparator + 1);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
return false;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
function toWorkspaceDeleteErrorCode(code: string | undefined): WorkspaceDeleteErrorCode {
|
|
281
|
-
if (
|
|
282
|
-
code === 'REMOVE_SCRIPT_FAILED' ||
|
|
283
|
-
code === 'WORKSPACE_NOT_FOUND' ||
|
|
284
|
-
code === 'WORKTREE_REMOVE_FAILED' ||
|
|
285
|
-
code === 'DELETE_FAILED' ||
|
|
286
|
-
code === 'NOT_FOUND' ||
|
|
287
|
-
code === 'RESOURCE_NOT_FOUND' ||
|
|
288
|
-
code === 'PERMISSION_DENIED' ||
|
|
289
|
-
code === 'DELETE_TIMEOUT'
|
|
290
|
-
) {
|
|
291
|
-
return code;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
return 'DELETE_FAILED';
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
export class RemoteSessionBackend<TSocket, THandshakeState, TServerHello, TServerAuth>
|
|
298
|
-
implements SessionBackend {
|
|
299
|
-
readonly descriptor: BackendDescriptor;
|
|
300
|
-
|
|
301
|
-
private readonly socket: TSocket;
|
|
302
|
-
private readonly socketAdapter: RemoteSessionSocketAdapter<TSocket>;
|
|
303
|
-
private readonly identity: Identity;
|
|
304
|
-
private readonly machineId: string;
|
|
305
|
-
private readonly inviteId?: string;
|
|
306
|
-
private readonly inviteToken?: string;
|
|
307
|
-
private readonly signer: <T extends object>(message: T, identity: Identity) => T;
|
|
308
|
-
private readonly crypto: RemoteSessionCryptoAdapter;
|
|
309
|
-
private readonly handshake: RemoteSessionHandshakeAdapter<THandshakeState, TServerHello, TServerAuth>;
|
|
310
|
-
private readonly handlers = new Set<(event: BackendEvent) => void>();
|
|
311
|
-
|
|
312
|
-
private mode: 'browsing' | 'attached' = 'browsing';
|
|
313
|
-
private attachedSessionId: string | null = null;
|
|
314
|
-
private viewOnly = false;
|
|
315
|
-
private handshakeState: THandshakeState | null = null;
|
|
316
|
-
private sessionKeys: SessionKeys | null = null;
|
|
317
|
-
private isConnected = false;
|
|
318
|
-
private listenersAttached = false;
|
|
319
|
-
private connectPromise: Promise<void> | null = null;
|
|
320
|
-
private connectResolve: (() => void) | null = null;
|
|
321
|
-
private connectReject: ((error: Error) => void) | null = null;
|
|
322
|
-
private pendingBundleRefreshPlan:
|
|
323
|
-
| {
|
|
324
|
-
resolve: (plan: BundleRefreshPlan) => void;
|
|
325
|
-
reject: (error: Error) => void;
|
|
326
|
-
timeout: ReturnType<typeof setTimeout>;
|
|
327
|
-
}
|
|
328
|
-
| null = null;
|
|
329
|
-
private pendingBundleRefreshApply:
|
|
330
|
-
| {
|
|
331
|
-
resolve: () => void;
|
|
332
|
-
reject: (error: Error) => void;
|
|
333
|
-
timeout: ReturnType<typeof setTimeout>;
|
|
334
|
-
}
|
|
335
|
-
| null = null;
|
|
336
|
-
private pendingDeleteWorkspace:
|
|
337
|
-
| {
|
|
338
|
-
workspaceId: string;
|
|
339
|
-
resolve: () => void;
|
|
340
|
-
reject: (error: Error) => void;
|
|
341
|
-
timeout: ReturnType<typeof setTimeout>;
|
|
342
|
-
}
|
|
343
|
-
| null = null;
|
|
344
|
-
private pendingReviewRequests = new Map<string, {
|
|
345
|
-
op: ReviewOperation['op'];
|
|
346
|
-
resolve: (result: ReviewResult) => void;
|
|
347
|
-
reject: (error: Error) => void;
|
|
348
|
-
timeout: ReturnType<typeof setTimeout>;
|
|
349
|
-
}>();
|
|
350
|
-
private ptyOutputHandler: ((data: Uint8Array) => void) | null = null;
|
|
351
|
-
private pendingPtyChunks: Uint8Array[] = [];
|
|
352
|
-
private pendingUtf8Bytes = new Uint8Array(0);
|
|
353
|
-
private pendingEventChunks = new Map<string, PendingEventsChunk>();
|
|
354
|
-
|
|
355
|
-
constructor(options: RemoteSessionBackendOptions<TSocket, THandshakeState, TServerHello, TServerAuth>) {
|
|
356
|
-
this.descriptor = options.descriptor;
|
|
357
|
-
this.socket = options.socket;
|
|
358
|
-
this.socketAdapter = options.socketAdapter;
|
|
359
|
-
this.identity = options.identity;
|
|
360
|
-
this.machineId = options.machineId;
|
|
361
|
-
this.inviteId = options.inviteId;
|
|
362
|
-
this.inviteToken = options.inviteToken;
|
|
363
|
-
this.signer = options.signer;
|
|
364
|
-
this.crypto = options.crypto;
|
|
365
|
-
this.handshake = options.handshake;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
onEvent(handler: (event: BackendEvent) => void): () => void {
|
|
369
|
-
this.handlers.add(handler);
|
|
370
|
-
return () => {
|
|
371
|
-
this.handlers.delete(handler);
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
setPtyOutputHandler(handler: ((data: Uint8Array) => void) | null): void {
|
|
376
|
-
this.ptyOutputHandler = handler;
|
|
377
|
-
if (!handler || this.pendingPtyChunks.length === 0) {
|
|
378
|
-
return;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
const pending = [...this.pendingPtyChunks];
|
|
382
|
-
this.pendingPtyChunks = [];
|
|
383
|
-
for (const chunk of pending) {
|
|
384
|
-
this.emitPtyData(chunk);
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
async connect(): Promise<void> {
|
|
389
|
-
if (this.isConnected) {
|
|
390
|
-
return;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
if (this.connectPromise) {
|
|
394
|
-
return this.connectPromise;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
this.emit({ type: 'status', status: 'connecting' });
|
|
398
|
-
this.attachSocketListeners();
|
|
399
|
-
|
|
400
|
-
this.connectPromise = new Promise<void>((resolve, reject) => {
|
|
401
|
-
this.connectResolve = resolve;
|
|
402
|
-
this.connectReject = reject;
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
if (this.isSocketOpen()) {
|
|
406
|
-
this.sendRelayConnectMessage();
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
return this.connectPromise;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
async disconnect(): Promise<void> {
|
|
413
|
-
if (!this.listenersAttached) {
|
|
414
|
-
this.resetState();
|
|
415
|
-
this.emit({ type: 'status', status: 'disconnected' });
|
|
416
|
-
return;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
this.socketAdapter.clearHandlers(this.socket);
|
|
420
|
-
this.listenersAttached = false;
|
|
421
|
-
this.socketAdapter.close(this.socket);
|
|
422
|
-
this.resetState();
|
|
423
|
-
this.emit({ type: 'status', status: 'disconnected' });
|
|
424
|
-
this.emit({ type: 'detached' });
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
async listProjects(): Promise<void> {
|
|
428
|
-
const command: ListProjectsRequest = { type: 'list_projects' };
|
|
429
|
-
await this.sendCommand(command);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
async listWorkspaces(): Promise<void> {
|
|
433
|
-
const command: ListWorkspacesRequest = { type: 'list_workspaces' };
|
|
434
|
-
await this.sendCommand(command);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
async listSessions(workspaceId?: string): Promise<void> {
|
|
438
|
-
const command: ListSessionsRequest = { type: 'list_sessions', workspaceId };
|
|
439
|
-
await this.sendCommand(command);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
async attachSession(params: AttachSessionParams): Promise<void> {
|
|
443
|
-
this.viewOnly = params.viewOnly ?? false;
|
|
444
|
-
const command: AttachSessionRequest = {
|
|
445
|
-
type: 'attach_session',
|
|
446
|
-
sessionId: params.sessionId,
|
|
447
|
-
workspaceId: params.workspaceId,
|
|
448
|
-
sessionName: params.sessionName,
|
|
449
|
-
cols: params.cols,
|
|
450
|
-
rows: params.rows,
|
|
451
|
-
scriptPolicy: params.scriptPolicy,
|
|
452
|
-
viewOnly: params.viewOnly,
|
|
453
|
-
command: params.command,
|
|
454
|
-
args: params.args,
|
|
455
|
-
env: params.env,
|
|
456
|
-
};
|
|
457
|
-
await this.sendCommand(command);
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
async detachSession(): Promise<void> {
|
|
461
|
-
const ctrl: SessionCtrl = { type: 'detach' };
|
|
462
|
-
await this.sendCommand(ctrl);
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
async killSession(sessionId: string): Promise<void> {
|
|
466
|
-
const command: KillSessionRequest = { type: 'kill_session', sessionId };
|
|
467
|
-
await this.sendCommand(command);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
async deleteWorkspace(
|
|
471
|
-
projectName: string,
|
|
472
|
-
workspaceId: string,
|
|
473
|
-
params: DeleteWorkspaceParams = {}
|
|
474
|
-
): Promise<void> {
|
|
475
|
-
if (this.pendingDeleteWorkspace) {
|
|
476
|
-
throw new Error('Workspace delete request already in progress');
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
const command: DeleteWorkspaceRequest = {
|
|
480
|
-
type: 'delete_workspace',
|
|
481
|
-
projectName,
|
|
482
|
-
workspaceId,
|
|
483
|
-
scriptPolicy: params.scriptPolicy,
|
|
484
|
-
};
|
|
485
|
-
const timeoutMs =
|
|
486
|
-
typeof params.timeoutMs === 'number' && Number.isFinite(params.timeoutMs) && params.timeoutMs > 0
|
|
487
|
-
? params.timeoutMs
|
|
488
|
-
: DEFAULT_DELETE_WORKSPACE_TIMEOUT_MS;
|
|
489
|
-
|
|
490
|
-
return new Promise<void>((resolve, reject) => {
|
|
491
|
-
const timeout = setTimeout(() => {
|
|
492
|
-
const pending = this.pendingDeleteWorkspace;
|
|
493
|
-
if (!pending || !workspaceIdsMatch(pending.workspaceId, workspaceId)) {
|
|
494
|
-
return;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
this.pendingDeleteWorkspace = null;
|
|
498
|
-
pending.reject(
|
|
499
|
-
new WorkspaceDeleteError(
|
|
500
|
-
`Timed out waiting for workspace deletion response (${workspaceId})`,
|
|
501
|
-
'DELETE_TIMEOUT'
|
|
502
|
-
)
|
|
503
|
-
);
|
|
504
|
-
}, timeoutMs);
|
|
505
|
-
|
|
506
|
-
this.pendingDeleteWorkspace = {
|
|
507
|
-
workspaceId,
|
|
508
|
-
resolve,
|
|
509
|
-
reject,
|
|
510
|
-
timeout,
|
|
511
|
-
};
|
|
512
|
-
|
|
513
|
-
void this.sendCommand(command).catch((error) => {
|
|
514
|
-
const pending = this.pendingDeleteWorkspace;
|
|
515
|
-
if (!pending) {
|
|
516
|
-
return;
|
|
517
|
-
}
|
|
518
|
-
clearTimeout(pending.timeout);
|
|
519
|
-
this.pendingDeleteWorkspace = null;
|
|
520
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
521
|
-
pending.reject(new WorkspaceDeleteError(message, 'DELETE_FAILED'));
|
|
522
|
-
});
|
|
523
|
-
});
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
async getBundleRefreshPlan(projectName: string, workspaceId: string): Promise<BundleRefreshPlan> {
|
|
527
|
-
if (this.pendingBundleRefreshPlan) {
|
|
528
|
-
throw new Error('Bundle refresh plan request already in progress');
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
const command: GetBundleRefreshPlanRequest = {
|
|
532
|
-
type: 'get_bundle_refresh_plan',
|
|
533
|
-
projectName,
|
|
534
|
-
workspaceId,
|
|
535
|
-
};
|
|
536
|
-
|
|
537
|
-
return new Promise<BundleRefreshPlan>((resolve, reject) => {
|
|
538
|
-
const timeout = setTimeout(() => {
|
|
539
|
-
if (!this.pendingBundleRefreshPlan) {
|
|
540
|
-
return;
|
|
541
|
-
}
|
|
542
|
-
this.pendingBundleRefreshPlan = null;
|
|
543
|
-
reject(new Error('Timed out waiting for bundle refresh plan'));
|
|
544
|
-
}, 15000);
|
|
545
|
-
|
|
546
|
-
this.pendingBundleRefreshPlan = { resolve, reject, timeout };
|
|
547
|
-
void this.sendCommand(command).catch((error) => {
|
|
548
|
-
const pending = this.pendingBundleRefreshPlan;
|
|
549
|
-
if (!pending) {
|
|
550
|
-
return;
|
|
551
|
-
}
|
|
552
|
-
clearTimeout(pending.timeout);
|
|
553
|
-
this.pendingBundleRefreshPlan = null;
|
|
554
|
-
reject(error instanceof Error ? error : new Error(String(error)));
|
|
555
|
-
});
|
|
556
|
-
});
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
async applyBundleRefresh(
|
|
560
|
-
projectName: string,
|
|
561
|
-
workspaceId: string,
|
|
562
|
-
submission: BundleRefreshSubmission
|
|
563
|
-
): Promise<void> {
|
|
564
|
-
if (this.pendingBundleRefreshApply) {
|
|
565
|
-
throw new Error('Bundle refresh apply request already in progress');
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
const command: ApplyBundleRefreshRequest = {
|
|
569
|
-
type: 'apply_bundle_refresh',
|
|
570
|
-
projectName,
|
|
571
|
-
workspaceId,
|
|
572
|
-
submission,
|
|
573
|
-
};
|
|
574
|
-
|
|
575
|
-
return new Promise<void>((resolve, reject) => {
|
|
576
|
-
const timeout = setTimeout(() => {
|
|
577
|
-
if (!this.pendingBundleRefreshApply) {
|
|
578
|
-
return;
|
|
579
|
-
}
|
|
580
|
-
this.pendingBundleRefreshApply = null;
|
|
581
|
-
reject(new Error('Timed out applying bundle refresh submission'));
|
|
582
|
-
}, 15000);
|
|
583
|
-
|
|
584
|
-
this.pendingBundleRefreshApply = { resolve, reject, timeout };
|
|
585
|
-
void this.sendCommand(command).catch((error) => {
|
|
586
|
-
const pending = this.pendingBundleRefreshApply;
|
|
587
|
-
if (!pending) {
|
|
588
|
-
return;
|
|
589
|
-
}
|
|
590
|
-
clearTimeout(pending.timeout);
|
|
591
|
-
this.pendingBundleRefreshApply = null;
|
|
592
|
-
reject(error instanceof Error ? error : new Error(String(error)));
|
|
593
|
-
});
|
|
594
|
-
});
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
async requestInbox(): Promise<void> {
|
|
598
|
-
const command: GetInboxRequest = { type: 'get_inbox' };
|
|
599
|
-
await this.sendCommand(command);
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
async clearInbox(id?: string): Promise<void> {
|
|
603
|
-
const command: ClearInboxRequest = { type: 'clear_inbox', id };
|
|
604
|
-
await this.sendCommand(command);
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
async markInboxRead(id: string): Promise<void> {
|
|
608
|
-
const command: MarkInboxReadRequest = { type: 'mark_inbox_read', id };
|
|
609
|
-
await this.sendCommand(command);
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
async getNotificationConfig(): Promise<void> {
|
|
613
|
-
const command: GetNotificationConfigRequest = { type: 'get_notification_config' };
|
|
614
|
-
await this.sendCommand(command);
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
async updateNotificationConfig(config: NotificationConfig): Promise<void> {
|
|
618
|
-
const command: UpdateNotificationConfigRequest = {
|
|
619
|
-
type: 'update_notification_config',
|
|
620
|
-
config,
|
|
621
|
-
};
|
|
622
|
-
await this.sendCommand(command);
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
async sendReviewRequest(operation: ReviewOperation): Promise<ReviewResult> {
|
|
626
|
-
const requestId = crypto.randomUUID();
|
|
627
|
-
|
|
628
|
-
const command: ReviewRequest = {
|
|
629
|
-
type: 'review_request',
|
|
630
|
-
requestId,
|
|
631
|
-
operation,
|
|
632
|
-
};
|
|
633
|
-
|
|
634
|
-
return new Promise<ReviewResult>((resolve, reject) => {
|
|
635
|
-
const timeout = setTimeout(() => {
|
|
636
|
-
if (!this.pendingReviewRequests.has(requestId)) {
|
|
637
|
-
return;
|
|
638
|
-
}
|
|
639
|
-
this.pendingReviewRequests.delete(requestId);
|
|
640
|
-
reject(
|
|
641
|
-
new ReviewRequestError(
|
|
642
|
-
`Timed out waiting for review response (${operation.op})`,
|
|
643
|
-
'REVIEW_TIMEOUT',
|
|
644
|
-
{ op: operation.op, requestId }
|
|
645
|
-
)
|
|
646
|
-
);
|
|
647
|
-
}, 30000);
|
|
648
|
-
|
|
649
|
-
this.pendingReviewRequests.set(requestId, {
|
|
650
|
-
op: operation.op,
|
|
651
|
-
resolve,
|
|
652
|
-
reject,
|
|
653
|
-
timeout,
|
|
654
|
-
});
|
|
655
|
-
|
|
656
|
-
void this.sendCommand(command).catch((error) => {
|
|
657
|
-
const pending = this.pendingReviewRequests.get(requestId);
|
|
658
|
-
if (!pending) {
|
|
659
|
-
return;
|
|
660
|
-
}
|
|
661
|
-
clearTimeout(pending.timeout);
|
|
662
|
-
this.pendingReviewRequests.delete(requestId);
|
|
663
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
664
|
-
reject(
|
|
665
|
-
new ReviewRequestError(message, 'REVIEW_FAILED', {
|
|
666
|
-
op: pending.op,
|
|
667
|
-
requestId,
|
|
668
|
-
})
|
|
669
|
-
);
|
|
670
|
-
});
|
|
671
|
-
});
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
async startProcess(workspaceId: string, processName: string, instance?: number): Promise<void> {
|
|
675
|
-
const command: StartProcessRequest = {
|
|
676
|
-
type: 'start_process',
|
|
677
|
-
workspaceId,
|
|
678
|
-
processName,
|
|
679
|
-
instance,
|
|
680
|
-
};
|
|
681
|
-
await this.sendCommand(command);
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
async stopProcess(workspaceId: string, processName: string): Promise<void> {
|
|
685
|
-
const command: StopProcessRequest = {
|
|
686
|
-
type: 'stop_process',
|
|
687
|
-
workspaceId,
|
|
688
|
-
processName,
|
|
689
|
-
};
|
|
690
|
-
await this.sendCommand(command);
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
async requestEvents(
|
|
694
|
-
workspacePath: string,
|
|
695
|
-
filter?: WideEventFilter,
|
|
696
|
-
limit?: number,
|
|
697
|
-
sinceMs?: number,
|
|
698
|
-
): Promise<void> {
|
|
699
|
-
const command: GetEventsRequest = {
|
|
700
|
-
type: 'get_events',
|
|
701
|
-
workspacePath,
|
|
702
|
-
filter,
|
|
703
|
-
limit,
|
|
704
|
-
sinceMs,
|
|
705
|
-
};
|
|
706
|
-
await this.sendCommand(command);
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
async writePtyData(data: Uint8Array): Promise<void> {
|
|
710
|
-
if (this.viewOnly) {
|
|
711
|
-
return;
|
|
712
|
-
}
|
|
713
|
-
this.assertConnected();
|
|
714
|
-
|
|
715
|
-
const key = this.sessionKeys;
|
|
716
|
-
if (!key) {
|
|
717
|
-
throw new Error('Session keys are not established');
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
const frame = await this.crypto.createFrame(this.crypto.masterStreamId, data, key.sendKey);
|
|
721
|
-
const encoded = this.crypto.encodeBase64(frame);
|
|
722
|
-
const message: RelayDataMessage = { type: 'data', data: encoded };
|
|
723
|
-
this.socketAdapter.send(this.socket, JSON.stringify(message));
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
async resizePty(cols: number, rows: number): Promise<void> {
|
|
727
|
-
const ctrl: SessionCtrl = { type: 'resize', cols, rows };
|
|
728
|
-
await this.sendCommand(ctrl);
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
private attachSocketListeners(): void {
|
|
732
|
-
if (this.listenersAttached) {
|
|
733
|
-
return;
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
this.socketAdapter.setHandlers(this.socket, {
|
|
737
|
-
onOpen: () => {
|
|
738
|
-
this.sendRelayConnectMessage();
|
|
739
|
-
},
|
|
740
|
-
onClose: () => {
|
|
741
|
-
this.rejectConnect(new Error('Socket closed before handshake completed'));
|
|
742
|
-
this.resetState();
|
|
743
|
-
this.emit({ type: 'status', status: 'disconnected' });
|
|
744
|
-
},
|
|
745
|
-
onMessage: (raw) => {
|
|
746
|
-
void this.handleRelayMessage(raw).catch((error) => {
|
|
747
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
748
|
-
this.emit({ type: 'error', message: `Failed to process relay message: ${message}` });
|
|
749
|
-
});
|
|
750
|
-
},
|
|
751
|
-
onError: (error) => {
|
|
752
|
-
this.rejectConnect(error);
|
|
753
|
-
this.rejectPendingBundleRefreshRequests(error.message);
|
|
754
|
-
this.rejectPendingWorkspaceDelete('DELETE_FAILED', error.message);
|
|
755
|
-
this.rejectAllPendingReviewRequests(error.message);
|
|
756
|
-
this.emit({ type: 'status', status: 'error', error: error.message });
|
|
757
|
-
},
|
|
758
|
-
});
|
|
759
|
-
|
|
760
|
-
this.listenersAttached = true;
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
private sendRelayConnectMessage(): void {
|
|
764
|
-
const relayMessage: RelayConnectMessage = this.inviteId
|
|
765
|
-
? {
|
|
766
|
-
type: 'connect_with_invite',
|
|
767
|
-
inviteId: this.inviteId,
|
|
768
|
-
clientIdentityId: this.identity.id,
|
|
769
|
-
}
|
|
770
|
-
: {
|
|
771
|
-
type: 'connect_to_machine',
|
|
772
|
-
machineId: this.machineId,
|
|
773
|
-
clientIdentityId: this.identity.id,
|
|
774
|
-
};
|
|
775
|
-
|
|
776
|
-
const signed = this.signer(relayMessage, this.identity);
|
|
777
|
-
this.socketAdapter.send(this.socket, JSON.stringify(signed));
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
private async handleRelayMessage(raw: string): Promise<void> {
|
|
781
|
-
const parsed = safeJsonParse(raw);
|
|
782
|
-
if (!parsed || typeof parsed !== 'object') {
|
|
783
|
-
this.emit({ type: 'error', message: 'Invalid relay message JSON' });
|
|
784
|
-
return;
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
const message = parsed as { type?: unknown; [key: string]: unknown };
|
|
788
|
-
if (typeof message.type !== 'string') {
|
|
789
|
-
this.emit({ type: 'error', message: 'Relay message missing type' });
|
|
790
|
-
return;
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
switch (message.type) {
|
|
794
|
-
case 'connection_established':
|
|
795
|
-
this.startHandshake();
|
|
796
|
-
return;
|
|
797
|
-
case 'data':
|
|
798
|
-
if (typeof message.data !== 'string') {
|
|
799
|
-
this.emit({ type: 'error', message: 'Relay data message missing payload' });
|
|
800
|
-
return;
|
|
801
|
-
}
|
|
802
|
-
await this.handleRelayDataPayload(message.data);
|
|
803
|
-
return;
|
|
804
|
-
case 'error':
|
|
805
|
-
if (typeof message.message !== 'string') {
|
|
806
|
-
this.emit({ type: 'error', message: 'Relay error message missing text' });
|
|
807
|
-
this.rejectConnect(new Error('Relay error message missing text'));
|
|
808
|
-
return;
|
|
809
|
-
}
|
|
810
|
-
this.emit({ type: 'status', status: 'error', error: message.message });
|
|
811
|
-
this.emit({ type: 'error', message: message.message });
|
|
812
|
-
this.rejectConnect(new Error(message.message));
|
|
813
|
-
return;
|
|
814
|
-
case 'pong':
|
|
815
|
-
return;
|
|
816
|
-
default:
|
|
817
|
-
return;
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
private startHandshake(): void {
|
|
822
|
-
const { state, message } = this.handshake.createClientHello(this.machineId);
|
|
823
|
-
this.handshakeState = state;
|
|
824
|
-
|
|
825
|
-
const envelope: HandshakeEnvelope = {
|
|
826
|
-
type: 'handshake',
|
|
827
|
-
phase: 'client_hello',
|
|
828
|
-
data: message,
|
|
829
|
-
};
|
|
830
|
-
|
|
831
|
-
this.socketAdapter.send(this.socket, JSON.stringify(envelope));
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
private async handleRelayDataPayload(base64Data: string): Promise<void> {
|
|
835
|
-
const encryptedBytes = this.crypto.decodeBase64(base64Data);
|
|
836
|
-
|
|
837
|
-
if (!this.isConnected) {
|
|
838
|
-
const plaintextMaybeHandshake = safeJsonParse(new TextDecoder().decode(encryptedBytes));
|
|
839
|
-
if (isHandshakeEnvelope(plaintextMaybeHandshake)) {
|
|
840
|
-
this.handleHandshakeEnvelope(plaintextMaybeHandshake);
|
|
841
|
-
return;
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
this.emit({ type: 'error', message: 'Unexpected pre-handshake relay payload' });
|
|
845
|
-
return;
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
const keys = this.sessionKeys;
|
|
849
|
-
if (!keys) {
|
|
850
|
-
this.emit({ type: 'error', message: 'Received encrypted payload before handshake completion' });
|
|
851
|
-
return;
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
const opened = await this.crypto.openFrame(encryptedBytes, keys.receiveKey);
|
|
855
|
-
if (!opened) {
|
|
856
|
-
this.emit({ type: 'error', message: 'Failed to decrypt frame payload' });
|
|
857
|
-
return;
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
const decodedText = new TextDecoder().decode(opened.data);
|
|
861
|
-
const parsedMessage = safeJsonParse(decodedText);
|
|
862
|
-
|
|
863
|
-
if (isPtyOutputMessage(parsedMessage)) {
|
|
864
|
-
this.emitPtyData(this.crypto.decodeBase64(parsedMessage.data));
|
|
865
|
-
return;
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
const machineMessage = toMachineMessage(parsedMessage);
|
|
869
|
-
if (machineMessage) {
|
|
870
|
-
await this.handleMachineMessage(machineMessage);
|
|
871
|
-
return;
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
if (isSessionEventMessage(parsedMessage)) {
|
|
875
|
-
this.handleSessionEvent(parsedMessage);
|
|
876
|
-
return;
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
if (this.mode === 'attached') {
|
|
880
|
-
this.emitPtyData(opened.data);
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
private handleHandshakeEnvelope(envelope: HandshakeEnvelope): void {
|
|
885
|
-
const state = this.handshakeState;
|
|
886
|
-
if (!state) {
|
|
887
|
-
this.emit({ type: 'status', status: 'error', error: 'Missing handshake state' });
|
|
888
|
-
this.rejectConnect(new Error('Missing handshake state'));
|
|
889
|
-
return;
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
if (envelope.phase === 'server_hello') {
|
|
893
|
-
if (!this.handshake.isServerHello(envelope.data)) {
|
|
894
|
-
this.emit({ type: 'status', status: 'error', error: 'Invalid server_hello payload' });
|
|
895
|
-
this.rejectConnect(new Error('Invalid server_hello payload'));
|
|
896
|
-
return;
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
const nextState = this.handshake.processServerHello(state, envelope.data);
|
|
900
|
-
if (!nextState) {
|
|
901
|
-
this.emit({ type: 'status', status: 'error', error: 'Failed to process server_hello' });
|
|
902
|
-
this.rejectConnect(new Error('Failed to process server_hello'));
|
|
903
|
-
return;
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
this.handshakeState = nextState;
|
|
907
|
-
|
|
908
|
-
const authorization: AuthorizationPayload = this.inviteToken
|
|
909
|
-
? { type: 'invite', inviteToken: this.inviteToken }
|
|
910
|
-
: { type: 'access_list' };
|
|
911
|
-
|
|
912
|
-
const auth = this.handshake.createClientAuth(nextState, this.identity, authorization);
|
|
913
|
-
this.handshakeState = auth.state;
|
|
914
|
-
this.sessionKeys = auth.sessionKeys;
|
|
915
|
-
|
|
916
|
-
const envelopeOut: HandshakeEnvelope = {
|
|
917
|
-
type: 'handshake',
|
|
918
|
-
phase: 'client_auth',
|
|
919
|
-
data: auth.message,
|
|
920
|
-
};
|
|
921
|
-
this.socketAdapter.send(this.socket, JSON.stringify(envelopeOut));
|
|
922
|
-
return;
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
if (envelope.phase === 'server_auth') {
|
|
926
|
-
if (!this.handshake.isServerAuth(envelope.data)) {
|
|
927
|
-
this.emit({ type: 'status', status: 'error', error: 'Invalid server_auth payload' });
|
|
928
|
-
this.rejectConnect(new Error('Invalid server_auth payload'));
|
|
929
|
-
return;
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
const keys = this.sessionKeys;
|
|
933
|
-
if (!keys) {
|
|
934
|
-
this.emit({ type: 'status', status: 'error', error: 'Missing session keys' });
|
|
935
|
-
this.rejectConnect(new Error('Missing session keys'));
|
|
936
|
-
return;
|
|
937
|
-
}
|
|
938
|
-
|
|
939
|
-
const result = this.handshake.processServerAuth(state, envelope.data, keys);
|
|
940
|
-
if (!result) {
|
|
941
|
-
this.emit({ type: 'status', status: 'error', error: 'Handshake rejected' });
|
|
942
|
-
this.rejectConnect(new Error('Handshake rejected'));
|
|
943
|
-
return;
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
this.isConnected = true;
|
|
947
|
-
this.mode = 'browsing';
|
|
948
|
-
this.handshakeState = null;
|
|
949
|
-
this.emit({ type: 'status', status: 'connected' });
|
|
950
|
-
this.resolveConnect();
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
private async handleMachineMessage(message: MachineToClientMessage): Promise<void> {
|
|
955
|
-
switch (message.type) {
|
|
956
|
-
case 'project_list':
|
|
957
|
-
this.emit({ type: 'projects', projects: message.projects });
|
|
958
|
-
return;
|
|
959
|
-
case 'workspace_list':
|
|
960
|
-
this.emit({
|
|
961
|
-
type: 'workspaces',
|
|
962
|
-
workspaces: message.workspaces,
|
|
963
|
-
savedEventFilters: message.savedEventFilters,
|
|
964
|
-
});
|
|
965
|
-
return;
|
|
966
|
-
case 'session_list':
|
|
967
|
-
this.emit({ type: 'sessions', sessions: message.sessions });
|
|
968
|
-
return;
|
|
969
|
-
case 'attached':
|
|
970
|
-
this.mode = 'attached';
|
|
971
|
-
this.attachedSessionId = message.sessionId;
|
|
972
|
-
this.emit({
|
|
973
|
-
type: 'attached',
|
|
974
|
-
sessionId: message.sessionId,
|
|
975
|
-
sessionName: message.sessionName,
|
|
976
|
-
viewOnly: this.viewOnly,
|
|
977
|
-
});
|
|
978
|
-
return;
|
|
979
|
-
case 'detached':
|
|
980
|
-
this.mode = 'browsing';
|
|
981
|
-
this.attachedSessionId = null;
|
|
982
|
-
this.viewOnly = false;
|
|
983
|
-
this.emit({ type: 'detached' });
|
|
984
|
-
return;
|
|
985
|
-
case 'session_exited':
|
|
986
|
-
this.mode = 'browsing';
|
|
987
|
-
this.attachedSessionId = null;
|
|
988
|
-
this.emit({
|
|
989
|
-
type: 'session_exited',
|
|
990
|
-
sessionId: message.sessionId,
|
|
991
|
-
exitCode: message.exitCode,
|
|
992
|
-
});
|
|
993
|
-
return;
|
|
994
|
-
case 'session_killed':
|
|
995
|
-
await this.listWorkspaces();
|
|
996
|
-
await this.listSessions(
|
|
997
|
-
message.workspaceId && message.workspaceId !== 'unknown' ? message.workspaceId : undefined
|
|
998
|
-
);
|
|
999
|
-
return;
|
|
1000
|
-
case 'workspace_deleted':
|
|
1001
|
-
this.resolveWorkspaceDelete(message.workspaceId);
|
|
1002
|
-
await this.listWorkspaces();
|
|
1003
|
-
return;
|
|
1004
|
-
case 'inbox_list':
|
|
1005
|
-
this.emit({
|
|
1006
|
-
type: 'inbox',
|
|
1007
|
-
items: message.items,
|
|
1008
|
-
unreadCount: message.unreadCount,
|
|
1009
|
-
});
|
|
1010
|
-
return;
|
|
1011
|
-
case 'inbox_cleared':
|
|
1012
|
-
case 'inbox_marked_read':
|
|
1013
|
-
await this.requestInbox();
|
|
1014
|
-
return;
|
|
1015
|
-
case 'notification_config':
|
|
1016
|
-
case 'notification_config_updated':
|
|
1017
|
-
this.emit({ type: 'notification_config', config: message.config });
|
|
1018
|
-
return;
|
|
1019
|
-
case 'script_output':
|
|
1020
|
-
this.handleScriptOutput(message);
|
|
1021
|
-
return;
|
|
1022
|
-
case 'bundle_refresh_plan': {
|
|
1023
|
-
this.resolveBundleRefreshPlan(message);
|
|
1024
|
-
return;
|
|
1025
|
-
}
|
|
1026
|
-
case 'bundle_refresh_applied': {
|
|
1027
|
-
this.resolveBundleRefreshApply(message);
|
|
1028
|
-
return;
|
|
1029
|
-
}
|
|
1030
|
-
case 'review_response': {
|
|
1031
|
-
this.resolveReviewRequest(message);
|
|
1032
|
-
return;
|
|
1033
|
-
}
|
|
1034
|
-
case 'events_list':
|
|
1035
|
-
this.handleEventsList(message);
|
|
1036
|
-
return;
|
|
1037
|
-
case 'process_started':
|
|
1038
|
-
this.emit({
|
|
1039
|
-
type: 'process_started',
|
|
1040
|
-
workspaceId: message.workspaceId,
|
|
1041
|
-
processName: message.processName,
|
|
1042
|
-
sessionId: message.sessionId,
|
|
1043
|
-
sessionIds: message.sessionIds,
|
|
1044
|
-
});
|
|
1045
|
-
return;
|
|
1046
|
-
case 'process_stopped':
|
|
1047
|
-
this.emit({
|
|
1048
|
-
type: 'process_stopped',
|
|
1049
|
-
workspaceId: message.workspaceId,
|
|
1050
|
-
processName: message.processName,
|
|
1051
|
-
});
|
|
1052
|
-
return;
|
|
1053
|
-
case 'error':
|
|
1054
|
-
this.rejectPendingBundleRefreshRequests(message.message);
|
|
1055
|
-
if (message.workspaceId) {
|
|
1056
|
-
this.rejectPendingWorkspaceDelete(message.code, message.message, message.workspaceId);
|
|
1057
|
-
}
|
|
1058
|
-
this.emit({
|
|
1059
|
-
type: 'command_error',
|
|
1060
|
-
code: message.code,
|
|
1061
|
-
message: message.message,
|
|
1062
|
-
});
|
|
1063
|
-
return;
|
|
1064
|
-
default:
|
|
1065
|
-
return;
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
private handleScriptOutput(message: ScriptOutputResponse): void {
|
|
1070
|
-
const data = message.data ? this.crypto.decodeBase64(message.data) : new Uint8Array(0);
|
|
1071
|
-
if (data.length > 0) {
|
|
1072
|
-
this.emitPtyData(data);
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
this.emit({
|
|
1076
|
-
type: 'script_output',
|
|
1077
|
-
phase: message.phase,
|
|
1078
|
-
data,
|
|
1079
|
-
done: message.done,
|
|
1080
|
-
error: message.error,
|
|
1081
|
-
exitCode: message.exitCode,
|
|
1082
|
-
});
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
private prunePendingEventChunks(nowMs = Date.now()): void {
|
|
1086
|
-
const ttlMs = 30_000;
|
|
1087
|
-
for (const [requestId, pending] of this.pendingEventChunks) {
|
|
1088
|
-
if (nowMs - pending.receivedAtMs > ttlMs) {
|
|
1089
|
-
this.pendingEventChunks.delete(requestId);
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
private handleEventsList(message: EventsListResponse): void {
|
|
1095
|
-
const totalChunks = message.totalChunks ?? 1;
|
|
1096
|
-
const chunkIndex = message.chunkIndex ?? 0;
|
|
1097
|
-
const requestId = message.requestId;
|
|
1098
|
-
|
|
1099
|
-
if (!requestId || totalChunks <= 1) {
|
|
1100
|
-
this.emit({
|
|
1101
|
-
type: 'events',
|
|
1102
|
-
events: message.events,
|
|
1103
|
-
liveEventIds: message.liveEventIds,
|
|
1104
|
-
savedEventFilters: message.savedEventFilters,
|
|
1105
|
-
});
|
|
1106
|
-
return;
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
|
-
this.prunePendingEventChunks();
|
|
1110
|
-
|
|
1111
|
-
const pending = this.pendingEventChunks.get(requestId) ?? {
|
|
1112
|
-
workspaceId: message.workspaceId,
|
|
1113
|
-
totalChunks,
|
|
1114
|
-
chunks: new Map<number, WideEvent[]>(),
|
|
1115
|
-
liveEventIds: message.liveEventIds,
|
|
1116
|
-
savedEventFilters: message.savedEventFilters,
|
|
1117
|
-
receivedAtMs: Date.now(),
|
|
1118
|
-
};
|
|
1119
|
-
|
|
1120
|
-
pending.workspaceId = message.workspaceId;
|
|
1121
|
-
pending.totalChunks = totalChunks;
|
|
1122
|
-
pending.liveEventIds = message.liveEventIds;
|
|
1123
|
-
pending.savedEventFilters = message.savedEventFilters;
|
|
1124
|
-
pending.receivedAtMs = Date.now();
|
|
1125
|
-
pending.chunks.set(chunkIndex, message.events);
|
|
1126
|
-
this.pendingEventChunks.set(requestId, pending);
|
|
1127
|
-
|
|
1128
|
-
if (pending.chunks.size < pending.totalChunks) {
|
|
1129
|
-
return;
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
|
-
const merged: WideEvent[] = [];
|
|
1133
|
-
for (let idx = 0; idx < pending.totalChunks; idx += 1) {
|
|
1134
|
-
const chunk = pending.chunks.get(idx);
|
|
1135
|
-
if (!chunk) {
|
|
1136
|
-
return;
|
|
1137
|
-
}
|
|
1138
|
-
merged.push(...chunk);
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
this.pendingEventChunks.delete(requestId);
|
|
1142
|
-
this.emit({
|
|
1143
|
-
type: 'events',
|
|
1144
|
-
events: merged,
|
|
1145
|
-
liveEventIds: pending.liveEventIds,
|
|
1146
|
-
savedEventFilters: pending.savedEventFilters,
|
|
1147
|
-
});
|
|
1148
|
-
}
|
|
1149
|
-
|
|
1150
|
-
private handleSessionEvent(message: SessionEventMessage): void {
|
|
1151
|
-
if (message.type === 'kicked') {
|
|
1152
|
-
this.mode = 'browsing';
|
|
1153
|
-
this.attachedSessionId = null;
|
|
1154
|
-
this.emit({ type: 'detached' });
|
|
1155
|
-
return;
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
if (message.type === 'exited') {
|
|
1159
|
-
const sessionId = this.attachedSessionId;
|
|
1160
|
-
this.mode = 'browsing';
|
|
1161
|
-
this.attachedSessionId = null;
|
|
1162
|
-
if (sessionId) {
|
|
1163
|
-
this.emit({ type: 'session_exited', sessionId, exitCode: message.code });
|
|
1164
|
-
}
|
|
1165
|
-
return;
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
if (message.type === 'attached' && this.attachedSessionId) {
|
|
1169
|
-
this.emit({ type: 'attached', sessionId: this.attachedSessionId });
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
private resolveBundleRefreshPlan(message: BundleRefreshPlanResponse): void {
|
|
1174
|
-
const pending = this.pendingBundleRefreshPlan;
|
|
1175
|
-
if (!pending) {
|
|
1176
|
-
return;
|
|
1177
|
-
}
|
|
1178
|
-
|
|
1179
|
-
clearTimeout(pending.timeout);
|
|
1180
|
-
this.pendingBundleRefreshPlan = null;
|
|
1181
|
-
pending.resolve(message.plan);
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
private resolveBundleRefreshApply(_message: BundleRefreshAppliedResponse): void {
|
|
1185
|
-
const pending = this.pendingBundleRefreshApply;
|
|
1186
|
-
if (!pending) {
|
|
1187
|
-
return;
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
clearTimeout(pending.timeout);
|
|
1191
|
-
this.pendingBundleRefreshApply = null;
|
|
1192
|
-
pending.resolve();
|
|
1193
|
-
}
|
|
1194
|
-
|
|
1195
|
-
private rejectPendingBundleRefreshRequests(message: string): void {
|
|
1196
|
-
const error = new Error(message);
|
|
1197
|
-
|
|
1198
|
-
if (this.pendingBundleRefreshPlan) {
|
|
1199
|
-
clearTimeout(this.pendingBundleRefreshPlan.timeout);
|
|
1200
|
-
this.pendingBundleRefreshPlan.reject(error);
|
|
1201
|
-
this.pendingBundleRefreshPlan = null;
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
|
-
if (this.pendingBundleRefreshApply) {
|
|
1205
|
-
clearTimeout(this.pendingBundleRefreshApply.timeout);
|
|
1206
|
-
this.pendingBundleRefreshApply.reject(error);
|
|
1207
|
-
this.pendingBundleRefreshApply = null;
|
|
1208
|
-
}
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1211
|
-
private resolveWorkspaceDelete(workspaceId: string): void {
|
|
1212
|
-
const pending = this.pendingDeleteWorkspace;
|
|
1213
|
-
if (!pending) {
|
|
1214
|
-
return;
|
|
1215
|
-
}
|
|
1216
|
-
|
|
1217
|
-
if (!workspaceIdsMatch(pending.workspaceId, workspaceId)) {
|
|
1218
|
-
return;
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
clearTimeout(pending.timeout);
|
|
1222
|
-
this.pendingDeleteWorkspace = null;
|
|
1223
|
-
pending.resolve();
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
private rejectPendingWorkspaceDelete(
|
|
1227
|
-
code: string | undefined,
|
|
1228
|
-
message: string,
|
|
1229
|
-
workspaceId?: string,
|
|
1230
|
-
force = false
|
|
1231
|
-
): void {
|
|
1232
|
-
const pending = this.pendingDeleteWorkspace;
|
|
1233
|
-
if (!pending) {
|
|
1234
|
-
return;
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
if (!force && workspaceId && !workspaceIdsMatch(pending.workspaceId, workspaceId)) {
|
|
1238
|
-
return;
|
|
1239
|
-
}
|
|
1240
|
-
|
|
1241
|
-
clearTimeout(pending.timeout);
|
|
1242
|
-
this.pendingDeleteWorkspace = null;
|
|
1243
|
-
pending.reject(new WorkspaceDeleteError(message, toWorkspaceDeleteErrorCode(code)));
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
private resolveReviewRequest(message: ReviewResponse): void {
|
|
1247
|
-
const pending = this.pendingReviewRequests.get(message.requestId);
|
|
1248
|
-
if (!pending) {
|
|
1249
|
-
return;
|
|
1250
|
-
}
|
|
1251
|
-
clearTimeout(pending.timeout);
|
|
1252
|
-
this.pendingReviewRequests.delete(message.requestId);
|
|
1253
|
-
if (message.error) {
|
|
1254
|
-
pending.reject(
|
|
1255
|
-
new ReviewRequestError(
|
|
1256
|
-
message.error.message,
|
|
1257
|
-
message.error.code || 'REVIEW_FAILED',
|
|
1258
|
-
{ op: pending.op, requestId: message.requestId }
|
|
1259
|
-
)
|
|
1260
|
-
);
|
|
1261
|
-
} else if (message.result) {
|
|
1262
|
-
pending.resolve(message.result);
|
|
1263
|
-
} else {
|
|
1264
|
-
pending.reject(
|
|
1265
|
-
new ReviewRequestError('Review response missing result', 'REVIEW_MISSING_RESULT', {
|
|
1266
|
-
op: pending.op,
|
|
1267
|
-
requestId: message.requestId,
|
|
1268
|
-
})
|
|
1269
|
-
);
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
private rejectAllPendingReviewRequests(message: string): void {
|
|
1274
|
-
for (const [requestId, pending] of this.pendingReviewRequests) {
|
|
1275
|
-
clearTimeout(pending.timeout);
|
|
1276
|
-
pending.reject(
|
|
1277
|
-
new ReviewRequestError(message, 'REVIEW_FAILED', {
|
|
1278
|
-
op: pending.op,
|
|
1279
|
-
requestId,
|
|
1280
|
-
})
|
|
1281
|
-
);
|
|
1282
|
-
this.pendingReviewRequests.delete(requestId);
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
|
-
|
|
1286
|
-
private emitPtyData(data: Uint8Array): void {
|
|
1287
|
-
if (!this.ptyOutputHandler) {
|
|
1288
|
-
this.pendingPtyChunks.push(data);
|
|
1289
|
-
return;
|
|
1290
|
-
}
|
|
1291
|
-
|
|
1292
|
-
const combined = this.pendingUtf8Bytes.length
|
|
1293
|
-
? concatUint8Array([this.pendingUtf8Bytes, data])
|
|
1294
|
-
: data;
|
|
1295
|
-
|
|
1296
|
-
const boundary = findUtf8Boundary(combined);
|
|
1297
|
-
if (boundary < combined.length) {
|
|
1298
|
-
this.pendingUtf8Bytes = combined.slice(boundary);
|
|
1299
|
-
} else {
|
|
1300
|
-
this.pendingUtf8Bytes = new Uint8Array(0);
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
const chunk = combined.slice(0, boundary);
|
|
1304
|
-
if (chunk.length > 0) {
|
|
1305
|
-
this.ptyOutputHandler(chunk);
|
|
1306
|
-
}
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
private async sendCommand(message: ClientToMachineMessage | SessionCtrl): Promise<void> {
|
|
1310
|
-
this.assertConnected();
|
|
1311
|
-
|
|
1312
|
-
const keys = this.sessionKeys;
|
|
1313
|
-
if (!keys) {
|
|
1314
|
-
throw new Error('Session keys are not established');
|
|
1315
|
-
}
|
|
1316
|
-
|
|
1317
|
-
const streamId = this.crypto.controlStreamId ?? DEFAULT_CONTROL_STREAM_ID;
|
|
1318
|
-
const payload = new TextEncoder().encode(JSON.stringify(message));
|
|
1319
|
-
const frame = await this.crypto.createFrame(streamId, payload, keys.sendKey);
|
|
1320
|
-
const encoded = this.crypto.encodeBase64(frame);
|
|
1321
|
-
const relayMessage: RelayDataMessage = { type: 'data', data: encoded };
|
|
1322
|
-
this.socketAdapter.send(this.socket, JSON.stringify(relayMessage));
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
|
-
private assertConnected(): void {
|
|
1326
|
-
if (!this.isSocketOpen()) {
|
|
1327
|
-
throw new Error('Socket is not connected');
|
|
1328
|
-
}
|
|
1329
|
-
}
|
|
1330
|
-
|
|
1331
|
-
private isSocketOpen(): boolean {
|
|
1332
|
-
return (
|
|
1333
|
-
this.socketAdapter.getReadyState(this.socket) ===
|
|
1334
|
-
this.socketAdapter.getOpenReadyStateValue()
|
|
1335
|
-
);
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
|
-
private emit(event: BackendEvent): void {
|
|
1339
|
-
for (const handler of this.handlers) {
|
|
1340
|
-
handler(event);
|
|
1341
|
-
}
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
private resolveConnect(): void {
|
|
1345
|
-
if (this.connectResolve) {
|
|
1346
|
-
this.connectResolve();
|
|
1347
|
-
}
|
|
1348
|
-
this.connectPromise = null;
|
|
1349
|
-
this.connectResolve = null;
|
|
1350
|
-
this.connectReject = null;
|
|
1351
|
-
}
|
|
1352
|
-
|
|
1353
|
-
private rejectConnect(error: Error): void {
|
|
1354
|
-
if (this.connectReject) {
|
|
1355
|
-
this.connectReject(error);
|
|
1356
|
-
}
|
|
1357
|
-
this.connectPromise = null;
|
|
1358
|
-
this.connectResolve = null;
|
|
1359
|
-
this.connectReject = null;
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
private resetState(): void {
|
|
1363
|
-
this.isConnected = false;
|
|
1364
|
-
this.mode = 'browsing';
|
|
1365
|
-
this.attachedSessionId = null;
|
|
1366
|
-
this.handshakeState = null;
|
|
1367
|
-
this.sessionKeys = null;
|
|
1368
|
-
this.pendingPtyChunks = [];
|
|
1369
|
-
this.pendingUtf8Bytes = new Uint8Array(0);
|
|
1370
|
-
this.pendingEventChunks.clear();
|
|
1371
|
-
this.rejectPendingBundleRefreshRequests('Remote session disconnected');
|
|
1372
|
-
this.rejectPendingWorkspaceDelete('DELETE_FAILED', 'Remote session disconnected', undefined, true);
|
|
1373
|
-
this.rejectAllPendingReviewRequests('Remote session disconnected');
|
|
1374
|
-
this.connectPromise = null;
|
|
1375
|
-
this.connectResolve = null;
|
|
1376
|
-
this.connectReject = null;
|
|
1377
|
-
}
|
|
1378
|
-
}
|