gitspace 0.2.0-rc.20 → 0.2.0-rc.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +11 -6
- package/.claude/settings.local.json +0 -10
- package/.gitspace/bundle.json +0 -50
- package/.gitspace/events.json +0 -11
- package/.gitspace/processes.json +0 -23
- package/.gitspace/scripts/select/01-status.sh +0 -39
- package/.gitspace/scripts/setup/01-install-deps.sh +0 -12
- package/.gitspace/scripts/setup/02-typecheck.sh +0 -16
- package/AGENTS.md +0 -469
- package/CLAUDE.md +0 -1
- package/bun.lock +0 -794
- package/docs/CONNECTION.md +0 -623
- package/docs/GATEWAY-WORKER.md +0 -319
- package/docs/GETTING-STARTED.md +0 -448
- package/docs/GITSPACE-PLATFORM.md +0 -1819
- package/docs/INFRASTRUCTURE.md +0 -1347
- package/docs/PROTOCOL.md +0 -619
- package/docs/QUICKSTART.md +0 -183
- package/docs/RELAY.md +0 -327
- package/docs/REMOTE-DESIGN.md +0 -554
- package/docs/ROADMAP.md +0 -564
- package/docs/SITE_DOCS_FIGMA_MAKE.md +0 -1176
- package/docs/STACK-DESIGN.md +0 -588
- package/docs/UNIFIED_ARCHITECTURE.md +0 -138
- package/experiments/pty-benchmark.ts +0 -148
- package/experiments/pty-latency.ts +0 -100
- package/experiments/router/client.ts +0 -199
- package/experiments/router/protocol.ts +0 -74
- package/experiments/router/router.ts +0 -217
- package/experiments/router/session.ts +0 -180
- package/experiments/router/test.ts +0 -133
- package/experiments/socket-bandwidth.ts +0 -77
- package/homebrew/gitspace.rb +0 -45
- package/landing-page/ATTRIBUTIONS.md +0 -3
- package/landing-page/README.md +0 -11
- package/landing-page/bun.lock +0 -801
- package/landing-page/guidelines/Guidelines.md +0 -61
- package/landing-page/index.html +0 -37
- package/landing-page/package.json +0 -90
- package/landing-page/postcss.config.mjs +0 -15
- package/landing-page/public/_redirects +0 -1
- package/landing-page/public/favicon.png +0 -0
- package/landing-page/src/app/App.tsx +0 -53
- package/landing-page/src/app/components/figma/ImageWithFallback.tsx +0 -27
- package/landing-page/src/app/components/ui/accordion.tsx +0 -66
- package/landing-page/src/app/components/ui/alert-dialog.tsx +0 -157
- package/landing-page/src/app/components/ui/alert.tsx +0 -66
- package/landing-page/src/app/components/ui/aspect-ratio.tsx +0 -11
- package/landing-page/src/app/components/ui/avatar.tsx +0 -53
- package/landing-page/src/app/components/ui/badge.tsx +0 -46
- package/landing-page/src/app/components/ui/breadcrumb.tsx +0 -109
- package/landing-page/src/app/components/ui/button.tsx +0 -57
- package/landing-page/src/app/components/ui/calendar.tsx +0 -75
- package/landing-page/src/app/components/ui/card.tsx +0 -92
- package/landing-page/src/app/components/ui/carousel.tsx +0 -241
- package/landing-page/src/app/components/ui/chart.tsx +0 -353
- package/landing-page/src/app/components/ui/checkbox.tsx +0 -32
- package/landing-page/src/app/components/ui/collapsible.tsx +0 -33
- package/landing-page/src/app/components/ui/command.tsx +0 -177
- package/landing-page/src/app/components/ui/context-menu.tsx +0 -252
- package/landing-page/src/app/components/ui/dialog.tsx +0 -135
- package/landing-page/src/app/components/ui/drawer.tsx +0 -132
- package/landing-page/src/app/components/ui/dropdown-menu.tsx +0 -257
- package/landing-page/src/app/components/ui/form.tsx +0 -168
- package/landing-page/src/app/components/ui/hover-card.tsx +0 -44
- package/landing-page/src/app/components/ui/input-otp.tsx +0 -77
- package/landing-page/src/app/components/ui/input.tsx +0 -21
- package/landing-page/src/app/components/ui/label.tsx +0 -24
- package/landing-page/src/app/components/ui/menubar.tsx +0 -276
- package/landing-page/src/app/components/ui/navigation-menu.tsx +0 -168
- package/landing-page/src/app/components/ui/pagination.tsx +0 -127
- package/landing-page/src/app/components/ui/popover.tsx +0 -48
- package/landing-page/src/app/components/ui/progress.tsx +0 -31
- package/landing-page/src/app/components/ui/radio-group.tsx +0 -45
- package/landing-page/src/app/components/ui/resizable.tsx +0 -56
- package/landing-page/src/app/components/ui/scroll-area.tsx +0 -58
- package/landing-page/src/app/components/ui/select.tsx +0 -189
- package/landing-page/src/app/components/ui/separator.tsx +0 -28
- package/landing-page/src/app/components/ui/sheet.tsx +0 -139
- package/landing-page/src/app/components/ui/sidebar.tsx +0 -726
- package/landing-page/src/app/components/ui/skeleton.tsx +0 -13
- package/landing-page/src/app/components/ui/slider.tsx +0 -63
- package/landing-page/src/app/components/ui/sonner.tsx +0 -25
- package/landing-page/src/app/components/ui/switch.tsx +0 -31
- package/landing-page/src/app/components/ui/table.tsx +0 -116
- package/landing-page/src/app/components/ui/tabs.tsx +0 -66
- package/landing-page/src/app/components/ui/textarea.tsx +0 -18
- package/landing-page/src/app/components/ui/toggle-group.tsx +0 -73
- package/landing-page/src/app/components/ui/toggle.tsx +0 -47
- package/landing-page/src/app/components/ui/tooltip.tsx +0 -61
- package/landing-page/src/app/components/ui/use-mobile.ts +0 -21
- package/landing-page/src/app/components/ui/utils.ts +0 -6
- package/landing-page/src/components/docs/DocsContent.tsx +0 -801
- package/landing-page/src/components/docs/DocsSidebar.tsx +0 -90
- package/landing-page/src/components/landing/CTA.tsx +0 -59
- package/landing-page/src/components/landing/Comparison.tsx +0 -84
- package/landing-page/src/components/landing/FaultyTerminal.tsx +0 -424
- package/landing-page/src/components/landing/Features.tsx +0 -201
- package/landing-page/src/components/landing/Hero.tsx +0 -142
- package/landing-page/src/components/landing/Pricing.tsx +0 -140
- package/landing-page/src/components/landing/Roadmap.tsx +0 -86
- package/landing-page/src/components/landing/Security.tsx +0 -81
- package/landing-page/src/components/landing/TerminalWindow.tsx +0 -27
- package/landing-page/src/components/landing/UseCases.tsx +0 -55
- package/landing-page/src/components/landing/Workflow.tsx +0 -101
- package/landing-page/src/components/layout/DashboardNavbar.tsx +0 -37
- package/landing-page/src/components/layout/Footer.tsx +0 -55
- package/landing-page/src/components/layout/LandingNavbar.tsx +0 -82
- package/landing-page/src/components/ui/badge.tsx +0 -39
- package/landing-page/src/components/ui/breadcrumb.tsx +0 -115
- package/landing-page/src/components/ui/button.tsx +0 -57
- package/landing-page/src/components/ui/card.tsx +0 -79
- package/landing-page/src/components/ui/mock-terminal.tsx +0 -68
- package/landing-page/src/components/ui/separator.tsx +0 -28
- package/landing-page/src/lib/utils.ts +0 -6
- package/landing-page/src/main.tsx +0 -10
- package/landing-page/src/pages/Dashboard.tsx +0 -133
- package/landing-page/src/pages/DocsPage.tsx +0 -79
- package/landing-page/src/pages/LandingPage.tsx +0 -31
- package/landing-page/src/pages/TerminalView.tsx +0 -106
- package/landing-page/src/styles/fonts.css +0 -0
- package/landing-page/src/styles/index.css +0 -3
- package/landing-page/src/styles/tailwind.css +0 -4
- package/landing-page/src/styles/theme.css +0 -181
- package/landing-page/vite.config.ts +0 -19
- package/scripts/GHOSTTY_TAB_BUG.md +0 -106
- package/scripts/build.ts +0 -298
- package/scripts/migrate-secrets.ts +0 -77
- package/scripts/release.ts +0 -140
- package/scripts/sample-events.ts +0 -263
- package/scripts/test-tabs-minimal.ts +0 -68
- package/scripts/test-tabs-workaround.ts +0 -95
- package/scripts/test-tabs.ts +0 -171
- package/src/__tests__/test-utils.ts +0 -298
- package/src/app/input/__tests__/sessionCommands.test.ts +0 -40
- package/src/app/input/sessionCommands.ts +0 -94
- package/src/app/session/__tests__/useAttachController.test.ts +0 -229
- package/src/app/session/createSessionBackend.bun.ts +0 -76
- package/src/app/session/createSessionBackend.web.ts +0 -104
- package/src/app/session/types.ts +0 -16
- package/src/app/session/useAttachController.ts +0 -220
- package/src/app/session/useProcessActions.ts +0 -201
- package/src/app/session/useSessionClient.ts +0 -35
- package/src/app/session/useWorkspaceDeleteFlow.ts +0 -170
- package/src/app.tui.tsx +0 -2929
- package/src/app.web.tsx +0 -1454
- package/src/commands/__tests__/connect-key.test.ts +0 -10
- package/src/commands/__tests__/events.test.ts +0 -201
- package/src/commands/__tests__/notifications.test.ts +0 -349
- package/src/commands/__tests__/process.test.ts +0 -251
- package/src/commands/__tests__/serve-messages.test.ts +0 -190
- package/src/commands/__tests__/serve-process-hosting.test.ts +0 -63
- package/src/commands/access.ts +0 -298
- package/src/commands/add.ts +0 -455
- package/src/commands/auth.ts +0 -369
- package/src/commands/bundle.ts +0 -232
- package/src/commands/config.ts +0 -242
- package/src/commands/connect-key.ts +0 -1
- package/src/commands/connect.ts +0 -576
- package/src/commands/directory.ts +0 -16
- package/src/commands/events.ts +0 -157
- package/src/commands/host.ts +0 -566
- package/src/commands/identity.ts +0 -184
- package/src/commands/linear.ts +0 -717
- package/src/commands/list.ts +0 -181
- package/src/commands/migrate.ts +0 -52
- package/src/commands/notifications.ts +0 -351
- package/src/commands/process.ts +0 -104
- package/src/commands/relay.ts +0 -315
- package/src/commands/remove.ts +0 -279
- package/src/commands/review.ts +0 -787
- package/src/commands/serve.ts +0 -1946
- package/src/commands/share.ts +0 -451
- package/src/commands/status.ts +0 -125
- package/src/commands/switch.ts +0 -361
- package/src/commands/tmux.ts +0 -317
- package/src/components/DPad.web.tsx +0 -343
- package/src/components/DiffViewer.web.tsx +0 -1192
- package/src/components/Events.tsx +0 -137
- package/src/components/Events.tui.tsx +0 -129
- package/src/components/Events.web.tsx +0 -386
- package/src/components/FloatingControls.web.tsx +0 -112
- package/src/components/FloatingJogWheel.web.tsx +0 -240
- package/src/components/Flow.tsx +0 -458
- package/src/components/Flow.tui.tsx +0 -343
- package/src/components/Flow.web.tsx +0 -442
- package/src/components/Inbox.tsx +0 -448
- package/src/components/Inbox.tui.tsx +0 -262
- package/src/components/Inbox.web.tsx +0 -329
- package/src/components/MachineList.tsx +0 -187
- package/src/components/MachineList.tui.tsx +0 -161
- package/src/components/MachineList.web.tsx +0 -210
- package/src/components/NumPad.web.tsx +0 -270
- package/src/components/ProjectList.tsx +0 -175
- package/src/components/ProjectList.tui.tsx +0 -109
- package/src/components/ProjectList.web.tsx +0 -143
- package/src/components/ProjectOnboardingStep.ts +0 -23
- package/src/components/ProjectOnboardingStep.tui.tsx +0 -88
- package/src/components/ProjectOnboardingStep.web.tsx +0 -59
- package/src/components/RemoteMachineScreen.tui.tsx +0 -690
- package/src/components/ScriptTerminal.tui.tsx +0 -160
- package/src/components/ScriptTerminal.web.tsx +0 -89
- package/src/components/SessionTerminal.tui.tsx +0 -406
- package/src/components/SessionTerminal.web.tsx +0 -467
- package/src/components/SpacesBrowser.tsx +0 -540
- package/src/components/SpacesBrowser.tui.tsx +0 -258
- package/src/components/SpacesBrowser.web.tsx +0 -332
- package/src/components/TerminalControls.web.tsx +0 -464
- package/src/components/ThreadPanel.web.tsx +0 -798
- package/src/components/__tests__/SpacesBrowser.test.ts +0 -541
- package/src/components/__tests__/SpacesBrowser.tui.test.tsx +0 -249
- package/src/components/__tests__/script-terminal-buffer.tui.test.ts +0 -72
- package/src/components/index.ts +0 -105
- package/src/components/review-decision-colors.ts +0 -11
- package/src/components/script-terminal-buffer.tui.ts +0 -37
- package/src/components/session-terminal-page-navigation.ts +0 -48
- package/src/components/terminal-bracketed-paste.tui.test.ts +0 -43
- package/src/components/terminal-bracketed-paste.tui.ts +0 -46
- package/src/core/__tests__/access.test.ts +0 -240
- package/src/core/__tests__/bundle-refresh.test.ts +0 -567
- package/src/core/__tests__/bundle.test.ts +0 -209
- package/src/core/__tests__/github-review.test.ts +0 -781
- package/src/core/__tests__/project-lifecycle.test.ts +0 -137
- package/src/core/__tests__/workspace-lifecycle.test.ts +0 -159
- package/src/core/__tests__/workspace.test.ts +0 -149
- package/src/core/access.ts +0 -277
- package/src/core/bundle-refresh.ts +0 -1064
- package/src/core/bundle.ts +0 -326
- package/src/core/config.ts +0 -405
- package/src/core/git.ts +0 -768
- package/src/core/github-review.ts +0 -761
- package/src/core/github.ts +0 -151
- package/src/core/identity.ts +0 -631
- package/src/core/linear.ts +0 -403
- package/src/core/preferences-service.ts +0 -17
- package/src/core/project-catalog.ts +0 -52
- package/src/core/project-lifecycle.ts +0 -163
- package/src/core/review-executor.ts +0 -316
- package/src/core/review.ts +0 -407
- package/src/core/secret-runtime.ts +0 -167
- package/src/core/shell.ts +0 -117
- package/src/core/trusted-relays.ts +0 -315
- package/src/core/workspace-lifecycle.ts +0 -216
- package/src/core/workspace.ts +0 -363
- package/src/hooks/__tests__/useLocalSession.tui.test.ts +0 -557
- package/src/hooks/index.ts +0 -8
- package/src/hooks/index.tui.ts +0 -32
- package/src/hooks/useDaemonStatus.tui.ts +0 -174
- package/src/hooks/useLocalSession.tui.ts +0 -395
- package/src/hooks/useRelayConnection.web.ts +0 -54
- package/src/hooks/useRemoteMachines.tui.ts +0 -166
- package/src/hooks/useRemoteTerminal.tui.ts +0 -22
- package/src/hooks/useReview.web.ts +0 -248
- package/src/hooks/useTerminal.web.ts +0 -36
- package/src/hooks/useUserActivity.ts +0 -61
- package/src/hooks/useVisualViewport.web.ts +0 -104
- package/src/index.ts +0 -1376
- package/src/lib/events/__tests__/collector-filter.test.ts +0 -105
- package/src/lib/events/__tests__/store-query.test.ts +0 -103
- package/src/lib/events/collector.ts +0 -494
- package/src/lib/events/filters.ts +0 -26
- package/src/lib/events/index.ts +0 -11
- package/src/lib/events/indexer.ts +0 -14
- package/src/lib/events/paths.ts +0 -69
- package/src/lib/events/reader.ts +0 -212
- package/src/lib/events/store.ts +0 -141
- package/src/lib/invite.web.ts +0 -58
- package/src/lib/preferences-service.web.ts +0 -41
- package/src/lib/processes/__tests__/config.test.ts +0 -83
- package/src/lib/processes/__tests__/names.test.ts +0 -125
- package/src/lib/processes/__tests__/schema.test.ts +0 -208
- package/src/lib/processes/__tests__/watchdog.test.ts +0 -210
- package/src/lib/processes/autostart.ts +0 -16
- package/src/lib/processes/config.ts +0 -187
- package/src/lib/processes/control.ts +0 -53
- package/src/lib/processes/editor.ts +0 -32
- package/src/lib/processes/events-config.ts +0 -37
- package/src/lib/processes/index.ts +0 -14
- package/src/lib/processes/instances.ts +0 -20
- package/src/lib/processes/manager.ts +0 -131
- package/src/lib/processes/names.ts +0 -71
- package/src/lib/processes/registry.ts +0 -26
- package/src/lib/processes/runner.ts +0 -211
- package/src/lib/processes/scheduler.ts +0 -17
- package/src/lib/processes/schema.ts +0 -74
- package/src/lib/processes/session-list.ts +0 -15
- package/src/lib/processes/state.ts +0 -82
- package/src/lib/processes/watchdog.test.ts +0 -79
- package/src/lib/processes/watchdog.ts +0 -106
- package/src/lib/remote-session/__tests__/protocol.test.ts +0 -291
- package/src/lib/remote-session/index.ts +0 -7
- package/src/lib/remote-session/protocol.ts +0 -443
- package/src/lib/remote-session/session-handler.ts +0 -1298
- package/src/lib/remote-session/workspace-scanner.ts +0 -161
- package/src/lib/sonner.web.ts +0 -1
- package/src/lib/storage/identity-store.web.ts +0 -94
- package/src/lib/tmux-lite/README.md +0 -81
- package/src/lib/tmux-lite/cli.ts +0 -855
- package/src/lib/tmux-lite/crypto/__tests__/helpers/handshake-runner.ts +0 -349
- package/src/lib/tmux-lite/crypto/__tests__/helpers/mock-relay.ts +0 -291
- package/src/lib/tmux-lite/crypto/__tests__/helpers/test-identities.ts +0 -142
- package/src/lib/tmux-lite/crypto/__tests__/integration/authorization.integration.test.ts +0 -339
- package/src/lib/tmux-lite/crypto/__tests__/integration/e2e-communication.integration.test.ts +0 -477
- package/src/lib/tmux-lite/crypto/__tests__/integration/error-handling.integration.test.ts +0 -499
- package/src/lib/tmux-lite/crypto/__tests__/integration/handshake.integration.test.ts +0 -371
- package/src/lib/tmux-lite/crypto/__tests__/integration/security.integration.test.ts +0 -573
- package/src/lib/tmux-lite/crypto/access-control.test.ts +0 -512
- package/src/lib/tmux-lite/crypto/access-control.ts +0 -320
- package/src/lib/tmux-lite/crypto/frames.test.ts +0 -262
- package/src/lib/tmux-lite/crypto/frames.ts +0 -141
- package/src/lib/tmux-lite/crypto/handshake.ts +0 -894
- package/src/lib/tmux-lite/crypto/identity.test.ts +0 -220
- package/src/lib/tmux-lite/crypto/identity.ts +0 -286
- package/src/lib/tmux-lite/crypto/index.ts +0 -51
- package/src/lib/tmux-lite/crypto/invites.test.ts +0 -381
- package/src/lib/tmux-lite/crypto/invites.ts +0 -215
- package/src/lib/tmux-lite/crypto/keyexchange.ts +0 -435
- package/src/lib/tmux-lite/crypto/keys.test.ts +0 -58
- package/src/lib/tmux-lite/crypto/keys.ts +0 -47
- package/src/lib/tmux-lite/crypto/secretbox.test.ts +0 -169
- package/src/lib/tmux-lite/crypto/secretbox.ts +0 -124
- package/src/lib/tmux-lite/handshake-handler.ts +0 -451
- package/src/lib/tmux-lite/process-run.integration.test.ts +0 -266
- package/src/lib/tmux-lite/protocol.test.ts +0 -307
- package/src/lib/tmux-lite/protocol.ts +0 -291
- package/src/lib/tmux-lite/relay-client.ts +0 -506
- package/src/lib/tmux-lite/server-lifecycle.test.ts +0 -212
- package/src/lib/tmux-lite/server.ts +0 -1412
- package/src/lib/tmux-lite/shell-integration.sh +0 -37
- package/src/lib/tmux-lite/terminal-queries.test.ts +0 -54
- package/src/lib/tmux-lite/terminal-queries.ts +0 -49
- package/src/notifications/__tests__/useNotifications.test.ts +0 -739
- package/src/notifications/index.ts +0 -32
- package/src/notifications/policy.test.ts +0 -424
- package/src/notifications/policy.ts +0 -139
- package/src/notifications/types.ts +0 -82
- package/src/notifications/useNotifications.ts +0 -350
- package/src/pages/ReviewPage.web.tsx +0 -511
- package/src/preferences/index.ts +0 -1
- package/src/preferences/types.ts +0 -9
- package/src/relay/__tests__/e2e-flow.test.ts +0 -1284
- package/src/relay/__tests__/helpers/auth.ts +0 -354
- package/src/relay/__tests__/helpers/ports.ts +0 -51
- package/src/relay/__tests__/protocol-validation.test.ts +0 -265
- package/src/relay/authorization.ts +0 -303
- package/src/relay/embedded-assets.generated.d.ts +0 -15
- package/src/relay/identity.ts +0 -352
- package/src/relay/index.ts +0 -57
- package/src/relay/pipes.test.ts +0 -427
- package/src/relay/pipes.ts +0 -195
- package/src/relay/protocol.ts +0 -804
- package/src/relay/registries.test.ts +0 -437
- package/src/relay/registries.ts +0 -593
- package/src/relay/server.test.ts +0 -1323
- package/src/relay/server.ts +0 -1128
- package/src/relay/signing.ts +0 -238
- package/src/relay/types.ts +0 -69
- package/src/relay-client/__tests__/machine-directory-client.test.ts +0 -152
- package/src/relay-client/__tests__/useMachineDirectory.test.ts +0 -172
- package/src/relay-client/adapters/browser.ts +0 -27
- package/src/relay-client/adapters/node.ts +0 -29
- package/src/relay-client/index.ts +0 -33
- package/src/relay-client/machine-directory-client.ts +0 -244
- package/src/relay-client/useMachineDirectory.ts +0 -175
- package/src/serve/client-session-manager.ts +0 -635
- package/src/serve/daemon.ts +0 -497
- package/src/serve/pty-session.ts +0 -236
- package/src/serve/types.ts +0 -174
- package/src/session/__tests__/backend-manager.test.ts +0 -101
- package/src/session/__tests__/local-session-backend.test.ts +0 -1129
- package/src/session/__tests__/reducer.test.ts +0 -80
- package/src/session/__tests__/remote-session-backend.test.ts +0 -995
- package/src/session/__tests__/session-name.test.ts +0 -35
- package/src/session/__tests__/useBundleRefreshAttachFlow.test.ts +0 -431
- package/src/session/__tests__/useRemoteSessionClient.test.ts +0 -424
- package/src/session/__tests__/workspace-shell-hooks.integration.test.ts +0 -268
- package/src/session/__tests__/workspace-shell-hooks.test.ts +0 -24
- package/src/session/adapters/browser-remote.ts +0 -101
- package/src/session/adapters/node-remote.ts +0 -135
- package/src/session/backend-key.ts +0 -5
- package/src/session/backend-manager.ts +0 -80
- package/src/session/backend.ts +0 -93
- package/src/session/backends/local-session-backend.ts +0 -1119
- package/src/session/backends/remote-session-backend.ts +0 -1378
- package/src/session/crypto/__tests__/web-terminal.test.ts +0 -1158
- package/src/session/crypto/frames.web.ts +0 -205
- package/src/session/crypto/handshake.web.ts +0 -396
- package/src/session/crypto/identity.web.ts +0 -133
- package/src/session/crypto/keyexchange.web.ts +0 -246
- package/src/session/crypto/relay-signing.web.ts +0 -53
- package/src/session/events.ts +0 -38
- package/src/session/index.ts +0 -116
- package/src/session/reducer.ts +0 -274
- package/src/session/selectors.ts +0 -28
- package/src/session/session-name.ts +0 -50
- package/src/session/types.ts +0 -101
- package/src/session/useBundleRefreshAttachFlow.ts +0 -608
- package/src/session/useRemoteSessionClient.ts +0 -424
- package/src/session/useSessionEngine.ts +0 -432
- package/src/session/workspace-shell-hooks.ts +0 -35
- package/src/tui/__tests__/input-text.test.ts +0 -24
- package/src/tui/__tests__/local-terminal-sync.test.ts +0 -82
- package/src/tui/__tests__/session-terminal-page-navigation.test.ts +0 -94
- package/src/tui/app.tsx +0 -2
- package/src/tui/index.ts +0 -18
- package/src/tui/input-text.ts +0 -38
- package/src/tui/local-terminal-sync.ts +0 -41
- package/src/types/bundle-refresh.ts +0 -42
- package/src/types/bundle.ts +0 -130
- package/src/types/config.ts +0 -287
- package/src/types/errors.ts +0 -292
- package/src/types/events.ts +0 -91
- package/src/types/identity.ts +0 -284
- package/src/types/processes.ts +0 -45
- package/src/types/review.ts +0 -349
- package/src/types/script-phase.ts +0 -3
- package/src/types/workspace-fuzzy.ts +0 -49
- package/src/types/workspace.ts +0 -151
- package/src/utils/__tests__/onboarding.test.ts +0 -358
- package/src/utils/__tests__/run-scripts.test.ts +0 -535
- package/src/utils/__tests__/run-workspace-scripts.test.ts +0 -406
- package/src/utils/__tests__/workspace-setup.integration.test.ts +0 -633
- package/src/utils/__tests__/workspace-state.test.ts +0 -78
- package/src/utils/bun-socket-writer.ts +0 -80
- package/src/utils/clipboard.ts +0 -53
- package/src/utils/deps.test.ts +0 -31
- package/src/utils/deps.ts +0 -145
- package/src/utils/device.web.ts +0 -163
- package/src/utils/fuzzy-match.ts +0 -125
- package/src/utils/hostnames.ts +0 -43
- package/src/utils/hunk-header.ts +0 -17
- package/src/utils/id.ts +0 -9
- package/src/utils/logger.ts +0 -127
- package/src/utils/markdown.ts +0 -254
- package/src/utils/normalize-env-key.ts +0 -13
- package/src/utils/onboarding.ts +0 -279
- package/src/utils/prompts.ts +0 -176
- package/src/utils/run-commands.ts +0 -112
- package/src/utils/run-scripts.ts +0 -337
- package/src/utils/run-workspace-scripts.ts +0 -355
- package/src/utils/sanitize.test.ts +0 -149
- package/src/utils/sanitize.ts +0 -162
- package/src/utils/secrets.ts +0 -836
- package/src/utils/shell-escape.ts +0 -40
- package/src/utils/utf8.ts +0 -79
- package/src/utils/workspace-id.ts +0 -55
- package/src/utils/workspace-state.ts +0 -427
- package/src/version.generated.d.ts +0 -2
- package/todo-security.md +0 -92
- package/tsconfig.json +0 -29
- package/web/README.md +0 -73
- package/web/bun.lock +0 -675
- package/web/eslint.config.js +0 -23
- package/web/index.css +0 -249
- package/web/index.html +0 -16
- package/web/main.tsx +0 -10
- package/web/package.json +0 -39
- package/web/public/vite.svg +0 -1
- package/web/tsconfig.app.json +0 -35
- package/web/tsconfig.json +0 -7
- package/web/tsconfig.node.json +0 -26
- package/web/vite.config.ts +0 -39
- package/worker/bun.lock +0 -237
- package/worker/package.json +0 -22
- package/worker/schema.sql +0 -96
- package/worker/src/handlers/auth.ts +0 -451
- package/worker/src/handlers/subdomains.ts +0 -376
- package/worker/src/handlers/user.ts +0 -98
- package/worker/src/index.ts +0 -70
- package/worker/src/middleware/auth.ts +0 -152
- package/worker/src/services/cloudflare.ts +0 -609
- package/worker/src/types.ts +0 -96
- package/worker/tsconfig.json +0 -15
- package/worker/wrangler.toml +0 -26
|
@@ -1,355 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Workspace script execution with phase tracking
|
|
3
|
-
*
|
|
4
|
-
* Consolidates the logic for running pre/setup/select scripts with
|
|
5
|
-
* proper error handling and phase identification.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { join } from 'path';
|
|
9
|
-
import { discoverScripts, runScriptsInTerminal, type RunScriptsOptions } from './run-scripts';
|
|
10
|
-
import {
|
|
11
|
-
buildBundleStepFingerprints,
|
|
12
|
-
buildSetupState,
|
|
13
|
-
createEmptyWorkspaceLockState,
|
|
14
|
-
fingerprintValue,
|
|
15
|
-
getBundleStepKey,
|
|
16
|
-
readWorkspaceLockState,
|
|
17
|
-
type WorkspaceLockState,
|
|
18
|
-
writeWorkspaceLockState,
|
|
19
|
-
} from './workspace-state';
|
|
20
|
-
import { readProjectConfig } from '../core/config';
|
|
21
|
-
import { detectBundleChanges } from '../core/bundle-refresh';
|
|
22
|
-
import { getProjectSecrets } from './secrets';
|
|
23
|
-
import { logger } from './logger';
|
|
24
|
-
import type { ConfirmStepResult, OnboardingStep, SpacesBundle } from '../types/bundle.js';
|
|
25
|
-
import type { WorkspaceScriptPhase } from '../types/script-phase';
|
|
26
|
-
|
|
27
|
-
export type ScriptPhase = WorkspaceScriptPhase;
|
|
28
|
-
|
|
29
|
-
export interface RunWorkspaceScriptsOptions {
|
|
30
|
-
projectName: string;
|
|
31
|
-
workspacePath: string;
|
|
32
|
-
workspaceName: string;
|
|
33
|
-
repository: string;
|
|
34
|
-
/** If true, skip pre/setup scripts on first-time workspace access. */
|
|
35
|
-
noSetup?: boolean;
|
|
36
|
-
/** If true, scripts can prompt for input. If false (default), stdin is closed. */
|
|
37
|
-
interactive?: boolean;
|
|
38
|
-
/**
|
|
39
|
-
* Callback to receive ANSI output as it arrives (for TUI/Web terminal display).
|
|
40
|
-
* Called with raw output from stdout/stderr.
|
|
41
|
-
*/
|
|
42
|
-
onOutput?: (data: Buffer) => void;
|
|
43
|
-
/**
|
|
44
|
-
* Callback when a new phase starts (for displaying phase name in UI).
|
|
45
|
-
*/
|
|
46
|
-
onPhaseStart?: (phase: ScriptPhase) => void;
|
|
47
|
-
/** Script execution policy for session attach attempts. */
|
|
48
|
-
scriptPolicy?: 'auto' | 'skip';
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export type RunWorkspaceScriptsResult =
|
|
52
|
-
| { success: true }
|
|
53
|
-
| { success: false; phase: ScriptPhase; error: string };
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Run workspace scripts based on setup state.
|
|
57
|
-
*
|
|
58
|
-
* - If setup has already been run: runs select scripts only
|
|
59
|
-
* - If first time: runs pre scripts, then setup scripts, then marks setup complete
|
|
60
|
-
*
|
|
61
|
-
* Returns success or failure with the specific phase that failed.
|
|
62
|
-
*/
|
|
63
|
-
export async function runWorkspaceScripts(
|
|
64
|
-
options: RunWorkspaceScriptsOptions
|
|
65
|
-
): Promise<RunWorkspaceScriptsResult> {
|
|
66
|
-
const {
|
|
67
|
-
projectName,
|
|
68
|
-
workspacePath,
|
|
69
|
-
workspaceName,
|
|
70
|
-
repository,
|
|
71
|
-
noSetup = false,
|
|
72
|
-
interactive = false,
|
|
73
|
-
onOutput,
|
|
74
|
-
onPhaseStart,
|
|
75
|
-
scriptPolicy = 'auto',
|
|
76
|
-
} = options;
|
|
77
|
-
|
|
78
|
-
const changes = detectBundleChanges(projectName, workspacePath);
|
|
79
|
-
if (changes.parseError) {
|
|
80
|
-
return {
|
|
81
|
-
success: false,
|
|
82
|
-
phase: 'pre',
|
|
83
|
-
error: changes.parseError,
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const currentBundle = changes.currentBundle;
|
|
88
|
-
const bundleHash = changes.currentHash;
|
|
89
|
-
|
|
90
|
-
// Read project config for bundle values and secrets
|
|
91
|
-
const config = readProjectConfig(projectName);
|
|
92
|
-
|
|
93
|
-
const bundleSecretKeys = new Set(config.bundleSecretKeys || []);
|
|
94
|
-
for (const step of currentBundle?.onboarding || []) {
|
|
95
|
-
if (step.type === 'secret') {
|
|
96
|
-
bundleSecretKeys.add(step.configKey);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const bundleSecrets = bundleSecretKeys.size > 0
|
|
101
|
-
? await getProjectSecrets(projectName, [...bundleSecretKeys])
|
|
102
|
-
: {};
|
|
103
|
-
|
|
104
|
-
// Build script options
|
|
105
|
-
const scriptOptions: RunScriptsOptions = {
|
|
106
|
-
bundleValues: config.bundleValues,
|
|
107
|
-
bundleSecrets,
|
|
108
|
-
nonInteractive: !interactive,
|
|
109
|
-
onOutput,
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const existingLock = readWorkspaceLockState(workspacePath) || createEmptyWorkspaceLockState();
|
|
113
|
-
const stepFingerprints = currentBundle ? buildBundleStepFingerprints(currentBundle) : {};
|
|
114
|
-
|
|
115
|
-
const setupNeeded = scriptPolicy !== 'skip' && !noSetup && shouldRunSetup({
|
|
116
|
-
lock: existingLock,
|
|
117
|
-
bundle: currentBundle,
|
|
118
|
-
bundleHash,
|
|
119
|
-
stepFingerprints,
|
|
120
|
-
bundleValues: config.bundleValues || {},
|
|
121
|
-
bundleSecrets,
|
|
122
|
-
confirmHistory: config.bundleConfirmHistory || {},
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
let lockState: WorkspaceLockState = {
|
|
126
|
-
...existingLock,
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
if (setupNeeded) {
|
|
130
|
-
const preScriptsDir = join(workspacePath, '.gitspace', 'scripts', 'pre');
|
|
131
|
-
const preScripts = discoverScripts(preScriptsDir);
|
|
132
|
-
if (preScripts.length > 0) {
|
|
133
|
-
logger.warning('Bundle script phase "pre" is deprecated. Move scripts into ordered setup scripts.');
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
let preScriptsSucceeded = false;
|
|
137
|
-
try {
|
|
138
|
-
onPhaseStart?.('pre');
|
|
139
|
-
await runScriptsInTerminal(preScriptsDir, workspacePath, workspaceName, repository, scriptOptions);
|
|
140
|
-
preScriptsSucceeded = true;
|
|
141
|
-
|
|
142
|
-
onPhaseStart?.('setup');
|
|
143
|
-
const setupScriptsDir = join(workspacePath, '.gitspace', 'scripts', 'setup');
|
|
144
|
-
await runScriptsInTerminal(setupScriptsDir, workspacePath, workspaceName, repository, scriptOptions);
|
|
145
|
-
|
|
146
|
-
const confirmResults = resolveCurrentConfirmResults(
|
|
147
|
-
currentBundle,
|
|
148
|
-
stepFingerprints,
|
|
149
|
-
config.bundleConfirmHistory || {}
|
|
150
|
-
);
|
|
151
|
-
|
|
152
|
-
lockState = {
|
|
153
|
-
...lockState,
|
|
154
|
-
bundle: bundleHash
|
|
155
|
-
? {
|
|
156
|
-
bundleHash,
|
|
157
|
-
stepFingerprints,
|
|
158
|
-
}
|
|
159
|
-
: lockState.bundle,
|
|
160
|
-
setup: buildSetupState({
|
|
161
|
-
bundle: currentBundle,
|
|
162
|
-
bundleHash,
|
|
163
|
-
stepFingerprints,
|
|
164
|
-
bundleValues: config.bundleValues || {},
|
|
165
|
-
bundleSecrets,
|
|
166
|
-
confirmResults,
|
|
167
|
-
}),
|
|
168
|
-
};
|
|
169
|
-
writeWorkspaceLockState(workspacePath, lockState);
|
|
170
|
-
} catch (error) {
|
|
171
|
-
const phase: ScriptPhase = preScriptsSucceeded ? 'setup' : 'pre';
|
|
172
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
173
|
-
lockState = {
|
|
174
|
-
...lockState,
|
|
175
|
-
bundle: bundleHash
|
|
176
|
-
? {
|
|
177
|
-
bundleHash,
|
|
178
|
-
stepFingerprints,
|
|
179
|
-
}
|
|
180
|
-
: lockState.bundle,
|
|
181
|
-
setup: {
|
|
182
|
-
...lockState.setup,
|
|
183
|
-
status: 'failed',
|
|
184
|
-
ranAt: new Date().toISOString(),
|
|
185
|
-
error: message,
|
|
186
|
-
},
|
|
187
|
-
};
|
|
188
|
-
writeWorkspaceLockState(workspacePath, lockState);
|
|
189
|
-
return { success: false, phase, error: message };
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (scriptPolicy === 'skip') {
|
|
194
|
-
return { success: true };
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Always run select scripts on terminal attach (new session path).
|
|
198
|
-
const selectScriptsDir = join(workspacePath, '.gitspace', 'scripts', 'select');
|
|
199
|
-
try {
|
|
200
|
-
onPhaseStart?.('select');
|
|
201
|
-
await runScriptsInTerminal(selectScriptsDir, workspacePath, workspaceName, repository, scriptOptions);
|
|
202
|
-
lockState = {
|
|
203
|
-
...lockState,
|
|
204
|
-
select: {
|
|
205
|
-
status: 'success',
|
|
206
|
-
ranAt: new Date().toISOString(),
|
|
207
|
-
},
|
|
208
|
-
};
|
|
209
|
-
writeWorkspaceLockState(workspacePath, lockState);
|
|
210
|
-
return { success: true };
|
|
211
|
-
} catch (error) {
|
|
212
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
213
|
-
lockState = {
|
|
214
|
-
...lockState,
|
|
215
|
-
select: {
|
|
216
|
-
status: 'failed',
|
|
217
|
-
ranAt: new Date().toISOString(),
|
|
218
|
-
error: message,
|
|
219
|
-
},
|
|
220
|
-
};
|
|
221
|
-
writeWorkspaceLockState(workspacePath, lockState);
|
|
222
|
-
return { success: false, phase: 'select', error: message };
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
function resolveCurrentConfirmResults(
|
|
227
|
-
bundle: SpacesBundle | undefined,
|
|
228
|
-
stepFingerprints: Record<string, string>,
|
|
229
|
-
confirmHistory: Record<string, { status: ConfirmStepResult['status'] }>
|
|
230
|
-
): Record<string, ConfirmStepResult> {
|
|
231
|
-
const results: Record<string, ConfirmStepResult> = {};
|
|
232
|
-
if (!bundle) {
|
|
233
|
-
return results;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
for (const step of bundle.onboarding || []) {
|
|
237
|
-
if (step.type !== 'confirm') {
|
|
238
|
-
continue;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const key = getBundleStepKey(step);
|
|
242
|
-
const fingerprint = stepFingerprints[key];
|
|
243
|
-
if (!fingerprint) {
|
|
244
|
-
continue;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const history = confirmHistory[fingerprint];
|
|
248
|
-
if (!history) {
|
|
249
|
-
continue;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
results[step.id] = {
|
|
253
|
-
status: history.status,
|
|
254
|
-
checkCommand: step.checkCommand,
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return results;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
interface SetupDecisionOptions {
|
|
262
|
-
lock: WorkspaceLockState;
|
|
263
|
-
bundle: SpacesBundle | undefined;
|
|
264
|
-
bundleHash: string | undefined;
|
|
265
|
-
stepFingerprints: Record<string, string>;
|
|
266
|
-
bundleValues: Record<string, string>;
|
|
267
|
-
bundleSecrets: Record<string, string>;
|
|
268
|
-
confirmHistory: Record<string, { status: ConfirmStepResult['status'] }>;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function shouldRunSetup(options: SetupDecisionOptions): boolean {
|
|
272
|
-
const {
|
|
273
|
-
lock,
|
|
274
|
-
bundle,
|
|
275
|
-
bundleHash,
|
|
276
|
-
stepFingerprints,
|
|
277
|
-
bundleValues,
|
|
278
|
-
bundleSecrets,
|
|
279
|
-
confirmHistory,
|
|
280
|
-
} = options;
|
|
281
|
-
|
|
282
|
-
if (lock.setup.status !== 'success') {
|
|
283
|
-
return true;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
if (!bundle) {
|
|
287
|
-
return false;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const previousBundle = lock.bundle;
|
|
291
|
-
if (!previousBundle || !bundleHash) {
|
|
292
|
-
return true;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
const relevantSteps = getRelevantSetupSteps(bundle.onboarding || [], lock.setup.usedOptionalSteps);
|
|
296
|
-
|
|
297
|
-
for (const step of relevantSteps) {
|
|
298
|
-
const key = getBundleStepKey(step);
|
|
299
|
-
const currentStepFingerprint = stepFingerprints[key];
|
|
300
|
-
if (!currentStepFingerprint || previousBundle.stepFingerprints[key] !== currentStepFingerprint) {
|
|
301
|
-
return true;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
if (step.type === 'input') {
|
|
305
|
-
const value = bundleValues[step.configKey] ?? '';
|
|
306
|
-
const currentValueFingerprint = fingerprintValue(value);
|
|
307
|
-
if (lock.setup.inputFingerprints[step.configKey] !== currentValueFingerprint) {
|
|
308
|
-
return true;
|
|
309
|
-
}
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (step.type === 'secret') {
|
|
314
|
-
const value = bundleSecrets[step.configKey] ?? '';
|
|
315
|
-
const currentSecretFingerprint = fingerprintValue(value);
|
|
316
|
-
if (lock.setup.secretFingerprints[step.configKey] !== currentSecretFingerprint) {
|
|
317
|
-
return true;
|
|
318
|
-
}
|
|
319
|
-
continue;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
if (step.type === 'confirm') {
|
|
323
|
-
const previous = lock.setup.confirmsUsed[key];
|
|
324
|
-
if (!previous) {
|
|
325
|
-
continue;
|
|
326
|
-
}
|
|
327
|
-
const history = confirmHistory[currentStepFingerprint];
|
|
328
|
-
const currentStatus = history?.status;
|
|
329
|
-
if (previous.status !== currentStatus || previous.fingerprint !== currentStepFingerprint) {
|
|
330
|
-
return true;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
return false;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
function getRelevantSetupSteps(
|
|
339
|
-
steps: OnboardingStep[],
|
|
340
|
-
usedOptionalSteps: Record<string, true>
|
|
341
|
-
): OnboardingStep[] {
|
|
342
|
-
return steps.filter((step) => {
|
|
343
|
-
const required = step.required !== false;
|
|
344
|
-
if (required) {
|
|
345
|
-
return step.type === 'input' || step.type === 'secret' || step.type === 'confirm';
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
const key = getBundleStepKey(step);
|
|
349
|
-
if (!usedOptionalSteps[key]) {
|
|
350
|
-
return false;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
return step.type === 'input' || step.type === 'secret' || step.type === 'confirm';
|
|
354
|
-
});
|
|
355
|
-
}
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'bun:test';
|
|
2
|
-
import {
|
|
3
|
-
sanitizeForFileSystem,
|
|
4
|
-
generateWorkspaceName,
|
|
5
|
-
isValidWorkspaceName,
|
|
6
|
-
isValidBranchName,
|
|
7
|
-
extractRepoName,
|
|
8
|
-
} from './sanitize';
|
|
9
|
-
|
|
10
|
-
describe('sanitizeForFileSystem', () => {
|
|
11
|
-
it('should convert slashes to hyphens', () => {
|
|
12
|
-
expect(sanitizeForFileSystem('fix/bla-bla-blah')).toBe('fix-bla-bla-blah');
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('should handle feature branch patterns', () => {
|
|
16
|
-
expect(sanitizeForFileSystem('feature/user-auth')).toBe('feature-user-auth');
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('should handle multiple slashes', () => {
|
|
20
|
-
expect(sanitizeForFileSystem('feature/auth/oauth')).toBe('feature-auth-oauth');
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('should convert to lowercase', () => {
|
|
24
|
-
expect(sanitizeForFileSystem('Fix/BLA-Blah')).toBe('fix-bla-blah');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('should collapse multiple hyphens', () => {
|
|
28
|
-
expect(sanitizeForFileSystem('fix//double-slash')).toBe('fix-double-slash');
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('should remove leading and trailing hyphens', () => {
|
|
32
|
-
expect(sanitizeForFileSystem('/leading-slash')).toBe('leading-slash');
|
|
33
|
-
expect(sanitizeForFileSystem('trailing-slash/')).toBe('trailing-slash');
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('should handle spaces', () => {
|
|
37
|
-
expect(sanitizeForFileSystem('fix login bug')).toBe('fix-login-bug');
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('should handle special characters', () => {
|
|
41
|
-
expect(sanitizeForFileSystem('fix!@#$%bug')).toBe('fix-bug');
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should preserve underscores', () => {
|
|
45
|
-
expect(sanitizeForFileSystem('fix_login_bug')).toBe('fix_login_bug');
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('should return empty string for input with no valid characters', () => {
|
|
49
|
-
expect(sanitizeForFileSystem('!@#$%')).toBe('');
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('should limit length to 100 characters', () => {
|
|
53
|
-
const longName = 'a'.repeat(150);
|
|
54
|
-
expect(sanitizeForFileSystem(longName).length).toBe(100);
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
describe('isValidWorkspaceName', () => {
|
|
59
|
-
it('should accept valid names', () => {
|
|
60
|
-
expect(isValidWorkspaceName('my-workspace')).toBe(true);
|
|
61
|
-
expect(isValidWorkspaceName('workspace123')).toBe(true);
|
|
62
|
-
expect(isValidWorkspaceName('my_workspace')).toBe(true);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('should reject names with spaces', () => {
|
|
66
|
-
expect(isValidWorkspaceName('my workspace')).toBe(false);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('should reject names with slashes', () => {
|
|
70
|
-
expect(isValidWorkspaceName('fix/bug')).toBe(false);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('should reject empty names', () => {
|
|
74
|
-
expect(isValidWorkspaceName('')).toBe(false);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('should reject names starting or ending with hyphens', () => {
|
|
78
|
-
expect(isValidWorkspaceName('-workspace')).toBe(false);
|
|
79
|
-
expect(isValidWorkspaceName('workspace-')).toBe(false);
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
describe('isValidBranchName', () => {
|
|
84
|
-
it('should accept valid branch names with slashes', () => {
|
|
85
|
-
expect(isValidBranchName('fix/bla-bla-blah')).toBe(true);
|
|
86
|
-
expect(isValidBranchName('feature/user-auth')).toBe(true);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('should accept simple branch names', () => {
|
|
90
|
-
expect(isValidBranchName('main')).toBe(true);
|
|
91
|
-
expect(isValidBranchName('develop')).toBe(true);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('should reject names with consecutive dots', () => {
|
|
95
|
-
expect(isValidBranchName('branch..name')).toBe(false);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('should reject names starting or ending with slash', () => {
|
|
99
|
-
expect(isValidBranchName('/branch')).toBe(false);
|
|
100
|
-
expect(isValidBranchName('branch/')).toBe(false);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should reject names with consecutive slashes', () => {
|
|
104
|
-
expect(isValidBranchName('branch//name')).toBe(false);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('should reject names ending with .lock', () => {
|
|
108
|
-
expect(isValidBranchName('branch.lock')).toBe(false);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('should reject names with special characters', () => {
|
|
112
|
-
expect(isValidBranchName('branch~name')).toBe(false);
|
|
113
|
-
expect(isValidBranchName('branch^name')).toBe(false);
|
|
114
|
-
expect(isValidBranchName('branch:name')).toBe(false);
|
|
115
|
-
expect(isValidBranchName('branch?name')).toBe(false);
|
|
116
|
-
expect(isValidBranchName('branch*name')).toBe(false);
|
|
117
|
-
expect(isValidBranchName('branch[name')).toBe(false);
|
|
118
|
-
expect(isValidBranchName('branch\\name')).toBe(false);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('should reject components starting with dot', () => {
|
|
122
|
-
expect(isValidBranchName('.hidden')).toBe(false);
|
|
123
|
-
expect(isValidBranchName('feature/.hidden')).toBe(false);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('should reject names starting with dash', () => {
|
|
127
|
-
expect(isValidBranchName('-branch')).toBe(false);
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
describe('generateWorkspaceName', () => {
|
|
132
|
-
it('should combine identifier and sanitized title', () => {
|
|
133
|
-
expect(generateWorkspaceName('ENG-123', 'Fix Login Bug!')).toBe('eng-123-fix-login-bug');
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it('should handle spaces in title', () => {
|
|
137
|
-
expect(generateWorkspaceName('FEAT-456', 'Add Dark Mode')).toBe('feat-456-add-dark-mode');
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
describe('extractRepoName', () => {
|
|
142
|
-
it('should extract repo name from owner/repo format', () => {
|
|
143
|
-
expect(extractRepoName('myorg/my-app')).toBe('my-app');
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it('should handle simple repo name', () => {
|
|
147
|
-
expect(extractRepoName('my-app')).toBe('my-app');
|
|
148
|
-
});
|
|
149
|
-
});
|
package/src/utils/sanitize.ts
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Name sanitization utilities for filesystem safety
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Sanitize a string for filesystem use
|
|
7
|
-
* - Converts to lowercase
|
|
8
|
-
* - Replaces invalid characters with hyphens
|
|
9
|
-
* - Collapses multiple hyphens
|
|
10
|
-
* - Removes leading/trailing hyphens
|
|
11
|
-
* - Limits length to 100 characters
|
|
12
|
-
*
|
|
13
|
-
* @param name String to sanitize
|
|
14
|
-
* @returns Sanitized string safe for filesystem
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* sanitizeForFileSystem("Fix Login Bug!") // "fix-login-bug"
|
|
18
|
-
* sanitizeForFileSystem("Feature / User Settings") // "feature-user-settings"
|
|
19
|
-
*/
|
|
20
|
-
export function sanitizeForFileSystem(name: string): string {
|
|
21
|
-
return (
|
|
22
|
-
name
|
|
23
|
-
.toLowerCase()
|
|
24
|
-
// Replace any non-alphanumeric characters (except hyphens and underscores) with hyphen
|
|
25
|
-
.replace(/[^a-z0-9-_]/g, '-')
|
|
26
|
-
// Collapse multiple hyphens into one
|
|
27
|
-
.replace(/-+/g, '-')
|
|
28
|
-
// Remove leading and trailing hyphens
|
|
29
|
-
.replace(/^-|-$/g, '')
|
|
30
|
-
// Limit length to 100 characters
|
|
31
|
-
.slice(0, 100)
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Generate a workspace name from a Linear issue identifier and title
|
|
37
|
-
*
|
|
38
|
-
* @param identifier Issue identifier (e.g., "ENG-123")
|
|
39
|
-
* @param title Issue title
|
|
40
|
-
* @returns Workspace name in format "{identifier}-{sanitized-title}"
|
|
41
|
-
*
|
|
42
|
-
* @example
|
|
43
|
-
* generateWorkspaceName("ENG-123", "Fix Login Bug!") // "eng-123-fix-login-bug"
|
|
44
|
-
* generateWorkspaceName("FEAT-456", "Add Dark Mode") // "feat-456-add-dark-mode"
|
|
45
|
-
*/
|
|
46
|
-
export function generateWorkspaceName(identifier: string, title: string): string {
|
|
47
|
-
const sanitizedTitle = sanitizeForFileSystem(title);
|
|
48
|
-
const sanitizedIdentifier = identifier.toLowerCase();
|
|
49
|
-
|
|
50
|
-
return `${sanitizedIdentifier}-${sanitizedTitle}`;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Validate a workspace name
|
|
55
|
-
* - Must contain only alphanumeric characters, hyphens, and underscores
|
|
56
|
-
* - Must not contain spaces
|
|
57
|
-
* - Must not be empty
|
|
58
|
-
* - Must not start or end with a hyphen
|
|
59
|
-
*
|
|
60
|
-
* @param name Workspace name to validate
|
|
61
|
-
* @returns true if valid, false otherwise
|
|
62
|
-
*/
|
|
63
|
-
export function isValidWorkspaceName(name: string): boolean {
|
|
64
|
-
if (!name || name.length === 0) {
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Check for spaces (explicitly rejected)
|
|
69
|
-
if (name.includes(' ')) {
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Check for valid characters
|
|
74
|
-
if (!/^[a-z0-9-_]+$/i.test(name)) {
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Check for leading/trailing hyphens
|
|
79
|
-
if (name.startsWith('-') || name.endsWith('-')) {
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Validate a git branch name according to git-check-ref-format rules
|
|
88
|
-
* See: https://git-scm.com/docs/git-check-ref-format
|
|
89
|
-
*
|
|
90
|
-
* @param name Branch name to validate
|
|
91
|
-
* @returns true if valid, false otherwise
|
|
92
|
-
*/
|
|
93
|
-
export function isValidBranchName(name: string): boolean {
|
|
94
|
-
if (!name || name.length === 0) {
|
|
95
|
-
return false;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Cannot have two consecutive dots
|
|
99
|
-
if (name.includes('..')) {
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Cannot have ASCII control characters, space, tilde, caret, colon,
|
|
104
|
-
// question mark, asterisk, open bracket, or backslash
|
|
105
|
-
if (/[\x00-\x1f\x7f ~^:?*\[\\]/.test(name)) {
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Cannot start or end with a slash, or have consecutive slashes
|
|
110
|
-
if (name.startsWith('/') || name.endsWith('/') || name.includes('//')) {
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Cannot end with a dot
|
|
115
|
-
if (name.endsWith('.')) {
|
|
116
|
-
return false;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Cannot contain @{
|
|
120
|
-
if (name.includes('@{')) {
|
|
121
|
-
return false;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Cannot be exactly @
|
|
125
|
-
if (name === '@') {
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Cannot end with .lock
|
|
130
|
-
if (name.endsWith('.lock')) {
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Cannot start with a dash
|
|
135
|
-
if (name.startsWith('-')) {
|
|
136
|
-
return false;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Each component cannot start with a dot
|
|
140
|
-
const components = name.split('/');
|
|
141
|
-
for (const component of components) {
|
|
142
|
-
if (component.startsWith('.') || component.length === 0) {
|
|
143
|
-
return false;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return true;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Extract repository name from owner/repo format
|
|
152
|
-
*
|
|
153
|
-
* @param repository Repository in "owner/repo" format
|
|
154
|
-
* @returns Repository name
|
|
155
|
-
*
|
|
156
|
-
* @example
|
|
157
|
-
* extractRepoName("myorg/my-app") // "my-app"
|
|
158
|
-
*/
|
|
159
|
-
export function extractRepoName(repository: string): string {
|
|
160
|
-
const parts = repository.split('/');
|
|
161
|
-
return parts[parts.length - 1];
|
|
162
|
-
}
|