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
package/src/index.ts
DELETED
|
@@ -1,1376 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* GitSpace CLI (gssh) - Main entry point
|
|
5
|
-
* Manages GitHub workspaces with git worktrees and secure remote terminal access
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
// Internal command: run tmux-lite server directly (for compiled binary)
|
|
9
|
-
// This must be checked before any other imports to avoid loading unnecessary modules
|
|
10
|
-
if (process.argv.includes('--internal-tmux-server')) {
|
|
11
|
-
// Pass through --test flag if present
|
|
12
|
-
if (process.argv.includes('--test')) {
|
|
13
|
-
process.env.TMUX_LITE_SOCKET = '/tmp/tmux-lite-test.sock';
|
|
14
|
-
process.env.TMUX_LITE_SESSION_DIR = '/tmp/tmux-lite-test';
|
|
15
|
-
process.env.TMUX_LITE_PID_FILE = '/tmp/tmux-lite-test.pid';
|
|
16
|
-
}
|
|
17
|
-
// Import and run server (module auto-starts on import)
|
|
18
|
-
await import('./lib/tmux-lite/server.js');
|
|
19
|
-
// Keep process alive - server runs via Bun.listen() which is async
|
|
20
|
-
// We need to prevent the rest of this file from executing
|
|
21
|
-
await new Promise(() => {}); // Block forever
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Internal command: run process runner directly
|
|
25
|
-
if (process.argv.includes('--internal-process-runner')) {
|
|
26
|
-
await import('./lib/processes/runner.js');
|
|
27
|
-
await new Promise(() => {}); // Runner will exit the process
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
import { Command } from 'commander'
|
|
31
|
-
import { readFileSync } from 'fs'
|
|
32
|
-
import { join } from 'path'
|
|
33
|
-
import { isFirstTimeSetup, initializeSpaces } from './core/config.js'
|
|
34
|
-
import { VERSION as GENERATED_VERSION } from './version.generated.js'
|
|
35
|
-
|
|
36
|
-
// Read version from package.json in dev, fall back to generated for compiled binary
|
|
37
|
-
let VERSION = GENERATED_VERSION
|
|
38
|
-
try {
|
|
39
|
-
const pkgPath = join(import.meta.dir, '../package.json')
|
|
40
|
-
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))
|
|
41
|
-
VERSION = pkg.version
|
|
42
|
-
} catch {
|
|
43
|
-
// Compiled binary - use generated version
|
|
44
|
-
}
|
|
45
|
-
import { logger } from './utils/logger.js'
|
|
46
|
-
import { SpacesError } from './types/errors.js'
|
|
47
|
-
import { addProject, addWorkspace } from './commands/add.js'
|
|
48
|
-
import { switchProject, switchWorkspace } from './commands/switch.js'
|
|
49
|
-
import { listProjects, listWorkspaces } from './commands/list.js'
|
|
50
|
-
import { removeWorkspace, removeProject } from './commands/remove.js'
|
|
51
|
-
import { ensureDependencies } from './utils/deps.js'
|
|
52
|
-
import { getProjectDirectory } from './commands/directory.js'
|
|
53
|
-
import { launchTUI } from './tui/index.js'
|
|
54
|
-
import { addAccessKey, listAccessKeys, removeAccessKey } from './commands/access.js'
|
|
55
|
-
import { createShare } from './commands/share.js'
|
|
56
|
-
import { initIdentity, showIdentity } from './commands/identity.js'
|
|
57
|
-
import { connectToRemote } from './commands/connect.js'
|
|
58
|
-
import { serve, serveStart, serveStop, serveStatus } from './commands/serve.js'
|
|
59
|
-
import { startRelay, authorizeMachine, revokeMachine, listMachines, listTrustedRelays, untrustRelay } from './commands/relay.js'
|
|
60
|
-
import { authLogin, authLogout, authStatus } from './commands/auth.js'
|
|
61
|
-
import { hostReserve, hostRelease, hostList, hostSetPrimary, hostStatus } from './commands/host.js'
|
|
62
|
-
import { startTmux, stopTmux, statusTmux, listTmux, newTmux, attachTmux, killTmux } from './commands/tmux.js'
|
|
63
|
-
import { showStatus } from './commands/status.js'
|
|
64
|
-
import { configNotifications, linearSetup, linearShow, linearClear } from './commands/config.js'
|
|
65
|
-
import { migrateCleanupLegacy } from './commands/migrate.js'
|
|
66
|
-
import { notificationsInstall, notificationsUninstall, notificationsHook, notificationsStatus } from './commands/notifications.js'
|
|
67
|
-
import { bundleRefresh, bundleStatus } from './commands/bundle.js'
|
|
68
|
-
import {
|
|
69
|
-
openReview,
|
|
70
|
-
showReviewNotes,
|
|
71
|
-
importReview,
|
|
72
|
-
pushReview,
|
|
73
|
-
listReviewHunks,
|
|
74
|
-
addHunkReview,
|
|
75
|
-
addFileReview,
|
|
76
|
-
addLineReview,
|
|
77
|
-
showSpaceContext,
|
|
78
|
-
} from './commands/review.js'
|
|
79
|
-
import { listEvents, showEvent, tailEvents } from './commands/events.js'
|
|
80
|
-
import { listProcesses, startProcess, stopProcess, attachProcess } from './commands/process.js'
|
|
81
|
-
|
|
82
|
-
const program = new Command()
|
|
83
|
-
|
|
84
|
-
// Package info
|
|
85
|
-
program
|
|
86
|
-
.name('gssh')
|
|
87
|
-
.description('GitSpace CLI - Manage GitHub workspaces with secure remote terminal access')
|
|
88
|
-
.version(VERSION)
|
|
89
|
-
|
|
90
|
-
// First-time setup check
|
|
91
|
-
async function checkFirstTimeSetup(): Promise<void> {
|
|
92
|
-
if (isFirstTimeSetup()) {
|
|
93
|
-
logger.bold('Welcome to GitSpace CLI!\n')
|
|
94
|
-
logger.log('Initializing gitspace directory...\n')
|
|
95
|
-
|
|
96
|
-
// Check dependencies
|
|
97
|
-
try {
|
|
98
|
-
await ensureDependencies()
|
|
99
|
-
} catch (error) {
|
|
100
|
-
if (error instanceof SpacesError) {
|
|
101
|
-
logger.error(error.message)
|
|
102
|
-
process.exit(error.exitCode)
|
|
103
|
-
}
|
|
104
|
-
throw error
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Initialize spaces
|
|
108
|
-
initializeSpaces()
|
|
109
|
-
|
|
110
|
-
logger.success('GitSpace initialized!\n')
|
|
111
|
-
logger.log('Get started by adding a project:')
|
|
112
|
-
logger.command(' gssh add project\n')
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// ============================================================================
|
|
117
|
-
// Add Commands
|
|
118
|
-
// ============================================================================
|
|
119
|
-
|
|
120
|
-
const addCommand = program
|
|
121
|
-
.command('add')
|
|
122
|
-
.description('Add a new project or workspace')
|
|
123
|
-
|
|
124
|
-
addCommand
|
|
125
|
-
.command('project')
|
|
126
|
-
.description('Add a new project from GitHub')
|
|
127
|
-
.option('--no-clone', 'Create project structure without cloning')
|
|
128
|
-
.option('--org <org>', 'Filter repos to specific organization')
|
|
129
|
-
.option('--linear-key <key>', 'Provide Linear API key via flag')
|
|
130
|
-
.option('--bundle-url <url>', 'Load bundle from remote URL (zip archive)')
|
|
131
|
-
.option('--bundle-path <path>', 'Load bundle from local directory')
|
|
132
|
-
.option('--skip-bundle', 'Skip bundle detection and onboarding')
|
|
133
|
-
.action(async (options) => {
|
|
134
|
-
await checkFirstTimeSetup()
|
|
135
|
-
try {
|
|
136
|
-
await addProject(options)
|
|
137
|
-
} catch (error) {
|
|
138
|
-
handleError(error)
|
|
139
|
-
}
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
addCommand
|
|
143
|
-
.argument('[workspace-name]', 'Name of the workspace to create')
|
|
144
|
-
.option(
|
|
145
|
-
'--branch <name>',
|
|
146
|
-
'Specify different branch name from workspace name'
|
|
147
|
-
)
|
|
148
|
-
.option('--from <branch>', 'Create from specific branch instead of base')
|
|
149
|
-
.option('--no-shell', "Don't open interactive shell after creating workspace")
|
|
150
|
-
.option('--no-setup', 'Skip setup commands')
|
|
151
|
-
.action(async (workspaceName, options) => {
|
|
152
|
-
await checkFirstTimeSetup()
|
|
153
|
-
try {
|
|
154
|
-
// Map commander option names to CreateWorkspaceOptions property names
|
|
155
|
-
await addWorkspace(workspaceName, {
|
|
156
|
-
branchName: options.branch,
|
|
157
|
-
fromBranch: options.from,
|
|
158
|
-
noShell: options.shell === false,
|
|
159
|
-
noSetup: options.setup === false,
|
|
160
|
-
})
|
|
161
|
-
} catch (error) {
|
|
162
|
-
handleError(error)
|
|
163
|
-
}
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
// ============================================================================
|
|
167
|
-
// Switch Commands
|
|
168
|
-
// ============================================================================
|
|
169
|
-
|
|
170
|
-
const switchCommand = program
|
|
171
|
-
.command('switch')
|
|
172
|
-
.alias('sw')
|
|
173
|
-
.description('Switch to a different project or workspace')
|
|
174
|
-
|
|
175
|
-
switchCommand
|
|
176
|
-
.command('project')
|
|
177
|
-
.description('Switch to a different project')
|
|
178
|
-
.argument('[project-name]', 'Name of the project to switch to')
|
|
179
|
-
.action(async (projectName) => {
|
|
180
|
-
await checkFirstTimeSetup()
|
|
181
|
-
try {
|
|
182
|
-
await switchProject(projectName)
|
|
183
|
-
} catch (error) {
|
|
184
|
-
handleError(error)
|
|
185
|
-
}
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
switchCommand
|
|
189
|
-
.argument('[workspace-name]', 'Name of the workspace to switch to')
|
|
190
|
-
.option('--no-shell', "Don't open interactive shell, just print path")
|
|
191
|
-
.option('-f, --force', 'Jump to first fuzzy match without confirmation')
|
|
192
|
-
.action(async (workspaceName, options) => {
|
|
193
|
-
await checkFirstTimeSetup()
|
|
194
|
-
try {
|
|
195
|
-
await switchWorkspace(workspaceName, options)
|
|
196
|
-
} catch (error) {
|
|
197
|
-
handleError(error)
|
|
198
|
-
}
|
|
199
|
-
})
|
|
200
|
-
|
|
201
|
-
// ============================================================================
|
|
202
|
-
// List Commands
|
|
203
|
-
// ============================================================================
|
|
204
|
-
|
|
205
|
-
const listCommand = program
|
|
206
|
-
.command('list')
|
|
207
|
-
.alias('ls')
|
|
208
|
-
.description('List projects or workspaces')
|
|
209
|
-
|
|
210
|
-
listCommand
|
|
211
|
-
.command('projects')
|
|
212
|
-
.description('List all projects')
|
|
213
|
-
.option('--json', 'Output in JSON format')
|
|
214
|
-
.option('--verbose', 'Show additional details')
|
|
215
|
-
.action(async (options) => {
|
|
216
|
-
await checkFirstTimeSetup()
|
|
217
|
-
try {
|
|
218
|
-
await listProjects(options)
|
|
219
|
-
} catch (error) {
|
|
220
|
-
handleError(error)
|
|
221
|
-
}
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
listCommand
|
|
225
|
-
.command('workspaces')
|
|
226
|
-
.description('List workspaces in current project')
|
|
227
|
-
.option('--json', 'Output in JSON format')
|
|
228
|
-
.option('--verbose', 'Show additional details')
|
|
229
|
-
.action(async (options) => {
|
|
230
|
-
await checkFirstTimeSetup()
|
|
231
|
-
try {
|
|
232
|
-
await listWorkspaces(options)
|
|
233
|
-
} catch (error) {
|
|
234
|
-
handleError(error)
|
|
235
|
-
}
|
|
236
|
-
})
|
|
237
|
-
|
|
238
|
-
// Default list command (alias for list workspaces)
|
|
239
|
-
listCommand.action(async (options) => {
|
|
240
|
-
await checkFirstTimeSetup()
|
|
241
|
-
try {
|
|
242
|
-
await listWorkspaces(options)
|
|
243
|
-
} catch (error) {
|
|
244
|
-
handleError(error)
|
|
245
|
-
}
|
|
246
|
-
})
|
|
247
|
-
|
|
248
|
-
// ============================================================================
|
|
249
|
-
// Remove Commands
|
|
250
|
-
// ============================================================================
|
|
251
|
-
|
|
252
|
-
const removeCommand = program
|
|
253
|
-
.command('remove')
|
|
254
|
-
.alias('rm')
|
|
255
|
-
.description('Remove a workspace or project')
|
|
256
|
-
|
|
257
|
-
removeCommand
|
|
258
|
-
.command('workspace')
|
|
259
|
-
.description('Remove a workspace')
|
|
260
|
-
.argument('[workspace-name]', 'Name of the workspace to remove')
|
|
261
|
-
.option('--force', 'Skip confirmation prompts')
|
|
262
|
-
.option('--keep-branch', "Don't delete git branch when removing workspace")
|
|
263
|
-
.action(async (workspaceName, options) => {
|
|
264
|
-
await checkFirstTimeSetup()
|
|
265
|
-
try {
|
|
266
|
-
await removeWorkspace(workspaceName, options)
|
|
267
|
-
} catch (error) {
|
|
268
|
-
handleError(error)
|
|
269
|
-
}
|
|
270
|
-
})
|
|
271
|
-
|
|
272
|
-
removeCommand
|
|
273
|
-
.command('project')
|
|
274
|
-
.description('Remove a project')
|
|
275
|
-
.argument('[project-name]', 'Name of the project to remove')
|
|
276
|
-
.option('--force', 'Skip confirmation prompts')
|
|
277
|
-
.action(async (projectName, options) => {
|
|
278
|
-
await checkFirstTimeSetup()
|
|
279
|
-
try {
|
|
280
|
-
await removeProject(projectName, options)
|
|
281
|
-
} catch (error) {
|
|
282
|
-
handleError(error)
|
|
283
|
-
}
|
|
284
|
-
})
|
|
285
|
-
|
|
286
|
-
// Default remove command (alias for remove workspace)
|
|
287
|
-
removeCommand.action(async (options) => {
|
|
288
|
-
await checkFirstTimeSetup()
|
|
289
|
-
try {
|
|
290
|
-
await removeWorkspace(undefined, options)
|
|
291
|
-
} catch (error) {
|
|
292
|
-
handleError(error)
|
|
293
|
-
}
|
|
294
|
-
})
|
|
295
|
-
|
|
296
|
-
// ============================================================================
|
|
297
|
-
// Directory Commands
|
|
298
|
-
// ============================================================================
|
|
299
|
-
|
|
300
|
-
const directoryCommand = program
|
|
301
|
-
.command('directory')
|
|
302
|
-
.alias('dir')
|
|
303
|
-
.description('Manage directories')
|
|
304
|
-
|
|
305
|
-
directoryCommand.action(async (options) => {
|
|
306
|
-
await checkFirstTimeSetup()
|
|
307
|
-
try {
|
|
308
|
-
await getProjectDirectory(options)
|
|
309
|
-
} catch (error) {
|
|
310
|
-
handleError(error)
|
|
311
|
-
}
|
|
312
|
-
})
|
|
313
|
-
|
|
314
|
-
// ============================================================================
|
|
315
|
-
// Identity Commands
|
|
316
|
-
// ============================================================================
|
|
317
|
-
|
|
318
|
-
const identityCommand = program
|
|
319
|
-
.command('identity')
|
|
320
|
-
.description('Manage machine identity for secure remote connections')
|
|
321
|
-
|
|
322
|
-
identityCommand
|
|
323
|
-
.command('init')
|
|
324
|
-
.description('Initialize a new identity keypair')
|
|
325
|
-
.option('--force', 'Overwrite existing identity')
|
|
326
|
-
.action(async (options) => {
|
|
327
|
-
await checkFirstTimeSetup()
|
|
328
|
-
try {
|
|
329
|
-
await initIdentity(options)
|
|
330
|
-
} catch (error) {
|
|
331
|
-
handleError(error)
|
|
332
|
-
}
|
|
333
|
-
})
|
|
334
|
-
|
|
335
|
-
identityCommand
|
|
336
|
-
.command('show')
|
|
337
|
-
.description('Show identity information')
|
|
338
|
-
.option('--fingerprint', 'Show only fingerprint')
|
|
339
|
-
.option('--json', 'Output in JSON format')
|
|
340
|
-
.action(async (options) => {
|
|
341
|
-
await checkFirstTimeSetup()
|
|
342
|
-
try {
|
|
343
|
-
await showIdentity(options)
|
|
344
|
-
} catch (error) {
|
|
345
|
-
handleError(error)
|
|
346
|
-
}
|
|
347
|
-
})
|
|
348
|
-
|
|
349
|
-
// ============================================================================
|
|
350
|
-
// Access Commands
|
|
351
|
-
// ============================================================================
|
|
352
|
-
|
|
353
|
-
const accessCommand = program
|
|
354
|
-
.command('access')
|
|
355
|
-
.description('Manage access control for remote connections')
|
|
356
|
-
|
|
357
|
-
accessCommand
|
|
358
|
-
.command('add')
|
|
359
|
-
.description('Add a new access key (grants full access)')
|
|
360
|
-
.argument('<pubkey>', 'Public key (gssh-pub:SIGNING:KEYEXCHANGE or just SIGNING)')
|
|
361
|
-
.option('--label <name>', 'Human-readable label for this key')
|
|
362
|
-
.action(async (pubkey, options) => {
|
|
363
|
-
await checkFirstTimeSetup()
|
|
364
|
-
try {
|
|
365
|
-
await addAccessKey(pubkey, options)
|
|
366
|
-
} catch (error) {
|
|
367
|
-
handleError(error)
|
|
368
|
-
}
|
|
369
|
-
})
|
|
370
|
-
|
|
371
|
-
accessCommand
|
|
372
|
-
.command('list')
|
|
373
|
-
.alias('ls')
|
|
374
|
-
.description('List all access keys')
|
|
375
|
-
.option('--json', 'Output in JSON format')
|
|
376
|
-
.action(async (options) => {
|
|
377
|
-
await checkFirstTimeSetup()
|
|
378
|
-
try {
|
|
379
|
-
await listAccessKeys(options)
|
|
380
|
-
} catch (error) {
|
|
381
|
-
handleError(error)
|
|
382
|
-
}
|
|
383
|
-
})
|
|
384
|
-
|
|
385
|
-
accessCommand
|
|
386
|
-
.command('remove')
|
|
387
|
-
.alias('rm')
|
|
388
|
-
.description('Remove an access key')
|
|
389
|
-
.argument('<pubkey|label>', 'Public key, identity ID prefix, or label')
|
|
390
|
-
.option('--force', 'Skip confirmation prompt')
|
|
391
|
-
.action(async (pubkeyOrLabel, options) => {
|
|
392
|
-
await checkFirstTimeSetup()
|
|
393
|
-
try {
|
|
394
|
-
await removeAccessKey(pubkeyOrLabel, options)
|
|
395
|
-
} catch (error) {
|
|
396
|
-
handleError(error)
|
|
397
|
-
}
|
|
398
|
-
})
|
|
399
|
-
|
|
400
|
-
// ============================================================================
|
|
401
|
-
// Share Commands
|
|
402
|
-
// ============================================================================
|
|
403
|
-
|
|
404
|
-
const shareCommand = program
|
|
405
|
-
.command('share')
|
|
406
|
-
.description('Share workspace access via invite tokens')
|
|
407
|
-
|
|
408
|
-
shareCommand
|
|
409
|
-
.command('create')
|
|
410
|
-
.description('Create a share invite token (view-only session access)')
|
|
411
|
-
.option('--expires <duration>', 'Token validity duration (e.g., 1h, 24h, 7d, 1w)', '24h')
|
|
412
|
-
.option('--session <id>', 'Specific session ID to share (defaults to current session)')
|
|
413
|
-
.option('--relay <url>', 'Relay server URL', 'wss://relay.gitspace.sh')
|
|
414
|
-
.action(async (options) => {
|
|
415
|
-
await checkFirstTimeSetup()
|
|
416
|
-
try {
|
|
417
|
-
await createShare(options)
|
|
418
|
-
} catch (error) {
|
|
419
|
-
handleError(error)
|
|
420
|
-
}
|
|
421
|
-
})
|
|
422
|
-
|
|
423
|
-
// ============================================================================
|
|
424
|
-
// Connect Command
|
|
425
|
-
// ============================================================================
|
|
426
|
-
|
|
427
|
-
program
|
|
428
|
-
.command('connect')
|
|
429
|
-
.description('Connect to a remote machine via invite token')
|
|
430
|
-
.argument('[invite]', 'Invite token or URL (https://gitspace.sh/join#...)')
|
|
431
|
-
.option('--relay <url>', 'Override relay URL from invite token')
|
|
432
|
-
.action(async (invite, options) => {
|
|
433
|
-
await checkFirstTimeSetup()
|
|
434
|
-
try {
|
|
435
|
-
await connectToRemote(invite, options)
|
|
436
|
-
} catch (error) {
|
|
437
|
-
handleError(error)
|
|
438
|
-
}
|
|
439
|
-
})
|
|
440
|
-
|
|
441
|
-
// ============================================================================
|
|
442
|
-
// Serve Command
|
|
443
|
-
// ============================================================================
|
|
444
|
-
|
|
445
|
-
const serveCommand = program
|
|
446
|
-
.command('serve')
|
|
447
|
-
.description('Manage remote access daemon')
|
|
448
|
-
|
|
449
|
-
serveCommand
|
|
450
|
-
.command('start')
|
|
451
|
-
.description('Start the serve daemon')
|
|
452
|
-
.option('--relay <url>', 'Override default relay URL')
|
|
453
|
-
.option('--relay-pubkey <pubkey>', 'Relay public key for explicit trust (base64)')
|
|
454
|
-
.option('--ignore-keychain-and-skip-secrets', 'Skip keychain preload and skip secret-dependent scripts')
|
|
455
|
-
.option('--password-stdin', 'Read password from stdin')
|
|
456
|
-
.option('--foreground', 'Run in foreground (don\'t daemonize)')
|
|
457
|
-
.action(async (options) => {
|
|
458
|
-
await checkFirstTimeSetup()
|
|
459
|
-
try {
|
|
460
|
-
await serveStart(options)
|
|
461
|
-
} catch (error) {
|
|
462
|
-
handleError(error)
|
|
463
|
-
}
|
|
464
|
-
})
|
|
465
|
-
|
|
466
|
-
serveCommand
|
|
467
|
-
.command('stop')
|
|
468
|
-
.description('Stop the serve daemon')
|
|
469
|
-
.action(async () => {
|
|
470
|
-
try {
|
|
471
|
-
await serveStop()
|
|
472
|
-
} catch (error) {
|
|
473
|
-
handleError(error)
|
|
474
|
-
}
|
|
475
|
-
})
|
|
476
|
-
|
|
477
|
-
serveCommand
|
|
478
|
-
.command('status')
|
|
479
|
-
.description('Show serve daemon status')
|
|
480
|
-
.action(async () => {
|
|
481
|
-
try {
|
|
482
|
-
await serveStatus()
|
|
483
|
-
} catch (error) {
|
|
484
|
-
handleError(error)
|
|
485
|
-
}
|
|
486
|
-
})
|
|
487
|
-
|
|
488
|
-
// Default action for 'gssh serve' (backwards compatibility - same as start)
|
|
489
|
-
serveCommand
|
|
490
|
-
.option('--relay <url>', 'Override default relay URL')
|
|
491
|
-
.option('--relay-pubkey <pubkey>', 'Relay public key for explicit trust (base64)')
|
|
492
|
-
.option('--ignore-keychain-and-skip-secrets', 'Skip keychain preload and skip secret-dependent scripts')
|
|
493
|
-
.action(async (options) => {
|
|
494
|
-
await checkFirstTimeSetup()
|
|
495
|
-
try {
|
|
496
|
-
// Default to interactive (non-daemon) mode for backwards compatibility
|
|
497
|
-
await serve(options)
|
|
498
|
-
} catch (error) {
|
|
499
|
-
handleError(error)
|
|
500
|
-
}
|
|
501
|
-
})
|
|
502
|
-
|
|
503
|
-
// ============================================================================
|
|
504
|
-
// Relay Commands
|
|
505
|
-
// ============================================================================
|
|
506
|
-
|
|
507
|
-
const relayCommand = program
|
|
508
|
-
.command('relay')
|
|
509
|
-
.description('Manage relay server')
|
|
510
|
-
|
|
511
|
-
relayCommand
|
|
512
|
-
.command('start')
|
|
513
|
-
.description('Start the relay server')
|
|
514
|
-
.option('--port <port>', 'Port to listen on', '4480')
|
|
515
|
-
.option('--bind <address>', 'Address to bind to', '0.0.0.0')
|
|
516
|
-
.option('--hostname <host>', 'Only serve requests for this domain (optional)')
|
|
517
|
-
.option('--label <label>', 'Human-readable label for this relay')
|
|
518
|
-
.action(async (options) => {
|
|
519
|
-
try {
|
|
520
|
-
await startRelay({
|
|
521
|
-
port: parseInt(options.port, 10),
|
|
522
|
-
bind: options.bind,
|
|
523
|
-
hostname: options.hostname,
|
|
524
|
-
label: options.label,
|
|
525
|
-
})
|
|
526
|
-
} catch (error) {
|
|
527
|
-
handleError(error)
|
|
528
|
-
}
|
|
529
|
-
})
|
|
530
|
-
|
|
531
|
-
relayCommand
|
|
532
|
-
.command('authorize')
|
|
533
|
-
.description('Authorize a machine to connect to this relay')
|
|
534
|
-
.argument('<pubkey>', 'Machine public key in gssh-pub:SIGNING:KEYEXCHANGE format')
|
|
535
|
-
.option('--label <label>', 'Human-readable label for this machine')
|
|
536
|
-
.action(async (pubkey, options) => {
|
|
537
|
-
try {
|
|
538
|
-
await authorizeMachine(pubkey, { label: options.label })
|
|
539
|
-
} catch (error) {
|
|
540
|
-
handleError(error)
|
|
541
|
-
}
|
|
542
|
-
})
|
|
543
|
-
|
|
544
|
-
relayCommand
|
|
545
|
-
.command('revoke')
|
|
546
|
-
.description("Revoke a machine's authorization")
|
|
547
|
-
.argument('<fingerprint-or-label>', 'Fingerprint or label of machine to revoke')
|
|
548
|
-
.action(async (fingerprintOrLabel) => {
|
|
549
|
-
try {
|
|
550
|
-
await revokeMachine(fingerprintOrLabel)
|
|
551
|
-
} catch (error) {
|
|
552
|
-
handleError(error)
|
|
553
|
-
}
|
|
554
|
-
})
|
|
555
|
-
|
|
556
|
-
relayCommand
|
|
557
|
-
.command('machines')
|
|
558
|
-
.description('List authorized machines')
|
|
559
|
-
.action(async () => {
|
|
560
|
-
try {
|
|
561
|
-
await listMachines()
|
|
562
|
-
} catch (error) {
|
|
563
|
-
handleError(error)
|
|
564
|
-
}
|
|
565
|
-
})
|
|
566
|
-
|
|
567
|
-
relayCommand
|
|
568
|
-
.command('trusted')
|
|
569
|
-
.description('List trusted relays (machine-side)')
|
|
570
|
-
.action(async () => {
|
|
571
|
-
try {
|
|
572
|
-
await listTrustedRelays()
|
|
573
|
-
} catch (error) {
|
|
574
|
-
handleError(error)
|
|
575
|
-
}
|
|
576
|
-
})
|
|
577
|
-
|
|
578
|
-
relayCommand
|
|
579
|
-
.command('untrust')
|
|
580
|
-
.description('Remove trust for a relay (machine-side)')
|
|
581
|
-
.argument('<url-or-fingerprint>', 'URL, fingerprint, or label of relay to untrust')
|
|
582
|
-
.action(async (urlOrFingerprint) => {
|
|
583
|
-
try {
|
|
584
|
-
await untrustRelay(urlOrFingerprint)
|
|
585
|
-
} catch (error) {
|
|
586
|
-
handleError(error)
|
|
587
|
-
}
|
|
588
|
-
})
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
// ============================================================================
|
|
592
|
-
// Tmux Commands (tmux-lite daemon management)
|
|
593
|
-
// ============================================================================
|
|
594
|
-
|
|
595
|
-
const tmuxCommand = program
|
|
596
|
-
.command('tmux')
|
|
597
|
-
.description('Manage tmux-lite terminal session daemon')
|
|
598
|
-
|
|
599
|
-
tmuxCommand
|
|
600
|
-
.command('start')
|
|
601
|
-
.description('Start the tmux-lite server daemon')
|
|
602
|
-
.action(async () => {
|
|
603
|
-
try {
|
|
604
|
-
await startTmux()
|
|
605
|
-
} catch (error) {
|
|
606
|
-
handleError(error)
|
|
607
|
-
}
|
|
608
|
-
})
|
|
609
|
-
|
|
610
|
-
tmuxCommand
|
|
611
|
-
.command('stop')
|
|
612
|
-
.description('Stop the tmux-lite server daemon')
|
|
613
|
-
.option('--force', 'Stop even if sessions are active')
|
|
614
|
-
.action(async (options) => {
|
|
615
|
-
try {
|
|
616
|
-
await stopTmux({ force: options.force })
|
|
617
|
-
} catch (error) {
|
|
618
|
-
handleError(error)
|
|
619
|
-
}
|
|
620
|
-
})
|
|
621
|
-
|
|
622
|
-
tmuxCommand
|
|
623
|
-
.command('status')
|
|
624
|
-
.description('Show tmux-lite server status')
|
|
625
|
-
.action(async () => {
|
|
626
|
-
try {
|
|
627
|
-
await statusTmux()
|
|
628
|
-
} catch (error) {
|
|
629
|
-
handleError(error)
|
|
630
|
-
}
|
|
631
|
-
})
|
|
632
|
-
|
|
633
|
-
tmuxCommand
|
|
634
|
-
.command('list')
|
|
635
|
-
.description('List active tmux-lite sessions')
|
|
636
|
-
.action(async () => {
|
|
637
|
-
try {
|
|
638
|
-
await listTmux()
|
|
639
|
-
} catch (error) {
|
|
640
|
-
handleError(error)
|
|
641
|
-
}
|
|
642
|
-
})
|
|
643
|
-
|
|
644
|
-
tmuxCommand
|
|
645
|
-
.command('new [name]')
|
|
646
|
-
.description('Create and attach to a new session')
|
|
647
|
-
.action(async (name) => {
|
|
648
|
-
try {
|
|
649
|
-
await newTmux(name)
|
|
650
|
-
} catch (error) {
|
|
651
|
-
handleError(error)
|
|
652
|
-
}
|
|
653
|
-
})
|
|
654
|
-
|
|
655
|
-
tmuxCommand
|
|
656
|
-
.command('attach <id>')
|
|
657
|
-
.description('Attach to a session (by id or name)')
|
|
658
|
-
.option('--force', 'Take over if attached elsewhere')
|
|
659
|
-
.action(async (id, options) => {
|
|
660
|
-
try {
|
|
661
|
-
await attachTmux(id, { force: options.force })
|
|
662
|
-
} catch (error) {
|
|
663
|
-
handleError(error)
|
|
664
|
-
}
|
|
665
|
-
})
|
|
666
|
-
|
|
667
|
-
tmuxCommand
|
|
668
|
-
.command('kill <id>')
|
|
669
|
-
.description('Kill a session (by id or name)')
|
|
670
|
-
.action(async (id) => {
|
|
671
|
-
try {
|
|
672
|
-
await killTmux(id)
|
|
673
|
-
} catch (error) {
|
|
674
|
-
handleError(error)
|
|
675
|
-
}
|
|
676
|
-
})
|
|
677
|
-
|
|
678
|
-
// ============================================================================
|
|
679
|
-
// Config Commands
|
|
680
|
-
// ============================================================================
|
|
681
|
-
|
|
682
|
-
const configCommand = program
|
|
683
|
-
.command('config')
|
|
684
|
-
.description('Configure gitspace settings')
|
|
685
|
-
|
|
686
|
-
// gssh config notifications
|
|
687
|
-
configCommand
|
|
688
|
-
.command('notifications')
|
|
689
|
-
.description('Configure notification settings')
|
|
690
|
-
.option('--show', 'Show current settings')
|
|
691
|
-
.option('--reset', 'Reset to defaults')
|
|
692
|
-
.action(async (options) => {
|
|
693
|
-
await checkFirstTimeSetup()
|
|
694
|
-
try {
|
|
695
|
-
await configNotifications(options)
|
|
696
|
-
} catch (error) {
|
|
697
|
-
handleError(error)
|
|
698
|
-
}
|
|
699
|
-
})
|
|
700
|
-
|
|
701
|
-
// gssh config linear
|
|
702
|
-
const configLinearCommand = configCommand
|
|
703
|
-
.command('linear')
|
|
704
|
-
.description('Configure Linear integration')
|
|
705
|
-
|
|
706
|
-
configLinearCommand
|
|
707
|
-
.command('setup')
|
|
708
|
-
.description('Configure Linear integration')
|
|
709
|
-
.option('--project <name>', 'Configure for specific project (uses user API key)')
|
|
710
|
-
.action(async (options) => {
|
|
711
|
-
await checkFirstTimeSetup()
|
|
712
|
-
try {
|
|
713
|
-
await linearSetup(options)
|
|
714
|
-
} catch (error) {
|
|
715
|
-
handleError(error)
|
|
716
|
-
}
|
|
717
|
-
})
|
|
718
|
-
|
|
719
|
-
configLinearCommand
|
|
720
|
-
.command('show')
|
|
721
|
-
.description('Show Linear configuration')
|
|
722
|
-
.option('--project <name>', 'Show project-specific configuration')
|
|
723
|
-
.action(async (options) => {
|
|
724
|
-
await checkFirstTimeSetup()
|
|
725
|
-
try {
|
|
726
|
-
await linearShow(options)
|
|
727
|
-
} catch (error) {
|
|
728
|
-
handleError(error)
|
|
729
|
-
}
|
|
730
|
-
})
|
|
731
|
-
|
|
732
|
-
configLinearCommand
|
|
733
|
-
.command('clear')
|
|
734
|
-
.description('Clear Linear configuration')
|
|
735
|
-
.option('--global', 'Clear user-level configuration')
|
|
736
|
-
.option('--project <name>', 'Clear project-specific configuration')
|
|
737
|
-
.action(async (options) => {
|
|
738
|
-
await checkFirstTimeSetup()
|
|
739
|
-
try {
|
|
740
|
-
await linearClear(options)
|
|
741
|
-
} catch (error) {
|
|
742
|
-
handleError(error)
|
|
743
|
-
}
|
|
744
|
-
})
|
|
745
|
-
|
|
746
|
-
// ============================================================================
|
|
747
|
-
// Events Commands
|
|
748
|
-
// ============================================================================
|
|
749
|
-
|
|
750
|
-
const eventsCommand = program
|
|
751
|
-
.command('events')
|
|
752
|
-
.description('Query wide event logs')
|
|
753
|
-
|
|
754
|
-
const eventsFilterOption = '--filter <expr>'
|
|
755
|
-
|
|
756
|
-
eventsCommand
|
|
757
|
-
.command('list')
|
|
758
|
-
.description('List events (NDJSON)')
|
|
759
|
-
.requiredOption('--project <name>', 'Project name')
|
|
760
|
-
.requiredOption('--workspace <name>', 'Workspace name')
|
|
761
|
-
.option(eventsFilterOption, 'Filter in key=value format')
|
|
762
|
-
.option('--limit <n>', 'Limit results', (value: string) => Number(value), 100)
|
|
763
|
-
.action(async (options: Record<string, unknown>) => {
|
|
764
|
-
await checkFirstTimeSetup()
|
|
765
|
-
try {
|
|
766
|
-
await listEvents(options)
|
|
767
|
-
} catch (error) {
|
|
768
|
-
handleError(error)
|
|
769
|
-
}
|
|
770
|
-
})
|
|
771
|
-
|
|
772
|
-
eventsCommand
|
|
773
|
-
.command('show')
|
|
774
|
-
.description('Show a single event by eventId')
|
|
775
|
-
.requiredOption('--project <name>', 'Project name')
|
|
776
|
-
.requiredOption('--workspace <name>', 'Workspace name')
|
|
777
|
-
.option(eventsFilterOption, 'Filter in key=value format')
|
|
778
|
-
.action(async (options: Record<string, unknown>) => {
|
|
779
|
-
await checkFirstTimeSetup()
|
|
780
|
-
try {
|
|
781
|
-
await showEvent(options)
|
|
782
|
-
} catch (error) {
|
|
783
|
-
handleError(error)
|
|
784
|
-
}
|
|
785
|
-
})
|
|
786
|
-
|
|
787
|
-
eventsCommand
|
|
788
|
-
.command('tail')
|
|
789
|
-
.description('Tail recent events (no follow yet)')
|
|
790
|
-
.requiredOption('--project <name>', 'Project name')
|
|
791
|
-
.requiredOption('--workspace <name>', 'Workspace name')
|
|
792
|
-
.option(eventsFilterOption, 'Filter in key=value format')
|
|
793
|
-
.option('--limit <n>', 'Limit results', (value: string) => Number(value), 50)
|
|
794
|
-
.action(async (options: Record<string, unknown>) => {
|
|
795
|
-
await checkFirstTimeSetup()
|
|
796
|
-
try {
|
|
797
|
-
await tailEvents(options)
|
|
798
|
-
} catch (error) {
|
|
799
|
-
handleError(error)
|
|
800
|
-
}
|
|
801
|
-
})
|
|
802
|
-
|
|
803
|
-
// ============================================================================
|
|
804
|
-
// Process Commands
|
|
805
|
-
// ============================================================================
|
|
806
|
-
|
|
807
|
-
const processCommand = program
|
|
808
|
-
.command('process')
|
|
809
|
-
.description('Manage workspace processes')
|
|
810
|
-
|
|
811
|
-
processCommand
|
|
812
|
-
.command('list')
|
|
813
|
-
.description('List configured processes')
|
|
814
|
-
.option('--workspace <path>', 'Workspace path (defaults to cwd)')
|
|
815
|
-
.action(async (options: Record<string, unknown>) => {
|
|
816
|
-
await checkFirstTimeSetup()
|
|
817
|
-
try {
|
|
818
|
-
await listProcesses(options)
|
|
819
|
-
} catch (error) {
|
|
820
|
-
handleError(error)
|
|
821
|
-
}
|
|
822
|
-
})
|
|
823
|
-
|
|
824
|
-
processCommand
|
|
825
|
-
.command('start')
|
|
826
|
-
.description('Start a process by name')
|
|
827
|
-
.option('--workspace <path>', 'Workspace path (defaults to cwd)')
|
|
828
|
-
.option('--name <name>', 'Process name')
|
|
829
|
-
.action(async (options: Record<string, unknown>) => {
|
|
830
|
-
await checkFirstTimeSetup()
|
|
831
|
-
try {
|
|
832
|
-
await startProcess(options)
|
|
833
|
-
} catch (error) {
|
|
834
|
-
handleError(error)
|
|
835
|
-
}
|
|
836
|
-
})
|
|
837
|
-
|
|
838
|
-
processCommand
|
|
839
|
-
.command('stop')
|
|
840
|
-
.description('Stop a process by name')
|
|
841
|
-
.option('--workspace <path>', 'Workspace path (defaults to cwd)')
|
|
842
|
-
.option('--name <name>', 'Process name')
|
|
843
|
-
.action(async (options: Record<string, unknown>) => {
|
|
844
|
-
await checkFirstTimeSetup()
|
|
845
|
-
try {
|
|
846
|
-
await stopProcess(options)
|
|
847
|
-
} catch (error) {
|
|
848
|
-
handleError(error)
|
|
849
|
-
}
|
|
850
|
-
})
|
|
851
|
-
|
|
852
|
-
processCommand
|
|
853
|
-
.command('attach')
|
|
854
|
-
.description('Show attach hint for process')
|
|
855
|
-
.option('--workspace <path>', 'Workspace path (defaults to cwd)')
|
|
856
|
-
.option('--name <name>', 'Process name')
|
|
857
|
-
.action(async (options: Record<string, unknown>) => {
|
|
858
|
-
await checkFirstTimeSetup()
|
|
859
|
-
try {
|
|
860
|
-
await attachProcess(options)
|
|
861
|
-
} catch (error) {
|
|
862
|
-
handleError(error)
|
|
863
|
-
}
|
|
864
|
-
})
|
|
865
|
-
|
|
866
|
-
// ============================================================================
|
|
867
|
-
// Migration Commands
|
|
868
|
-
// ============================================================================
|
|
869
|
-
|
|
870
|
-
const migrateCommand = program
|
|
871
|
-
.command('migrate')
|
|
872
|
-
.description('Migration and cleanup utilities')
|
|
873
|
-
|
|
874
|
-
migrateCommand
|
|
875
|
-
.command('cleanup-legacy')
|
|
876
|
-
.description('Delete legacy keychain entries kept for backwards compatibility')
|
|
877
|
-
.option('-y, --yes', 'Skip confirmation prompt')
|
|
878
|
-
.action(async (options) => {
|
|
879
|
-
await checkFirstTimeSetup()
|
|
880
|
-
try {
|
|
881
|
-
await migrateCleanupLegacy(options)
|
|
882
|
-
} catch (error) {
|
|
883
|
-
handleError(error)
|
|
884
|
-
}
|
|
885
|
-
})
|
|
886
|
-
|
|
887
|
-
// ============================================================================
|
|
888
|
-
// Notifications Commands
|
|
889
|
-
// ============================================================================
|
|
890
|
-
|
|
891
|
-
const notificationsCommand = program
|
|
892
|
-
.command('notifications')
|
|
893
|
-
.alias('notify')
|
|
894
|
-
.description('Manage notification settings and shell hooks')
|
|
895
|
-
|
|
896
|
-
notificationsCommand
|
|
897
|
-
.command('install')
|
|
898
|
-
.description('Install shell hooks for notification integration')
|
|
899
|
-
.action(async () => {
|
|
900
|
-
try {
|
|
901
|
-
await notificationsInstall()
|
|
902
|
-
} catch (error) {
|
|
903
|
-
handleError(error)
|
|
904
|
-
}
|
|
905
|
-
})
|
|
906
|
-
|
|
907
|
-
notificationsCommand
|
|
908
|
-
.command('uninstall')
|
|
909
|
-
.description('Remove shell hooks from shell config files')
|
|
910
|
-
.action(async () => {
|
|
911
|
-
try {
|
|
912
|
-
await notificationsUninstall()
|
|
913
|
-
} catch (error) {
|
|
914
|
-
handleError(error)
|
|
915
|
-
}
|
|
916
|
-
})
|
|
917
|
-
|
|
918
|
-
notificationsCommand
|
|
919
|
-
.command('hook')
|
|
920
|
-
.description('Print shell hook snippet for manual installation')
|
|
921
|
-
.option('--shell <shell>', 'Shell type (bash, zsh, fish)')
|
|
922
|
-
.action(async (options) => {
|
|
923
|
-
try {
|
|
924
|
-
await notificationsHook(options.shell)
|
|
925
|
-
} catch (error) {
|
|
926
|
-
handleError(error)
|
|
927
|
-
}
|
|
928
|
-
})
|
|
929
|
-
|
|
930
|
-
notificationsCommand
|
|
931
|
-
.command('status')
|
|
932
|
-
.description('Show notification settings and hook installation status')
|
|
933
|
-
.action(async () => {
|
|
934
|
-
try {
|
|
935
|
-
await notificationsStatus()
|
|
936
|
-
} catch (error) {
|
|
937
|
-
handleError(error)
|
|
938
|
-
}
|
|
939
|
-
})
|
|
940
|
-
|
|
941
|
-
// ============================================================================
|
|
942
|
-
// Bundle Commands
|
|
943
|
-
// ============================================================================
|
|
944
|
-
|
|
945
|
-
const bundleCommand = program
|
|
946
|
-
.command('bundle')
|
|
947
|
-
.description('Manage bundle configuration')
|
|
948
|
-
|
|
949
|
-
bundleCommand
|
|
950
|
-
.command('refresh')
|
|
951
|
-
.description('Re-run bundle onboarding (keeps previous values as defaults)')
|
|
952
|
-
.option('--force', 'Force refresh even if no changes detected')
|
|
953
|
-
.option('--project <name>', 'Specify project name')
|
|
954
|
-
.action(async (options) => {
|
|
955
|
-
await checkFirstTimeSetup()
|
|
956
|
-
try {
|
|
957
|
-
await bundleRefresh(options)
|
|
958
|
-
} catch (error) {
|
|
959
|
-
handleError(error)
|
|
960
|
-
}
|
|
961
|
-
})
|
|
962
|
-
|
|
963
|
-
bundleCommand
|
|
964
|
-
.command('status')
|
|
965
|
-
.description('Show bundle status for current project')
|
|
966
|
-
.option('--project <name>', 'Specify project name')
|
|
967
|
-
.action(async (options) => {
|
|
968
|
-
await checkFirstTimeSetup()
|
|
969
|
-
try {
|
|
970
|
-
await bundleStatus(options)
|
|
971
|
-
} catch (error) {
|
|
972
|
-
handleError(error)
|
|
973
|
-
}
|
|
974
|
-
})
|
|
975
|
-
|
|
976
|
-
// ============================================================================
|
|
977
|
-
// Review Commands
|
|
978
|
-
// ============================================================================
|
|
979
|
-
|
|
980
|
-
function withReviewSetup<T extends unknown[]>(
|
|
981
|
-
handler: (...args: T) => Promise<void>
|
|
982
|
-
): (...args: T) => Promise<void> {
|
|
983
|
-
return async (...args: T): Promise<void> => {
|
|
984
|
-
await checkFirstTimeSetup()
|
|
985
|
-
try {
|
|
986
|
-
await handler(...args)
|
|
987
|
-
} catch (error) {
|
|
988
|
-
handleError(error)
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
function addReviewScopeOptions(command: Command): Command {
|
|
994
|
-
return command
|
|
995
|
-
.option('--workspace <name>', 'Workspace name')
|
|
996
|
-
.option('--project <name>', 'Project name')
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
function registerReviewSubcommands(command: Command): void {
|
|
1000
|
-
addReviewScopeOptions(
|
|
1001
|
-
command
|
|
1002
|
-
.command('notes')
|
|
1003
|
-
.description('Print review threads as structured JSON (LLM-friendly)')
|
|
1004
|
-
)
|
|
1005
|
-
.option('--format <format>', 'Output format: json (default) or text')
|
|
1006
|
-
.action(withReviewSetup(async (options) => {
|
|
1007
|
-
await showReviewNotes(options)
|
|
1008
|
-
}))
|
|
1009
|
-
|
|
1010
|
-
addReviewScopeOptions(
|
|
1011
|
-
command
|
|
1012
|
-
.command('import')
|
|
1013
|
-
.description('Import GitHub PR review comments as local threads')
|
|
1014
|
-
)
|
|
1015
|
-
.option('--pr <number>', 'PR number to import from', (v) => parseInt(v, 10))
|
|
1016
|
-
.action(withReviewSetup(async (options) => {
|
|
1017
|
-
await importReview(options)
|
|
1018
|
-
}))
|
|
1019
|
-
|
|
1020
|
-
addReviewScopeOptions(
|
|
1021
|
-
command
|
|
1022
|
-
.command('push')
|
|
1023
|
-
.description('Push local review decisions to GitHub as a formal PR review')
|
|
1024
|
-
)
|
|
1025
|
-
.option('--pr <number>', 'PR number to submit review on', (v) => parseInt(v, 10))
|
|
1026
|
-
.action(withReviewSetup(async (options) => {
|
|
1027
|
-
await pushReview(options)
|
|
1028
|
-
}))
|
|
1029
|
-
|
|
1030
|
-
addReviewScopeOptions(
|
|
1031
|
-
command
|
|
1032
|
-
.command('hunks <file>')
|
|
1033
|
-
.description('List hunks in a changed file (AI-friendly target IDs)')
|
|
1034
|
-
)
|
|
1035
|
-
.option('--format <format>', 'Output format: json (default) or text')
|
|
1036
|
-
.action(withReviewSetup(async (file, options) => {
|
|
1037
|
-
await listReviewHunks(file, options)
|
|
1038
|
-
}))
|
|
1039
|
-
|
|
1040
|
-
addReviewScopeOptions(
|
|
1041
|
-
command
|
|
1042
|
-
.command('add-hunk <file>')
|
|
1043
|
-
.description('Add or update hunk review by hunk index')
|
|
1044
|
-
)
|
|
1045
|
-
.requiredOption('--index <number>', '1-based hunk index', (v) => parseInt(v, 10))
|
|
1046
|
-
.option('--body <text>', 'Optional comment body')
|
|
1047
|
-
.option('--approve', 'Set hunk decision to approved')
|
|
1048
|
-
.option('--reject', 'Set hunk decision to rejected')
|
|
1049
|
-
.option('--pending', 'Set hunk decision to pending')
|
|
1050
|
-
.option('--json', 'Output structured JSON')
|
|
1051
|
-
.action(withReviewSetup(async (file, options) => {
|
|
1052
|
-
await addHunkReview(file, options)
|
|
1053
|
-
}))
|
|
1054
|
-
|
|
1055
|
-
addReviewScopeOptions(
|
|
1056
|
-
command
|
|
1057
|
-
.command('add-file <file>')
|
|
1058
|
-
.description('Add a file-level review thread')
|
|
1059
|
-
)
|
|
1060
|
-
.requiredOption('--body <text>', 'Comment body')
|
|
1061
|
-
.option('--json', 'Output structured JSON')
|
|
1062
|
-
.action(withReviewSetup(async (file, options) => {
|
|
1063
|
-
await addFileReview(file, options)
|
|
1064
|
-
}))
|
|
1065
|
-
|
|
1066
|
-
addReviewScopeOptions(
|
|
1067
|
-
command
|
|
1068
|
-
.command('add-line <file>')
|
|
1069
|
-
.description('Add a line-range review thread')
|
|
1070
|
-
)
|
|
1071
|
-
.requiredOption('--start <number>', '1-based start line', (v) => parseInt(v, 10))
|
|
1072
|
-
.option('--end <number>', '1-based end line (defaults to start)', (v) => parseInt(v, 10))
|
|
1073
|
-
.option('--side <side>', 'LEFT or RIGHT side of diff (default: RIGHT)')
|
|
1074
|
-
.requiredOption('--body <text>', 'Comment body')
|
|
1075
|
-
.option('--json', 'Output structured JSON')
|
|
1076
|
-
.action(withReviewSetup(async (file, options) => {
|
|
1077
|
-
await addLineReview(file, options)
|
|
1078
|
-
}))
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
const reviewCommand = program
|
|
1082
|
-
.command('review')
|
|
1083
|
-
.description('Open or interact with the diff review system')
|
|
1084
|
-
.option('--workspace <name>', 'Workspace name')
|
|
1085
|
-
.option('--project <name>', 'Project name')
|
|
1086
|
-
.option('--port <number>', 'Port of the serve daemon (default: 4480)', (v) => parseInt(v, 10))
|
|
1087
|
-
.action(async (options) => {
|
|
1088
|
-
await checkFirstTimeSetup()
|
|
1089
|
-
try {
|
|
1090
|
-
await openReview(options)
|
|
1091
|
-
} catch (error) {
|
|
1092
|
-
handleError(error)
|
|
1093
|
-
}
|
|
1094
|
-
})
|
|
1095
|
-
|
|
1096
|
-
registerReviewSubcommands(reviewCommand)
|
|
1097
|
-
|
|
1098
|
-
// Hidden workspace-scoped command surface.
|
|
1099
|
-
// Intended to be used from `space` shell function injected into workspace sessions.
|
|
1100
|
-
// Scope flags intentionally live on leaf subcommands so Commander passes them
|
|
1101
|
-
// to each handler's `options` object.
|
|
1102
|
-
const spaceCommand = program
|
|
1103
|
-
.command('space', { hidden: true })
|
|
1104
|
-
.description('Workspace-scoped commands')
|
|
1105
|
-
|
|
1106
|
-
spaceCommand
|
|
1107
|
-
.command('context')
|
|
1108
|
-
.description('Show resolved workspace context')
|
|
1109
|
-
.option('--workspace <name>', 'Workspace name')
|
|
1110
|
-
.option('--project <name>', 'Project name')
|
|
1111
|
-
.option('--json', 'Output structured JSON')
|
|
1112
|
-
.action(async (options) => {
|
|
1113
|
-
await checkFirstTimeSetup()
|
|
1114
|
-
try {
|
|
1115
|
-
await showSpaceContext(options)
|
|
1116
|
-
} catch (error) {
|
|
1117
|
-
handleError(error)
|
|
1118
|
-
}
|
|
1119
|
-
})
|
|
1120
|
-
|
|
1121
|
-
const spaceReviewCommand = spaceCommand
|
|
1122
|
-
.command('review')
|
|
1123
|
-
.description('Workspace review commands')
|
|
1124
|
-
|
|
1125
|
-
registerReviewSubcommands(spaceReviewCommand)
|
|
1126
|
-
|
|
1127
|
-
// ============================================================================
|
|
1128
|
-
// Auth Commands (gitspace.sh)
|
|
1129
|
-
// ============================================================================
|
|
1130
|
-
|
|
1131
|
-
const authCommand = program
|
|
1132
|
-
.command('auth')
|
|
1133
|
-
.description('Manage gitspace.sh authentication')
|
|
1134
|
-
|
|
1135
|
-
authCommand
|
|
1136
|
-
.command('login')
|
|
1137
|
-
.description('Login with GitHub')
|
|
1138
|
-
.action(async () => {
|
|
1139
|
-
await checkFirstTimeSetup()
|
|
1140
|
-
try {
|
|
1141
|
-
await authLogin()
|
|
1142
|
-
} catch (error) {
|
|
1143
|
-
handleError(error)
|
|
1144
|
-
}
|
|
1145
|
-
})
|
|
1146
|
-
|
|
1147
|
-
authCommand
|
|
1148
|
-
.command('logout')
|
|
1149
|
-
.description('Logout and clear credentials')
|
|
1150
|
-
.action(async () => {
|
|
1151
|
-
try {
|
|
1152
|
-
await authLogout()
|
|
1153
|
-
} catch (error) {
|
|
1154
|
-
handleError(error)
|
|
1155
|
-
}
|
|
1156
|
-
})
|
|
1157
|
-
|
|
1158
|
-
authCommand
|
|
1159
|
-
.command('status')
|
|
1160
|
-
.description('Show login status')
|
|
1161
|
-
.action(async () => {
|
|
1162
|
-
try {
|
|
1163
|
-
await authStatus()
|
|
1164
|
-
} catch (error) {
|
|
1165
|
-
handleError(error)
|
|
1166
|
-
}
|
|
1167
|
-
})
|
|
1168
|
-
|
|
1169
|
-
// ============================================================================
|
|
1170
|
-
// Host Commands (gitspace.sh hosting)
|
|
1171
|
-
// ============================================================================
|
|
1172
|
-
|
|
1173
|
-
const hostCommand = program
|
|
1174
|
-
.command('host')
|
|
1175
|
-
.description('Manage gitspace.sh hosting')
|
|
1176
|
-
|
|
1177
|
-
hostCommand
|
|
1178
|
-
.command('reserve <subdomain>')
|
|
1179
|
-
.description('Reserve a subdomain (e.g., brad.gitspace.sh)')
|
|
1180
|
-
.action(async (subdomain) => {
|
|
1181
|
-
await checkFirstTimeSetup()
|
|
1182
|
-
try {
|
|
1183
|
-
await hostReserve(subdomain)
|
|
1184
|
-
} catch (error) {
|
|
1185
|
-
handleError(error)
|
|
1186
|
-
}
|
|
1187
|
-
})
|
|
1188
|
-
|
|
1189
|
-
hostCommand
|
|
1190
|
-
.command('release [subdomain]')
|
|
1191
|
-
.description('Release a subdomain')
|
|
1192
|
-
.action(async (subdomain) => {
|
|
1193
|
-
try {
|
|
1194
|
-
await hostRelease(subdomain)
|
|
1195
|
-
} catch (error) {
|
|
1196
|
-
handleError(error)
|
|
1197
|
-
}
|
|
1198
|
-
})
|
|
1199
|
-
|
|
1200
|
-
hostCommand
|
|
1201
|
-
.command('list')
|
|
1202
|
-
.alias('ls')
|
|
1203
|
-
.description('List your subdomains')
|
|
1204
|
-
.action(async () => {
|
|
1205
|
-
try {
|
|
1206
|
-
await hostList()
|
|
1207
|
-
} catch (error) {
|
|
1208
|
-
handleError(error)
|
|
1209
|
-
}
|
|
1210
|
-
})
|
|
1211
|
-
|
|
1212
|
-
hostCommand
|
|
1213
|
-
.command('set-primary <subdomain>')
|
|
1214
|
-
.description('Set primary subdomain for `gssh serve`')
|
|
1215
|
-
.action(async (subdomain) => {
|
|
1216
|
-
try {
|
|
1217
|
-
await hostSetPrimary(subdomain)
|
|
1218
|
-
} catch (error) {
|
|
1219
|
-
handleError(error)
|
|
1220
|
-
}
|
|
1221
|
-
})
|
|
1222
|
-
|
|
1223
|
-
hostCommand
|
|
1224
|
-
.command('status')
|
|
1225
|
-
.description('Show hosting status')
|
|
1226
|
-
.action(async () => {
|
|
1227
|
-
try {
|
|
1228
|
-
await hostStatus()
|
|
1229
|
-
} catch (error) {
|
|
1230
|
-
handleError(error)
|
|
1231
|
-
}
|
|
1232
|
-
})
|
|
1233
|
-
|
|
1234
|
-
// ============================================================================
|
|
1235
|
-
// Status Command (unified daemon status)
|
|
1236
|
-
// ============================================================================
|
|
1237
|
-
|
|
1238
|
-
program
|
|
1239
|
-
.command('status')
|
|
1240
|
-
.description('Show status of all spaces daemons')
|
|
1241
|
-
.action(async () => {
|
|
1242
|
-
try {
|
|
1243
|
-
await showStatus()
|
|
1244
|
-
} catch (error) {
|
|
1245
|
-
handleError(error)
|
|
1246
|
-
}
|
|
1247
|
-
})
|
|
1248
|
-
|
|
1249
|
-
// ============================================================================
|
|
1250
|
-
// Error Handling
|
|
1251
|
-
// ============================================================================
|
|
1252
|
-
|
|
1253
|
-
function handleError(error: unknown): never {
|
|
1254
|
-
if (error instanceof SpacesError) {
|
|
1255
|
-
logger.error(error.message)
|
|
1256
|
-
process.exit(error.exitCode)
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
|
-
if (error instanceof Error) {
|
|
1260
|
-
logger.error(`Unexpected error: ${error.message}`)
|
|
1261
|
-
logger.debug(error.stack || '')
|
|
1262
|
-
process.exit(1)
|
|
1263
|
-
}
|
|
1264
|
-
|
|
1265
|
-
logger.error('An unexpected error occurred')
|
|
1266
|
-
process.exit(1)
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
|
-
// ============================================================================
|
|
1270
|
-
// Parse and Execute
|
|
1271
|
-
// ============================================================================
|
|
1272
|
-
|
|
1273
|
-
// Handle uncaught errors
|
|
1274
|
-
process.on('uncaughtException', (error) => {
|
|
1275
|
-
logger.error(`Uncaught exception: ${error.message}`)
|
|
1276
|
-
logger.debug(error.stack || '')
|
|
1277
|
-
process.exit(1)
|
|
1278
|
-
})
|
|
1279
|
-
|
|
1280
|
-
process.on('unhandledRejection', (reason) => {
|
|
1281
|
-
logger.error(`Unhandled rejection: ${reason}`)
|
|
1282
|
-
process.exit(1)
|
|
1283
|
-
})
|
|
1284
|
-
|
|
1285
|
-
function isWorkspaceScopedSession(): boolean {
|
|
1286
|
-
return process.env.GSSH_SESSION_MODE === 'workspace'
|
|
1287
|
-
}
|
|
1288
|
-
|
|
1289
|
-
function isAllowedWorkspaceSessionCommand(args: string[]): boolean {
|
|
1290
|
-
if (args.length === 0) {
|
|
1291
|
-
return false
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
const first = args[0]
|
|
1295
|
-
if (!first) {
|
|
1296
|
-
return false
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
|
-
if (first === 'space') {
|
|
1300
|
-
return true
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
if (first === '--help' || first === '-h' || first === '--version' || first === '-V') {
|
|
1304
|
-
return true
|
|
1305
|
-
}
|
|
1306
|
-
|
|
1307
|
-
if (first === 'help') {
|
|
1308
|
-
return true
|
|
1309
|
-
}
|
|
1310
|
-
|
|
1311
|
-
return false
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
// Parse command line arguments
|
|
1315
|
-
// Check for global relay options (TUI mode with relay)
|
|
1316
|
-
const args = process.argv.slice(2)
|
|
1317
|
-
|
|
1318
|
-
if (isWorkspaceScopedSession() && !isAllowedWorkspaceSessionCommand(args)) {
|
|
1319
|
-
if (args.length === 0) {
|
|
1320
|
-
logger.error('Bare `gssh` is disabled inside a workspace session.')
|
|
1321
|
-
} else if (args[0] === 'tmux') {
|
|
1322
|
-
logger.error('tmux commands are disabled inside workspace sessions.')
|
|
1323
|
-
} else {
|
|
1324
|
-
logger.error('This command is disabled inside a workspace session.')
|
|
1325
|
-
}
|
|
1326
|
-
logger.log('Use `space ...` for workspace-scoped operations.')
|
|
1327
|
-
process.exit(1)
|
|
1328
|
-
}
|
|
1329
|
-
|
|
1330
|
-
let relayUrlFromArgs: string | undefined
|
|
1331
|
-
let ignoreKeychainAndSkipSecrets = false
|
|
1332
|
-
let hasOnlyTuiOptions = true
|
|
1333
|
-
|
|
1334
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
1335
|
-
const arg = args[i]
|
|
1336
|
-
if (arg === '--relay') {
|
|
1337
|
-
const value = args[i + 1]
|
|
1338
|
-
if (!value || value.startsWith('--')) {
|
|
1339
|
-
hasOnlyTuiOptions = false
|
|
1340
|
-
break
|
|
1341
|
-
}
|
|
1342
|
-
relayUrlFromArgs = value
|
|
1343
|
-
i += 1
|
|
1344
|
-
continue
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
if (arg === '--ignore-keychain-and-skip-secrets') {
|
|
1348
|
-
ignoreKeychainAndSkipSecrets = true
|
|
1349
|
-
continue
|
|
1350
|
-
}
|
|
1351
|
-
|
|
1352
|
-
hasOnlyTuiOptions = false
|
|
1353
|
-
break
|
|
1354
|
-
}
|
|
1355
|
-
|
|
1356
|
-
// If no args provided or only relay options, launch TUI
|
|
1357
|
-
if (process.argv.length === 2 || hasOnlyTuiOptions) {
|
|
1358
|
-
// Build relay config if provided (auth now via challenge-response, not token)
|
|
1359
|
-
const relayConfig = relayUrlFromArgs ? {
|
|
1360
|
-
url: relayUrlFromArgs,
|
|
1361
|
-
} : undefined
|
|
1362
|
-
|
|
1363
|
-
// Launch TUI
|
|
1364
|
-
checkFirstTimeSetup()
|
|
1365
|
-
.then(() => launchTUI(relayConfig, { ignoreKeychainAndSkipSecrets }))
|
|
1366
|
-
.catch((error) => {
|
|
1367
|
-
if (error instanceof SpacesError) {
|
|
1368
|
-
logger.error(error.message)
|
|
1369
|
-
process.exit(error.exitCode)
|
|
1370
|
-
}
|
|
1371
|
-
logger.error(`Failed to launch TUI: ${error instanceof Error ? error.message : 'Unknown error'}`)
|
|
1372
|
-
process.exit(1)
|
|
1373
|
-
})
|
|
1374
|
-
} else {
|
|
1375
|
-
program.parse()
|
|
1376
|
-
}
|