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,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shell escaping utilities for safe command execution
|
|
3
|
-
* Prevents command injection vulnerabilities when passing user input to shell commands
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Escape a string argument for safe use in shell commands
|
|
8
|
-
*
|
|
9
|
-
* This function wraps the argument in single quotes and escapes any single quotes
|
|
10
|
-
* within the string by replacing them with '\''
|
|
11
|
-
*
|
|
12
|
-
* @param arg The string to escape
|
|
13
|
-
* @returns Safely escaped string for shell use
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* escapeShellArg("my-branch") // "'my-branch'"
|
|
17
|
-
* escapeShellArg("branch'name") // "'branch'\''name'"
|
|
18
|
-
* escapeShellArg("feature/test") // "'feature/test'"
|
|
19
|
-
* escapeShellArg('"; rm -rf / #') // "'\"rm -rf / #'"
|
|
20
|
-
*/
|
|
21
|
-
export function escapeShellArg(arg: string): string {
|
|
22
|
-
// Single quotes protect against all shell metacharacters except single quote itself
|
|
23
|
-
// To include a single quote, we end the single-quoted string, add an escaped single quote,
|
|
24
|
-
// and start a new single-quoted string: 'don'\''t' -> "don't"
|
|
25
|
-
return `'${arg.replace(/'/g, "'\\''")}'`;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Escape multiple arguments and join them with spaces
|
|
30
|
-
*
|
|
31
|
-
* @param args Array of arguments to escape
|
|
32
|
-
* @returns Space-separated string of escaped arguments
|
|
33
|
-
*
|
|
34
|
-
* @example
|
|
35
|
-
* escapeShellArgs(["git", "commit", "-m", "my message"])
|
|
36
|
-
* // "'git' 'commit' '-m' 'my message'"
|
|
37
|
-
*/
|
|
38
|
-
export function escapeShellArgs(...args: string[]): string {
|
|
39
|
-
return args.map(escapeShellArg).join(' ');
|
|
40
|
-
}
|
package/src/utils/utf8.ts
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* UTF-8 Boundary Handling Utilities
|
|
3
|
-
*
|
|
4
|
-
* Provides functions for handling UTF-8 encoded data streams,
|
|
5
|
-
* particularly for finding safe split points in byte buffers
|
|
6
|
-
* to avoid breaking multi-byte character sequences.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Find the boundary where complete UTF-8 sequences end.
|
|
11
|
-
* Returns the number of bytes that form complete UTF-8 sequences.
|
|
12
|
-
* Any bytes after this boundary are incomplete and should be buffered.
|
|
13
|
-
*
|
|
14
|
-
* This function is useful when streaming UTF-8 data in chunks,
|
|
15
|
-
* where a multi-byte character might be split across chunk boundaries.
|
|
16
|
-
*
|
|
17
|
-
* @param buf - The byte buffer to analyze (Buffer or Uint8Array)
|
|
18
|
-
* @returns The byte offset where complete UTF-8 sequences end
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* ```ts
|
|
22
|
-
* const chunk = new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xc3]); // "Hello" + incomplete é
|
|
23
|
-
* const boundary = findUtf8Boundary(chunk); // Returns 5
|
|
24
|
-
* const complete = chunk.slice(0, boundary); // "Hello"
|
|
25
|
-
* const incomplete = chunk.slice(boundary); // [0xc3] - buffer for next chunk
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
export function findUtf8Boundary(buf: Uint8Array | Buffer): number {
|
|
29
|
-
if (buf.length === 0) return 0;
|
|
30
|
-
|
|
31
|
-
// UTF-8 encoding:
|
|
32
|
-
// - 1 byte: 0xxxxxxx (0x00-0x7F) - always complete
|
|
33
|
-
// - 2 bytes: 110xxxxx 10xxxxxx (starts with 0xC0-0xDF)
|
|
34
|
-
// - 3 bytes: 1110xxxx 10xxxxxx 10xxxxxx (starts with 0xE0-0xEF)
|
|
35
|
-
// - 4 bytes: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (starts with 0xF0-0xF7)
|
|
36
|
-
// Continuation bytes: 10xxxxxx (0x80-0xBF)
|
|
37
|
-
|
|
38
|
-
// Start from the last byte and work backwards (up to 3 bytes back)
|
|
39
|
-
for (let i = 0; i < Math.min(4, buf.length); i++) {
|
|
40
|
-
const pos = buf.length - 1 - i;
|
|
41
|
-
const byte = buf[pos];
|
|
42
|
-
|
|
43
|
-
// Skip continuation bytes (10xxxxxx) - keep looking for the leading byte
|
|
44
|
-
if ((byte & 0xc0) === 0x80) {
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Found a leading byte at pos - check if sequence is complete
|
|
49
|
-
let expectedLen: number;
|
|
50
|
-
if ((byte & 0x80) === 0x00) {
|
|
51
|
-
// ASCII (0xxxxxxx) - 1 byte
|
|
52
|
-
expectedLen = 1;
|
|
53
|
-
} else if ((byte & 0xe0) === 0xc0) {
|
|
54
|
-
// 2-byte sequence (110xxxxx)
|
|
55
|
-
expectedLen = 2;
|
|
56
|
-
} else if ((byte & 0xf0) === 0xe0) {
|
|
57
|
-
// 3-byte sequence (1110xxxx)
|
|
58
|
-
expectedLen = 3;
|
|
59
|
-
} else if ((byte & 0xf8) === 0xf0) {
|
|
60
|
-
// 4-byte sequence (11110xxx)
|
|
61
|
-
expectedLen = 4;
|
|
62
|
-
} else {
|
|
63
|
-
// Invalid leading byte - treat buffer as complete
|
|
64
|
-
return buf.length;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const actualLen = buf.length - pos;
|
|
68
|
-
if (actualLen >= expectedLen) {
|
|
69
|
-
// Sequence is complete
|
|
70
|
-
return buf.length;
|
|
71
|
-
} else {
|
|
72
|
-
// Incomplete sequence - boundary is at this position
|
|
73
|
-
return pos;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Only found continuation bytes (malformed UTF-8) - buffer conservatively
|
|
78
|
-
return Math.max(0, buf.length - 3);
|
|
79
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { isAbsolute, relative, resolve, sep } from 'path';
|
|
2
|
-
|
|
3
|
-
export function toWorkspaceId(projectName: string, workspaceName: string): string {
|
|
4
|
-
return `${projectName}:${workspaceName}`;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export function toCanonicalWorkspaceId(workspace: { projectName: string; id: string }): string {
|
|
8
|
-
return toWorkspaceId(workspace.projectName, workspace.id);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function matchesWorkspaceId(
|
|
12
|
-
workspace: { projectName: string; id: string },
|
|
13
|
-
workspaceId: string
|
|
14
|
-
): boolean {
|
|
15
|
-
return workspace.id === workspaceId || toCanonicalWorkspaceId(workspace) === workspaceId;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function resolveWorkspaceName(projectName: string, workspaceId: string): string {
|
|
19
|
-
const prefix = `${projectName}:`;
|
|
20
|
-
if (workspaceId.startsWith(prefix)) {
|
|
21
|
-
return workspaceId.slice(prefix.length);
|
|
22
|
-
}
|
|
23
|
-
return workspaceId;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function detectWorkspaceContextFromCwd(
|
|
27
|
-
cwd: string,
|
|
28
|
-
gitspaceDir: string
|
|
29
|
-
): { projectName: string; workspaceName: string } | null {
|
|
30
|
-
const resolvedCwd = resolve(cwd);
|
|
31
|
-
const resolvedGitspaceDir = resolve(gitspaceDir);
|
|
32
|
-
|
|
33
|
-
const cwdRelativeToGitspace = relative(resolvedGitspaceDir, resolvedCwd);
|
|
34
|
-
if (
|
|
35
|
-
cwdRelativeToGitspace === '' ||
|
|
36
|
-
cwdRelativeToGitspace === '.' ||
|
|
37
|
-
cwdRelativeToGitspace.startsWith('..') ||
|
|
38
|
-
isAbsolute(cwdRelativeToGitspace)
|
|
39
|
-
) {
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const parts = cwdRelativeToGitspace.split(sep).filter(Boolean);
|
|
44
|
-
if (parts.length < 3 || parts[1] !== 'workspaces') {
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const projectName = parts[0];
|
|
49
|
-
const workspaceName = parts[2];
|
|
50
|
-
if (!projectName || !workspaceName) {
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return { projectName, workspaceName };
|
|
55
|
-
}
|
|
@@ -1,427 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Workspace state tracking utilities.
|
|
3
|
-
*
|
|
4
|
-
* State is persisted in workspace-local gitspace.lock (JSON).
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { createHash } from 'crypto';
|
|
8
|
-
import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'fs';
|
|
9
|
-
import { join } from 'path';
|
|
10
|
-
import { SpacesError } from '../types/errors.js';
|
|
11
|
-
import type {
|
|
12
|
-
ConfirmStep,
|
|
13
|
-
InputStep,
|
|
14
|
-
OnboardingStep,
|
|
15
|
-
SecretStep,
|
|
16
|
-
SpacesBundle,
|
|
17
|
-
ConfirmStepResult,
|
|
18
|
-
} from '../types/bundle.js';
|
|
19
|
-
|
|
20
|
-
const SETUP_MARKER_FILE = 'gitspace.lock';
|
|
21
|
-
|
|
22
|
-
export type WorkspaceLockPhaseStatus = 'never' | 'success' | 'failed';
|
|
23
|
-
|
|
24
|
-
export interface WorkspaceLockConfirmState {
|
|
25
|
-
status: ConfirmStepResult['status'];
|
|
26
|
-
fingerprint: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface WorkspaceLockSetupState {
|
|
30
|
-
status: WorkspaceLockPhaseStatus;
|
|
31
|
-
ranAt?: string;
|
|
32
|
-
error?: string;
|
|
33
|
-
inputsUsed: Record<string, string>;
|
|
34
|
-
inputFingerprints: Record<string, string>;
|
|
35
|
-
secretFingerprints: Record<string, string>;
|
|
36
|
-
confirmsUsed: Record<string, WorkspaceLockConfirmState>;
|
|
37
|
-
usedOptionalSteps: Record<string, true>;
|
|
38
|
-
setupFingerprint?: string;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface WorkspaceLockSelectState {
|
|
42
|
-
status: WorkspaceLockPhaseStatus;
|
|
43
|
-
ranAt?: string;
|
|
44
|
-
error?: string;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export interface WorkspaceLockBundleState {
|
|
48
|
-
bundleHash: string;
|
|
49
|
-
stepFingerprints: Record<string, string>;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface WorkspaceLockState {
|
|
53
|
-
version: 1;
|
|
54
|
-
bundle?: WorkspaceLockBundleState;
|
|
55
|
-
setup: WorkspaceLockSetupState;
|
|
56
|
-
select: WorkspaceLockSelectState;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
interface BuildSetupStateOptions {
|
|
60
|
-
bundle?: SpacesBundle;
|
|
61
|
-
bundleHash?: string;
|
|
62
|
-
stepFingerprints?: Record<string, string>;
|
|
63
|
-
bundleValues?: Record<string, string>;
|
|
64
|
-
bundleSecrets?: Record<string, string>;
|
|
65
|
-
confirmResults?: Record<string, ConfirmStepResult>;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function getWorkspaceLockPath(workspacePath: string): string {
|
|
69
|
-
return join(workspacePath, SETUP_MARKER_FILE);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function createEmptyWorkspaceLockState(): WorkspaceLockState {
|
|
73
|
-
return {
|
|
74
|
-
version: 1,
|
|
75
|
-
setup: {
|
|
76
|
-
status: 'never',
|
|
77
|
-
inputsUsed: {},
|
|
78
|
-
inputFingerprints: {},
|
|
79
|
-
secretFingerprints: {},
|
|
80
|
-
confirmsUsed: {},
|
|
81
|
-
usedOptionalSteps: {},
|
|
82
|
-
},
|
|
83
|
-
select: {
|
|
84
|
-
status: 'never',
|
|
85
|
-
},
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
90
|
-
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function normalizeSetupState(value: unknown): WorkspaceLockSetupState {
|
|
94
|
-
const defaults = createEmptyWorkspaceLockState().setup;
|
|
95
|
-
if (!isRecord(value)) {
|
|
96
|
-
return defaults;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const status = value.status;
|
|
100
|
-
const normalizedStatus: WorkspaceLockPhaseStatus =
|
|
101
|
-
status === 'success' || status === 'failed' || status === 'never'
|
|
102
|
-
? status
|
|
103
|
-
: 'never';
|
|
104
|
-
|
|
105
|
-
return {
|
|
106
|
-
status: normalizedStatus,
|
|
107
|
-
ranAt: typeof value.ranAt === 'string' ? value.ranAt : undefined,
|
|
108
|
-
error: typeof value.error === 'string' ? value.error : undefined,
|
|
109
|
-
inputsUsed: isRecord(value.inputsUsed)
|
|
110
|
-
? Object.fromEntries(Object.entries(value.inputsUsed).filter(([, v]) => typeof v === 'string')) as Record<string, string>
|
|
111
|
-
: {},
|
|
112
|
-
inputFingerprints: isRecord(value.inputFingerprints)
|
|
113
|
-
? Object.fromEntries(Object.entries(value.inputFingerprints).filter(([, v]) => typeof v === 'string')) as Record<string, string>
|
|
114
|
-
: {},
|
|
115
|
-
secretFingerprints: isRecord(value.secretFingerprints)
|
|
116
|
-
? Object.fromEntries(Object.entries(value.secretFingerprints).filter(([, v]) => typeof v === 'string')) as Record<string, string>
|
|
117
|
-
: {},
|
|
118
|
-
confirmsUsed: isRecord(value.confirmsUsed)
|
|
119
|
-
? Object.fromEntries(
|
|
120
|
-
Object.entries(value.confirmsUsed)
|
|
121
|
-
.filter(([, v]) => isRecord(v) && typeof v.fingerprint === 'string' && (v.status === 'passed' || v.status === 'skipped'))
|
|
122
|
-
.map(([k, v]) => {
|
|
123
|
-
const item = v as { status: ConfirmStepResult['status']; fingerprint: string };
|
|
124
|
-
return [k, { status: item.status, fingerprint: item.fingerprint }];
|
|
125
|
-
})
|
|
126
|
-
)
|
|
127
|
-
: {},
|
|
128
|
-
usedOptionalSteps: isRecord(value.usedOptionalSteps)
|
|
129
|
-
? Object.fromEntries(Object.keys(value.usedOptionalSteps).map((key) => [key, true])) as Record<string, true>
|
|
130
|
-
: {},
|
|
131
|
-
setupFingerprint: typeof value.setupFingerprint === 'string' ? value.setupFingerprint : undefined,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function normalizeSelectState(value: unknown): WorkspaceLockSelectState {
|
|
136
|
-
const defaults = createEmptyWorkspaceLockState().select;
|
|
137
|
-
if (!isRecord(value)) {
|
|
138
|
-
return defaults;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const status = value.status;
|
|
142
|
-
const normalizedStatus: WorkspaceLockPhaseStatus =
|
|
143
|
-
status === 'success' || status === 'failed' || status === 'never'
|
|
144
|
-
? status
|
|
145
|
-
: 'never';
|
|
146
|
-
|
|
147
|
-
return {
|
|
148
|
-
status: normalizedStatus,
|
|
149
|
-
ranAt: typeof value.ranAt === 'string' ? value.ranAt : undefined,
|
|
150
|
-
error: typeof value.error === 'string' ? value.error : undefined,
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function normalizeBundleState(value: unknown): WorkspaceLockBundleState | undefined {
|
|
155
|
-
if (!isRecord(value)) {
|
|
156
|
-
return undefined;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (typeof value.bundleHash !== 'string') {
|
|
160
|
-
return undefined;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const stepFingerprints = isRecord(value.stepFingerprints)
|
|
164
|
-
? Object.fromEntries(Object.entries(value.stepFingerprints).filter(([, v]) => typeof v === 'string')) as Record<string, string>
|
|
165
|
-
: {};
|
|
166
|
-
|
|
167
|
-
return {
|
|
168
|
-
bundleHash: value.bundleHash,
|
|
169
|
-
stepFingerprints,
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export function readWorkspaceLockState(workspacePath: string): WorkspaceLockState | null {
|
|
174
|
-
const markerPath = getWorkspaceLockPath(workspacePath);
|
|
175
|
-
if (!existsSync(markerPath)) {
|
|
176
|
-
return null;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
try {
|
|
180
|
-
const raw = readFileSync(markerPath, 'utf-8');
|
|
181
|
-
const parsed = JSON.parse(raw) as unknown;
|
|
182
|
-
if (!isRecord(parsed) || parsed.version !== 1) {
|
|
183
|
-
return null;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return {
|
|
187
|
-
version: 1,
|
|
188
|
-
bundle: normalizeBundleState(parsed.bundle),
|
|
189
|
-
setup: normalizeSetupState(parsed.setup),
|
|
190
|
-
select: normalizeSelectState(parsed.select),
|
|
191
|
-
};
|
|
192
|
-
} catch {
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
export function writeWorkspaceLockState(workspacePath: string, state: WorkspaceLockState): void {
|
|
198
|
-
const markerPath = getWorkspaceLockPath(workspacePath);
|
|
199
|
-
|
|
200
|
-
try {
|
|
201
|
-
writeFileSync(markerPath, `${JSON.stringify(state, null, 2)}\n`, 'utf-8');
|
|
202
|
-
} catch (error) {
|
|
203
|
-
throw new SpacesError(
|
|
204
|
-
`Failed to write workspace lock: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
205
|
-
'SYSTEM_ERROR',
|
|
206
|
-
2
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
export function hasSetupBeenRun(workspacePath: string): boolean {
|
|
212
|
-
const state = readWorkspaceLockState(workspacePath);
|
|
213
|
-
return state?.setup.status === 'success';
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
export function markSetupComplete(workspacePath: string): void {
|
|
217
|
-
const state = readWorkspaceLockState(workspacePath) || createEmptyWorkspaceLockState();
|
|
218
|
-
state.setup.status = 'success';
|
|
219
|
-
state.setup.ranAt = new Date().toISOString();
|
|
220
|
-
state.setup.error = undefined;
|
|
221
|
-
writeWorkspaceLockState(workspacePath, state);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
export function clearSetupMarker(workspacePath: string): void {
|
|
225
|
-
const markerPath = getWorkspaceLockPath(workspacePath);
|
|
226
|
-
|
|
227
|
-
try {
|
|
228
|
-
if (existsSync(markerPath)) {
|
|
229
|
-
unlinkSync(markerPath);
|
|
230
|
-
}
|
|
231
|
-
} catch {
|
|
232
|
-
// Ignore errors - this is just for testing
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
function deepSortForHash(value: unknown): unknown {
|
|
237
|
-
if (Array.isArray(value)) {
|
|
238
|
-
return value.map((item) => deepSortForHash(item));
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (value && typeof value === 'object') {
|
|
242
|
-
const record = value as Record<string, unknown>;
|
|
243
|
-
const sorted: Record<string, unknown> = {};
|
|
244
|
-
for (const key of Object.keys(record).sort()) {
|
|
245
|
-
sorted[key] = deepSortForHash(record[key]);
|
|
246
|
-
}
|
|
247
|
-
return sorted;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
return value;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
export function fingerprintValue(value: string): string {
|
|
254
|
-
return createHash('sha256').update(value).digest('hex').slice(0, 16);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
export function getBundleStepKey(step: OnboardingStep): string {
|
|
258
|
-
if (step.type === 'input' || step.type === 'secret') {
|
|
259
|
-
return `${step.type}:${step.configKey}`;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return `${step.type}:${step.id}`;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
function fingerprintInputStep(step: InputStep): string {
|
|
266
|
-
return createHash('sha256')
|
|
267
|
-
.update(JSON.stringify(deepSortForHash({
|
|
268
|
-
type: step.type,
|
|
269
|
-
id: step.id,
|
|
270
|
-
title: step.title,
|
|
271
|
-
description: step.description,
|
|
272
|
-
required: step.required !== false,
|
|
273
|
-
configKey: step.configKey,
|
|
274
|
-
defaultValue: step.defaultValue ?? null,
|
|
275
|
-
validationPattern: step.validationPattern ?? null,
|
|
276
|
-
validationMessage: step.validationMessage ?? null,
|
|
277
|
-
})))
|
|
278
|
-
.digest('hex')
|
|
279
|
-
.slice(0, 16);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
function fingerprintSecretStep(step: SecretStep): string {
|
|
283
|
-
return createHash('sha256')
|
|
284
|
-
.update(JSON.stringify(deepSortForHash({
|
|
285
|
-
type: step.type,
|
|
286
|
-
id: step.id,
|
|
287
|
-
title: step.title,
|
|
288
|
-
description: step.description,
|
|
289
|
-
required: step.required !== false,
|
|
290
|
-
configKey: step.configKey,
|
|
291
|
-
validationPattern: step.validationPattern ?? null,
|
|
292
|
-
validationMessage: step.validationMessage ?? null,
|
|
293
|
-
})))
|
|
294
|
-
.digest('hex')
|
|
295
|
-
.slice(0, 16);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
function fingerprintConfirmStep(step: ConfirmStep): string {
|
|
299
|
-
return createHash('sha256')
|
|
300
|
-
.update(JSON.stringify(deepSortForHash({
|
|
301
|
-
type: step.type,
|
|
302
|
-
id: step.id,
|
|
303
|
-
title: step.title,
|
|
304
|
-
description: step.description,
|
|
305
|
-
required: step.required !== false,
|
|
306
|
-
checkCommand: step.checkCommand ?? null,
|
|
307
|
-
installUrl: step.installUrl ?? null,
|
|
308
|
-
confirmPrompt: step.confirmPrompt ?? null,
|
|
309
|
-
})))
|
|
310
|
-
.digest('hex')
|
|
311
|
-
.slice(0, 16);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
function fingerprintStep(step: OnboardingStep): string {
|
|
315
|
-
if (step.type === 'input') {
|
|
316
|
-
return fingerprintInputStep(step);
|
|
317
|
-
}
|
|
318
|
-
if (step.type === 'secret') {
|
|
319
|
-
return fingerprintSecretStep(step);
|
|
320
|
-
}
|
|
321
|
-
if (step.type === 'confirm') {
|
|
322
|
-
return fingerprintConfirmStep(step);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
return createHash('sha256')
|
|
326
|
-
.update(JSON.stringify(deepSortForHash({
|
|
327
|
-
type: step.type,
|
|
328
|
-
id: step.id,
|
|
329
|
-
title: step.title,
|
|
330
|
-
description: step.description,
|
|
331
|
-
required: step.required !== false,
|
|
332
|
-
})))
|
|
333
|
-
.digest('hex')
|
|
334
|
-
.slice(0, 16);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
export function buildBundleStepFingerprints(bundle: SpacesBundle): Record<string, string> {
|
|
338
|
-
const steps = bundle.onboarding || [];
|
|
339
|
-
const fingerprints: Record<string, string> = {};
|
|
340
|
-
|
|
341
|
-
for (const step of steps) {
|
|
342
|
-
fingerprints[getBundleStepKey(step)] = fingerprintStep(step);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
return fingerprints;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
export function buildSetupState(options: BuildSetupStateOptions): WorkspaceLockSetupState {
|
|
349
|
-
const {
|
|
350
|
-
bundle,
|
|
351
|
-
bundleHash,
|
|
352
|
-
stepFingerprints,
|
|
353
|
-
bundleValues = {},
|
|
354
|
-
bundleSecrets = {},
|
|
355
|
-
confirmResults = {},
|
|
356
|
-
} = options;
|
|
357
|
-
|
|
358
|
-
const steps = bundle?.onboarding || [];
|
|
359
|
-
const usedOptionalSteps: Record<string, true> = {};
|
|
360
|
-
const inputFingerprints: Record<string, string> = {};
|
|
361
|
-
const secretFingerprints: Record<string, string> = {};
|
|
362
|
-
const confirmsUsed: Record<string, WorkspaceLockConfirmState> = {};
|
|
363
|
-
const inputsUsed: Record<string, string> = {};
|
|
364
|
-
|
|
365
|
-
for (const step of steps) {
|
|
366
|
-
const key = getBundleStepKey(step);
|
|
367
|
-
const isOptional = step.required === false;
|
|
368
|
-
|
|
369
|
-
if (step.type === 'input') {
|
|
370
|
-
const value = bundleValues[step.configKey] ?? '';
|
|
371
|
-
if (value.length > 0) {
|
|
372
|
-
inputsUsed[step.configKey] = value;
|
|
373
|
-
}
|
|
374
|
-
inputFingerprints[step.configKey] = fingerprintValue(value);
|
|
375
|
-
if (isOptional && value.length > 0) {
|
|
376
|
-
usedOptionalSteps[key] = true;
|
|
377
|
-
}
|
|
378
|
-
continue;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
if (step.type === 'secret') {
|
|
382
|
-
const value = bundleSecrets[step.configKey] ?? '';
|
|
383
|
-
secretFingerprints[step.configKey] = fingerprintValue(value);
|
|
384
|
-
if (isOptional && value.length > 0) {
|
|
385
|
-
usedOptionalSteps[key] = true;
|
|
386
|
-
}
|
|
387
|
-
continue;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
if (step.type === 'confirm') {
|
|
391
|
-
const result = confirmResults[step.id];
|
|
392
|
-
if (result) {
|
|
393
|
-
confirmsUsed[key] = {
|
|
394
|
-
status: result.status,
|
|
395
|
-
fingerprint: stepFingerprints?.[key] ?? fingerprintStep(step),
|
|
396
|
-
};
|
|
397
|
-
if (isOptional) {
|
|
398
|
-
usedOptionalSteps[key] = true;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
const setupFingerprint = createHash('sha256')
|
|
405
|
-
.update(JSON.stringify(deepSortForHash({
|
|
406
|
-
bundleHash: bundleHash ?? null,
|
|
407
|
-
stepFingerprints: stepFingerprints ?? {},
|
|
408
|
-
inputsUsed,
|
|
409
|
-
inputFingerprints,
|
|
410
|
-
secretFingerprints,
|
|
411
|
-
confirmsUsed,
|
|
412
|
-
usedOptionalSteps,
|
|
413
|
-
})))
|
|
414
|
-
.digest('hex')
|
|
415
|
-
.slice(0, 16);
|
|
416
|
-
|
|
417
|
-
return {
|
|
418
|
-
status: 'success',
|
|
419
|
-
ranAt: new Date().toISOString(),
|
|
420
|
-
inputsUsed,
|
|
421
|
-
inputFingerprints,
|
|
422
|
-
secretFingerprints,
|
|
423
|
-
confirmsUsed,
|
|
424
|
-
usedOptionalSteps,
|
|
425
|
-
setupFingerprint,
|
|
426
|
-
};
|
|
427
|
-
}
|
package/todo-security.md
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
# Security Audit Findings
|
|
2
|
-
|
|
3
|
-
**Date:** 2026-01-07
|
|
4
|
-
**Last Updated:** 2026-01-08 (P1 auth + relay signature fixes)
|
|
5
|
-
**Scope:** Worker API, Relay Server, Crypto Implementation, CLI/Serve Daemon
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Priority Checklist (single source of truth)
|
|
10
|
-
|
|
11
|
-
### P0 - Before Launch
|
|
12
|
-
- [ ] Add backpressure handling - Prevent memory exhaustion (`src/relay/server.ts`)
|
|
13
|
-
- [ ] Add idle connection timeout - Clean up stale relay connections (`src/relay/server.ts`)
|
|
14
|
-
- [ ] Add rate limiting to auth endpoints - Cloudflare WAF rate limiting rule (infra) (`worker/src/handlers/auth.ts`, `worker/src/handlers/subdomains.ts`)
|
|
15
|
-
- [x] Fix path traversal - Validate pathname doesn't escape WEB_DIST_PATH (`src/relay/server.ts`)
|
|
16
|
-
- [x] Fix encryption key derivation - Use HKDF instead of zero-padding (legacy decrypt fallback) (`worker/src/services/cloudflare.ts`)
|
|
17
|
-
- [x] Add connection rate limiting - Per-IP limits on relay connections (`src/relay/server.ts`)
|
|
18
|
-
- [x] Fix command injection - Sanitize checkCommand in onboarding.ts (`src/utils/onboarding.ts`)
|
|
19
|
-
- [x] Session fixation vulnerability
|
|
20
|
-
- [x] X3DH handshake security
|
|
21
|
-
- [x] Client identity proof bypass
|
|
22
|
-
- [x] Relay machine takeover
|
|
23
|
-
|
|
24
|
-
### P1 - Soon After Launch
|
|
25
|
-
- [x] Device fingerprint stored but not validated during token usage (`worker/src/middleware/auth.ts:53`)
|
|
26
|
-
- [x] Validate device_name length/charset (`worker/src/handlers/auth.ts:146`)
|
|
27
|
-
- [x] Session cookie uses `SameSite=Lax` instead of `Strict` (`worker/src/handlers/auth.ts:108`)
|
|
28
|
-
- [x] Signature verification not enforced on client messages (`src/relay/server.ts`)
|
|
29
|
-
- [x] Client can claim any `clientIdentityId` without proof on data messages (`src/relay/server.ts:731`)
|
|
30
|
-
- [x] Fix file permissions - chmod 0o600 on config files after write (`src/core/config.ts`)
|
|
31
|
-
- [x] Fix tunnel token exposure - Use stdin/fd instead of env var (`src/commands/serve.ts`)
|
|
32
|
-
- [x] Fix Zip Slip - Validate extracted paths don't escape target dir + reject symlinks (`src/core/bundle.ts`)
|
|
33
|
-
- [x] OAuth state parameter not validated
|
|
34
|
-
- [x] Account limit race condition
|
|
35
|
-
- [x] Identity file permissions
|
|
36
|
-
- [x] Permission flags not enforced
|
|
37
|
-
- [x] Invite singleUse not enforced
|
|
38
|
-
|
|
39
|
-
### P2 - Follow-up
|
|
40
|
-
- [ ] Restrict CORS in production - Remove localhost origin (`worker/src/index.ts:22-30`)
|
|
41
|
-
- [ ] Challenge timeout of 30s may be too long (`src/relay/server.ts:102`)
|
|
42
|
-
- [ ] Health endpoint exposes metrics without auth (`src/relay/server.ts:165`)
|
|
43
|
-
- [ ] Add audit logging of critical operations
|
|
44
|
-
- [ ] Session enumeration possible without granular permission check (`src/lib/remote-session/session-handler.ts:246`)
|
|
45
|
-
- [ ] Unix socket permissions not explicitly set (`src/serve/daemon.ts:228`)
|
|
46
|
-
- [ ] Relay port/bind parameters not validated (`src/commands/relay.ts:49-51`)
|
|
47
|
-
- [x] Access list file permissions (`src/core/access.ts`)
|
|
48
|
-
|
|
49
|
-
### P3 - Backlog
|
|
50
|
-
- [ ] No signing key rotation mechanism (`src/core/identity.ts`)
|
|
51
|
-
- [ ] Missing X-Content-Type-Options header (`worker/src/index.ts`)
|
|
52
|
-
- [ ] Missing security headers in relay responses (`src/relay/server.ts`)
|
|
53
|
-
- [ ] Documentation says ChaCha20 but uses AES-GCM (`src/lib/tmux-lite/crypto/secretbox.ts` comments)
|
|
54
|
-
- [ ] Verbose error messages may leak paths (`src/commands/serve.ts`)
|
|
55
|
-
|
|
56
|
-
---
|
|
57
|
-
|
|
58
|
-
## Removed / Not Applicable After Verification
|
|
59
|
-
|
|
60
|
-
- Subdomain token endpoint does verify active status (`worker/src/handlers/subdomains.ts:262`)
|
|
61
|
-
- accountId is always set (fallbacks to machineId) (`src/relay/server.ts:436`)
|
|
62
|
-
- JWT clock skew entry references a file not present in this repo (`jwt.ts`)
|
|
63
|
-
- Custom timingSafeEqual entry references a file not present in this repo (`jwt.ts`)
|
|
64
|
-
|
|
65
|
-
---
|
|
66
|
-
|
|
67
|
-
## Crypto Assessment: STRONG
|
|
68
|
-
|
|
69
|
-
The crypto implementation passed security review:
|
|
70
|
-
- AES-256-GCM with proper nonce handling
|
|
71
|
-
- scrypt for password-based key derivation (N=32K, r=8, p=1)
|
|
72
|
-
- X25519 with low-order point validation (all 8 points checked)
|
|
73
|
-
- X3DH handshake with mutual authentication
|
|
74
|
-
- Ed25519 signatures with timestamp replay protection
|
|
75
|
-
- HKDF-SHA256 with domain separation for key derivation
|
|
76
|
-
- File permissions: 0o700 for directories, 0o600 for files
|
|
77
|
-
|
|
78
|
-
Note: This assessment applies to tmux-lite crypto. Worker tunnel token encryption now uses HKDF with a legacy decrypt fallback.
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
## Summary
|
|
83
|
-
|
|
84
|
-
| Severity | Count | Status |
|
|
85
|
-
|----------|-------|--------|
|
|
86
|
-
| **Critical** | 6 | 2 Open / 4 Resolved |
|
|
87
|
-
| **High** | 9 | 1 Open / 8 Resolved |
|
|
88
|
-
| **Medium** | 8 | 7 Open / 1 Resolved |
|
|
89
|
-
| **Low** | 5 | 5 Open |
|
|
90
|
-
| **Fixed** | 22 | Resolved |
|
|
91
|
-
|
|
92
|
-
**Priority Focus:** Idle connection timeout, WAF auth rate limiting, backpressure handling, CORS restriction, audit logging, session enumeration controls.
|