adhdev 0.8.50 → 0.8.54

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 (53) hide show
  1. package/dist/cli/index.js +2115 -853
  2. package/dist/cli/index.js.map +1 -1
  3. package/dist/index.js +1391 -613
  4. package/dist/index.js.map +1 -1
  5. package/package.json +3 -2
  6. package/vendor/session-host-daemon/index.d.mts +3 -0
  7. package/vendor/session-host-daemon/index.d.ts +3 -0
  8. package/vendor/session-host-daemon/index.js +33 -2
  9. package/vendor/session-host-daemon/index.js.map +1 -1
  10. package/vendor/session-host-daemon/index.mjs +34 -2
  11. package/vendor/session-host-daemon/index.mjs.map +1 -1
  12. package/vendor/session-host-daemon/node_modules/@adhdev/session-host-core/index.d.mts +16 -1
  13. package/vendor/session-host-daemon/node_modules/@adhdev/session-host-core/index.d.ts +16 -1
  14. package/vendor/session-host-daemon/node_modules/@adhdev/session-host-core/index.js +59 -0
  15. package/vendor/session-host-daemon/node_modules/@adhdev/session-host-core/index.js.map +1 -1
  16. package/vendor/session-host-daemon/node_modules/@adhdev/session-host-core/index.mjs +54 -0
  17. package/vendor/session-host-daemon/node_modules/@adhdev/session-host-core/index.mjs.map +1 -1
  18. package/vendor/terminal-mux-cli/index.d.mts +1 -0
  19. package/vendor/terminal-mux-cli/index.d.ts +1 -0
  20. package/vendor/terminal-mux-cli/index.js +2070 -0
  21. package/vendor/terminal-mux-cli/index.mjs +2062 -0
  22. package/vendor/terminal-mux-cli/node_modules/@adhdev/session-host-core/index.d.mts +442 -0
  23. package/vendor/terminal-mux-cli/node_modules/@adhdev/session-host-core/index.d.ts +442 -0
  24. package/vendor/terminal-mux-cli/node_modules/@adhdev/session-host-core/index.js +676 -0
  25. package/vendor/terminal-mux-cli/node_modules/@adhdev/session-host-core/index.js.map +1 -0
  26. package/vendor/terminal-mux-cli/node_modules/@adhdev/session-host-core/index.mjs +627 -0
  27. package/vendor/terminal-mux-cli/node_modules/@adhdev/session-host-core/index.mjs.map +1 -0
  28. package/vendor/terminal-mux-cli/node_modules/@adhdev/session-host-core/package.json +7 -0
  29. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/api.d.mts +16 -0
  30. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/api.d.ts +16 -0
  31. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/api.js +206 -0
  32. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/api.mjs +17 -0
  33. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/chunk-7RNMRPVZ.mjs +183 -0
  34. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/chunk-R4EFW6W3.mjs +46 -0
  35. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/chunk-XZWWVN5W.mjs +164 -0
  36. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/control-socket.d.mts +35 -0
  37. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/control-socket.d.ts +35 -0
  38. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/control-socket.js +219 -0
  39. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/control-socket.mjs +13 -0
  40. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/index.d.mts +5 -0
  41. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/index.d.ts +5 -0
  42. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/index.js +427 -0
  43. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/index.mjs +34 -0
  44. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/package.json +33 -0
  45. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/storage.d.mts +49 -0
  46. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/storage.d.ts +49 -0
  47. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/storage.js +222 -0
  48. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-control/storage.mjs +16 -0
  49. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-core/index.d.mts +164 -0
  50. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-core/index.d.ts +164 -0
  51. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-core/index.js +993 -0
  52. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-core/index.mjs +957 -0
  53. package/vendor/terminal-mux-cli/node_modules/@adhdev/terminal-mux-core/package.json +7 -0
@@ -0,0 +1,993 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ GhosttyTerminalSurface: () => GhosttyTerminalSurface,
24
+ SessionHostMuxClient: () => SessionHostMuxClient,
25
+ applyMuxLayoutPreset: () => applyMuxLayoutPreset,
26
+ createMuxWorkspace: () => createMuxWorkspace,
27
+ focusMuxPane: () => focusMuxPane,
28
+ rebalanceMuxLayout: () => rebalanceMuxLayout,
29
+ removeMuxPane: () => removeMuxPane,
30
+ resizeMuxPane: () => resizeMuxPane,
31
+ resolveMuxOpenRuntimeRecord: () => resolveMuxOpenRuntimeRecord,
32
+ serializeWorkspace: () => serializeWorkspace,
33
+ splitMuxPane: () => splitMuxPane,
34
+ swapMuxPanePositions: () => swapMuxPanePositions,
35
+ toggleMuxPaneZoom: () => toggleMuxPaneZoom,
36
+ updateMuxPane: () => updateMuxPane
37
+ });
38
+ module.exports = __toCommonJS(index_exports);
39
+
40
+ // src/layout.ts
41
+ var import_crypto = require("crypto");
42
+ function cloneNode(node) {
43
+ if (node.type === "pane") {
44
+ return { ...node };
45
+ }
46
+ return {
47
+ ...node,
48
+ first: cloneNode(node.first),
49
+ second: cloneNode(node.second)
50
+ };
51
+ }
52
+ function replaceNode(node, paneId, next) {
53
+ if (node.type === "pane") {
54
+ return node.paneId === paneId ? next : node;
55
+ }
56
+ return {
57
+ ...node,
58
+ first: replaceNode(node.first, paneId, next),
59
+ second: replaceNode(node.second, paneId, next)
60
+ };
61
+ }
62
+ function removeNode(node, paneId) {
63
+ if (node.type === "pane") {
64
+ return node.paneId === paneId ? null : node;
65
+ }
66
+ const first = removeNode(node.first, paneId);
67
+ const second = removeNode(node.second, paneId);
68
+ if (!first && !second) return null;
69
+ if (!first) return second;
70
+ if (!second) return first;
71
+ return {
72
+ ...node,
73
+ first,
74
+ second
75
+ };
76
+ }
77
+ function cloneWorkspace(workspace) {
78
+ return {
79
+ ...workspace,
80
+ root: cloneNode(workspace.root),
81
+ panes: Object.fromEntries(
82
+ Object.entries(workspace.panes).map(([paneId, pane]) => [paneId, { ...pane, viewport: { ...pane.viewport } }])
83
+ )
84
+ };
85
+ }
86
+ function nodeContainsPane(node, paneId) {
87
+ if (node.type === "pane") {
88
+ return node.paneId === paneId;
89
+ }
90
+ return nodeContainsPane(node.first, paneId) || nodeContainsPane(node.second, paneId);
91
+ }
92
+ function clampRatio(value) {
93
+ return Math.max(0.15, Math.min(0.85, value));
94
+ }
95
+ function adjustNodeForPane(node, paneId, axis, delta) {
96
+ if (node.type === "pane") {
97
+ return { node, changed: false };
98
+ }
99
+ const firstContains = nodeContainsPane(node.first, paneId);
100
+ const secondContains = !firstContains && nodeContainsPane(node.second, paneId);
101
+ let changed = false;
102
+ let nextNode = node;
103
+ if (node.axis === axis && (firstContains || secondContains)) {
104
+ const directionDelta = firstContains ? delta : -delta;
105
+ nextNode = {
106
+ ...node,
107
+ ratio: clampRatio(node.ratio + directionDelta)
108
+ };
109
+ changed = true;
110
+ }
111
+ if (firstContains) {
112
+ const adjusted = adjustNodeForPane(nextNode.first, paneId, axis, delta);
113
+ if (adjusted.changed) {
114
+ nextNode = {
115
+ ...nextNode,
116
+ first: adjusted.node
117
+ };
118
+ changed = true;
119
+ }
120
+ } else if (secondContains) {
121
+ const adjusted = adjustNodeForPane(nextNode.second, paneId, axis, delta);
122
+ if (adjusted.changed) {
123
+ nextNode = {
124
+ ...nextNode,
125
+ second: adjusted.node
126
+ };
127
+ changed = true;
128
+ }
129
+ }
130
+ return { node: nextNode, changed };
131
+ }
132
+ function rebalanceNode(node) {
133
+ if (node.type === "pane") return node;
134
+ return {
135
+ ...node,
136
+ ratio: 0.5,
137
+ first: rebalanceNode(node.first),
138
+ second: rebalanceNode(node.second)
139
+ };
140
+ }
141
+ function mapPaneIds(node, firstPaneId, secondPaneId) {
142
+ if (node.type === "pane") {
143
+ if (node.paneId === firstPaneId) return { ...node, paneId: secondPaneId };
144
+ if (node.paneId === secondPaneId) return { ...node, paneId: firstPaneId };
145
+ return node;
146
+ }
147
+ return {
148
+ ...node,
149
+ first: mapPaneIds(node.first, firstPaneId, secondPaneId),
150
+ second: mapPaneIds(node.second, firstPaneId, secondPaneId)
151
+ };
152
+ }
153
+ function collectPaneIds(node, acc = []) {
154
+ if (node.type === "pane") {
155
+ acc.push(node.paneId);
156
+ return acc;
157
+ }
158
+ collectPaneIds(node.first, acc);
159
+ collectPaneIds(node.second, acc);
160
+ return acc;
161
+ }
162
+ function buildEvenLayout(paneIds, axis = "vertical") {
163
+ if (paneIds.length === 0) throw new Error("Cannot build layout without panes");
164
+ if (paneIds.length === 1) return { type: "pane", paneId: paneIds[0] };
165
+ const midpoint = Math.ceil(paneIds.length / 2);
166
+ const nextAxis = axis === "vertical" ? "horizontal" : "vertical";
167
+ return {
168
+ type: "split",
169
+ axis,
170
+ ratio: 0.5,
171
+ first: buildEvenLayout(paneIds.slice(0, midpoint), nextAxis),
172
+ second: buildEvenLayout(paneIds.slice(midpoint), nextAxis)
173
+ };
174
+ }
175
+ function buildStackLayout(paneIds, axis) {
176
+ if (paneIds.length === 0) throw new Error("Cannot build stack layout without panes");
177
+ if (paneIds.length === 1) return { type: "pane", paneId: paneIds[0] };
178
+ const [first, ...rest] = paneIds;
179
+ return {
180
+ type: "split",
181
+ axis,
182
+ ratio: 0.5,
183
+ first: { type: "pane", paneId: first },
184
+ second: buildStackLayout(rest, axis)
185
+ };
186
+ }
187
+ function buildMainLayout(paneIds, rootAxis) {
188
+ if (paneIds.length === 0) throw new Error("Cannot build layout without panes");
189
+ if (paneIds.length === 1) return { type: "pane", paneId: paneIds[0] };
190
+ const [primary, ...rest] = paneIds;
191
+ const stackAxis = rootAxis === "vertical" ? "horizontal" : "vertical";
192
+ return {
193
+ type: "split",
194
+ axis: rootAxis,
195
+ ratio: rest.length === 1 ? 0.5 : 0.62,
196
+ first: { type: "pane", paneId: primary },
197
+ second: buildStackLayout(rest, stackAxis)
198
+ };
199
+ }
200
+ function createMuxWorkspace(initialPane, options = {}) {
201
+ return {
202
+ workspaceId: options.workspaceId || (0, import_crypto.randomUUID)(),
203
+ title: options.title || initialPane.displayName,
204
+ root: {
205
+ type: "pane",
206
+ paneId: initialPane.paneId
207
+ },
208
+ focusedPaneId: initialPane.paneId,
209
+ zoomedPaneId: null,
210
+ panes: {
211
+ [initialPane.paneId]: initialPane
212
+ }
213
+ };
214
+ }
215
+ function splitMuxPane(workspace, targetPaneId, axis, nextPane) {
216
+ if (!workspace.panes[targetPaneId]) {
217
+ throw new Error(`Unknown pane: ${targetPaneId}`);
218
+ }
219
+ const next = cloneWorkspace(workspace);
220
+ next.root = replaceNode(next.root, targetPaneId, {
221
+ type: "split",
222
+ axis,
223
+ ratio: 0.5,
224
+ first: { type: "pane", paneId: targetPaneId },
225
+ second: { type: "pane", paneId: nextPane.paneId }
226
+ });
227
+ next.panes[nextPane.paneId] = nextPane;
228
+ next.focusedPaneId = nextPane.paneId;
229
+ return next;
230
+ }
231
+ function removeMuxPane(workspace, paneId) {
232
+ if (!workspace.panes[paneId]) return workspace;
233
+ const root = removeNode(workspace.root, paneId);
234
+ if (!root) return null;
235
+ const next = cloneWorkspace(workspace);
236
+ delete next.panes[paneId];
237
+ next.root = root;
238
+ if (next.focusedPaneId === paneId) {
239
+ next.focusedPaneId = Object.keys(next.panes)[0] || "";
240
+ }
241
+ if (next.zoomedPaneId === paneId) {
242
+ next.zoomedPaneId = null;
243
+ }
244
+ return next;
245
+ }
246
+ function focusMuxPane(workspace, paneId) {
247
+ if (!workspace.panes[paneId]) {
248
+ throw new Error(`Unknown pane: ${paneId}`);
249
+ }
250
+ return {
251
+ ...workspace,
252
+ focusedPaneId: paneId
253
+ };
254
+ }
255
+ function updateMuxPane(workspace, pane) {
256
+ if (!workspace.panes[pane.paneId]) return workspace;
257
+ return {
258
+ ...workspace,
259
+ panes: {
260
+ ...workspace.panes,
261
+ [pane.paneId]: pane
262
+ }
263
+ };
264
+ }
265
+ function toggleMuxPaneZoom(workspace, paneId) {
266
+ if (!workspace.panes[paneId]) {
267
+ throw new Error(`Unknown pane: ${paneId}`);
268
+ }
269
+ return {
270
+ ...workspace,
271
+ focusedPaneId: paneId,
272
+ zoomedPaneId: workspace.zoomedPaneId === paneId ? null : paneId
273
+ };
274
+ }
275
+ function resizeMuxPane(workspace, paneId, direction, amount = 0.05) {
276
+ if (!workspace.panes[paneId]) {
277
+ throw new Error(`Unknown pane: ${paneId}`);
278
+ }
279
+ const axis = direction === "left" || direction === "right" ? "vertical" : "horizontal";
280
+ const delta = direction === "left" || direction === "up" ? -Math.abs(amount) : Math.abs(amount);
281
+ const adjusted = adjustNodeForPane(workspace.root, paneId, axis, delta);
282
+ if (!adjusted.changed) {
283
+ return workspace;
284
+ }
285
+ return {
286
+ ...workspace,
287
+ root: adjusted.node
288
+ };
289
+ }
290
+ function rebalanceMuxLayout(workspace) {
291
+ return {
292
+ ...workspace,
293
+ root: rebalanceNode(workspace.root)
294
+ };
295
+ }
296
+ function swapMuxPanePositions(workspace, firstPaneId, secondPaneId) {
297
+ if (firstPaneId === secondPaneId) return workspace;
298
+ if (!workspace.panes[firstPaneId]) throw new Error(`Unknown pane: ${firstPaneId}`);
299
+ if (!workspace.panes[secondPaneId]) throw new Error(`Unknown pane: ${secondPaneId}`);
300
+ return {
301
+ ...workspace,
302
+ root: mapPaneIds(workspace.root, firstPaneId, secondPaneId),
303
+ focusedPaneId: workspace.focusedPaneId === firstPaneId ? secondPaneId : workspace.focusedPaneId === secondPaneId ? firstPaneId : workspace.focusedPaneId,
304
+ zoomedPaneId: workspace.zoomedPaneId === firstPaneId ? secondPaneId : workspace.zoomedPaneId === secondPaneId ? firstPaneId : workspace.zoomedPaneId
305
+ };
306
+ }
307
+ function applyMuxLayoutPreset(workspace, preset) {
308
+ const paneIds = collectPaneIds(workspace.root);
309
+ const orderedPaneIds = [workspace.focusedPaneId, ...paneIds.filter((paneId) => paneId !== workspace.focusedPaneId)];
310
+ let root;
311
+ switch (preset) {
312
+ case "main-vertical":
313
+ root = buildMainLayout(orderedPaneIds, "vertical");
314
+ break;
315
+ case "main-horizontal":
316
+ root = buildMainLayout(orderedPaneIds, "horizontal");
317
+ break;
318
+ case "tiled":
319
+ case "even":
320
+ default:
321
+ root = buildEvenLayout(orderedPaneIds, "vertical");
322
+ break;
323
+ }
324
+ return { ...workspace, root };
325
+ }
326
+
327
+ // src/ghostty-terminal-surface.ts
328
+ var import_module = require("module");
329
+ var import_meta = {};
330
+ var require2 = (0, import_module.createRequire)(
331
+ typeof __filename === "string" ? __filename : import_meta.url
332
+ );
333
+ var ghosttyBinding = require2("@adhdev/ghostty-vt-node");
334
+ var GhosttyTerminalSurface = class {
335
+ terminal;
336
+ cols;
337
+ rows;
338
+ snapshotSeq = 0;
339
+ constructor(options = {}) {
340
+ this.cols = Math.max(1, options.cols ?? 120);
341
+ this.rows = Math.max(1, options.rows ?? 36);
342
+ const terminalOptions = {
343
+ cols: this.cols,
344
+ rows: this.rows,
345
+ scrollback: Math.max(1024, options.scrollback ?? 32768)
346
+ };
347
+ this.terminal = ghosttyBinding.createTerminal(terminalOptions);
348
+ }
349
+ resetFromText(text, snapshotSeq = 0) {
350
+ this.terminal.dispose();
351
+ this.terminal = ghosttyBinding.createTerminal({
352
+ cols: this.cols,
353
+ rows: this.rows,
354
+ scrollback: 32768
355
+ });
356
+ if (text) {
357
+ this.terminal.write(text);
358
+ }
359
+ this.snapshotSeq = snapshotSeq;
360
+ }
361
+ write(data, snapshotSeq) {
362
+ if (data) {
363
+ this.terminal.write(data);
364
+ }
365
+ if (typeof snapshotSeq === "number") {
366
+ this.snapshotSeq = snapshotSeq;
367
+ }
368
+ }
369
+ resize(cols, rows) {
370
+ this.cols = Math.max(1, cols | 0);
371
+ this.rows = Math.max(1, rows | 0);
372
+ this.terminal.resize(this.cols, this.rows);
373
+ }
374
+ getViewportState() {
375
+ return {
376
+ cols: this.cols,
377
+ rows: this.rows,
378
+ snapshotSeq: this.snapshotSeq,
379
+ text: this.terminal.formatPlainText({ trim: true }) || ""
380
+ };
381
+ }
382
+ dispose() {
383
+ this.terminal.dispose();
384
+ }
385
+ };
386
+
387
+ // src/runtime-targeting.ts
388
+ var import_session_host_core = require("@adhdev/session-host-core");
389
+ function resolveMuxOpenRuntimeRecord(records, identifier) {
390
+ return (0, import_session_host_core.resolveAttachableRuntimeRecord)(records, identifier);
391
+ }
392
+
393
+ // src/session-host-mux-client.ts
394
+ var import_crypto2 = require("crypto");
395
+ var import_session_host_core2 = require("@adhdev/session-host-core");
396
+
397
+ // src/workspace-persistence.ts
398
+ function serializeWorkspace(workspace) {
399
+ return {
400
+ workspaceId: workspace.workspaceId,
401
+ title: workspace.title,
402
+ focusedPaneId: workspace.focusedPaneId,
403
+ zoomedPaneId: workspace.zoomedPaneId || null,
404
+ root: workspace.root,
405
+ panes: Object.fromEntries(
406
+ Object.entries(workspace.panes).map(([paneId, pane]) => [
407
+ paneId,
408
+ {
409
+ runtimeId: pane.runtimeId,
410
+ runtimeKey: pane.runtimeKey,
411
+ paneKind: pane.paneKind,
412
+ accessMode: pane.accessMode
413
+ }
414
+ ])
415
+ )
416
+ };
417
+ }
418
+
419
+ // src/session-host-mux-client.ts
420
+ function paneFromRecord(paneId, record, surface, paneKind, accessMode) {
421
+ return {
422
+ paneId,
423
+ paneKind,
424
+ runtimeId: record.sessionId,
425
+ runtimeKey: record.runtimeKey,
426
+ displayName: record.displayName,
427
+ workspaceLabel: record.workspaceLabel,
428
+ accessMode,
429
+ lifecycle: record.lifecycle,
430
+ writeOwner: record.writeOwner,
431
+ attachedClients: record.attachedClients,
432
+ viewport: surface.getViewportState()
433
+ };
434
+ }
435
+ var SessionHostMuxClient = class {
436
+ clientId;
437
+ clientType = "local-terminal";
438
+ client;
439
+ paneById = /* @__PURE__ */ new Map();
440
+ paneIdsByRuntime = /* @__PURE__ */ new Map();
441
+ workspaceById = /* @__PURE__ */ new Map();
442
+ listeners = /* @__PURE__ */ new Set();
443
+ unsubEvents = null;
444
+ constructor(options = {}) {
445
+ this.client = new import_session_host_core2.SessionHostClient(options);
446
+ this.clientId = options.clientId || `terminal-ui-${(0, import_crypto2.randomUUID)()}`;
447
+ }
448
+ async connect() {
449
+ await this.client.connect();
450
+ if (!this.unsubEvents) {
451
+ this.unsubEvents = this.client.onEvent((event) => {
452
+ void this.handleHostEvent(event);
453
+ });
454
+ }
455
+ }
456
+ onEvent(listener) {
457
+ this.listeners.add(listener);
458
+ return () => {
459
+ this.listeners.delete(listener);
460
+ };
461
+ }
462
+ async createWorkspace(target, options = {}) {
463
+ const pane = await this.openRuntime(target, options);
464
+ const workspace = createMuxWorkspace(pane, {
465
+ workspaceId: options.workspaceId,
466
+ title: options.title
467
+ });
468
+ this.workspaceById.set(workspace.workspaceId, workspace);
469
+ this.emit({ kind: "workspace", workspace });
470
+ return workspace;
471
+ }
472
+ async splitWorkspacePane(workspaceId, targetPaneId, runtimeTarget, options) {
473
+ const workspace = this.requireWorkspace(workspaceId);
474
+ const nextPane = await this.openRuntime(runtimeTarget, options);
475
+ const updated = splitMuxPane(workspace, targetPaneId, options.axis, nextPane);
476
+ this.workspaceById.set(workspaceId, updated);
477
+ this.emit({ kind: "workspace", workspace: updated });
478
+ return updated;
479
+ }
480
+ async splitWorkspaceMirror(workspaceId, targetPaneId, sourcePaneId, axis) {
481
+ const workspace = this.requireWorkspace(workspaceId);
482
+ const source = this.requirePane(sourcePaneId);
483
+ const paneId = (0, import_crypto2.randomUUID)();
484
+ const viewport = source.surface.getViewportState();
485
+ const surface = new GhosttyTerminalSurface({
486
+ cols: viewport.cols,
487
+ rows: viewport.rows
488
+ });
489
+ surface.resetFromText(viewport.text, viewport.snapshotSeq);
490
+ const paneState = paneFromRecord(paneId, source.record, surface, "mirror", "read-only");
491
+ this.paneById.set(paneId, {
492
+ record: source.record,
493
+ surface,
494
+ paneKind: "mirror",
495
+ accessMode: "read-only",
496
+ requestedReadOnly: true
497
+ });
498
+ const paneIds = this.paneIdsByRuntime.get(source.record.sessionId) || /* @__PURE__ */ new Set();
499
+ paneIds.add(paneId);
500
+ this.paneIdsByRuntime.set(source.record.sessionId, paneIds);
501
+ const updated = splitMuxPane(workspace, targetPaneId, axis, paneState);
502
+ this.workspaceById.set(workspaceId, updated);
503
+ this.emit({ kind: "runtime", pane: paneState });
504
+ this.emit({ kind: "workspace", workspace: updated });
505
+ return updated;
506
+ }
507
+ async replacePaneRuntime(workspaceId, paneId, runtimeTarget, options = {}) {
508
+ const workspace = this.requireWorkspace(workspaceId);
509
+ const existing = this.requirePane(paneId);
510
+ const previousRuntimeId = existing.record.sessionId;
511
+ const replacement = await this.openRuntime(runtimeTarget, {
512
+ ...options,
513
+ paneId
514
+ });
515
+ existing.surface.dispose();
516
+ if (previousRuntimeId !== replacement.runtimeId) {
517
+ const existingPaneIds = this.paneIdsByRuntime.get(previousRuntimeId);
518
+ existingPaneIds?.delete(paneId);
519
+ if (existingPaneIds && existingPaneIds.size === 0) {
520
+ this.paneIdsByRuntime.delete(previousRuntimeId);
521
+ await this.client.request({
522
+ type: "detach_session",
523
+ payload: {
524
+ sessionId: previousRuntimeId,
525
+ clientId: this.clientId
526
+ }
527
+ });
528
+ }
529
+ }
530
+ const updated = updateMuxPane(workspace, replacement);
531
+ this.workspaceById.set(workspaceId, updated);
532
+ this.emit({ kind: "workspace", workspace: updated });
533
+ return updated;
534
+ }
535
+ async restoreWorkspace(snapshot) {
536
+ await this.connect();
537
+ const panes = Object.entries(snapshot.panes);
538
+ if (panes.length === 0) {
539
+ throw new Error(`Workspace ${snapshot.workspaceId} has no panes`);
540
+ }
541
+ const orderedPanes = panes.sort(([, left], [, right]) => {
542
+ const leftKind = left.paneKind || "runtime";
543
+ const rightKind = right.paneKind || "runtime";
544
+ if (leftKind === rightKind) return 0;
545
+ return leftKind === "runtime" ? -1 : 1;
546
+ });
547
+ const restoredPanes = [];
548
+ for (const [paneId, pane] of orderedPanes) {
549
+ const paneKind = pane.paneKind || "runtime";
550
+ const opened = paneKind === "mirror" ? await this.openMirrorRuntime(pane.runtimeId || pane.runtimeKey, { paneId }) : await this.openRuntime(pane.runtimeId || pane.runtimeKey, {
551
+ paneId,
552
+ readOnly: pane.accessMode === "read-only",
553
+ takeover: false
554
+ });
555
+ restoredPanes.push([paneId, opened]);
556
+ }
557
+ const workspace = {
558
+ workspaceId: snapshot.workspaceId,
559
+ title: snapshot.title,
560
+ root: snapshot.root,
561
+ focusedPaneId: snapshot.focusedPaneId in snapshot.panes ? snapshot.focusedPaneId : restoredPanes[0][0],
562
+ zoomedPaneId: snapshot.zoomedPaneId && snapshot.zoomedPaneId in snapshot.panes ? snapshot.zoomedPaneId : null,
563
+ panes: Object.fromEntries(restoredPanes)
564
+ };
565
+ this.workspaceById.set(workspace.workspaceId, workspace);
566
+ this.emit({ kind: "workspace", workspace });
567
+ return workspace;
568
+ }
569
+ async closePane(workspaceId, paneId) {
570
+ const workspace = this.requireWorkspace(workspaceId);
571
+ const next = removeMuxPane(workspace, paneId);
572
+ const pane = this.paneById.get(paneId);
573
+ if (pane) {
574
+ pane.surface.dispose();
575
+ this.paneById.delete(paneId);
576
+ const paneIds = this.paneIdsByRuntime.get(pane.record.sessionId);
577
+ paneIds?.delete(paneId);
578
+ if (paneIds && paneIds.size === 0) {
579
+ this.paneIdsByRuntime.delete(pane.record.sessionId);
580
+ await this.client.request({
581
+ type: "detach_session",
582
+ payload: {
583
+ sessionId: pane.record.sessionId,
584
+ clientId: this.clientId
585
+ }
586
+ });
587
+ }
588
+ }
589
+ if (!next) {
590
+ this.workspaceById.delete(workspaceId);
591
+ return null;
592
+ }
593
+ this.workspaceById.set(workspaceId, next);
594
+ this.emit({ kind: "workspace", workspace: next });
595
+ return next;
596
+ }
597
+ async focusPane(workspaceId, paneId) {
598
+ const workspace = this.requireWorkspace(workspaceId);
599
+ const next = focusMuxPane(workspace, paneId);
600
+ this.workspaceById.set(workspaceId, next);
601
+ this.emit({ kind: "workspace", workspace: next });
602
+ return next;
603
+ }
604
+ async resizeLayoutPane(workspaceId, paneId, direction, amount = 0.05) {
605
+ const workspace = this.requireWorkspace(workspaceId);
606
+ const next = resizeMuxPane(workspace, paneId, direction, amount);
607
+ this.workspaceById.set(workspaceId, next);
608
+ this.emit({ kind: "workspace", workspace: next });
609
+ return next;
610
+ }
611
+ async rebalanceWorkspaceLayout(workspaceId) {
612
+ const workspace = this.requireWorkspace(workspaceId);
613
+ const next = rebalanceMuxLayout(workspace);
614
+ this.workspaceById.set(workspaceId, next);
615
+ this.emit({ kind: "workspace", workspace: next });
616
+ return next;
617
+ }
618
+ async applyLayoutPreset(workspaceId, preset) {
619
+ const workspace = this.requireWorkspace(workspaceId);
620
+ const next = applyMuxLayoutPreset(workspace, preset);
621
+ this.workspaceById.set(workspaceId, next);
622
+ this.emit({ kind: "workspace", workspace: next });
623
+ return next;
624
+ }
625
+ async swapPanePositions(workspaceId, firstPaneId, secondPaneId) {
626
+ const workspace = this.requireWorkspace(workspaceId);
627
+ const next = swapMuxPanePositions(workspace, firstPaneId, secondPaneId);
628
+ this.workspaceById.set(workspaceId, next);
629
+ this.emit({ kind: "workspace", workspace: next });
630
+ return next;
631
+ }
632
+ async togglePaneZoom(workspaceId, paneId) {
633
+ const workspace = this.requireWorkspace(workspaceId);
634
+ const next = toggleMuxPaneZoom(workspace, paneId);
635
+ this.workspaceById.set(workspaceId, next);
636
+ this.emit({ kind: "workspace", workspace: next });
637
+ return next;
638
+ }
639
+ async sendInput(paneId, data) {
640
+ const pane = this.requirePane(paneId);
641
+ if (pane.accessMode === "read-only" && !pane.requestedReadOnly && pane.paneKind === "runtime") {
642
+ await this.takeoverPane(paneId);
643
+ }
644
+ if (pane.accessMode === "read-only") {
645
+ throw new Error(`Pane ${paneId} is read-only`);
646
+ }
647
+ const response = await this.client.request({
648
+ type: "send_input",
649
+ payload: {
650
+ sessionId: pane.record.sessionId,
651
+ clientId: this.clientId,
652
+ data
653
+ }
654
+ });
655
+ if (!response.success) {
656
+ if ((response.error || "").includes("Write owned by") && !pane.requestedReadOnly && pane.paneKind === "runtime") {
657
+ await this.takeoverPane(paneId);
658
+ const retry = await this.client.request({
659
+ type: "send_input",
660
+ payload: {
661
+ sessionId: pane.record.sessionId,
662
+ clientId: this.clientId,
663
+ data
664
+ }
665
+ });
666
+ if (!retry.success) {
667
+ throw new Error(retry.error || `Failed to send input to pane ${paneId}`);
668
+ }
669
+ return;
670
+ }
671
+ throw new Error(response.error || `Failed to send input to pane ${paneId}`);
672
+ }
673
+ }
674
+ async resizePane(paneId, cols, rows) {
675
+ const pane = this.requirePane(paneId);
676
+ pane.surface.resize(cols, rows);
677
+ await this.client.request({
678
+ type: "resize_session",
679
+ payload: {
680
+ sessionId: pane.record.sessionId,
681
+ cols,
682
+ rows
683
+ }
684
+ });
685
+ this.publishPaneUpdate(paneId);
686
+ }
687
+ async takeoverPane(paneId) {
688
+ const pane = this.requirePane(paneId);
689
+ const response = await this.client.request({
690
+ type: "acquire_write",
691
+ payload: {
692
+ sessionId: pane.record.sessionId,
693
+ clientId: this.clientId,
694
+ ownerType: "user",
695
+ force: true
696
+ }
697
+ });
698
+ if (!response.success || !response.result) {
699
+ throw new Error(response.error || "Failed to acquire write owner");
700
+ }
701
+ pane.record = response.result;
702
+ pane.requestedReadOnly = false;
703
+ pane.accessMode = "interactive";
704
+ this.publishPaneUpdate(paneId);
705
+ }
706
+ async releasePane(paneId) {
707
+ const pane = this.requirePane(paneId);
708
+ const response = await this.client.request({
709
+ type: "release_write",
710
+ payload: {
711
+ sessionId: pane.record.sessionId,
712
+ clientId: this.clientId
713
+ }
714
+ });
715
+ if (!response.success || !response.result) {
716
+ throw new Error(response.error || "Failed to release write owner");
717
+ }
718
+ pane.record = response.result;
719
+ pane.requestedReadOnly = false;
720
+ pane.accessMode = this.computeAccessMode(pane.record, false, pane.paneKind);
721
+ this.publishPaneUpdate(paneId);
722
+ }
723
+ listWorkspaces() {
724
+ return Array.from(this.workspaceById.values());
725
+ }
726
+ async listRuntimes() {
727
+ await this.connect();
728
+ const list = await this.client.request({ type: "list_sessions" });
729
+ if (!list.success || !list.result) {
730
+ throw new Error(list.error || "Failed to list runtimes");
731
+ }
732
+ return list.result;
733
+ }
734
+ async resumeRuntime(target) {
735
+ await this.connect();
736
+ const record = (0, import_session_host_core2.resolveRuntimeRecord)(await this.listRuntimes(), target);
737
+ const response = await this.client.request({
738
+ type: "resume_session",
739
+ payload: {
740
+ sessionId: record.sessionId
741
+ }
742
+ });
743
+ if (!response.success || !response.result) {
744
+ throw new Error(response.error || `Failed to resume runtime ${target}`);
745
+ }
746
+ for (const [paneId, pane] of this.paneById) {
747
+ if (pane.record.sessionId !== record.sessionId) continue;
748
+ pane.record = response.result;
749
+ this.publishPaneUpdate(paneId);
750
+ }
751
+ return response.result;
752
+ }
753
+ serializeWorkspace(workspaceId) {
754
+ return serializeWorkspace(this.requireWorkspace(workspaceId));
755
+ }
756
+ async close() {
757
+ for (const [paneId, pane] of this.paneById) {
758
+ try {
759
+ if (pane.record.writeOwner?.clientId === this.clientId) {
760
+ await this.client.request({
761
+ type: "release_write",
762
+ payload: {
763
+ sessionId: pane.record.sessionId,
764
+ clientId: this.clientId
765
+ }
766
+ });
767
+ }
768
+ const siblings = this.paneIdsByRuntime.get(pane.record.sessionId);
769
+ if (siblings?.size === 1) {
770
+ await this.client.request({
771
+ type: "detach_session",
772
+ payload: {
773
+ sessionId: pane.record.sessionId,
774
+ clientId: this.clientId
775
+ }
776
+ });
777
+ }
778
+ } catch {
779
+ }
780
+ pane.surface.dispose();
781
+ this.paneById.delete(paneId);
782
+ }
783
+ this.paneIdsByRuntime.clear();
784
+ this.workspaceById.clear();
785
+ this.unsubEvents?.();
786
+ this.unsubEvents = null;
787
+ await this.client.close();
788
+ }
789
+ async openRuntime(target, options) {
790
+ await this.connect();
791
+ let record = resolveMuxOpenRuntimeRecord(await this.listRuntimes(), target);
792
+ if (record.lifecycle === "interrupted") {
793
+ try {
794
+ record = await this.resumeRuntime(target);
795
+ } catch {
796
+ }
797
+ }
798
+ const readOnly = options.takeover ? false : !!options.readOnly || !!(record.writeOwner && record.writeOwner.clientId !== this.clientId);
799
+ const attachResponse = await this.client.request({
800
+ type: "attach_session",
801
+ payload: {
802
+ sessionId: record.sessionId,
803
+ clientId: this.clientId,
804
+ clientType: this.clientType,
805
+ readOnly
806
+ }
807
+ });
808
+ if (!attachResponse.success || !attachResponse.result) {
809
+ throw new Error(attachResponse.error || `Failed to attach runtime ${target}`);
810
+ }
811
+ record = attachResponse.result;
812
+ if (options.takeover) {
813
+ const takeoverResponse = await this.client.request({
814
+ type: "acquire_write",
815
+ payload: {
816
+ sessionId: record.sessionId,
817
+ clientId: this.clientId,
818
+ ownerType: "user",
819
+ force: true
820
+ }
821
+ });
822
+ if (!takeoverResponse.success || !takeoverResponse.result) {
823
+ throw new Error(takeoverResponse.error || `Failed to acquire runtime ${target}`);
824
+ }
825
+ record = takeoverResponse.result;
826
+ }
827
+ const snapshot = await this.client.request({
828
+ type: "get_snapshot",
829
+ payload: {
830
+ sessionId: record.sessionId
831
+ }
832
+ });
833
+ if (!snapshot.success || !snapshot.result) {
834
+ throw new Error(snapshot.error || "Failed to get runtime snapshot");
835
+ }
836
+ const paneId = options.paneId || (0, import_crypto2.randomUUID)();
837
+ const surface = new GhosttyTerminalSurface({
838
+ cols: options.cols ?? 120,
839
+ rows: options.rows ?? 36
840
+ });
841
+ surface.resetFromText(snapshot.result.text, snapshot.result.seq);
842
+ const runtimeRecord = {
843
+ ...record,
844
+ attachedClients: record.attachedClients
845
+ };
846
+ const accessMode = this.computeAccessMode(runtimeRecord, readOnly, "runtime");
847
+ const paneState = paneFromRecord(paneId, runtimeRecord, surface, "runtime", accessMode);
848
+ this.paneById.set(paneId, {
849
+ record: runtimeRecord,
850
+ surface,
851
+ paneKind: "runtime",
852
+ accessMode,
853
+ requestedReadOnly: readOnly
854
+ });
855
+ const paneIds = this.paneIdsByRuntime.get(runtimeRecord.sessionId) || /* @__PURE__ */ new Set();
856
+ paneIds.add(paneId);
857
+ this.paneIdsByRuntime.set(runtimeRecord.sessionId, paneIds);
858
+ this.emit({ kind: "runtime", pane: paneState });
859
+ return paneState;
860
+ }
861
+ async openMirrorRuntime(target, options = {}) {
862
+ const record = (0, import_session_host_core2.resolveRuntimeRecord)(await this.listRuntimes(), target);
863
+ const runtimePane = Array.from(this.paneById.values()).find((pane) => pane.record.sessionId === record.sessionId);
864
+ if (!runtimePane) {
865
+ throw new Error(`Cannot mirror runtime ${record.runtimeKey} before it is attached`);
866
+ }
867
+ const paneId = options.paneId || (0, import_crypto2.randomUUID)();
868
+ const viewport = runtimePane.surface.getViewportState();
869
+ const surface = new GhosttyTerminalSurface({
870
+ cols: viewport.cols,
871
+ rows: viewport.rows
872
+ });
873
+ surface.resetFromText(viewport.text, viewport.snapshotSeq);
874
+ const paneState = paneFromRecord(paneId, record, surface, "mirror", "read-only");
875
+ this.paneById.set(paneId, {
876
+ record,
877
+ surface,
878
+ paneKind: "mirror",
879
+ accessMode: "read-only",
880
+ requestedReadOnly: true
881
+ });
882
+ const paneIds = this.paneIdsByRuntime.get(record.sessionId) || /* @__PURE__ */ new Set();
883
+ paneIds.add(paneId);
884
+ this.paneIdsByRuntime.set(record.sessionId, paneIds);
885
+ this.emit({ kind: "runtime", pane: paneState });
886
+ return paneState;
887
+ }
888
+ async handleHostEvent(event) {
889
+ const sessionId = "sessionId" in event ? event.sessionId : event.type === "runtime_transition" ? event.transition.sessionId : event.type === "request_trace" ? event.trace.sessionId : event.entry.sessionId;
890
+ if (!sessionId) return;
891
+ const paneIds = this.paneIdsByRuntime.get(sessionId);
892
+ if (!paneIds || paneIds.size === 0) return;
893
+ for (const paneId of paneIds) {
894
+ const pane = this.paneById.get(paneId);
895
+ if (!pane) continue;
896
+ switch (event.type) {
897
+ case "session_output":
898
+ pane.surface.write(event.data, event.seq);
899
+ break;
900
+ case "session_started":
901
+ case "session_resumed":
902
+ pane.record = { ...pane.record, lifecycle: "running", osPid: event.pid ?? pane.record.osPid };
903
+ break;
904
+ case "session_exit":
905
+ pane.record = { ...pane.record, lifecycle: event.exitCode === 0 ? "stopped" : "failed" };
906
+ break;
907
+ case "session_stopped":
908
+ pane.record = { ...pane.record, lifecycle: "stopped" };
909
+ break;
910
+ case "session_resized":
911
+ pane.surface.resize(event.cols, event.rows);
912
+ break;
913
+ case "write_owner_changed":
914
+ pane.record = { ...pane.record, writeOwner: event.owner };
915
+ pane.accessMode = this.computeAccessMode(pane.record, pane.requestedReadOnly, pane.paneKind);
916
+ break;
917
+ case "client_attached":
918
+ pane.record = {
919
+ ...pane.record,
920
+ attachedClients: [
921
+ ...pane.record.attachedClients.filter((client) => client.clientId !== event.client.clientId),
922
+ event.client
923
+ ]
924
+ };
925
+ pane.accessMode = this.computeAccessMode(pane.record, pane.requestedReadOnly, pane.paneKind);
926
+ break;
927
+ case "client_detached":
928
+ pane.record = {
929
+ ...pane.record,
930
+ attachedClients: pane.record.attachedClients.filter((client) => client.clientId !== event.clientId)
931
+ };
932
+ pane.accessMode = this.computeAccessMode(pane.record, pane.requestedReadOnly, pane.paneKind);
933
+ break;
934
+ case "session_created":
935
+ pane.record = event.record;
936
+ pane.accessMode = this.computeAccessMode(pane.record, pane.requestedReadOnly, pane.paneKind);
937
+ break;
938
+ }
939
+ this.publishPaneUpdate(paneId, event);
940
+ }
941
+ }
942
+ publishPaneUpdate(paneId, event) {
943
+ const pane = this.requirePane(paneId);
944
+ const paneState = paneFromRecord(paneId, pane.record, pane.surface, pane.paneKind, pane.accessMode);
945
+ this.emit({ kind: "runtime", pane: paneState, event });
946
+ for (const [workspaceId, workspace] of this.workspaceById) {
947
+ if (!workspace.panes[paneId]) continue;
948
+ const updated = updateMuxPane(workspace, paneState);
949
+ this.workspaceById.set(workspaceId, updated);
950
+ this.emit({ kind: "workspace", workspace: updated });
951
+ }
952
+ }
953
+ emit(event) {
954
+ for (const listener of this.listeners) {
955
+ listener(event);
956
+ }
957
+ }
958
+ requireWorkspace(workspaceId) {
959
+ const workspace = this.workspaceById.get(workspaceId);
960
+ if (!workspace) throw new Error(`Unknown workspace: ${workspaceId}`);
961
+ return workspace;
962
+ }
963
+ requirePane(paneId) {
964
+ const pane = this.paneById.get(paneId);
965
+ if (!pane) throw new Error(`Unknown pane: ${paneId}`);
966
+ return pane;
967
+ }
968
+ computeAccessMode(record, requestedReadOnly, paneKind) {
969
+ if (paneKind === "mirror") return "read-only";
970
+ if (requestedReadOnly) return "read-only";
971
+ if (record.writeOwner && record.writeOwner.clientId !== this.clientId) return "read-only";
972
+ const attachedClient = record.attachedClients.find((client) => client.clientId === this.clientId);
973
+ if (attachedClient?.readOnly) return "read-only";
974
+ return "interactive";
975
+ }
976
+ };
977
+ // Annotate the CommonJS export names for ESM import in node:
978
+ 0 && (module.exports = {
979
+ GhosttyTerminalSurface,
980
+ SessionHostMuxClient,
981
+ applyMuxLayoutPreset,
982
+ createMuxWorkspace,
983
+ focusMuxPane,
984
+ rebalanceMuxLayout,
985
+ removeMuxPane,
986
+ resizeMuxPane,
987
+ resolveMuxOpenRuntimeRecord,
988
+ serializeWorkspace,
989
+ splitMuxPane,
990
+ swapMuxPanePositions,
991
+ toggleMuxPaneZoom,
992
+ updateMuxPane
993
+ });