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/commands/linear.ts
DELETED
|
@@ -1,717 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Linear integration CLI commands
|
|
3
|
-
*
|
|
4
|
-
* Provides commands for configuring Linear integration at user and project levels.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { logger } from '../utils/logger.js'
|
|
8
|
-
import { SpacesError } from '../types/errors.js'
|
|
9
|
-
import {
|
|
10
|
-
promptPassword,
|
|
11
|
-
promptConfirm,
|
|
12
|
-
selectMultiple,
|
|
13
|
-
selectOne,
|
|
14
|
-
} from '../utils/prompts.js'
|
|
15
|
-
import {
|
|
16
|
-
validateLinearApiKey,
|
|
17
|
-
fetchLinearTeams,
|
|
18
|
-
getLinearConfig,
|
|
19
|
-
resetLinearClient,
|
|
20
|
-
LINEAR_API_KEY_SECRET,
|
|
21
|
-
getProjectLinearSecretKey,
|
|
22
|
-
} from '../core/linear.js'
|
|
23
|
-
import {
|
|
24
|
-
readGlobalConfig,
|
|
25
|
-
updateGlobalConfig,
|
|
26
|
-
readProjectConfig,
|
|
27
|
-
updateProjectConfig,
|
|
28
|
-
projectExists,
|
|
29
|
-
} from '../core/config.js'
|
|
30
|
-
import { setSecret, getSecret, deleteSecret } from '../utils/secrets.js'
|
|
31
|
-
import type { LinearTeamInfo } from '../types/config.js'
|
|
32
|
-
|
|
33
|
-
// ============================================================================
|
|
34
|
-
// Types
|
|
35
|
-
// ============================================================================
|
|
36
|
-
|
|
37
|
-
export interface LinearSetupOptions {
|
|
38
|
-
project?: string
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface LinearShowOptions {
|
|
42
|
-
project?: string
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface LinearClearOptions {
|
|
46
|
-
global?: boolean
|
|
47
|
-
project?: string
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** Result of team selection */
|
|
51
|
-
interface TeamSelectionResult {
|
|
52
|
-
selectedTeams: LinearTeamInfo[]
|
|
53
|
-
defaultTeam: string
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// ============================================================================
|
|
57
|
-
// Shared Helpers
|
|
58
|
-
// ============================================================================
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Prompt for API key and validate it
|
|
62
|
-
* @returns Valid API key or null if cancelled
|
|
63
|
-
*/
|
|
64
|
-
async function promptAndValidateApiKey(promptMessage: string): Promise<string | null> {
|
|
65
|
-
const apiKey = await promptPassword(promptMessage)
|
|
66
|
-
|
|
67
|
-
if (!apiKey) {
|
|
68
|
-
logger.info('Cancelled')
|
|
69
|
-
return null
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
logger.info('Validating API key...')
|
|
73
|
-
const valid = await validateLinearApiKey(apiKey)
|
|
74
|
-
|
|
75
|
-
if (!valid) {
|
|
76
|
-
throw new SpacesError(
|
|
77
|
-
'Invalid Linear API key. Get your API key from Linear → Settings → API → Personal API keys',
|
|
78
|
-
'USER_ERROR',
|
|
79
|
-
1
|
|
80
|
-
)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
logger.success('API key valid')
|
|
84
|
-
return apiKey
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Select teams and choose a default team
|
|
89
|
-
* @param availableTeams Teams to choose from
|
|
90
|
-
* @param currentTeamKeys Currently selected team keys (for pre-checking)
|
|
91
|
-
* @param message Prompt message
|
|
92
|
-
* @returns Selected teams and default, or null if cancelled
|
|
93
|
-
*/
|
|
94
|
-
async function selectAndConfigureTeams(
|
|
95
|
-
availableTeams: LinearTeamInfo[],
|
|
96
|
-
currentTeamKeys?: Set<string>,
|
|
97
|
-
message = 'Select teams you work with:'
|
|
98
|
-
): Promise<TeamSelectionResult | null> {
|
|
99
|
-
const selectedTeams = await selectMultiple(
|
|
100
|
-
availableTeams.map((t) => ({
|
|
101
|
-
label: `${t.key} - ${t.name}`,
|
|
102
|
-
value: t,
|
|
103
|
-
checked: currentTeamKeys ? currentTeamKeys.has(t.key) : true,
|
|
104
|
-
})),
|
|
105
|
-
message
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
if (selectedTeams === null || selectedTeams.length === 0) {
|
|
109
|
-
logger.info('Cancelled - at least one team is required')
|
|
110
|
-
return null
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Determine default team
|
|
114
|
-
let defaultTeam: string
|
|
115
|
-
|
|
116
|
-
if (selectedTeams.length === 1) {
|
|
117
|
-
defaultTeam = selectedTeams[0].key
|
|
118
|
-
} else {
|
|
119
|
-
const selected = await selectOne(
|
|
120
|
-
selectedTeams.map((t) => ({
|
|
121
|
-
label: `${t.key} - ${t.name}`,
|
|
122
|
-
value: t.key,
|
|
123
|
-
})),
|
|
124
|
-
'Select your default team:'
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
if (selected === null) {
|
|
128
|
-
logger.info('Cancelled')
|
|
129
|
-
return null
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
defaultTeam = selected
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return { selectedTeams, defaultTeam }
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Mask API key for display
|
|
140
|
-
*/
|
|
141
|
-
function maskApiKey(apiKey: string): string {
|
|
142
|
-
if (apiKey.length <= 8) {
|
|
143
|
-
return '●'.repeat(apiKey.length)
|
|
144
|
-
}
|
|
145
|
-
return '●'.repeat(apiKey.length - 4) + apiKey.slice(-4)
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// ============================================================================
|
|
149
|
-
// Setup Command
|
|
150
|
-
// ============================================================================
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Run Linear setup wizard
|
|
154
|
-
*
|
|
155
|
-
* - Without --project: User-level setup (API key + teams)
|
|
156
|
-
* - With --project: Project-level setup (team selection only, uses user API key)
|
|
157
|
-
*/
|
|
158
|
-
export async function linearSetup(options: LinearSetupOptions = {}): Promise<void> {
|
|
159
|
-
const { project } = options
|
|
160
|
-
|
|
161
|
-
if (project) {
|
|
162
|
-
await setupProjectLinear(project)
|
|
163
|
-
} else {
|
|
164
|
-
await setupUserLinear()
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* User-level Linear setup wizard
|
|
170
|
-
*/
|
|
171
|
-
async function setupUserLinear(): Promise<void> {
|
|
172
|
-
// Check if already configured
|
|
173
|
-
const existingApiKey = await getSecret(LINEAR_API_KEY_SECRET)
|
|
174
|
-
const globalConfig = readGlobalConfig()
|
|
175
|
-
const existingTeams = globalConfig.linearTeams || []
|
|
176
|
-
|
|
177
|
-
if (existingApiKey && existingTeams.length > 0) {
|
|
178
|
-
// Already configured - show menu
|
|
179
|
-
const maskedKey = maskApiKey(existingApiKey)
|
|
180
|
-
const teamList = existingTeams.map((t) => t.key).join(', ')
|
|
181
|
-
const defaultTeam = globalConfig.linearDefaultTeam || existingTeams[0]?.key
|
|
182
|
-
|
|
183
|
-
logger.log('\nLinear is already configured:')
|
|
184
|
-
logger.log(` API key: ${maskedKey}`)
|
|
185
|
-
logger.log(` Teams: ${teamList} (default: ${defaultTeam})`)
|
|
186
|
-
logger.log('')
|
|
187
|
-
|
|
188
|
-
const action = await selectOne(
|
|
189
|
-
[
|
|
190
|
-
{ label: 'Update API key', value: 'api-key' },
|
|
191
|
-
{ label: 'Modify teams', value: 'teams' },
|
|
192
|
-
{ label: 'Cancel', value: 'cancel' },
|
|
193
|
-
],
|
|
194
|
-
'What would you like to do?'
|
|
195
|
-
)
|
|
196
|
-
|
|
197
|
-
if (action === null || action === 'cancel') {
|
|
198
|
-
logger.info('Cancelled')
|
|
199
|
-
return
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (action === 'api-key') {
|
|
203
|
-
await updateApiKey()
|
|
204
|
-
} else if (action === 'teams') {
|
|
205
|
-
await updateTeams(existingApiKey)
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
return
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// First-time setup
|
|
212
|
-
logger.log('\n── Linear Integration Setup ──\n')
|
|
213
|
-
|
|
214
|
-
// Step 1: API Key
|
|
215
|
-
const apiKey = await promptAndValidateApiKey('Enter your Linear API key (from Settings → API):')
|
|
216
|
-
if (!apiKey) return
|
|
217
|
-
|
|
218
|
-
// Step 2: Fetch and select teams
|
|
219
|
-
logger.info('Fetching teams...')
|
|
220
|
-
const teams = await fetchLinearTeams(apiKey)
|
|
221
|
-
|
|
222
|
-
if (teams.length === 0) {
|
|
223
|
-
logger.warning('No teams found for this API key')
|
|
224
|
-
// Save API key and reset client after saving
|
|
225
|
-
await setSecret(LINEAR_API_KEY_SECRET, apiKey)
|
|
226
|
-
resetLinearClient()
|
|
227
|
-
updateGlobalConfig({ linearTeams: [], linearDefaultTeam: undefined })
|
|
228
|
-
logger.success('\nLinear API key saved (no teams available)')
|
|
229
|
-
return
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const result = await selectAndConfigureTeams(teams)
|
|
233
|
-
if (!result) return
|
|
234
|
-
|
|
235
|
-
// Save configuration - reset client AFTER saving to keychain
|
|
236
|
-
await setSecret(LINEAR_API_KEY_SECRET, apiKey)
|
|
237
|
-
resetLinearClient()
|
|
238
|
-
updateGlobalConfig({
|
|
239
|
-
linearTeams: result.selectedTeams,
|
|
240
|
-
linearDefaultTeam: result.defaultTeam,
|
|
241
|
-
})
|
|
242
|
-
|
|
243
|
-
const teamList = result.selectedTeams.map((t) => t.key).join(', ')
|
|
244
|
-
logger.success(`\nLinear configured (teams: ${teamList}, default: ${result.defaultTeam})`)
|
|
245
|
-
logger.log('\nProjects will inherit this config unless overridden.')
|
|
246
|
-
logger.log("Run 'gssh linear setup --project <name>' to customize per-project.")
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Update API key (part of re-setup flow)
|
|
251
|
-
*/
|
|
252
|
-
async function updateApiKey(): Promise<void> {
|
|
253
|
-
const apiKey = await promptAndValidateApiKey('Enter new Linear API key:')
|
|
254
|
-
if (!apiKey) return
|
|
255
|
-
|
|
256
|
-
// Save new key and reset client AFTER saving
|
|
257
|
-
await setSecret(LINEAR_API_KEY_SECRET, apiKey)
|
|
258
|
-
resetLinearClient()
|
|
259
|
-
|
|
260
|
-
// Re-fetch teams with new key
|
|
261
|
-
logger.info('Fetching teams with new API key...')
|
|
262
|
-
const teams = await fetchLinearTeams(apiKey)
|
|
263
|
-
|
|
264
|
-
if (teams.length === 0) {
|
|
265
|
-
logger.warning('No teams found for this API key')
|
|
266
|
-
updateGlobalConfig({ linearTeams: [], linearDefaultTeam: undefined })
|
|
267
|
-
logger.success('\nLinear API key updated (no teams available)')
|
|
268
|
-
return
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const result = await selectAndConfigureTeams(teams)
|
|
272
|
-
if (!result) {
|
|
273
|
-
logger.info('Keeping existing team config')
|
|
274
|
-
return
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
updateGlobalConfig({
|
|
278
|
-
linearTeams: result.selectedTeams,
|
|
279
|
-
linearDefaultTeam: result.defaultTeam,
|
|
280
|
-
})
|
|
281
|
-
|
|
282
|
-
logger.success('\nLinear API key and teams updated')
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Modify teams (part of re-setup flow)
|
|
287
|
-
*/
|
|
288
|
-
async function updateTeams(apiKey: string): Promise<void> {
|
|
289
|
-
const globalConfig = readGlobalConfig()
|
|
290
|
-
const currentTeamKeys = new Set((globalConfig.linearTeams || []).map((t) => t.key))
|
|
291
|
-
const currentDefault = globalConfig.linearDefaultTeam
|
|
292
|
-
|
|
293
|
-
logger.info('Fetching teams...')
|
|
294
|
-
const teams = await fetchLinearTeams(apiKey)
|
|
295
|
-
|
|
296
|
-
if (teams.length === 0) {
|
|
297
|
-
logger.warning('No teams found for this API key')
|
|
298
|
-
return
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
const result = await selectAndConfigureTeams(teams, currentTeamKeys)
|
|
302
|
-
if (!result) return
|
|
303
|
-
|
|
304
|
-
// Preserve current default if it's still in selection
|
|
305
|
-
const selectedKeys = result.selectedTeams.map((t) => t.key)
|
|
306
|
-
const defaultTeam = currentDefault && selectedKeys.includes(currentDefault)
|
|
307
|
-
? currentDefault
|
|
308
|
-
: result.defaultTeam
|
|
309
|
-
|
|
310
|
-
updateGlobalConfig({
|
|
311
|
-
linearTeams: result.selectedTeams,
|
|
312
|
-
linearDefaultTeam: defaultTeam,
|
|
313
|
-
})
|
|
314
|
-
|
|
315
|
-
logger.success('\nLinear teams updated')
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
/**
|
|
319
|
-
* Project-level Linear setup
|
|
320
|
-
*/
|
|
321
|
-
async function setupProjectLinear(projectName: string): Promise<void> {
|
|
322
|
-
if (!projectExists(projectName)) {
|
|
323
|
-
throw new SpacesError(`Project '${projectName}' not found`, 'USER_ERROR', 1)
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
// Check for existing project-level API key
|
|
327
|
-
const projectSecretKey = getProjectLinearSecretKey(projectName)
|
|
328
|
-
const existingProjectApiKey = await getSecret(projectSecretKey)
|
|
329
|
-
|
|
330
|
-
// Check for user-level API key
|
|
331
|
-
const userApiKey = await getSecret(LINEAR_API_KEY_SECRET)
|
|
332
|
-
|
|
333
|
-
logger.log(`\n── Linear Setup (Project: ${projectName}) ──\n`)
|
|
334
|
-
|
|
335
|
-
// If project already has its own API key, show menu
|
|
336
|
-
if (existingProjectApiKey) {
|
|
337
|
-
const maskedKey = maskApiKey(existingProjectApiKey)
|
|
338
|
-
logger.log(` Current API key: ${maskedKey} (project-specific)`)
|
|
339
|
-
logger.log('')
|
|
340
|
-
|
|
341
|
-
const action = await selectOne(
|
|
342
|
-
[
|
|
343
|
-
{ label: 'Update project API key', value: 'update-key' },
|
|
344
|
-
{ label: 'Switch to user-level API key', value: 'use-user' },
|
|
345
|
-
{ label: 'Modify teams', value: 'teams' },
|
|
346
|
-
{ label: 'Cancel', value: 'cancel' },
|
|
347
|
-
],
|
|
348
|
-
'What would you like to do?'
|
|
349
|
-
)
|
|
350
|
-
|
|
351
|
-
if (action === null || action === 'cancel') {
|
|
352
|
-
logger.info('Cancelled')
|
|
353
|
-
return
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
if (action === 'update-key') {
|
|
357
|
-
await setupProjectWithOwnKey(projectName)
|
|
358
|
-
} else if (action === 'use-user') {
|
|
359
|
-
// Clear project API key, fall back to user
|
|
360
|
-
await deleteSecret(projectSecretKey)
|
|
361
|
-
resetLinearClient()
|
|
362
|
-
logger.success(`\nProject '${projectName}' will now use user-level API key`)
|
|
363
|
-
|
|
364
|
-
// Let user configure teams
|
|
365
|
-
const globalConfig = readGlobalConfig()
|
|
366
|
-
const userTeams = globalConfig.linearTeams || []
|
|
367
|
-
if (userTeams.length > 0) {
|
|
368
|
-
await selectProjectTeams(projectName, userTeams)
|
|
369
|
-
}
|
|
370
|
-
} else if (action === 'teams') {
|
|
371
|
-
// Use existing project API key to fetch teams
|
|
372
|
-
await updateProjectTeamsWithKey(projectName, existingProjectApiKey)
|
|
373
|
-
}
|
|
374
|
-
return
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// No project API key - ask what they want to do
|
|
378
|
-
if (userApiKey) {
|
|
379
|
-
const choice = await selectOne(
|
|
380
|
-
[
|
|
381
|
-
{ label: 'Use user-level API key', value: 'user', description: 'Inherit from user config' },
|
|
382
|
-
{ label: 'Use project-specific API key', value: 'project', description: 'Enter a different API key for this project' },
|
|
383
|
-
{ label: 'Cancel', value: 'cancel' },
|
|
384
|
-
],
|
|
385
|
-
'Which API key would you like to use?'
|
|
386
|
-
)
|
|
387
|
-
|
|
388
|
-
if (choice === null || choice === 'cancel') {
|
|
389
|
-
logger.info('Cancelled')
|
|
390
|
-
return
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
if (choice === 'project') {
|
|
394
|
-
await setupProjectWithOwnKey(projectName)
|
|
395
|
-
} else {
|
|
396
|
-
// Use user-level key, just configure teams
|
|
397
|
-
const globalConfig = readGlobalConfig()
|
|
398
|
-
const userTeams = globalConfig.linearTeams || []
|
|
399
|
-
|
|
400
|
-
if (userTeams.length === 0) {
|
|
401
|
-
logger.warning('No teams configured at user level.')
|
|
402
|
-
logger.log("Run 'gssh linear setup' to configure teams first.")
|
|
403
|
-
return
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
await selectProjectTeams(projectName, userTeams)
|
|
407
|
-
}
|
|
408
|
-
} else {
|
|
409
|
-
// No user API key - must set up project-specific key or user key first
|
|
410
|
-
const choice = await selectOne(
|
|
411
|
-
[
|
|
412
|
-
{ label: 'Set up user-level API key first', value: 'user', description: 'Recommended - share across projects' },
|
|
413
|
-
{ label: 'Use project-specific API key', value: 'project', description: 'Only for this project' },
|
|
414
|
-
{ label: 'Cancel', value: 'cancel' },
|
|
415
|
-
],
|
|
416
|
-
'No user-level API key configured. What would you like to do?'
|
|
417
|
-
)
|
|
418
|
-
|
|
419
|
-
if (choice === null || choice === 'cancel') {
|
|
420
|
-
logger.info('Cancelled')
|
|
421
|
-
return
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
if (choice === 'user') {
|
|
425
|
-
await setupUserLinear()
|
|
426
|
-
|
|
427
|
-
// Check if setup was successful, then configure project teams
|
|
428
|
-
const newApiKey = await getSecret(LINEAR_API_KEY_SECRET)
|
|
429
|
-
if (newApiKey) {
|
|
430
|
-
const newConfig = readGlobalConfig()
|
|
431
|
-
const newTeams = newConfig.linearTeams || []
|
|
432
|
-
if (newTeams.length > 0) {
|
|
433
|
-
logger.log('')
|
|
434
|
-
await selectProjectTeams(projectName, newTeams)
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
} else {
|
|
438
|
-
await setupProjectWithOwnKey(projectName)
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
/**
|
|
444
|
-
* Set up a project with its own API key
|
|
445
|
-
*/
|
|
446
|
-
async function setupProjectWithOwnKey(projectName: string): Promise<void> {
|
|
447
|
-
const apiKey = await promptAndValidateApiKey('Enter Linear API key for this project:')
|
|
448
|
-
if (!apiKey) return
|
|
449
|
-
|
|
450
|
-
// Fetch teams with this key
|
|
451
|
-
logger.info('Fetching teams...')
|
|
452
|
-
const teams = await fetchLinearTeams(apiKey)
|
|
453
|
-
|
|
454
|
-
if (teams.length === 0) {
|
|
455
|
-
logger.warning('No teams found for this API key')
|
|
456
|
-
// Still save the key
|
|
457
|
-
const projectSecretKey = getProjectLinearSecretKey(projectName)
|
|
458
|
-
await setSecret(projectSecretKey, apiKey)
|
|
459
|
-
resetLinearClient()
|
|
460
|
-
updateProjectConfig(projectName, { linearTeams: [] })
|
|
461
|
-
logger.success(`\nProject API key saved for '${projectName}' (no teams available)`)
|
|
462
|
-
return
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
const result = await selectAndConfigureTeams(teams, undefined, `Select teams for '${projectName}':`)
|
|
466
|
-
if (!result) return
|
|
467
|
-
|
|
468
|
-
// Save project-specific API key
|
|
469
|
-
const projectSecretKey = getProjectLinearSecretKey(projectName)
|
|
470
|
-
await setSecret(projectSecretKey, apiKey)
|
|
471
|
-
resetLinearClient()
|
|
472
|
-
|
|
473
|
-
// Save selected teams
|
|
474
|
-
const teamKeys = result.selectedTeams.map((t) => t.key)
|
|
475
|
-
updateProjectConfig(projectName, { linearTeams: teamKeys })
|
|
476
|
-
|
|
477
|
-
const teamList = teamKeys.join(', ')
|
|
478
|
-
logger.success(`\nProject '${projectName}' configured with its own API key (teams: ${teamList})`)
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* Update project teams using an existing project API key
|
|
483
|
-
*/
|
|
484
|
-
async function updateProjectTeamsWithKey(projectName: string, apiKey: string): Promise<void> {
|
|
485
|
-
const projectConfig = readProjectConfig(projectName)
|
|
486
|
-
const currentTeamKeys = new Set(projectConfig.linearTeams || [])
|
|
487
|
-
|
|
488
|
-
logger.info('Fetching teams...')
|
|
489
|
-
const teams = await fetchLinearTeams(apiKey)
|
|
490
|
-
|
|
491
|
-
if (teams.length === 0) {
|
|
492
|
-
logger.warning('No teams found for this API key')
|
|
493
|
-
return
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
const result = await selectAndConfigureTeams(teams, currentTeamKeys, `Select teams for '${projectName}':`)
|
|
497
|
-
if (!result) return
|
|
498
|
-
|
|
499
|
-
const teamKeys = result.selectedTeams.map((t) => t.key)
|
|
500
|
-
updateProjectConfig(projectName, { linearTeams: teamKeys })
|
|
501
|
-
|
|
502
|
-
const teamList = teamKeys.join(', ')
|
|
503
|
-
logger.success(`\nProject '${projectName}' teams updated: ${teamList}`)
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
/**
|
|
507
|
-
* Select teams for a project
|
|
508
|
-
*/
|
|
509
|
-
async function selectProjectTeams(
|
|
510
|
-
projectName: string,
|
|
511
|
-
userTeams: LinearTeamInfo[]
|
|
512
|
-
): Promise<void> {
|
|
513
|
-
const projectConfig = readProjectConfig(projectName)
|
|
514
|
-
const currentProjectTeams = new Set(projectConfig.linearTeams || [])
|
|
515
|
-
|
|
516
|
-
const selectedTeams = await selectMultiple(
|
|
517
|
-
userTeams.map((t) => ({
|
|
518
|
-
label: `${t.key} - ${t.name}`,
|
|
519
|
-
value: t.key,
|
|
520
|
-
checked: currentProjectTeams.size > 0
|
|
521
|
-
? currentProjectTeams.has(t.key)
|
|
522
|
-
: true, // Default to all if no current config
|
|
523
|
-
})),
|
|
524
|
-
`Select teams for '${projectName}':`
|
|
525
|
-
)
|
|
526
|
-
|
|
527
|
-
if (selectedTeams === null) {
|
|
528
|
-
logger.info('Cancelled')
|
|
529
|
-
return
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
if (selectedTeams.length === 0) {
|
|
533
|
-
// Clear project-level override (use user defaults)
|
|
534
|
-
updateProjectConfig(projectName, { linearTeams: undefined })
|
|
535
|
-
const globalConfig = readGlobalConfig()
|
|
536
|
-
const defaultTeam = globalConfig.linearDefaultTeam || globalConfig.linearTeams?.[0]?.key || 'user default'
|
|
537
|
-
logger.success(`\nProject '${projectName}' will use: ${defaultTeam}`)
|
|
538
|
-
} else {
|
|
539
|
-
updateProjectConfig(projectName, { linearTeams: selectedTeams })
|
|
540
|
-
const teamList = selectedTeams.join(', ')
|
|
541
|
-
logger.success(`\nProject '${projectName}' will use: ${teamList}`)
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
// ============================================================================
|
|
546
|
-
// Show Command
|
|
547
|
-
// ============================================================================
|
|
548
|
-
|
|
549
|
-
/**
|
|
550
|
-
* Show Linear configuration
|
|
551
|
-
*/
|
|
552
|
-
export async function linearShow(options: LinearShowOptions = {}): Promise<void> {
|
|
553
|
-
const { project } = options
|
|
554
|
-
|
|
555
|
-
if (project) {
|
|
556
|
-
await showProjectConfig(project)
|
|
557
|
-
} else {
|
|
558
|
-
await showUserConfig()
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
/**
|
|
563
|
-
* Show user-level config
|
|
564
|
-
*/
|
|
565
|
-
async function showUserConfig(): Promise<void> {
|
|
566
|
-
const apiKey = await getSecret(LINEAR_API_KEY_SECRET)
|
|
567
|
-
const globalConfig = readGlobalConfig()
|
|
568
|
-
const teams = globalConfig.linearTeams || []
|
|
569
|
-
const defaultTeam = globalConfig.linearDefaultTeam
|
|
570
|
-
|
|
571
|
-
logger.log('\n── Linear Configuration (User) ──\n')
|
|
572
|
-
|
|
573
|
-
if (!apiKey) {
|
|
574
|
-
logger.log(' Status: Not configured')
|
|
575
|
-
logger.log("\n Run 'gssh linear setup' to configure")
|
|
576
|
-
} else {
|
|
577
|
-
logger.log(` API key: ${maskApiKey(apiKey)}`)
|
|
578
|
-
|
|
579
|
-
if (teams.length > 0) {
|
|
580
|
-
logger.log(' Teams:')
|
|
581
|
-
for (const team of teams) {
|
|
582
|
-
const isDefault = team.key === defaultTeam ? ' (default)' : ''
|
|
583
|
-
logger.log(` - ${team.key} - ${team.name}${isDefault}`)
|
|
584
|
-
}
|
|
585
|
-
} else {
|
|
586
|
-
logger.log(' Teams: None configured')
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
logger.log('')
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
/**
|
|
594
|
-
* Show project-level config with resolution
|
|
595
|
-
*/
|
|
596
|
-
async function showProjectConfig(projectName: string): Promise<void> {
|
|
597
|
-
if (!projectExists(projectName)) {
|
|
598
|
-
throw new SpacesError(`Project '${projectName}' not found`, 'USER_ERROR', 1)
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
const config = await getLinearConfig(projectName)
|
|
602
|
-
const projectConfig = readProjectConfig(projectName)
|
|
603
|
-
|
|
604
|
-
logger.log(`\n── Linear Configuration (Project: ${projectName}) ──\n`)
|
|
605
|
-
|
|
606
|
-
if (!config.apiKey) {
|
|
607
|
-
logger.log(' Status: Not configured')
|
|
608
|
-
logger.log("\n Run 'gssh linear setup' to configure")
|
|
609
|
-
logger.log('')
|
|
610
|
-
return
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
const apiKeySource = config.hasProjectApiKey ? '(project-specific)' : '(from user config)'
|
|
614
|
-
logger.log(` API key: ${maskApiKey(config.apiKey)} ${apiKeySource}`)
|
|
615
|
-
|
|
616
|
-
if (config.scope === 'project') {
|
|
617
|
-
const teams = projectConfig.linearTeams || []
|
|
618
|
-
if (teams.length > 0) {
|
|
619
|
-
logger.log(` Teams: ${teams.join(', ')}`)
|
|
620
|
-
}
|
|
621
|
-
} else {
|
|
622
|
-
logger.log(` Teams: ${config.teamKeys.join(', ')} (inherited from user config)`)
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
// Show legacy config warning if present
|
|
626
|
-
if (projectConfig.linearApiKey) {
|
|
627
|
-
logger.log('')
|
|
628
|
-
logger.warning(' Legacy config detected (linearApiKey in project config)')
|
|
629
|
-
logger.log(" Run 'gssh linear setup --project' to migrate")
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
logger.log('')
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
// ============================================================================
|
|
636
|
-
// Clear Command
|
|
637
|
-
// ============================================================================
|
|
638
|
-
|
|
639
|
-
/**
|
|
640
|
-
* Clear Linear configuration
|
|
641
|
-
*/
|
|
642
|
-
export async function linearClear(options: LinearClearOptions = {}): Promise<void> {
|
|
643
|
-
const { global: clearGlobal, project } = options
|
|
644
|
-
|
|
645
|
-
if (project) {
|
|
646
|
-
await clearProjectConfig(project)
|
|
647
|
-
} else if (clearGlobal) {
|
|
648
|
-
await clearUserConfig()
|
|
649
|
-
} else {
|
|
650
|
-
// Default to user config
|
|
651
|
-
await clearUserConfig()
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
/**
|
|
656
|
-
* Clear user-level config
|
|
657
|
-
*/
|
|
658
|
-
async function clearUserConfig(): Promise<void> {
|
|
659
|
-
const confirmed = await promptConfirm(
|
|
660
|
-
'Clear user-level Linear configuration (API key and teams)?',
|
|
661
|
-
false
|
|
662
|
-
)
|
|
663
|
-
|
|
664
|
-
if (!confirmed) {
|
|
665
|
-
logger.info('Cancelled')
|
|
666
|
-
return
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
await deleteSecret(LINEAR_API_KEY_SECRET)
|
|
670
|
-
resetLinearClient()
|
|
671
|
-
updateGlobalConfig({
|
|
672
|
-
linearTeams: undefined,
|
|
673
|
-
linearDefaultTeam: undefined,
|
|
674
|
-
})
|
|
675
|
-
|
|
676
|
-
logger.success('Linear configuration cleared')
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
/**
|
|
680
|
-
* Clear project-level config
|
|
681
|
-
*/
|
|
682
|
-
async function clearProjectConfig(projectName: string): Promise<void> {
|
|
683
|
-
if (!projectExists(projectName)) {
|
|
684
|
-
throw new SpacesError(`Project '${projectName}' not found`, 'USER_ERROR', 1)
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
// Check if project has its own API key
|
|
688
|
-
const projectSecretKey = getProjectLinearSecretKey(projectName)
|
|
689
|
-
const hasProjectApiKey = await getSecret(projectSecretKey)
|
|
690
|
-
|
|
691
|
-
const message = hasProjectApiKey
|
|
692
|
-
? `Clear Linear configuration for project '${projectName}' (including project-specific API key)?`
|
|
693
|
-
: `Clear Linear configuration for project '${projectName}'?`
|
|
694
|
-
|
|
695
|
-
const confirmed = await promptConfirm(message, false)
|
|
696
|
-
|
|
697
|
-
if (!confirmed) {
|
|
698
|
-
logger.info('Cancelled')
|
|
699
|
-
return
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
// Clear project-specific API key if present
|
|
703
|
-
if (hasProjectApiKey) {
|
|
704
|
-
await deleteSecret(projectSecretKey)
|
|
705
|
-
resetLinearClient()
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
updateProjectConfig(projectName, {
|
|
709
|
-
linearTeams: undefined,
|
|
710
|
-
// Also clear legacy fields
|
|
711
|
-
linearApiKey: undefined,
|
|
712
|
-
linearTeamKey: undefined,
|
|
713
|
-
})
|
|
714
|
-
|
|
715
|
-
logger.success(`Linear configuration cleared for project '${projectName}'`)
|
|
716
|
-
logger.log('Project will now use user-level defaults')
|
|
717
|
-
}
|