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,506 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Relay client - WebSocket connection from client to relay
|
|
3
|
-
*
|
|
4
|
-
* Handles:
|
|
5
|
-
* - Connection to relay server
|
|
6
|
-
* - X3DH handshake for mutual authentication and key exchange
|
|
7
|
-
* - Reconnection with exponential backoff
|
|
8
|
-
* - Sending/receiving encrypted frames
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { createHash } from "crypto";
|
|
12
|
-
import {
|
|
13
|
-
createFrame,
|
|
14
|
-
openFrame,
|
|
15
|
-
MASTER_STREAM_ID,
|
|
16
|
-
} from "./crypto";
|
|
17
|
-
import {
|
|
18
|
-
createClientHello,
|
|
19
|
-
processServerHello,
|
|
20
|
-
createClientAuth,
|
|
21
|
-
processServerAuth,
|
|
22
|
-
type X3DHClientState,
|
|
23
|
-
} from "./crypto/handshake.js";
|
|
24
|
-
import type {
|
|
25
|
-
Identity,
|
|
26
|
-
SessionKeys,
|
|
27
|
-
AccessType,
|
|
28
|
-
X3DHResponseMessage,
|
|
29
|
-
X3DHAuthMessage,
|
|
30
|
-
X3DHResultMessage,
|
|
31
|
-
} from "../../types/identity.js";
|
|
32
|
-
import { signMessage, type SignatureBlock } from "../../relay/signing.js";
|
|
33
|
-
|
|
34
|
-
/** Relay client configuration (identity/X3DH handshake) */
|
|
35
|
-
export interface RelayClientConfig {
|
|
36
|
-
/** Relay WebSocket URL (e.g., wss://relay.example.com/ws) */
|
|
37
|
-
relayUrl: string;
|
|
38
|
-
/** Machine ID hint for relay routing */
|
|
39
|
-
machineId?: string;
|
|
40
|
-
/** Client's identity for authentication */
|
|
41
|
-
identity: Identity;
|
|
42
|
-
/** Invite token (if connecting via invite) */
|
|
43
|
-
inviteToken?: string;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** Connection state */
|
|
47
|
-
export type ConnectionState =
|
|
48
|
-
| "disconnected"
|
|
49
|
-
| "connecting"
|
|
50
|
-
| "handshaking"
|
|
51
|
-
| "connected"
|
|
52
|
-
| "reconnecting";
|
|
53
|
-
|
|
54
|
-
/** Relay client events */
|
|
55
|
-
export interface RelayClientEvents {
|
|
56
|
-
/** Called when connected to relay */
|
|
57
|
-
onConnect?: () => void;
|
|
58
|
-
/** Called when disconnected from relay */
|
|
59
|
-
onDisconnect?: (code: number, reason: string) => void;
|
|
60
|
-
/** Called when a message is received (already decrypted) */
|
|
61
|
-
onMessage?: (streamId: number, data: Buffer) => void;
|
|
62
|
-
/** Called on connection error */
|
|
63
|
-
onError?: (error: Error) => void;
|
|
64
|
-
/** Called when connection state changes */
|
|
65
|
-
onStateChange?: (state: ConnectionState) => void;
|
|
66
|
-
/** Called when handshake completes */
|
|
67
|
-
onHandshakeComplete?: (peerIdentityId: string, accessType: AccessType, sessionId?: string) => void;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Relay client for identity-based connections
|
|
72
|
-
*
|
|
73
|
-
* Uses X3DH handshake with Ed25519/X25519 keys.
|
|
74
|
-
*/
|
|
75
|
-
export class RelayClient {
|
|
76
|
-
private ws: WebSocket | null = null;
|
|
77
|
-
private config: RelayClientConfig;
|
|
78
|
-
private events: RelayClientEvents;
|
|
79
|
-
private state: ConnectionState = "disconnected";
|
|
80
|
-
private reconnectAttempts = 0;
|
|
81
|
-
private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
|
82
|
-
private readKey: Buffer | null = null;
|
|
83
|
-
private writeKey: Buffer | null = null;
|
|
84
|
-
|
|
85
|
-
// X3DH handshake state (identity mode)
|
|
86
|
-
private handshakeState: X3DHClientState | null = null;
|
|
87
|
-
private sessionKeys: SessionKeys | null = null;
|
|
88
|
-
private peerIdentityId: string | null = null;
|
|
89
|
-
private accessType: AccessType | null = null;
|
|
90
|
-
private sessionId: string | undefined = undefined;
|
|
91
|
-
|
|
92
|
-
/** Maximum reconnect attempts */
|
|
93
|
-
private readonly maxReconnectAttempts = 10;
|
|
94
|
-
/** Base reconnect delay in ms */
|
|
95
|
-
private readonly baseReconnectDelay = 1000;
|
|
96
|
-
/** Maximum reconnect delay in ms */
|
|
97
|
-
private readonly maxReconnectDelay = 30000;
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Create a new relay client
|
|
101
|
-
*
|
|
102
|
-
* @param config - Client configuration
|
|
103
|
-
* @param events - Event handlers
|
|
104
|
-
*/
|
|
105
|
-
constructor(config: RelayClientConfig, events: RelayClientEvents = {}) {
|
|
106
|
-
this.config = config;
|
|
107
|
-
this.events = events;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/** Get current connection state */
|
|
111
|
-
getState(): ConnectionState {
|
|
112
|
-
return this.state;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/** Check if connected */
|
|
116
|
-
isConnected(): boolean {
|
|
117
|
-
return this.state === "connected" && this.ws?.readyState === WebSocket.OPEN;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/** Get peer identity ID */
|
|
121
|
-
getPeerIdentityId(): string | null {
|
|
122
|
-
return this.peerIdentityId;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/** Get current access type */
|
|
126
|
-
getAccessType(): AccessType | null {
|
|
127
|
-
return this.accessType;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/** Get current session ID */
|
|
131
|
-
getSessionId(): string | undefined {
|
|
132
|
-
return this.sessionId;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Connect to the relay server
|
|
137
|
-
*/
|
|
138
|
-
async connect(): Promise<void> {
|
|
139
|
-
if (this.state === "connecting" || this.state === "connected" || this.state === "handshaking") {
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
this.setState("connecting");
|
|
144
|
-
this.doConnect();
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Disconnect from the relay server
|
|
149
|
-
*/
|
|
150
|
-
disconnect(): void {
|
|
151
|
-
this.cancelReconnect();
|
|
152
|
-
if (this.ws) {
|
|
153
|
-
this.ws.close(1000, "Client disconnect");
|
|
154
|
-
this.ws = null;
|
|
155
|
-
}
|
|
156
|
-
this.setState("disconnected");
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Send data to all connected clients (via relay)
|
|
161
|
-
*
|
|
162
|
-
* @param data - Plaintext data to send (will be encrypted)
|
|
163
|
-
* @param streamId - Stream ID (default: master stream)
|
|
164
|
-
*/
|
|
165
|
-
send(data: Buffer | Uint8Array, streamId = MASTER_STREAM_ID): boolean {
|
|
166
|
-
if (!this.isConnected() || !this.writeKey) {
|
|
167
|
-
return false;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
try {
|
|
171
|
-
const frame = createFrame(streamId, data, this.writeKey);
|
|
172
|
-
|
|
173
|
-
// Relay mode: wrap encrypted frame in JSON data message
|
|
174
|
-
this.ws!.send(JSON.stringify({
|
|
175
|
-
type: "data",
|
|
176
|
-
data: Buffer.from(frame).toString("base64"),
|
|
177
|
-
}));
|
|
178
|
-
return true;
|
|
179
|
-
} catch (e) {
|
|
180
|
-
console.error("[relay-client] Send error:", e);
|
|
181
|
-
return false;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
private setState(state: ConnectionState): void {
|
|
186
|
-
if (this.state !== state) {
|
|
187
|
-
this.state = state;
|
|
188
|
-
this.events.onStateChange?.(state);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
private doConnect(): void {
|
|
193
|
-
const { relayUrl, machineId } = this.config;
|
|
194
|
-
|
|
195
|
-
// Build WebSocket URL
|
|
196
|
-
const url = new URL(relayUrl);
|
|
197
|
-
if (machineId) {
|
|
198
|
-
url.searchParams.set("m", machineId);
|
|
199
|
-
}
|
|
200
|
-
url.searchParams.set("role", "client");
|
|
201
|
-
|
|
202
|
-
try {
|
|
203
|
-
this.ws = new WebSocket(url.toString());
|
|
204
|
-
this.ws.binaryType = "arraybuffer";
|
|
205
|
-
|
|
206
|
-
this.ws.onopen = () => {
|
|
207
|
-
console.log("[relay-client] Connected to relay");
|
|
208
|
-
this.reconnectAttempts = 0;
|
|
209
|
-
|
|
210
|
-
// Need to send protocol message for routing
|
|
211
|
-
// Then wait for connection_established before starting handshake
|
|
212
|
-
if (this.config.inviteToken) {
|
|
213
|
-
// Client connecting via invite - send connect_with_invite
|
|
214
|
-
this.sendConnectWithInvite();
|
|
215
|
-
} else {
|
|
216
|
-
// Direct connection to machine - send connect_to_machine
|
|
217
|
-
this.sendConnectToMachine();
|
|
218
|
-
}
|
|
219
|
-
// Handshake will start when we receive connection_established
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
this.ws.onclose = (event) => {
|
|
223
|
-
console.log(
|
|
224
|
-
`[relay-client] Disconnected: ${event.code} ${event.reason}`
|
|
225
|
-
);
|
|
226
|
-
this.ws = null;
|
|
227
|
-
this.handshakeState = null;
|
|
228
|
-
this.events.onDisconnect?.(event.code, event.reason);
|
|
229
|
-
|
|
230
|
-
// Auto-reconnect unless explicitly disconnected
|
|
231
|
-
if (this.state !== "disconnected") {
|
|
232
|
-
this.scheduleReconnect();
|
|
233
|
-
}
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
this.ws.onerror = (event) => {
|
|
237
|
-
console.error("[relay-client] WebSocket error:", event);
|
|
238
|
-
this.events.onError?.(new Error("WebSocket error"));
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
this.ws.onmessage = (event) => {
|
|
242
|
-
this.handleMessage(event.data);
|
|
243
|
-
};
|
|
244
|
-
} catch (e) {
|
|
245
|
-
console.error("[relay-client] Connection error:", e);
|
|
246
|
-
this.events.onError?.(e as Error);
|
|
247
|
-
this.scheduleReconnect();
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Start X3DH handshake (identity mode)
|
|
253
|
-
*/
|
|
254
|
-
private startHandshake(): void {
|
|
255
|
-
if (!this.ws) {
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
console.log("[relay-client] Starting X3DH handshake");
|
|
260
|
-
|
|
261
|
-
// Create ClientHello
|
|
262
|
-
const { state, message } = createClientHello(this.config.machineId);
|
|
263
|
-
this.handshakeState = state;
|
|
264
|
-
|
|
265
|
-
// Send as JSON (handshake messages are not encrypted)
|
|
266
|
-
this.ws.send(JSON.stringify({ type: "handshake", phase: "client_hello", data: message }));
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
private signClientMessage<T extends object>(message: T): T & { signature: SignatureBlock } {
|
|
270
|
-
const privateKey = this.config.identity.signing.secretKey.slice(0, 32);
|
|
271
|
-
const publicKey = this.config.identity.signing.publicKey;
|
|
272
|
-
return signMessage(message, privateKey, publicKey);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Send connect_with_invite protocol message for relay routing
|
|
277
|
-
*/
|
|
278
|
-
private sendConnectWithInvite(): void {
|
|
279
|
-
if (!this.ws || !this.config.inviteToken) {
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Generate inviteId from token hash (same as share.ts)
|
|
284
|
-
const inviteId = createHash("sha256")
|
|
285
|
-
.update(this.config.inviteToken)
|
|
286
|
-
.digest("hex")
|
|
287
|
-
.substring(0, 16);
|
|
288
|
-
|
|
289
|
-
const clientIdentityId = this.config.identity.id;
|
|
290
|
-
|
|
291
|
-
console.log("[relay-client] Sending connect_with_invite");
|
|
292
|
-
const signed = this.signClientMessage({
|
|
293
|
-
type: "connect_with_invite",
|
|
294
|
-
inviteId,
|
|
295
|
-
clientIdentityId,
|
|
296
|
-
});
|
|
297
|
-
this.ws.send(JSON.stringify(signed));
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Send connect_to_machine protocol message for direct connection
|
|
302
|
-
*/
|
|
303
|
-
private sendConnectToMachine(): void {
|
|
304
|
-
if (!this.ws) {
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const clientIdentityId = this.config.identity.id;
|
|
309
|
-
const machineId = this.config.machineId;
|
|
310
|
-
|
|
311
|
-
console.log("[relay-client] Sending connect_to_machine");
|
|
312
|
-
const signed = this.signClientMessage({
|
|
313
|
-
type: "connect_to_machine",
|
|
314
|
-
machineId,
|
|
315
|
-
clientIdentityId,
|
|
316
|
-
});
|
|
317
|
-
this.ws.send(JSON.stringify(signed));
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
private handleMessage(data: ArrayBuffer | string): void {
|
|
321
|
-
try {
|
|
322
|
-
const jsonData = data instanceof ArrayBuffer
|
|
323
|
-
? new TextDecoder().decode(data)
|
|
324
|
-
: data;
|
|
325
|
-
|
|
326
|
-
const msg = JSON.parse(jsonData);
|
|
327
|
-
|
|
328
|
-
// Handle relay protocol messages
|
|
329
|
-
if (msg.type === "connection_established") {
|
|
330
|
-
console.log("[relay-client] Connection established to machine:", msg.machineId);
|
|
331
|
-
// Now start X3DH handshake - relay has set up the connection
|
|
332
|
-
this.setState("handshaking");
|
|
333
|
-
this.startHandshake();
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
if (msg.type === "error") {
|
|
338
|
-
console.error("[relay-client] Relay error:", msg.message);
|
|
339
|
-
this.events.onError?.(new Error(msg.message || "Relay error"));
|
|
340
|
-
this.disconnect();
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
if (msg.type === "handshake" && this.state === "handshaking") {
|
|
345
|
-
this.handleHandshakeMessage(msg);
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
if (msg.type === "data" && msg.data && this.state === "handshaking") {
|
|
350
|
-
// Handle handshake wrapped in data message from relay
|
|
351
|
-
try {
|
|
352
|
-
const decodedData = Buffer.from(msg.data, "base64").toString("utf-8");
|
|
353
|
-
const innerMsg = JSON.parse(decodedData);
|
|
354
|
-
if (innerMsg.type === "handshake") {
|
|
355
|
-
this.handleHandshakeMessage(innerMsg);
|
|
356
|
-
}
|
|
357
|
-
} catch {
|
|
358
|
-
// Ignore non-handshake data during handshaking
|
|
359
|
-
}
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
if (msg.type === "data" && msg.data && this.state === "connected" && this.readKey) {
|
|
364
|
-
const frameBuffer = Buffer.from(msg.data, "base64");
|
|
365
|
-
const result = openFrame(frameBuffer, this.readKey);
|
|
366
|
-
if (result) {
|
|
367
|
-
this.events.onMessage?.(result.streamId, result.data);
|
|
368
|
-
} else {
|
|
369
|
-
console.warn("[relay-client] Failed to decrypt frame");
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
} catch (e) {
|
|
373
|
-
console.error("[relay-client] Message handling error:", e);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
/**
|
|
378
|
-
* Handle X3DH handshake messages
|
|
379
|
-
*/
|
|
380
|
-
private handleHandshakeMessage(msg: { type: string; phase: string; data: unknown }): void {
|
|
381
|
-
if (!this.handshakeState || !this.ws) {
|
|
382
|
-
return;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
try {
|
|
386
|
-
switch (msg.phase) {
|
|
387
|
-
case "server_hello": {
|
|
388
|
-
console.log("[relay-client] Received ServerHello");
|
|
389
|
-
|
|
390
|
-
// Process ServerHello
|
|
391
|
-
const serverHello = msg.data as X3DHResponseMessage;
|
|
392
|
-
const newState = processServerHello(this.handshakeState, serverHello);
|
|
393
|
-
|
|
394
|
-
if (!newState) {
|
|
395
|
-
console.error("[relay-client] Invalid ServerHello");
|
|
396
|
-
this.events.onError?.(new Error("Handshake failed: invalid ServerHello"));
|
|
397
|
-
this.disconnect();
|
|
398
|
-
return;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
// Create and send ClientAuth
|
|
402
|
-
const authorization: X3DHAuthMessage["authorization"] = this.config.inviteToken
|
|
403
|
-
? { type: "invite", inviteToken: this.config.inviteToken }
|
|
404
|
-
: { type: "access_list" };
|
|
405
|
-
|
|
406
|
-
const { state, message, sessionKeys } = createClientAuth(
|
|
407
|
-
newState,
|
|
408
|
-
this.config.identity,
|
|
409
|
-
authorization
|
|
410
|
-
);
|
|
411
|
-
|
|
412
|
-
this.handshakeState = state;
|
|
413
|
-
this.sessionKeys = sessionKeys;
|
|
414
|
-
|
|
415
|
-
console.log("[relay-client] Sending ClientAuth");
|
|
416
|
-
this.ws.send(JSON.stringify({ type: "handshake", phase: "client_auth", data: message }));
|
|
417
|
-
break;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
case "server_auth": {
|
|
421
|
-
console.log("[relay-client] Received ServerAuth");
|
|
422
|
-
|
|
423
|
-
if (!this.sessionKeys) {
|
|
424
|
-
console.error("[relay-client] Missing session keys");
|
|
425
|
-
this.disconnect();
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// Process ServerAuth
|
|
430
|
-
const serverAuth = msg.data as X3DHResultMessage;
|
|
431
|
-
const result = processServerAuth(this.handshakeState, serverAuth, this.sessionKeys);
|
|
432
|
-
|
|
433
|
-
if (!result) {
|
|
434
|
-
console.error("[relay-client] Handshake failed: invalid ServerAuth");
|
|
435
|
-
this.events.onError?.(new Error("Handshake failed: authentication rejected"));
|
|
436
|
-
this.disconnect();
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
// Check if accepted
|
|
441
|
-
if (result.authResult.type === "rejected") {
|
|
442
|
-
console.error("[relay-client] Access denied:", result.authResult.reason);
|
|
443
|
-
this.events.onError?.(new Error(`Access denied: ${result.authResult.reason}`));
|
|
444
|
-
this.disconnect();
|
|
445
|
-
return;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
// Handshake complete - set up encryption keys
|
|
449
|
-
this.peerIdentityId = result.peerIdentityId;
|
|
450
|
-
this.accessType = result.authResult.accessType;
|
|
451
|
-
this.sessionId = result.authResult.sessionId;
|
|
452
|
-
|
|
453
|
-
// Convert session keys to Buffers for frame encryption
|
|
454
|
-
this.writeKey = Buffer.from(result.sessionKeys.sendKey);
|
|
455
|
-
this.readKey = Buffer.from(result.sessionKeys.receiveKey);
|
|
456
|
-
|
|
457
|
-
console.log("[relay-client] Handshake complete, session established");
|
|
458
|
-
this.setState("connected");
|
|
459
|
-
this.events.onConnect?.();
|
|
460
|
-
this.events.onHandshakeComplete?.(this.peerIdentityId, this.accessType, this.sessionId);
|
|
461
|
-
break;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
default:
|
|
465
|
-
console.warn("[relay-client] Unknown handshake phase:", msg.phase);
|
|
466
|
-
}
|
|
467
|
-
} catch (e) {
|
|
468
|
-
console.error("[relay-client] Handshake error:", e);
|
|
469
|
-
this.events.onError?.(new Error(`Handshake error: ${e instanceof Error ? e.message : String(e)}`));
|
|
470
|
-
this.disconnect();
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
private scheduleReconnect(): void {
|
|
475
|
-
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
476
|
-
console.error("[relay-client] Max reconnect attempts reached");
|
|
477
|
-
this.setState("disconnected");
|
|
478
|
-
return;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
this.setState("reconnecting");
|
|
482
|
-
this.reconnectAttempts++;
|
|
483
|
-
|
|
484
|
-
// Exponential backoff with jitter
|
|
485
|
-
const delay = Math.min(
|
|
486
|
-
this.baseReconnectDelay * Math.pow(2, this.reconnectAttempts - 1) +
|
|
487
|
-
Math.random() * 1000,
|
|
488
|
-
this.maxReconnectDelay
|
|
489
|
-
);
|
|
490
|
-
|
|
491
|
-
console.log(
|
|
492
|
-
`[relay-client] Reconnecting in ${Math.round(delay)}ms (attempt ${this.reconnectAttempts})`
|
|
493
|
-
);
|
|
494
|
-
|
|
495
|
-
this.reconnectTimer = setTimeout(() => {
|
|
496
|
-
this.doConnect();
|
|
497
|
-
}, delay);
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
private cancelReconnect(): void {
|
|
501
|
-
if (this.reconnectTimer) {
|
|
502
|
-
clearTimeout(this.reconnectTimer);
|
|
503
|
-
this.reconnectTimer = null;
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
}
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E2E tests for tmux-lite server lifecycle
|
|
3
|
-
*
|
|
4
|
-
* Tests server start, stop, and restart scenarios including
|
|
5
|
-
* proper cleanup of socket files.
|
|
6
|
-
*/
|
|
7
|
-
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
|
8
|
-
import { spawn } from "bun";
|
|
9
|
-
import { existsSync, unlinkSync, mkdirSync, rmSync } from "fs";
|
|
10
|
-
import { join } from "path";
|
|
11
|
-
|
|
12
|
-
// Use the same paths as --test mode in cli.ts and server.ts
|
|
13
|
-
const TEST_SOCKET = "/tmp/tmux-lite-test.sock";
|
|
14
|
-
const TEST_SESSION_DIR = "/tmp/tmux-lite-test";
|
|
15
|
-
const CLI_SCRIPT = join(import.meta.dir, "cli.ts");
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Helper to run CLI commands in test mode
|
|
19
|
-
*/
|
|
20
|
-
async function runCli(command: string): Promise<{ stdout: string; stderr: string; exitCode: number }> {
|
|
21
|
-
const proc = spawn({
|
|
22
|
-
cmd: ["bun", "run", CLI_SCRIPT, command, "--test"],
|
|
23
|
-
stdout: "pipe",
|
|
24
|
-
stderr: "pipe",
|
|
25
|
-
env: {
|
|
26
|
-
...process.env,
|
|
27
|
-
TMUX_LITE: "",
|
|
28
|
-
},
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
const stdout = await new Response(proc.stdout).text();
|
|
32
|
-
const stderr = await new Response(proc.stderr).text();
|
|
33
|
-
const exitCode = await proc.exited;
|
|
34
|
-
|
|
35
|
-
// Give server time to initialize after CLI returns
|
|
36
|
-
await Bun.sleep(500);
|
|
37
|
-
|
|
38
|
-
return { stdout, stderr, exitCode };
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Helper to check if server is running by checking socket exists and responds
|
|
43
|
-
*/
|
|
44
|
-
async function isServerRunning(): Promise<boolean> {
|
|
45
|
-
if (!existsSync(TEST_SOCKET)) return false;
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
const result = await runCli("list");
|
|
49
|
-
// If we can list sessions, server is running
|
|
50
|
-
return result.exitCode === 0;
|
|
51
|
-
} catch {
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Helper to wait for a condition with timeout
|
|
58
|
-
*/
|
|
59
|
-
async function waitFor(
|
|
60
|
-
condition: () => Promise<boolean>,
|
|
61
|
-
timeoutMs: number = 5000,
|
|
62
|
-
intervalMs: number = 100
|
|
63
|
-
): Promise<boolean> {
|
|
64
|
-
const start = Date.now();
|
|
65
|
-
while (Date.now() - start < timeoutMs) {
|
|
66
|
-
if (await condition()) return true;
|
|
67
|
-
await Bun.sleep(intervalMs);
|
|
68
|
-
}
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Force cleanup any leftover test artifacts
|
|
74
|
-
*/
|
|
75
|
-
function forceCleanup(): void {
|
|
76
|
-
try { unlinkSync(TEST_SOCKET); } catch {}
|
|
77
|
-
try { rmSync(TEST_SESSION_DIR, { recursive: true, force: true }); } catch {}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
describe("tmux-lite server lifecycle", () => {
|
|
81
|
-
beforeEach(() => {
|
|
82
|
-
// Ensure clean state before each test
|
|
83
|
-
forceCleanup();
|
|
84
|
-
mkdirSync(TEST_SESSION_DIR, { recursive: true });
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
afterEach(async () => {
|
|
88
|
-
// Kill server if still running and cleanup
|
|
89
|
-
try {
|
|
90
|
-
await runCli("kill-server");
|
|
91
|
-
await Bun.sleep(200); // Wait for cleanup
|
|
92
|
-
} catch {}
|
|
93
|
-
forceCleanup();
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
describe("server start", () => {
|
|
97
|
-
it("should start server and create socket file", async () => {
|
|
98
|
-
// Socket should not exist initially
|
|
99
|
-
expect(existsSync(TEST_SOCKET)).toBe(false);
|
|
100
|
-
|
|
101
|
-
// Start server by running any command (list auto-starts)
|
|
102
|
-
const result = await runCli("list");
|
|
103
|
-
|
|
104
|
-
// Wait for server to be ready
|
|
105
|
-
const started = await waitFor(async () => existsSync(TEST_SOCKET));
|
|
106
|
-
expect(started).toBe(true);
|
|
107
|
-
|
|
108
|
-
// Server should be running
|
|
109
|
-
const running = await isServerRunning();
|
|
110
|
-
expect(running).toBe(true);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it("should not fail if server is already running", async () => {
|
|
114
|
-
// Start server first time
|
|
115
|
-
await runCli("list");
|
|
116
|
-
await waitFor(async () => existsSync(TEST_SOCKET));
|
|
117
|
-
|
|
118
|
-
// Run another command - should not fail
|
|
119
|
-
const result = await runCli("list");
|
|
120
|
-
expect(result.exitCode).toBe(0);
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
describe("server stop", () => {
|
|
125
|
-
it("should stop server and remove socket file", async () => {
|
|
126
|
-
// Start server
|
|
127
|
-
await runCli("list");
|
|
128
|
-
await waitFor(async () => existsSync(TEST_SOCKET));
|
|
129
|
-
expect(existsSync(TEST_SOCKET)).toBe(true);
|
|
130
|
-
|
|
131
|
-
// Stop server
|
|
132
|
-
await runCli("kill-server");
|
|
133
|
-
|
|
134
|
-
// Wait for socket to be cleaned up
|
|
135
|
-
const cleaned = await waitFor(async () => !existsSync(TEST_SOCKET), 2000);
|
|
136
|
-
expect(cleaned).toBe(true);
|
|
137
|
-
|
|
138
|
-
// Socket file should be removed
|
|
139
|
-
expect(existsSync(TEST_SOCKET)).toBe(false);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it("should not fail if server is not running", async () => {
|
|
143
|
-
// Ensure server is not running
|
|
144
|
-
expect(existsSync(TEST_SOCKET)).toBe(false);
|
|
145
|
-
|
|
146
|
-
// Stop should handle gracefully
|
|
147
|
-
const result = await runCli("kill-server");
|
|
148
|
-
// CLI shows "Server not running" but exits cleanly
|
|
149
|
-
expect(result.exitCode).toBe(0);
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
describe("server restart cycle", () => {
|
|
154
|
-
it("should successfully restart after stop (socket cleanup regression test)", async () => {
|
|
155
|
-
// This is the main regression test for the socket cleanup fix
|
|
156
|
-
|
|
157
|
-
// Start server
|
|
158
|
-
await runCli("list");
|
|
159
|
-
const started1 = await waitFor(async () => existsSync(TEST_SOCKET));
|
|
160
|
-
expect(started1).toBe(true);
|
|
161
|
-
|
|
162
|
-
// Stop server
|
|
163
|
-
await runCli("kill-server");
|
|
164
|
-
const stopped = await waitFor(async () => !existsSync(TEST_SOCKET), 2000);
|
|
165
|
-
expect(stopped).toBe(true);
|
|
166
|
-
|
|
167
|
-
// Start server again - THIS WAS FAILING before the fix
|
|
168
|
-
// because the socket file wasn't being cleaned up
|
|
169
|
-
await runCli("list");
|
|
170
|
-
const started2 = await waitFor(async () => existsSync(TEST_SOCKET));
|
|
171
|
-
expect(started2).toBe(true);
|
|
172
|
-
|
|
173
|
-
// Verify server is actually running
|
|
174
|
-
const running = await isServerRunning();
|
|
175
|
-
expect(running).toBe(true);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it("should handle multiple restart cycles", async () => {
|
|
179
|
-
for (let i = 0; i < 3; i++) {
|
|
180
|
-
// Start
|
|
181
|
-
await runCli("list");
|
|
182
|
-
const started = await waitFor(async () => existsSync(TEST_SOCKET));
|
|
183
|
-
expect(started).toBe(true);
|
|
184
|
-
|
|
185
|
-
// Verify running
|
|
186
|
-
const running = await isServerRunning();
|
|
187
|
-
expect(running).toBe(true);
|
|
188
|
-
|
|
189
|
-
// Stop
|
|
190
|
-
await runCli("kill-server");
|
|
191
|
-
const stopped = await waitFor(async () => !existsSync(TEST_SOCKET), 2000);
|
|
192
|
-
expect(stopped).toBe(true);
|
|
193
|
-
}
|
|
194
|
-
}, 15000); // Increase timeout for 3 cycles
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
describe("stale socket handling", () => {
|
|
198
|
-
it("should clean up stale socket on server start", async () => {
|
|
199
|
-
// Create a stale socket file (simulating crashed server)
|
|
200
|
-
Bun.write(TEST_SOCKET, "stale");
|
|
201
|
-
expect(existsSync(TEST_SOCKET)).toBe(true);
|
|
202
|
-
|
|
203
|
-
// Start server - should clean up stale socket and start fresh
|
|
204
|
-
await runCli("list");
|
|
205
|
-
const started = await waitFor(async () => {
|
|
206
|
-
// Socket exists AND server responds
|
|
207
|
-
return await isServerRunning();
|
|
208
|
-
});
|
|
209
|
-
expect(started).toBe(true);
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
});
|