gitspace 0.2.0-rc.2 → 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.
Files changed (316) hide show
  1. package/README.md +68 -38
  2. package/package.json +36 -25
  3. package/.claude/settings.local.json +0 -21
  4. package/.gitspace/bundle.json +0 -50
  5. package/.gitspace/select/01-status.sh +0 -40
  6. package/.gitspace/setup/01-install-deps.sh +0 -12
  7. package/.gitspace/setup/02-typecheck.sh +0 -16
  8. package/AGENTS.md +0 -439
  9. package/CLAUDE.md +0 -1
  10. package/bun.lock +0 -647
  11. package/docs/CONNECTION.md +0 -623
  12. package/docs/GATEWAY-WORKER.md +0 -319
  13. package/docs/GETTING-STARTED.md +0 -448
  14. package/docs/GITSPACE-PLATFORM.md +0 -1819
  15. package/docs/INFRASTRUCTURE.md +0 -1347
  16. package/docs/PROTOCOL.md +0 -619
  17. package/docs/QUICKSTART.md +0 -174
  18. package/docs/RELAY.md +0 -327
  19. package/docs/REMOTE-DESIGN.md +0 -549
  20. package/docs/ROADMAP.md +0 -564
  21. package/docs/SITE_DOCS_FIGMA_MAKE.md +0 -1167
  22. package/docs/STACK-DESIGN.md +0 -588
  23. package/docs/UNIFIED_ARCHITECTURE.md +0 -292
  24. package/experiments/pty-benchmark.ts +0 -148
  25. package/experiments/pty-latency.ts +0 -100
  26. package/experiments/router/client.ts +0 -199
  27. package/experiments/router/protocol.ts +0 -74
  28. package/experiments/router/router.ts +0 -217
  29. package/experiments/router/session.ts +0 -180
  30. package/experiments/router/test.ts +0 -133
  31. package/experiments/socket-bandwidth.ts +0 -77
  32. package/homebrew/gitspace.rb +0 -45
  33. package/landing-page/ATTRIBUTIONS.md +0 -3
  34. package/landing-page/README.md +0 -11
  35. package/landing-page/bun.lock +0 -801
  36. package/landing-page/guidelines/Guidelines.md +0 -61
  37. package/landing-page/index.html +0 -37
  38. package/landing-page/package.json +0 -90
  39. package/landing-page/postcss.config.mjs +0 -15
  40. package/landing-page/public/_redirects +0 -1
  41. package/landing-page/public/favicon.png +0 -0
  42. package/landing-page/src/app/App.tsx +0 -53
  43. package/landing-page/src/app/components/figma/ImageWithFallback.tsx +0 -27
  44. package/landing-page/src/app/components/ui/accordion.tsx +0 -66
  45. package/landing-page/src/app/components/ui/alert-dialog.tsx +0 -157
  46. package/landing-page/src/app/components/ui/alert.tsx +0 -66
  47. package/landing-page/src/app/components/ui/aspect-ratio.tsx +0 -11
  48. package/landing-page/src/app/components/ui/avatar.tsx +0 -53
  49. package/landing-page/src/app/components/ui/badge.tsx +0 -46
  50. package/landing-page/src/app/components/ui/breadcrumb.tsx +0 -109
  51. package/landing-page/src/app/components/ui/button.tsx +0 -57
  52. package/landing-page/src/app/components/ui/calendar.tsx +0 -75
  53. package/landing-page/src/app/components/ui/card.tsx +0 -92
  54. package/landing-page/src/app/components/ui/carousel.tsx +0 -241
  55. package/landing-page/src/app/components/ui/chart.tsx +0 -353
  56. package/landing-page/src/app/components/ui/checkbox.tsx +0 -32
  57. package/landing-page/src/app/components/ui/collapsible.tsx +0 -33
  58. package/landing-page/src/app/components/ui/command.tsx +0 -177
  59. package/landing-page/src/app/components/ui/context-menu.tsx +0 -252
  60. package/landing-page/src/app/components/ui/dialog.tsx +0 -135
  61. package/landing-page/src/app/components/ui/drawer.tsx +0 -132
  62. package/landing-page/src/app/components/ui/dropdown-menu.tsx +0 -257
  63. package/landing-page/src/app/components/ui/form.tsx +0 -168
  64. package/landing-page/src/app/components/ui/hover-card.tsx +0 -44
  65. package/landing-page/src/app/components/ui/input-otp.tsx +0 -77
  66. package/landing-page/src/app/components/ui/input.tsx +0 -21
  67. package/landing-page/src/app/components/ui/label.tsx +0 -24
  68. package/landing-page/src/app/components/ui/menubar.tsx +0 -276
  69. package/landing-page/src/app/components/ui/navigation-menu.tsx +0 -168
  70. package/landing-page/src/app/components/ui/pagination.tsx +0 -127
  71. package/landing-page/src/app/components/ui/popover.tsx +0 -48
  72. package/landing-page/src/app/components/ui/progress.tsx +0 -31
  73. package/landing-page/src/app/components/ui/radio-group.tsx +0 -45
  74. package/landing-page/src/app/components/ui/resizable.tsx +0 -56
  75. package/landing-page/src/app/components/ui/scroll-area.tsx +0 -58
  76. package/landing-page/src/app/components/ui/select.tsx +0 -189
  77. package/landing-page/src/app/components/ui/separator.tsx +0 -28
  78. package/landing-page/src/app/components/ui/sheet.tsx +0 -139
  79. package/landing-page/src/app/components/ui/sidebar.tsx +0 -726
  80. package/landing-page/src/app/components/ui/skeleton.tsx +0 -13
  81. package/landing-page/src/app/components/ui/slider.tsx +0 -63
  82. package/landing-page/src/app/components/ui/sonner.tsx +0 -25
  83. package/landing-page/src/app/components/ui/switch.tsx +0 -31
  84. package/landing-page/src/app/components/ui/table.tsx +0 -116
  85. package/landing-page/src/app/components/ui/tabs.tsx +0 -66
  86. package/landing-page/src/app/components/ui/textarea.tsx +0 -18
  87. package/landing-page/src/app/components/ui/toggle-group.tsx +0 -73
  88. package/landing-page/src/app/components/ui/toggle.tsx +0 -47
  89. package/landing-page/src/app/components/ui/tooltip.tsx +0 -61
  90. package/landing-page/src/app/components/ui/use-mobile.ts +0 -21
  91. package/landing-page/src/app/components/ui/utils.ts +0 -6
  92. package/landing-page/src/components/docs/DocsContent.tsx +0 -718
  93. package/landing-page/src/components/docs/DocsSidebar.tsx +0 -84
  94. package/landing-page/src/components/landing/CTA.tsx +0 -59
  95. package/landing-page/src/components/landing/Comparison.tsx +0 -84
  96. package/landing-page/src/components/landing/FaultyTerminal.tsx +0 -424
  97. package/landing-page/src/components/landing/Features.tsx +0 -201
  98. package/landing-page/src/components/landing/Hero.tsx +0 -142
  99. package/landing-page/src/components/landing/Pricing.tsx +0 -140
  100. package/landing-page/src/components/landing/Roadmap.tsx +0 -86
  101. package/landing-page/src/components/landing/Security.tsx +0 -81
  102. package/landing-page/src/components/landing/TerminalWindow.tsx +0 -27
  103. package/landing-page/src/components/landing/UseCases.tsx +0 -55
  104. package/landing-page/src/components/landing/Workflow.tsx +0 -101
  105. package/landing-page/src/components/layout/DashboardNavbar.tsx +0 -37
  106. package/landing-page/src/components/layout/Footer.tsx +0 -55
  107. package/landing-page/src/components/layout/LandingNavbar.tsx +0 -82
  108. package/landing-page/src/components/ui/badge.tsx +0 -39
  109. package/landing-page/src/components/ui/breadcrumb.tsx +0 -115
  110. package/landing-page/src/components/ui/button.tsx +0 -57
  111. package/landing-page/src/components/ui/card.tsx +0 -79
  112. package/landing-page/src/components/ui/mock-terminal.tsx +0 -68
  113. package/landing-page/src/components/ui/separator.tsx +0 -28
  114. package/landing-page/src/lib/utils.ts +0 -6
  115. package/landing-page/src/main.tsx +0 -10
  116. package/landing-page/src/pages/Dashboard.tsx +0 -133
  117. package/landing-page/src/pages/DocsPage.tsx +0 -79
  118. package/landing-page/src/pages/LandingPage.tsx +0 -31
  119. package/landing-page/src/pages/TerminalView.tsx +0 -106
  120. package/landing-page/src/styles/fonts.css +0 -0
  121. package/landing-page/src/styles/index.css +0 -3
  122. package/landing-page/src/styles/tailwind.css +0 -4
  123. package/landing-page/src/styles/theme.css +0 -181
  124. package/landing-page/vite.config.ts +0 -19
  125. package/npm/darwin-arm64/bin/gssh +0 -0
  126. package/npm/darwin-arm64/package.json +0 -20
  127. package/scripts/build.ts +0 -298
  128. package/scripts/release.ts +0 -140
  129. package/src/__tests__/test-utils.ts +0 -298
  130. package/src/commands/__tests__/serve-messages.test.ts +0 -190
  131. package/src/commands/access.ts +0 -298
  132. package/src/commands/add.ts +0 -452
  133. package/src/commands/auth.ts +0 -364
  134. package/src/commands/connect.ts +0 -287
  135. package/src/commands/directory.ts +0 -16
  136. package/src/commands/host.ts +0 -396
  137. package/src/commands/identity.ts +0 -184
  138. package/src/commands/list.ts +0 -200
  139. package/src/commands/relay.ts +0 -315
  140. package/src/commands/remove.ts +0 -241
  141. package/src/commands/serve.ts +0 -1493
  142. package/src/commands/share.ts +0 -456
  143. package/src/commands/status.ts +0 -125
  144. package/src/commands/switch.ts +0 -353
  145. package/src/commands/tmux.ts +0 -317
  146. package/src/core/__tests__/access.test.ts +0 -240
  147. package/src/core/access.ts +0 -277
  148. package/src/core/bundle.ts +0 -342
  149. package/src/core/config.ts +0 -510
  150. package/src/core/git.ts +0 -317
  151. package/src/core/github.ts +0 -151
  152. package/src/core/identity.ts +0 -631
  153. package/src/core/linear.ts +0 -225
  154. package/src/core/shell.ts +0 -161
  155. package/src/core/trusted-relays.ts +0 -315
  156. package/src/index.ts +0 -810
  157. package/src/lib/remote-session/index.ts +0 -7
  158. package/src/lib/remote-session/protocol.ts +0 -267
  159. package/src/lib/remote-session/session-handler.ts +0 -581
  160. package/src/lib/remote-session/workspace-scanner.ts +0 -167
  161. package/src/lib/tmux-lite/README.md +0 -81
  162. package/src/lib/tmux-lite/cli.ts +0 -796
  163. package/src/lib/tmux-lite/crypto/__tests__/helpers/handshake-runner.ts +0 -349
  164. package/src/lib/tmux-lite/crypto/__tests__/helpers/mock-relay.ts +0 -291
  165. package/src/lib/tmux-lite/crypto/__tests__/helpers/test-identities.ts +0 -142
  166. package/src/lib/tmux-lite/crypto/__tests__/integration/authorization.integration.test.ts +0 -339
  167. package/src/lib/tmux-lite/crypto/__tests__/integration/e2e-communication.integration.test.ts +0 -477
  168. package/src/lib/tmux-lite/crypto/__tests__/integration/error-handling.integration.test.ts +0 -499
  169. package/src/lib/tmux-lite/crypto/__tests__/integration/handshake.integration.test.ts +0 -371
  170. package/src/lib/tmux-lite/crypto/__tests__/integration/security.integration.test.ts +0 -573
  171. package/src/lib/tmux-lite/crypto/access-control.test.ts +0 -512
  172. package/src/lib/tmux-lite/crypto/access-control.ts +0 -320
  173. package/src/lib/tmux-lite/crypto/frames.test.ts +0 -262
  174. package/src/lib/tmux-lite/crypto/frames.ts +0 -141
  175. package/src/lib/tmux-lite/crypto/handshake.ts +0 -894
  176. package/src/lib/tmux-lite/crypto/identity.test.ts +0 -220
  177. package/src/lib/tmux-lite/crypto/identity.ts +0 -286
  178. package/src/lib/tmux-lite/crypto/index.ts +0 -51
  179. package/src/lib/tmux-lite/crypto/invites.test.ts +0 -381
  180. package/src/lib/tmux-lite/crypto/invites.ts +0 -215
  181. package/src/lib/tmux-lite/crypto/keyexchange.ts +0 -435
  182. package/src/lib/tmux-lite/crypto/keys.test.ts +0 -58
  183. package/src/lib/tmux-lite/crypto/keys.ts +0 -47
  184. package/src/lib/tmux-lite/crypto/secretbox.test.ts +0 -169
  185. package/src/lib/tmux-lite/crypto/secretbox.ts +0 -124
  186. package/src/lib/tmux-lite/handshake-handler.ts +0 -451
  187. package/src/lib/tmux-lite/protocol.test.ts +0 -307
  188. package/src/lib/tmux-lite/protocol.ts +0 -266
  189. package/src/lib/tmux-lite/relay-client.ts +0 -506
  190. package/src/lib/tmux-lite/server.ts +0 -1250
  191. package/src/lib/tmux-lite/shell-integration.sh +0 -37
  192. package/src/lib/tmux-lite/terminal-queries.test.ts +0 -54
  193. package/src/lib/tmux-lite/terminal-queries.ts +0 -49
  194. package/src/relay/__tests__/e2e-flow.test.ts +0 -1284
  195. package/src/relay/__tests__/helpers/auth.ts +0 -354
  196. package/src/relay/__tests__/helpers/ports.ts +0 -51
  197. package/src/relay/__tests__/protocol-validation.test.ts +0 -265
  198. package/src/relay/authorization.ts +0 -303
  199. package/src/relay/embedded-assets.generated.d.ts +0 -15
  200. package/src/relay/identity.ts +0 -352
  201. package/src/relay/index.ts +0 -57
  202. package/src/relay/pipes.test.ts +0 -427
  203. package/src/relay/pipes.ts +0 -195
  204. package/src/relay/protocol.ts +0 -804
  205. package/src/relay/registries.test.ts +0 -437
  206. package/src/relay/registries.ts +0 -593
  207. package/src/relay/server.test.ts +0 -1323
  208. package/src/relay/server.ts +0 -1092
  209. package/src/relay/signing.ts +0 -238
  210. package/src/relay/types.ts +0 -69
  211. package/src/serve/client-session-manager.ts +0 -622
  212. package/src/serve/daemon.ts +0 -497
  213. package/src/serve/pty-session.ts +0 -236
  214. package/src/serve/types.ts +0 -169
  215. package/src/shared/components/Flow.tsx +0 -453
  216. package/src/shared/components/Flow.tui.tsx +0 -343
  217. package/src/shared/components/Flow.web.tsx +0 -442
  218. package/src/shared/components/Inbox.tsx +0 -446
  219. package/src/shared/components/Inbox.tui.tsx +0 -262
  220. package/src/shared/components/Inbox.web.tsx +0 -329
  221. package/src/shared/components/MachineList.tsx +0 -187
  222. package/src/shared/components/MachineList.tui.tsx +0 -161
  223. package/src/shared/components/MachineList.web.tsx +0 -210
  224. package/src/shared/components/ProjectList.tsx +0 -176
  225. package/src/shared/components/ProjectList.tui.tsx +0 -109
  226. package/src/shared/components/ProjectList.web.tsx +0 -143
  227. package/src/shared/components/SpacesBrowser.tsx +0 -332
  228. package/src/shared/components/SpacesBrowser.tui.tsx +0 -163
  229. package/src/shared/components/SpacesBrowser.web.tsx +0 -221
  230. package/src/shared/components/index.ts +0 -103
  231. package/src/shared/hooks/index.ts +0 -16
  232. package/src/shared/hooks/useNavigation.ts +0 -226
  233. package/src/shared/index.ts +0 -122
  234. package/src/shared/providers/LocalMachineProvider.ts +0 -425
  235. package/src/shared/providers/MachineProvider.ts +0 -165
  236. package/src/shared/providers/RemoteMachineProvider.ts +0 -444
  237. package/src/shared/providers/index.ts +0 -26
  238. package/src/shared/types.ts +0 -145
  239. package/src/tui/adapters.ts +0 -120
  240. package/src/tui/app.tsx +0 -1816
  241. package/src/tui/components/Terminal.tsx +0 -580
  242. package/src/tui/hooks/index.ts +0 -35
  243. package/src/tui/hooks/useAppState.ts +0 -314
  244. package/src/tui/hooks/useDaemonStatus.ts +0 -174
  245. package/src/tui/hooks/useInboxTUI.ts +0 -113
  246. package/src/tui/hooks/useRemoteMachines.ts +0 -209
  247. package/src/tui/index.ts +0 -24
  248. package/src/tui/state.ts +0 -299
  249. package/src/tui/terminal-bracketed-paste.test.ts +0 -45
  250. package/src/tui/terminal-bracketed-paste.ts +0 -47
  251. package/src/types/bundle.ts +0 -112
  252. package/src/types/config.ts +0 -89
  253. package/src/types/errors.ts +0 -206
  254. package/src/types/identity.ts +0 -284
  255. package/src/types/workspace-fuzzy.ts +0 -49
  256. package/src/types/workspace.ts +0 -151
  257. package/src/utils/bun-socket-writer.ts +0 -80
  258. package/src/utils/deps.ts +0 -127
  259. package/src/utils/fuzzy-match.ts +0 -125
  260. package/src/utils/logger.ts +0 -127
  261. package/src/utils/markdown.ts +0 -254
  262. package/src/utils/onboarding.ts +0 -229
  263. package/src/utils/prompts.ts +0 -114
  264. package/src/utils/run-commands.ts +0 -112
  265. package/src/utils/run-scripts.ts +0 -142
  266. package/src/utils/sanitize.ts +0 -98
  267. package/src/utils/secrets.ts +0 -122
  268. package/src/utils/shell-escape.ts +0 -40
  269. package/src/utils/utf8.ts +0 -79
  270. package/src/utils/workspace-state.ts +0 -47
  271. package/src/web/README.md +0 -73
  272. package/src/web/bun.lock +0 -575
  273. package/src/web/eslint.config.js +0 -23
  274. package/src/web/index.html +0 -16
  275. package/src/web/package.json +0 -37
  276. package/src/web/public/vite.svg +0 -1
  277. package/src/web/src/App.tsx +0 -604
  278. package/src/web/src/assets/react.svg +0 -1
  279. package/src/web/src/components/Terminal.tsx +0 -207
  280. package/src/web/src/hooks/useRelayConnection.ts +0 -224
  281. package/src/web/src/hooks/useTerminal.ts +0 -699
  282. package/src/web/src/index.css +0 -55
  283. package/src/web/src/lib/crypto/__tests__/web-terminal.test.ts +0 -1158
  284. package/src/web/src/lib/crypto/frames.ts +0 -205
  285. package/src/web/src/lib/crypto/handshake.ts +0 -396
  286. package/src/web/src/lib/crypto/identity.ts +0 -128
  287. package/src/web/src/lib/crypto/keyexchange.ts +0 -246
  288. package/src/web/src/lib/crypto/relay-signing.ts +0 -53
  289. package/src/web/src/lib/invite.ts +0 -58
  290. package/src/web/src/lib/storage/identity-store.ts +0 -94
  291. package/src/web/src/main.tsx +0 -10
  292. package/src/web/src/types/identity.ts +0 -45
  293. package/src/web/tsconfig.app.json +0 -28
  294. package/src/web/tsconfig.json +0 -7
  295. package/src/web/tsconfig.node.json +0 -26
  296. package/src/web/vite.config.ts +0 -31
  297. package/todo-security.md +0 -92
  298. package/tsconfig.json +0 -23
  299. package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/12b7107e435bf1b9a8713a7f320472a63e543104d633d89a26f8d21f4e4ef182.sqlite +0 -0
  300. package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/12b7107e435bf1b9a8713a7f320472a63e543104d633d89a26f8d21f4e4ef182.sqlite-shm +0 -0
  301. package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/12b7107e435bf1b9a8713a7f320472a63e543104d633d89a26f8d21f4e4ef182.sqlite-wal +0 -0
  302. package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/1a1ac3db1ab86ecf712f90322868a9aabc2c7dc9fe2dfbe94f9b075096276b0f.sqlite +0 -0
  303. package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/1a1ac3db1ab86ecf712f90322868a9aabc2c7dc9fe2dfbe94f9b075096276b0f.sqlite-shm +0 -0
  304. package/worker/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/1a1ac3db1ab86ecf712f90322868a9aabc2c7dc9fe2dfbe94f9b075096276b0f.sqlite-wal +0 -0
  305. package/worker/bun.lock +0 -237
  306. package/worker/package.json +0 -22
  307. package/worker/schema.sql +0 -96
  308. package/worker/src/handlers/auth.ts +0 -451
  309. package/worker/src/handlers/subdomains.ts +0 -376
  310. package/worker/src/handlers/user.ts +0 -98
  311. package/worker/src/index.ts +0 -70
  312. package/worker/src/middleware/auth.ts +0 -152
  313. package/worker/src/services/cloudflare.ts +0 -609
  314. package/worker/src/types.ts +0 -96
  315. package/worker/tsconfig.json +0 -15
  316. package/worker/wrangler.toml +0 -26
@@ -1,425 +0,0 @@
1
- /**
2
- * LocalMachineProvider
3
- *
4
- * MachineProvider implementation for the local machine.
5
- * Wraps existing TUI state functions and tmux-lite CLI.
6
- */
7
-
8
- import { existsSync, readdirSync } from 'fs';
9
- import { join } from 'path';
10
- import { hostname } from 'os';
11
- import { createBufferedSocketWriter } from '../../utils/bun-socket-writer.js';
12
- import {
13
- getAllProjectNames,
14
- readProjectConfig,
15
- getCurrentProject,
16
- getProjectWorkspacesDir,
17
- } from '../../core/config.js';
18
- import { getWorktreeInfo } from '../../core/git.js';
19
- import {
20
- listSessions as tmuxListSessions,
21
- createSession as tmuxCreateSession,
22
- getInbox as tmuxGetInbox,
23
- markInboxRead as tmuxMarkInboxRead,
24
- clearInbox as tmuxClearInbox,
25
- ensureServer,
26
- send,
27
- type Session as TmuxSession,
28
- } from '../../lib/tmux-lite/cli.js';
29
- import type {
30
- MachineProvider,
31
- CreateSessionOptions,
32
- AttachSessionOptions,
33
- MachineProviderEvent,
34
- MachineProviderEventHandler,
35
- EventedMachineProvider,
36
- } from './MachineProvider.js';
37
- import type {
38
- MachineInfo,
39
- Project,
40
- Workspace,
41
- WorkspaceSession,
42
- InboxItem,
43
- SessionStream,
44
- } from '../types.js';
45
-
46
- const STALE_DAYS = 30;
47
- const LOCAL_MACHINE_ID = 'local';
48
-
49
- /**
50
- * Convert tmux-lite session to shared WorkspaceSession type
51
- */
52
- function toWorkspaceSession(session: TmuxSession): WorkspaceSession {
53
- return {
54
- id: session.id,
55
- name: session.name,
56
- attached: session.attached,
57
- createdAt: session.createdAt,
58
- processTitle: session.processTitle,
59
- };
60
- }
61
-
62
- /**
63
- * LocalMachineProvider - access local machine resources
64
- *
65
- * @example
66
- * ```typescript
67
- * const provider = new LocalMachineProvider();
68
- * const projects = await provider.listProjects();
69
- * const workspaces = await provider.listWorkspaces('my-project');
70
- * ```
71
- */
72
- export class LocalMachineProvider implements EventedMachineProvider {
73
- private eventHandlers: Set<MachineProviderEventHandler> = new Set();
74
- private disposed = false;
75
-
76
- /**
77
- * Get information about the local machine
78
- */
79
- async getMachineInfo(): Promise<MachineInfo> {
80
- return {
81
- id: LOCAL_MACHINE_ID,
82
- label: hostname() || 'Local',
83
- isLocal: true,
84
- status: 'connected',
85
- };
86
- }
87
-
88
- /**
89
- * List all projects on the local machine
90
- */
91
- async listProjects(): Promise<Project[]> {
92
- const projectNames = getAllProjectNames();
93
- const currentProject = getCurrentProject();
94
-
95
- return projectNames.map((name) => {
96
- const config = readProjectConfig(name);
97
- const workspacesDir = getProjectWorkspacesDir(name);
98
- let workspaceCount = 0;
99
-
100
- if (existsSync(workspacesDir)) {
101
- workspaceCount = readdirSync(workspacesDir).filter((entry) => {
102
- const path = join(workspacesDir, entry);
103
- return existsSync(path) && readdirSync(path).length > 0;
104
- }).length;
105
- }
106
-
107
- return {
108
- name,
109
- repository: config.repository,
110
- workspaceCount,
111
- isCurrent: name === currentProject,
112
- };
113
- });
114
- }
115
-
116
- /**
117
- * List workspaces for a project
118
- */
119
- async listWorkspaces(projectName: string): Promise<Workspace[]> {
120
- const workspacesDir = getProjectWorkspacesDir(projectName);
121
-
122
- if (!existsSync(workspacesDir)) {
123
- return [];
124
- }
125
-
126
- const workspaceNames = readdirSync(workspacesDir).filter((entry) => {
127
- const path = join(workspacesDir, entry);
128
- return existsSync(path) && readdirSync(path).length > 0;
129
- });
130
-
131
- // Get all tmux-lite sessions
132
- let allSessions: TmuxSession[] = [];
133
- try {
134
- allSessions = await tmuxListSessions();
135
- } catch {
136
- // Server might not be running, that's fine
137
- }
138
-
139
- const workspaces: Workspace[] = [];
140
- const now = new Date();
141
-
142
- for (const name of workspaceNames) {
143
- const workspacePath = join(workspacesDir, name);
144
- const info = await getWorktreeInfo(workspacePath);
145
-
146
- if (info) {
147
- const daysSinceCommit = Math.floor(
148
- (now.getTime() - info.lastCommitDate.getTime()) / (1000 * 60 * 60 * 24)
149
- );
150
-
151
- // Find sessions for this workspace (name pattern: project:workspace:n)
152
- const sessionPrefix = `${projectName}:${name}:`;
153
- const workspaceSessions = allSessions
154
- .filter(s => s.name.startsWith(sessionPrefix))
155
- .map(toWorkspaceSession);
156
-
157
- workspaces.push({
158
- name: info.name,
159
- path: info.path,
160
- branch: info.branch,
161
- ahead: info.ahead,
162
- behind: info.behind,
163
- uncommittedChanges: info.uncommittedChanges,
164
- lastCommitDate: info.lastCommitDate,
165
- isStale: daysSinceCommit > STALE_DAYS,
166
- sessions: workspaceSessions,
167
- });
168
- }
169
- }
170
-
171
- return workspaces;
172
- }
173
-
174
- /**
175
- * Create a new session in a workspace
176
- */
177
- async createSession(
178
- projectName: string,
179
- workspaceName: string,
180
- options?: CreateSessionOptions
181
- ): Promise<string> {
182
- const workspacesDir = getProjectWorkspacesDir(projectName);
183
- const workspacePath = join(workspacesDir, workspaceName);
184
-
185
- // Count existing sessions to generate unique name
186
- let allSessions: TmuxSession[] = [];
187
- try {
188
- allSessions = await tmuxListSessions();
189
- } catch {
190
- // Ignore
191
- }
192
-
193
- const sessionPrefix = `${projectName}:${workspaceName}:`;
194
- const existingCount = allSessions.filter(s => s.name.startsWith(sessionPrefix)).length;
195
- const sessionName = `${sessionPrefix}${existingCount + 1}`;
196
-
197
- const cwd = options?.cwd ?? workspacePath;
198
- const session = await tmuxCreateSession(sessionName, cwd);
199
-
200
- return session.id;
201
- }
202
-
203
- /**
204
- * Attach to an existing session
205
- *
206
- * Returns a SessionStream for terminal I/O.
207
- * For local sessions, this connects directly to the tmux-lite socket.
208
- */
209
- async attachSession(
210
- sessionId: string,
211
- options: AttachSessionOptions
212
- ): Promise<SessionStream> {
213
- await ensureServer();
214
-
215
- // Get session info
216
- const sessions = await tmuxListSessions();
217
- const session = sessions.find(s => s.id === sessionId);
218
- if (!session) {
219
- throw new Error(`Session not found: ${sessionId}`);
220
- }
221
-
222
- // Connect to session socket
223
- const socketPath = session.socketPath;
224
-
225
- return new Promise((resolve, reject) => {
226
- let dataHandler: ((data: Uint8Array) => void) | null = null;
227
- let closeHandler: ((exitCode?: number) => void) | null = null;
228
- let socket: Awaited<ReturnType<typeof Bun.connect>> | null = null;
229
- let socketWriter: ReturnType<typeof createBufferedSocketWriter> | null = null;
230
- let buffer = Buffer.alloc(0);
231
-
232
- const stream: SessionStream = {
233
- write(data: Uint8Array) {
234
- if (socket) {
235
- const { encodePTY } = require('../../lib/tmux-lite/protocol.js');
236
- const frame = encodePTY(Buffer.from(data));
237
- if (socketWriter) socketWriter.write(frame);
238
- else socket.write(frame);
239
- }
240
- },
241
- resize(cols: number, rows: number) {
242
- if (socket) {
243
- const { encodeControl } = require('../../lib/tmux-lite/protocol.js');
244
- const frame = encodeControl({ type: 'resize', cols, rows });
245
- if (socketWriter) socketWriter.write(frame);
246
- else socket.write(frame);
247
- }
248
- },
249
- detach() {
250
- if (socket) {
251
- const { encodeControl } = require('../../lib/tmux-lite/protocol.js');
252
- const frame = encodeControl({ type: 'detach' });
253
- if (socketWriter) socketWriter.write(frame);
254
- else socket.write(frame);
255
- }
256
- },
257
- close() {
258
- socket?.end();
259
- socket = null;
260
- socketWriter = null;
261
- },
262
- onData(handler: (data: Uint8Array) => void) {
263
- dataHandler = handler;
264
- },
265
- onClose(handler: (exitCode?: number) => void) {
266
- closeHandler = handler;
267
- },
268
- };
269
-
270
- Bun.connect({
271
- unix: socketPath,
272
- socket: {
273
- open(s) {
274
- socket = s;
275
- socketWriter = createBufferedSocketWriter(s);
276
- // Send attach-init
277
- const { encodeControl } = require('../../lib/tmux-lite/protocol.js');
278
- socketWriter.write(encodeControl({
279
- type: 'attach-init',
280
- cols: options.cols,
281
- rows: options.rows,
282
- clientType: options.clientType,
283
- }));
284
- resolve(stream);
285
- },
286
- drain() {
287
- socketWriter?.flush();
288
- },
289
- data(_, data) {
290
- const { parseFrames, decodeControl, FrameType } = require('../../lib/tmux-lite/protocol.js');
291
- buffer = Buffer.concat([buffer, Buffer.from(data)]);
292
-
293
- let frames;
294
- let remaining;
295
- try {
296
- const result = parseFrames(buffer);
297
- frames = result.frames;
298
- remaining = result.remaining;
299
- } catch (err) {
300
- // Protocol error - likely desync or corrupted data
301
- const msg = err instanceof Error ? err.message : 'Frame parse error';
302
- console.error(`[LocalMachineProvider] Frame parse error: ${msg}`);
303
- closeHandler?.();
304
- socket?.end();
305
- socket = null;
306
- return;
307
- }
308
- // Copy remaining bytes - subarray references can become invalid when Bun reuses buffers
309
- buffer = Buffer.from(remaining);
310
-
311
- for (const frame of frames) {
312
- if (frame.type === FrameType.CONTROL) {
313
- const event = decodeControl(frame.payload);
314
- if (event.type === 'exited') {
315
- closeHandler?.(event.code);
316
- socket?.end();
317
- socket = null;
318
- } else if (event.type === 'kicked') {
319
- closeHandler?.();
320
- socket?.end();
321
- socket = null;
322
- }
323
- } else if (frame.type === FrameType.PTY) {
324
- dataHandler?.(frame.payload);
325
- }
326
- }
327
- },
328
- close() {
329
- closeHandler?.();
330
- socket = null;
331
- },
332
- error(_, e) {
333
- reject(e);
334
- },
335
- connectError(_, e) {
336
- reject(e);
337
- },
338
- },
339
- }).catch(reject);
340
- });
341
- }
342
-
343
- /**
344
- * Detach from a session
345
- */
346
- async detachSession(sessionId: string): Promise<void> {
347
- // Detach is handled by the SessionStream.detach() method
348
- // This is a no-op for local sessions
349
- }
350
-
351
- /**
352
- * Get inbox notifications
353
- */
354
- async getInbox(): Promise<InboxItem[]> {
355
- try {
356
- const items = await tmuxGetInbox();
357
- return items.map(item => ({
358
- id: item.id,
359
- sessionId: item.sessionId,
360
- sessionName: item.sessionName,
361
- type: item.type,
362
- timestamp: item.timestamp,
363
- read: item.read,
364
- context: item.context,
365
- processTitle: item.processTitle,
366
- exitCode: item.exitCode,
367
- }));
368
- } catch {
369
- // Server might not be running
370
- return [];
371
- }
372
- }
373
-
374
- /**
375
- * Mark inbox item as read
376
- */
377
- async markInboxRead(itemId: string): Promise<void> {
378
- await tmuxMarkInboxRead(itemId);
379
- }
380
-
381
- /**
382
- * Clear all inbox items
383
- */
384
- async clearInbox(): Promise<void> {
385
- await tmuxClearInbox();
386
- }
387
-
388
- /**
389
- * Subscribe to provider events
390
- */
391
- onEvent(handler: MachineProviderEventHandler): () => void {
392
- this.eventHandlers.add(handler);
393
- return () => this.eventHandlers.delete(handler);
394
- }
395
-
396
- /**
397
- * Emit an event to all handlers
398
- */
399
- private emit(event: MachineProviderEvent): void {
400
- for (const handler of this.eventHandlers) {
401
- handler(event);
402
- }
403
- }
404
-
405
- /**
406
- * Dispose of the provider
407
- */
408
- dispose(): void {
409
- if (this.disposed) return;
410
- this.disposed = true;
411
- this.eventHandlers.clear();
412
- }
413
- }
414
-
415
- /**
416
- * Singleton instance for convenience
417
- */
418
- let localProvider: LocalMachineProvider | null = null;
419
-
420
- export function getLocalMachineProvider(): LocalMachineProvider {
421
- if (!localProvider) {
422
- localProvider = new LocalMachineProvider();
423
- }
424
- return localProvider;
425
- }
@@ -1,165 +0,0 @@
1
- /**
2
- * MachineProvider Interface
3
- *
4
- * Abstraction for accessing machine resources (projects, workspaces, sessions).
5
- * Implementations include:
6
- * - LocalMachineProvider: Direct access to local machine
7
- * - RemoteMachineProvider: Access via encrypted relay connection
8
- */
9
-
10
- import type {
11
- MachineInfo,
12
- Project,
13
- Workspace,
14
- InboxItem,
15
- SessionStream,
16
- } from '../types.js';
17
-
18
- /**
19
- * Options for creating a new session
20
- */
21
- export interface CreateSessionOptions {
22
- /** Session name (user-friendly identifier) */
23
- sessionName?: string;
24
- /** Working directory for the session */
25
- cwd?: string;
26
- /** Shell to use (defaults to $SHELL or /bin/bash) */
27
- shell?: string;
28
- /** Environment variables */
29
- env?: Record<string, string>;
30
- /** Initial terminal size */
31
- cols?: number;
32
- rows?: number;
33
- }
34
-
35
- /**
36
- * Options for attaching to an existing session
37
- */
38
- export interface AttachSessionOptions {
39
- /** Terminal size */
40
- cols: number;
41
- rows: number;
42
- /** Client type for session tracking */
43
- clientType?: 'cli' | 'web';
44
- /** Force attach (detach other clients) */
45
- force?: boolean;
46
- }
47
-
48
- /**
49
- * MachineProvider interface
50
- *
51
- * Provides a unified API for accessing machine resources,
52
- * whether local or remote.
53
- *
54
- * @example
55
- * ```typescript
56
- * // Local machine
57
- * const local = new LocalMachineProvider();
58
- * const projects = await local.listProjects();
59
- *
60
- * // Remote machine
61
- * const remote = new RemoteMachineProvider(relayClient, machineId);
62
- * const workspaces = await remote.listWorkspaces('my-project');
63
- * ```
64
- */
65
- export interface MachineProvider {
66
- /**
67
- * Get information about this machine
68
- */
69
- getMachineInfo(): Promise<MachineInfo>;
70
-
71
- /**
72
- * List all projects on this machine
73
- */
74
- listProjects(): Promise<Project[]>;
75
-
76
- /**
77
- * List workspaces for a project
78
- *
79
- * @param projectName - Name of the project
80
- */
81
- listWorkspaces(projectName: string): Promise<Workspace[]>;
82
-
83
- /**
84
- * Create a new session in a workspace
85
- *
86
- * @param projectName - Name of the project
87
- * @param workspaceName - Name of the workspace
88
- * @param options - Session creation options
89
- * @returns Session ID of the created session
90
- */
91
- createSession(
92
- projectName: string,
93
- workspaceName: string,
94
- options?: CreateSessionOptions
95
- ): Promise<string>;
96
-
97
- /**
98
- * Attach to an existing session
99
- *
100
- * @param sessionId - ID of the session to attach to
101
- * @param options - Attachment options
102
- * @returns Stream for terminal I/O
103
- */
104
- attachSession(
105
- sessionId: string,
106
- options: AttachSessionOptions
107
- ): Promise<SessionStream>;
108
-
109
- /**
110
- * Detach from a session
111
- *
112
- * @param sessionId - ID of the session to detach from
113
- */
114
- detachSession(sessionId: string): Promise<void>;
115
-
116
- /**
117
- * Get inbox notifications
118
- */
119
- getInbox(): Promise<InboxItem[]>;
120
-
121
- /**
122
- * Mark inbox item as read
123
- *
124
- * @param itemId - ID of the inbox item
125
- */
126
- markInboxRead(itemId: string): Promise<void>;
127
-
128
- /**
129
- * Clear all inbox items
130
- */
131
- clearInbox(): Promise<void>;
132
-
133
- /**
134
- * Dispose of the provider and clean up resources
135
- */
136
- dispose(): void;
137
- }
138
-
139
- /**
140
- * Events emitted by MachineProvider
141
- */
142
- export type MachineProviderEvent =
143
- | { type: 'connected' }
144
- | { type: 'disconnected'; reason: string }
145
- | { type: 'error'; error: Error }
146
- | { type: 'inbox_updated'; items: InboxItem[] }
147
- | { type: 'session_updated'; sessionId: string };
148
-
149
- /**
150
- * Event handler for MachineProvider events
151
- */
152
- export type MachineProviderEventHandler = (event: MachineProviderEvent) => void;
153
-
154
- /**
155
- * Extended MachineProvider with event support
156
- */
157
- export interface EventedMachineProvider extends MachineProvider {
158
- /**
159
- * Subscribe to provider events
160
- *
161
- * @param handler - Event handler
162
- * @returns Unsubscribe function
163
- */
164
- onEvent(handler: MachineProviderEventHandler): () => void;
165
- }