@kdonev/termscape 0.1.0

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 (139) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +288 -0
  3. package/dist/agents/env.d.ts +36 -0
  4. package/dist/agents/env.d.ts.map +1 -0
  5. package/dist/agents/env.js +61 -0
  6. package/dist/agents/env.js.map +1 -0
  7. package/dist/agents/profiles.d.ts +45 -0
  8. package/dist/agents/profiles.d.ts.map +1 -0
  9. package/dist/agents/profiles.js +123 -0
  10. package/dist/agents/profiles.js.map +1 -0
  11. package/dist/agents/resolve.d.ts +16 -0
  12. package/dist/agents/resolve.d.ts.map +1 -0
  13. package/dist/agents/resolve.js +71 -0
  14. package/dist/agents/resolve.js.map +1 -0
  15. package/dist/agents/router.d.ts +29 -0
  16. package/dist/agents/router.d.ts.map +1 -0
  17. package/dist/agents/router.js +83 -0
  18. package/dist/agents/router.js.map +1 -0
  19. package/dist/agents/tokens.d.ts +21 -0
  20. package/dist/agents/tokens.d.ts.map +1 -0
  21. package/dist/agents/tokens.js +39 -0
  22. package/dist/agents/tokens.js.map +1 -0
  23. package/dist/agents/wiring.d.ts +30 -0
  24. package/dist/agents/wiring.d.ts.map +1 -0
  25. package/dist/agents/wiring.js +110 -0
  26. package/dist/agents/wiring.js.map +1 -0
  27. package/dist/browser.d.ts +13 -0
  28. package/dist/browser.d.ts.map +1 -0
  29. package/dist/browser.js +34 -0
  30. package/dist/browser.js.map +1 -0
  31. package/dist/cli.d.ts +3 -0
  32. package/dist/cli.d.ts.map +1 -0
  33. package/dist/cli.js +200 -0
  34. package/dist/cli.js.map +1 -0
  35. package/dist/db/index.d.ts +14 -0
  36. package/dist/db/index.d.ts.map +1 -0
  37. package/dist/db/index.js +24 -0
  38. package/dist/db/index.js.map +1 -0
  39. package/dist/db/migrations.d.ts +15 -0
  40. package/dist/db/migrations.d.ts.map +1 -0
  41. package/dist/db/migrations.js +176 -0
  42. package/dist/db/migrations.js.map +1 -0
  43. package/dist/db/store.d.ts +71 -0
  44. package/dist/db/store.d.ts.map +1 -0
  45. package/dist/db/store.js +341 -0
  46. package/dist/db/store.js.map +1 -0
  47. package/dist/hub.d.ts +188 -0
  48. package/dist/hub.d.ts.map +1 -0
  49. package/dist/hub.js +636 -0
  50. package/dist/hub.js.map +1 -0
  51. package/dist/hub.tgz +0 -0
  52. package/dist/mcp/server.d.ts +31 -0
  53. package/dist/mcp/server.d.ts.map +1 -0
  54. package/dist/mcp/server.js +66 -0
  55. package/dist/mcp/server.js.map +1 -0
  56. package/dist/paths.d.ts +27 -0
  57. package/dist/paths.d.ts.map +1 -0
  58. package/dist/paths.js +31 -0
  59. package/dist/paths.js.map +1 -0
  60. package/dist/protocol/address.d.ts +15 -0
  61. package/dist/protocol/address.d.ts.map +1 -0
  62. package/dist/protocol/address.js +39 -0
  63. package/dist/protocol/address.js.map +1 -0
  64. package/dist/protocol/domain.d.ts +151 -0
  65. package/dist/protocol/domain.d.ts.map +1 -0
  66. package/dist/protocol/domain.js +104 -0
  67. package/dist/protocol/domain.js.map +1 -0
  68. package/dist/protocol/index.d.ts +7 -0
  69. package/dist/protocol/index.d.ts.map +1 -0
  70. package/dist/protocol/index.js +7 -0
  71. package/dist/protocol/index.js.map +1 -0
  72. package/dist/protocol/inject.d.ts +29 -0
  73. package/dist/protocol/inject.d.ts.map +1 -0
  74. package/dist/protocol/inject.js +40 -0
  75. package/dist/protocol/inject.js.map +1 -0
  76. package/dist/protocol/mcp-tools.d.ts +45 -0
  77. package/dist/protocol/mcp-tools.d.ts.map +1 -0
  78. package/dist/protocol/mcp-tools.js +46 -0
  79. package/dist/protocol/mcp-tools.js.map +1 -0
  80. package/dist/protocol/peer.d.ts +304 -0
  81. package/dist/protocol/peer.d.ts.map +1 -0
  82. package/dist/protocol/peer.js +183 -0
  83. package/dist/protocol/peer.js.map +1 -0
  84. package/dist/protocol/ws.d.ts +416 -0
  85. package/dist/protocol/ws.d.ts.map +1 -0
  86. package/dist/protocol/ws.js +119 -0
  87. package/dist/protocol/ws.js.map +1 -0
  88. package/dist/remote/deployer.d.ts +78 -0
  89. package/dist/remote/deployer.d.ts.map +1 -0
  90. package/dist/remote/deployer.js +216 -0
  91. package/dist/remote/deployer.js.map +1 -0
  92. package/dist/remote/enroll.d.ts +14 -0
  93. package/dist/remote/enroll.d.ts.map +1 -0
  94. package/dist/remote/enroll.js +244 -0
  95. package/dist/remote/enroll.js.map +1 -0
  96. package/dist/remote/join-script.d.ts +29 -0
  97. package/dist/remote/join-script.d.ts.map +1 -0
  98. package/dist/remote/join-script.js +709 -0
  99. package/dist/remote/join-script.js.map +1 -0
  100. package/dist/remote/join.d.ts +19 -0
  101. package/dist/remote/join.d.ts.map +1 -0
  102. package/dist/remote/join.js +195 -0
  103. package/dist/remote/join.js.map +1 -0
  104. package/dist/remote/lan.d.ts +45 -0
  105. package/dist/remote/lan.d.ts.map +1 -0
  106. package/dist/remote/lan.js +105 -0
  107. package/dist/remote/lan.js.map +1 -0
  108. package/dist/remote/peer-serve.d.ts +27 -0
  109. package/dist/remote/peer-serve.d.ts.map +1 -0
  110. package/dist/remote/peer-serve.js +274 -0
  111. package/dist/remote/peer-serve.js.map +1 -0
  112. package/dist/remote/peer.d.ts +62 -0
  113. package/dist/remote/peer.d.ts.map +1 -0
  114. package/dist/remote/peer.js +250 -0
  115. package/dist/remote/peer.js.map +1 -0
  116. package/dist/remote/registry.d.ts +104 -0
  117. package/dist/remote/registry.d.ts.map +1 -0
  118. package/dist/remote/registry.js +359 -0
  119. package/dist/remote/registry.js.map +1 -0
  120. package/dist/remote/tarball.d.ts +9 -0
  121. package/dist/remote/tarball.d.ts.map +1 -0
  122. package/dist/remote/tarball.js +22 -0
  123. package/dist/remote/tarball.js.map +1 -0
  124. package/dist/server.d.ts +54 -0
  125. package/dist/server.d.ts.map +1 -0
  126. package/dist/server.js +438 -0
  127. package/dist/server.js.map +1 -0
  128. package/dist/session/manager.d.ts +104 -0
  129. package/dist/session/manager.d.ts.map +1 -0
  130. package/dist/session/manager.js +421 -0
  131. package/dist/session/manager.js.map +1 -0
  132. package/dist/session/pty.d.ts +114 -0
  133. package/dist/session/pty.d.ts.map +1 -0
  134. package/dist/session/pty.js +300 -0
  135. package/dist/session/pty.js.map +1 -0
  136. package/package.json +49 -0
  137. package/web/assets/index-B2JtCtHa.css +1 -0
  138. package/web/assets/index-BOqZOoep.js +107 -0
  139. package/web/index.html +13 -0
package/dist/server.js ADDED
@@ -0,0 +1,438 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { existsSync } from 'node:fs';
3
+ import { dirname, join, sep } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import Fastify from 'fastify';
6
+ import websocket from '@fastify/websocket';
7
+ import fastifyStatic from '@fastify/static';
8
+ import { ClientMsg, BinaryFrameKind, decodeBinaryFrame, encodeBinaryFrame, } from './protocol/index.js';
9
+ import { HUB_VERSION } from './hub.js';
10
+ import { createPeerServer } from './remote/peer-serve.js';
11
+ import { registerEnrollment } from './remote/enroll.js';
12
+ import { advertisedHost, coversLoopback, preferredHostname, urlHost, } from './remote/lan.js';
13
+ import { buildMcpServer, buildMcpTransport } from './mcp/server.js';
14
+ /**
15
+ * Ports to try before falling back to whatever the OS hands out.
16
+ *
17
+ * The join page is the one URL someone has to type by hand, on a different
18
+ * machine, from memory — it carries no token precisely so it can be typed. A
19
+ * five-digit ephemeral port makes that miserable, so prefer something a person
20
+ * can hold in their head.
21
+ *
22
+ * Ordered by memorability, skipping ports that common dev tooling squats on:
23
+ * 3000/8000/8080 (everything), 5555 (adb), 8888 (Jupyter), 2222 (alt ssh).
24
+ */
25
+ export const MEMORABLE_PORTS = [7777, 4242, 7333, 3333, 9999, 7070, 4040, 1212];
26
+ /**
27
+ * Locate the built web assets relative to this file, if they exist.
28
+ *
29
+ * Two layouts have to work. In the monorepo the hub runs from
30
+ * `packages/hub/dist/` and the UI is its sibling at `packages/web/dist`. In the
31
+ * published package there are no siblings: the UI is copied to `web/` beside
32
+ * `dist/`, because npm ships one package and a relative walk out of it would
33
+ * land in whatever the user happens to have installed next door.
34
+ */
35
+ function webRoot() {
36
+ const here = dirname(fileURLToPath(import.meta.url));
37
+ const candidates = [
38
+ // Published layout: <pkg>/dist/server.js -> <pkg>/web
39
+ join(here, '..', 'web'),
40
+ join(here, '..', '..', 'web', 'dist'),
41
+ join(here, '..', '..', '..', 'web', 'dist'),
42
+ ];
43
+ return candidates.find((p) => existsSync(p)) ?? null;
44
+ }
45
+ export async function serve(opts) {
46
+ const { hub, clientToken } = opts;
47
+ const app = Fastify({ logger: false });
48
+ await app.register(websocket, {
49
+ options: { maxPayload: 8 * 1024 * 1024 },
50
+ });
51
+ /* ------------------------------------------------------------- health */
52
+ app.get('/health', async () => ({
53
+ ok: true,
54
+ hubVersion: HUB_VERSION,
55
+ pid: process.pid,
56
+ }));
57
+ /* -------------------------------------------------------------- hooks */
58
+ /**
59
+ * Agent CLIs POST here at turn boundaries. The path carries the agent's own
60
+ * bearer token, which is also how we know which window changed state.
61
+ */
62
+ app.post('/hook/:token', async (req, reply) => {
63
+ const sessionId = hub.tokens.resolve(req.params.token);
64
+ if (!sessionId)
65
+ return reply.code(404).send({ ok: false });
66
+ const event = req.query.event === 'idle' ? 'idle' : 'busy';
67
+ hub.sessions.setStatusFromHook(sessionId, event);
68
+ return { ok: true };
69
+ });
70
+ /* ---------------------------------------------------------------- MCP */
71
+ app.all('/mcp', async (req, reply) => {
72
+ const auth = req.headers.authorization ?? '';
73
+ const token = auth.startsWith('Bearer ') ? auth.slice(7) : '';
74
+ const sessionId = token ? hub.tokens.resolve(token) : null;
75
+ if (!sessionId) {
76
+ return reply.code(401).send({
77
+ jsonrpc: '2.0',
78
+ error: { code: -32001, message: 'unauthorized' },
79
+ id: null,
80
+ });
81
+ }
82
+ // Stateless: a fresh server + transport per request, bound to this caller.
83
+ const server = buildMcpServer(sessionId, hub);
84
+ const transport = buildMcpTransport();
85
+ reply.raw.on('close', () => {
86
+ void transport.close();
87
+ void server.close();
88
+ });
89
+ await server.connect(transport);
90
+ await transport.handleRequest(req.raw, reply.raw, req.body);
91
+ return reply;
92
+ });
93
+ /* --------------------------------------------------------- peer (hub↔hub) */
94
+ /**
95
+ * The answering half of the hub-to-hub link. A hub deployed over SSH serves
96
+ * this and the canvas-owning hub dials it through the tunnel, so on that
97
+ * path the endpoint is only ever reachable over loopback on the remote
98
+ * machine. The logic lives in peer-serve.ts because a hub that enrolled
99
+ * itself runs the identical thing over a socket it dialled out instead.
100
+ */
101
+ const peerServer = createPeerServer(hub, clientToken);
102
+ // It is also this hub's link back to whatever canvas it was attached to.
103
+ // On a hub that owns its own canvas nothing ever attaches, and the uplink
104
+ // reports itself as detached forever.
105
+ hub.setUplink(peerServer);
106
+ app.get('/peer', { websocket: true }, (socket) => {
107
+ peerServer.serve(socket);
108
+ });
109
+ /* --------------------------------------------------------------- join */
110
+ // The bind address is not known until listen() resolves, so the enroll
111
+ // routes read it through a closure rather than a captured value.
112
+ let enrollOrigin = null;
113
+ let enrollAltOrigin = null;
114
+ const enrollment = registerEnrollment(app, { hub, enrollOrigin: () => enrollOrigin });
115
+ /* ---------------------------------------------------------- WebSocket */
116
+ const clients = new Set();
117
+ const send = (ws, msg) => {
118
+ if (ws.readyState === ws.OPEN)
119
+ ws.send(JSON.stringify(msg));
120
+ };
121
+ const broadcast = (msg) => {
122
+ for (const ws of clients)
123
+ send(ws, msg);
124
+ };
125
+ const snapshotState = () => ({
126
+ hubVersion: HUB_VERSION,
127
+ enrollUrl: enrollOrigin ? `${enrollOrigin}/join` : null,
128
+ enrollAltUrl: enrollAltOrigin ? `${enrollAltOrigin}/join` : null,
129
+ hosts: hub.store.listHosts(),
130
+ workspaces: hub.store.listWorkspaces(),
131
+ sessions: hub.allSessions(),
132
+ messages: hub.messages(),
133
+ viewport: hub.store.getViewport(),
134
+ profiles: hub.profiles.list().map((p) => ({
135
+ id: p.id,
136
+ description: p.description,
137
+ mcp: p.mcp,
138
+ resumable: !!p.resumeArgs,
139
+ })),
140
+ });
141
+ hub.on('session', (s) => broadcast({ t: 'sessionUpserted', session: s }));
142
+ hub.on('removed', (id) => broadcast({ t: 'sessionRemoved', sessionId: id }));
143
+ hub.on('workspace', (w) => broadcast({ t: 'workspaceUpserted', workspace: w }));
144
+ hub.on('workspaceRemoved', (id) => broadcast({ t: 'workspaceRemoved', workspaceId: id }));
145
+ hub.on('message', (m) => broadcast({ t: 'messageSent', message: m }));
146
+ hub.on('host', (h) => broadcast({ t: 'hostUpserted', host: h }));
147
+ hub.on('hostRemoved', (id) => broadcast({ t: 'hostRemoved', hostId: id }));
148
+ // Deploy progress. Installing on a remote can take minutes; the panel shows
149
+ // these lines so it is not a frozen button.
150
+ hub.on('hostLog', (hostId, line) => broadcast({ t: 'hostLog', hostId, line }));
151
+ // A peer connecting, dropping, or resyncing changes many sessions at
152
+ // once, so push the whole list rather than diffing it here.
153
+ hub.on('peersChanged', () => {
154
+ for (const s of hub.peers.sessions())
155
+ broadcast({ t: 'sessionUpserted', session: s });
156
+ });
157
+ // Terminal output goes only to clients that have attached to that window,
158
+ // so a canvas showing 20 terminals zoomed out is not paying for 20 streams.
159
+ const attached = new Map();
160
+ hub.on('data', (sessionId, chunk) => {
161
+ const frame = encodeBinaryFrame(BinaryFrameKind.PtyOutput, sessionId, Buffer.from(chunk, 'utf8'));
162
+ for (const ws of clients) {
163
+ if (attached.get(ws)?.has(sessionId) && ws.readyState === ws.OPEN)
164
+ ws.send(frame);
165
+ }
166
+ });
167
+ app.get('/ws', { websocket: true }, (socket) => {
168
+ let authed = false;
169
+ clients.add(socket);
170
+ attached.set(socket, new Set());
171
+ socket.on('message', (raw, isBinary) => {
172
+ try {
173
+ if (isBinary) {
174
+ if (!authed)
175
+ return;
176
+ const f = decodeBinaryFrame(new Uint8Array(raw));
177
+ if (f.kind === BinaryFrameKind.PtyInput) {
178
+ const data = Buffer.from(f.payload).toString('utf8');
179
+ const remote = hub.peers.find(f.sessionId);
180
+ if (remote) {
181
+ // Typing into a remote terminal whose PTY has since exited is
182
+ // ordinary, and the peer says so by rejecting. Unhandled, that
183
+ // rejection reaches the process and takes the whole canvas with
184
+ // it — so it is reported to this browser and goes no further.
185
+ void remote.peer
186
+ .request({ t: 'input', id: randomUUID(), address: f.sessionId, data })
187
+ .catch((e) => send(socket, { t: 'error', message: e.message, sessionId: f.sessionId }));
188
+ }
189
+ else {
190
+ hub.sessions.write(f.sessionId, data);
191
+ }
192
+ }
193
+ return;
194
+ }
195
+ const parsed = ClientMsg.safeParse(JSON.parse(raw.toString('utf8')));
196
+ if (!parsed.success) {
197
+ send(socket, { t: 'error', message: 'malformed message' });
198
+ return;
199
+ }
200
+ const msg = parsed.data;
201
+ if (!authed) {
202
+ if (msg.t !== 'hello' || msg.token !== clientToken) {
203
+ send(socket, { t: 'error', message: 'unauthorized' });
204
+ socket.close();
205
+ return;
206
+ }
207
+ authed = true;
208
+ send(socket, { t: 'ready', state: snapshotState() });
209
+ return;
210
+ }
211
+ void handleClientMsg(socket, msg);
212
+ }
213
+ catch (err) {
214
+ send(socket, { t: 'error', message: err.message });
215
+ }
216
+ });
217
+ socket.on('close', () => {
218
+ clients.delete(socket);
219
+ attached.delete(socket);
220
+ });
221
+ });
222
+ async function handleClientMsg(socket, msg) {
223
+ try {
224
+ switch (msg.t) {
225
+ case 'hello':
226
+ return;
227
+ // Remote sessions are keyed by their address, so a single lookup
228
+ // decides whether a request is served locally or forwarded to a peer.
229
+ case 'attach': {
230
+ attached.get(socket)?.add(msg.sessionId);
231
+ const remote = hub.peers.find(msg.sessionId);
232
+ if (remote) {
233
+ await remote.peer.attach(msg.sessionId);
234
+ return;
235
+ }
236
+ const snap = hub.sessions.snapshotForAttach(msg.sessionId);
237
+ if (snap) {
238
+ send(socket, {
239
+ t: 'snapshot',
240
+ sessionId: msg.sessionId,
241
+ serialized: snap.serialized,
242
+ cols: snap.cols,
243
+ rows: snap.rows,
244
+ });
245
+ }
246
+ return;
247
+ }
248
+ case 'detach': {
249
+ attached.get(socket)?.delete(msg.sessionId);
250
+ const remote = hub.peers.find(msg.sessionId);
251
+ if (remote)
252
+ await remote.peer.detach(msg.sessionId);
253
+ return;
254
+ }
255
+ case 'resize': {
256
+ const remote = hub.peers.find(msg.sessionId);
257
+ if (remote) {
258
+ await remote.peer.request({
259
+ t: 'resize',
260
+ id: randomUUID(),
261
+ address: msg.sessionId,
262
+ cols: msg.cols,
263
+ rows: msg.rows,
264
+ });
265
+ return;
266
+ }
267
+ hub.sessions.resize(msg.sessionId, msg.cols, msg.rows);
268
+ return;
269
+ }
270
+ case 'createWorkspace':
271
+ hub.createWorkspace(msg.name, msg.rootPath, msg.hostId ?? null);
272
+ return;
273
+ case 'removeWorkspace':
274
+ await hub.removeWorkspace(msg.workspaceId);
275
+ return;
276
+ case 'startSession':
277
+ await hub.startSession({
278
+ workspaceId: msg.workspaceId,
279
+ profile: msg.profile,
280
+ name: msg.name,
281
+ cwd: msg.cwd,
282
+ });
283
+ return;
284
+ case 'stopSession': {
285
+ const remote = hub.peers.find(msg.sessionId);
286
+ if (remote) {
287
+ await remote.peer.request({ t: 'stopSession', id: randomUUID(), address: msg.sessionId });
288
+ return;
289
+ }
290
+ hub.sessions.stop(msg.sessionId);
291
+ return;
292
+ }
293
+ case 'removeSession':
294
+ // A remote window's session belongs to its peer. Removing it here
295
+ // would delete nothing and merely hide the window until that peer
296
+ // reported it again.
297
+ if (await hub.peers.removeSession(msg.sessionId))
298
+ return;
299
+ hub.sessions.remove(msg.sessionId);
300
+ return;
301
+ case 'resumeSession': {
302
+ const remote = hub.peers.find(msg.sessionId);
303
+ if (remote) {
304
+ await remote.peer.request({ t: 'resumeSession', id: randomUUID(), address: msg.sessionId });
305
+ return;
306
+ }
307
+ await hub.sessions.resume(msg.sessionId);
308
+ return;
309
+ }
310
+ case 'resumeWorkspace':
311
+ await hub.resumeWorkspace(msg.workspaceId);
312
+ return;
313
+ case 'moveWindow':
314
+ // Layout is always local, even for a peer's session.
315
+ if (!hub.peers.saveLayout(msg.sessionId, msg.rect)) {
316
+ hub.moveWindow(msg.sessionId, msg.rect);
317
+ }
318
+ return;
319
+ case 'setViewport':
320
+ hub.setViewport(msg.viewport);
321
+ return;
322
+ case 'addHost': {
323
+ const host = hub.addHost(msg);
324
+ // Connect immediately: adding a host the user then has to connect
325
+ // by hand is a pointless second step.
326
+ void hub.connectHost(host.id).catch((e) => send(socket, { t: 'error', message: `connect ${host.label}: ${e.message}` }));
327
+ return;
328
+ }
329
+ case 'removeHost':
330
+ await hub.removeHost(msg.hostId);
331
+ return;
332
+ case 'connectHost':
333
+ await hub.connectHost(msg.hostId);
334
+ return;
335
+ default:
336
+ send(socket, { t: 'error', message: `unhandled: ${msg.t}` });
337
+ }
338
+ }
339
+ catch (err) {
340
+ send(socket, { t: 'error', message: err.message });
341
+ }
342
+ }
343
+ /* ------------------------------------------------------------- static */
344
+ if (!opts.headless) {
345
+ const root = webRoot();
346
+ if (root) {
347
+ await app.register(fastifyStatic, {
348
+ root,
349
+ wildcard: false,
350
+ // Asset filenames are content-hashed by vite, so they may be cached
351
+ // forever. index.html must NOT be: a cached copy points at the
352
+ // previous build's hashed bundle, which now 404s, leaving a blank
353
+ // page that only a manual hard-reload fixes.
354
+ setHeaders: (res, filePath) => {
355
+ const immutable = filePath.includes(`${sep}assets${sep}`);
356
+ res.header('cache-control', immutable ? 'public, max-age=31536000, immutable' : 'no-cache');
357
+ },
358
+ });
359
+ // SPA fallback, but only for navigations. Serving index.html for a
360
+ // missing /assets/*.js turns a cache-stale asset into a confusing MIME
361
+ // type error instead of an honest 404, so anything that looks like a
362
+ // file must 404 as a file.
363
+ app.setNotFoundHandler((req, reply) => {
364
+ const url = (req.raw.url ?? '/').split('?')[0];
365
+ const looksLikeFile = /\.[a-z0-9]+$/i.test(url);
366
+ const isApi = url.startsWith('/mcp') || url.startsWith('/hook') || url.startsWith('/ws');
367
+ if (looksLikeFile || isApi) {
368
+ return reply.code(404).send({ error: 'not found', path: url });
369
+ }
370
+ return reply.sendFile('index.html');
371
+ });
372
+ }
373
+ }
374
+ /* ----------------------------------------------------------- shutdown */
375
+ /**
376
+ * Fastify's close() waits for open connections to drain, and a WebSocket
377
+ * never drains on its own — an idle browser tab or a connected peer would
378
+ * hang shutdown indefinitely. Terminate them explicitly instead.
379
+ */
380
+ app.addHook('onClose', async () => {
381
+ for (const ws of clients) {
382
+ try {
383
+ ws.terminate();
384
+ }
385
+ catch {
386
+ // Already gone.
387
+ }
388
+ }
389
+ peerServer.closeAll();
390
+ clients.clear();
391
+ attached.clear();
392
+ });
393
+ /* --------------------------------------------------------------- bind */
394
+ // Loopback unless the operator asked otherwise. An SSH-deployed hub is
395
+ // always reached through its tunnel and never binds wider; `--listen` exists
396
+ // so a machine can fetch the join page and enroll itself.
397
+ const host = opts.host ?? '127.0.0.1';
398
+ const address = await listenOnFirstFree(app, host, opts.port ?? 0);
399
+ const port = app.server.address().port;
400
+ // Agents' MCP endpoint stays on loopback whenever the bind answers there,
401
+ // which for `--listen lan` it does: that binds the wildcard precisely so
402
+ // this stays true.
403
+ const origin = `http://${coversLoopback(host) ? '127.0.0.1' : urlHost(host)}:${port}`;
404
+ hub.setOrigin(origin);
405
+ const advertised = advertisedHost(host);
406
+ if (advertised) {
407
+ // Prefer this machine's name: it is the URL someone has to carry to
408
+ // another machine and type in. The IP stays available underneath.
409
+ const name = await preferredHostname(advertised);
410
+ enrollOrigin = `http://${name ?? advertised}:${port}`;
411
+ enrollAltOrigin = name ? `http://${advertised}:${port}` : null;
412
+ }
413
+ void address;
414
+ return { app, origin, enrollOrigin, enrollAltOrigin, port, peerServer, enrollment };
415
+ }
416
+ /**
417
+ * Bind the first candidate that is free.
418
+ *
419
+ * A probe-then-bind would race, so this just tries to bind for real and moves
420
+ * on when the port is taken. The last resort is `0` — an unmemorable port
421
+ * still beats refusing to start.
422
+ */
423
+ async function listenOnFirstFree(app, host, port) {
424
+ const candidates = Array.isArray(port) ? [...port, 0] : [port];
425
+ for (let i = 0; i < candidates.length; i++) {
426
+ try {
427
+ return await app.listen({ host, port: candidates[i] });
428
+ }
429
+ catch (err) {
430
+ const code = err.code;
431
+ const inUse = code === 'EADDRINUSE' || code === 'EACCES';
432
+ if (!inUse || i === candidates.length - 1)
433
+ throw err;
434
+ }
435
+ }
436
+ throw new Error('no port available');
437
+ }
438
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import { type Session, type WindowRect } from '../protocol/index.js';
3
+ import { Store } from '../db/store.js';
4
+ import { ProfileRegistry } from '../agents/profiles.js';
5
+ import { TokenRegistry } from '../agents/tokens.js';
6
+ import { PtySession } from './pty.js';
7
+ /** Debounce for persisting window geometry while a window is being dragged. */
8
+ export declare const LAYOUT_DEBOUNCE_MS = 250;
9
+ export interface StartOptions {
10
+ workspaceId: string;
11
+ profileId: string;
12
+ name?: string;
13
+ cwd?: string;
14
+ spawnedBy?: string | null;
15
+ cols?: number;
16
+ rows?: number;
17
+ window?: WindowRect;
18
+ }
19
+ export interface ManagerEvents {
20
+ session: (s: Session) => void;
21
+ /**
22
+ * The address travels with the id: after the delete there is nothing left
23
+ * to look it up from, and peers address sessions by name.
24
+ */
25
+ removed: (sessionId: string, address: string | null) => void;
26
+ data: (sessionId: string, chunk: string) => void;
27
+ exit: (sessionId: string, code: number) => void;
28
+ }
29
+ /**
30
+ * Owns every PTY on this hub, plus the persistence of their state.
31
+ *
32
+ * The invariant worth stating: SQLite always holds enough to redraw the canvas
33
+ * and relaunch each agent. Live objects here are a cache over that, never the
34
+ * other way round.
35
+ */
36
+ export declare class SessionManager extends EventEmitter {
37
+ private readonly store;
38
+ private readonly profiles;
39
+ readonly tokens: TokenRegistry;
40
+ private hubOrigin;
41
+ private readonly live;
42
+ private readonly snapshotTimers;
43
+ private readonly layoutTimers;
44
+ /** Latest title per session, waiting for the coalescing window to close. */
45
+ private readonly pendingTitles;
46
+ private titleTimer;
47
+ constructor(store: Store, profiles: ProfileRegistry, tokens: TokenRegistry, hubOrigin: string);
48
+ setHubOrigin(origin: string): void;
49
+ private isResumable;
50
+ list(): Session[];
51
+ get(id: string): Session | null;
52
+ getByAddress(address: string): Session | null;
53
+ /** Overlay live runtime status onto the persisted row. */
54
+ private decorate;
55
+ pty(id: string): PtySession | null;
56
+ /** Build the resolved argv/env for a launch. `resume` swaps in --resume. */
57
+ private buildSpec;
58
+ private peersOf;
59
+ start(opts: StartOptions): Promise<Session>;
60
+ /** Bring a stopped session back, reusing its recorded argv and cwd. */
61
+ resume(sessionId: string): Promise<Session>;
62
+ private spawn;
63
+ write(sessionId: string, data: string): void;
64
+ resize(sessionId: string, cols: number, rows: number): void;
65
+ setStatusText(sessionId: string, text: string): void;
66
+ /**
67
+ * A title the program in the PTY set for itself.
68
+ *
69
+ * An agent rewrites this as it works, several times a second, so the write
70
+ * and the broadcast are held to the last value in a window rather than one
71
+ * apiece. Unrefed: a pending title is never a reason to keep the process up.
72
+ */
73
+ private noteTitle;
74
+ /** Called by the hooks endpoint: exact turn boundaries from the agent CLI. */
75
+ setStatusFromHook(sessionId: string, status: 'busy' | 'idle'): void;
76
+ stop(sessionId: string): void;
77
+ remove(sessionId: string): void;
78
+ private scheduleSnapshot;
79
+ /** Screen only — the deliberate cost of the snapshot-only design. */
80
+ persistSnapshot(sessionId: string): void;
81
+ persistAllSnapshots(): void;
82
+ snapshotForAttach(sessionId: string): {
83
+ serialized: string;
84
+ cols: number;
85
+ rows: number;
86
+ } | null;
87
+ moveWindow(sessionId: string, rect: WindowRect): void;
88
+ /**
89
+ * Place a new window: tucked below-right of its parent when spawned by
90
+ * another agent, otherwise in the next free slot of a simple grid.
91
+ */
92
+ private placeWindow;
93
+ /** Workspaces are laid out left to right so their groups do not overlap. */
94
+ private workspaceOriginX;
95
+ private clearTimer;
96
+ private emitSession;
97
+ /**
98
+ * Startup reconciliation. Sessions cannot outlive the hub, so anything the
99
+ * DB still calls running is stale and is marked stopped, ready to resume.
100
+ */
101
+ reconcileOnBoot(): void;
102
+ shutdown(): void;
103
+ }
104
+ //# sourceMappingURL=manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/session/manager.ts"],"names":[],"mappings":"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"}