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,676 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ SessionHostClient: () => SessionHostClient,
34
+ SessionHostRegistry: () => SessionHostRegistry,
35
+ SessionRingBuffer: () => SessionRingBuffer,
36
+ applyTerminalColorEnv: () => applyTerminalColorEnv,
37
+ buildRuntimeDisplayName: () => buildRuntimeDisplayName,
38
+ buildRuntimeKey: () => buildRuntimeKey,
39
+ createLineParser: () => createLineParser,
40
+ createResponseEnvelope: () => createResponseEnvelope,
41
+ ensureNodePtySpawnHelperPermissions: () => ensureNodePtySpawnHelperPermissions,
42
+ formatRuntimeOwner: () => formatRuntimeOwner,
43
+ getDefaultSessionHostEndpoint: () => getDefaultSessionHostEndpoint,
44
+ getSessionHostRecoveryLabel: () => getSessionHostRecoveryLabel,
45
+ getSessionHostSurfaceKind: () => getSessionHostSurfaceKind,
46
+ getWorkspaceLabel: () => getWorkspaceLabel,
47
+ isSessionHostLiveRuntime: () => isSessionHostLiveRuntime,
48
+ isSessionHostRecoverySnapshot: () => isSessionHostRecoverySnapshot,
49
+ resolveAttachableRuntimeRecord: () => resolveAttachableRuntimeRecord,
50
+ resolveRuntimeRecord: () => resolveRuntimeRecord,
51
+ sanitizeSpawnEnv: () => sanitizeSpawnEnv,
52
+ writeEnvelope: () => writeEnvelope
53
+ });
54
+ module.exports = __toCommonJS(index_exports);
55
+
56
+ // src/buffer.ts
57
+ var SessionRingBuffer = class {
58
+ maxBytes;
59
+ chunks = [];
60
+ nextSeq = 1;
61
+ totalBytes = 0;
62
+ constructor(options = {}) {
63
+ this.maxBytes = options.maxBytes ?? 512 * 1024;
64
+ }
65
+ append(data) {
66
+ const normalized = typeof data === "string" ? data : String(data ?? "");
67
+ const bytes = Buffer.byteLength(normalized, "utf8");
68
+ const seq = this.nextSeq++;
69
+ this.chunks.push({ seq, data: normalized, bytes });
70
+ this.totalBytes += bytes;
71
+ this.trim();
72
+ return seq;
73
+ }
74
+ snapshot(sinceSeq) {
75
+ const relevant = typeof sinceSeq === "number" ? this.chunks.filter((chunk) => chunk.seq > sinceSeq) : this.chunks;
76
+ const text = relevant.map((chunk) => chunk.data).join("");
77
+ const truncated = !!this.chunks[0] && typeof sinceSeq === "number" && sinceSeq < this.chunks[0].seq - 1;
78
+ return {
79
+ seq: this.nextSeq - 1,
80
+ text,
81
+ truncated
82
+ };
83
+ }
84
+ getState() {
85
+ return {
86
+ scrollbackBytes: this.totalBytes,
87
+ snapshotSeq: this.nextSeq - 1
88
+ };
89
+ }
90
+ clear() {
91
+ this.chunks = [];
92
+ this.totalBytes = 0;
93
+ this.nextSeq = 1;
94
+ }
95
+ restore(snapshot) {
96
+ this.clear();
97
+ const text = String(snapshot.text || "");
98
+ if (!text) {
99
+ this.nextSeq = Math.max(1, Number(snapshot.seq || 0) + 1);
100
+ return;
101
+ }
102
+ const bytes = Buffer.byteLength(text, "utf8");
103
+ const seq = Math.max(1, Number(snapshot.seq || 1));
104
+ this.chunks = [{ seq, data: text, bytes }];
105
+ this.totalBytes = bytes;
106
+ this.nextSeq = seq + 1;
107
+ this.trim();
108
+ }
109
+ trim() {
110
+ while (this.totalBytes > this.maxBytes && this.chunks.length > 1) {
111
+ const removed = this.chunks.shift();
112
+ if (!removed) break;
113
+ this.totalBytes -= removed.bytes;
114
+ }
115
+ }
116
+ };
117
+
118
+ // src/registry.ts
119
+ var import_crypto = require("crypto");
120
+
121
+ // src/runtime-labels.ts
122
+ var path = __toESM(require("path"));
123
+ function normalizeSlug(input) {
124
+ return input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
125
+ }
126
+ function normalizeValue(input) {
127
+ return input.trim().toLowerCase();
128
+ }
129
+ function getWorkspaceLabel(workspace) {
130
+ const trimmed = workspace.trim();
131
+ if (!trimmed) return "workspace";
132
+ const normalized = trimmed.replace(/[\\/]+$/, "");
133
+ const base = path.basename(normalized);
134
+ return base || normalized;
135
+ }
136
+ function buildRuntimeDisplayName(payload) {
137
+ const explicit = payload.displayName?.trim();
138
+ if (explicit) return explicit;
139
+ const workspaceLabel = getWorkspaceLabel(payload.workspace);
140
+ const providerLabel = payload.providerType.trim() || "runtime";
141
+ return `${providerLabel} @ ${workspaceLabel}`;
142
+ }
143
+ function buildRuntimeKey(payload, existingKeys) {
144
+ const requested = payload.runtimeKey?.trim();
145
+ const existing = new Set(Array.from(existingKeys, (key) => key.toLowerCase()));
146
+ const displayName = buildRuntimeDisplayName(payload);
147
+ const baseKey = normalizeSlug(requested || displayName || getWorkspaceLabel(payload.workspace) || payload.providerType || "runtime") || "runtime";
148
+ if (!existing.has(baseKey)) return baseKey;
149
+ let suffix = 2;
150
+ let candidate = `${baseKey}-${suffix}`;
151
+ while (existing.has(candidate)) {
152
+ suffix += 1;
153
+ candidate = `${baseKey}-${suffix}`;
154
+ }
155
+ return candidate;
156
+ }
157
+ var LIVE_LIFECYCLES = /* @__PURE__ */ new Set(["starting", "running", "stopping", "interrupted"]);
158
+ function isSessionHostLiveRuntime(record) {
159
+ if (!record) return false;
160
+ if (record.surfaceKind === "live_runtime") return true;
161
+ if (record.surfaceKind === "recovery_snapshot" || record.surfaceKind === "inactive_record") return false;
162
+ const lifecycle = String(record.lifecycle || "").trim();
163
+ return LIVE_LIFECYCLES.has(lifecycle);
164
+ }
165
+ function getSessionHostRecoveryLabel(meta) {
166
+ const recoveryState = typeof meta?.runtimeRecoveryState === "string" ? String(meta.runtimeRecoveryState).trim() : "";
167
+ if (!recoveryState) return null;
168
+ if (recoveryState === "auto_resumed") return "restored after restart";
169
+ if (recoveryState === "resume_failed") return "restore failed";
170
+ if (recoveryState === "host_restart_interrupted") return "host restart interrupted";
171
+ if (recoveryState === "orphan_snapshot") return "snapshot recovered";
172
+ return recoveryState.replace(/_/g, " ");
173
+ }
174
+ function isSessionHostRecoverySnapshot(record) {
175
+ if (!record) return false;
176
+ if (record.surfaceKind === "recovery_snapshot") return true;
177
+ if (record.surfaceKind === "live_runtime" || record.surfaceKind === "inactive_record") return false;
178
+ if (isSessionHostLiveRuntime(record)) return false;
179
+ const lifecycle = String(record.lifecycle || "").trim();
180
+ if (lifecycle && lifecycle !== "stopped" && lifecycle !== "failed") {
181
+ return false;
182
+ }
183
+ const meta = record.meta || void 0;
184
+ if (meta?.restoredFromStorage === true) return true;
185
+ return getSessionHostRecoveryLabel(meta) !== null;
186
+ }
187
+ function getSessionHostSurfaceKind(record) {
188
+ if (record?.surfaceKind === "live_runtime" || record?.surfaceKind === "recovery_snapshot" || record?.surfaceKind === "inactive_record") {
189
+ return record.surfaceKind;
190
+ }
191
+ if (isSessionHostLiveRuntime(record)) return "live_runtime";
192
+ if (isSessionHostRecoverySnapshot(record)) return "recovery_snapshot";
193
+ return "inactive_record";
194
+ }
195
+ function resolveAttachableRuntimeRecord(records, identifier) {
196
+ const record = resolveRuntimeRecord(records, identifier);
197
+ const surfaceKind = getSessionHostSurfaceKind(record);
198
+ if (surfaceKind === "live_runtime") {
199
+ return record;
200
+ }
201
+ if (surfaceKind === "recovery_snapshot") {
202
+ throw new Error(`Runtime ${record.runtimeKey} is a recovery snapshot, not a live attach target. Resume or recover it first.`);
203
+ }
204
+ throw new Error(`Runtime ${record.runtimeKey} is ${record.lifecycle}, not a live attach target.`);
205
+ }
206
+ function uniqueMatch(records, predicate) {
207
+ const matches = records.filter(predicate);
208
+ if (matches.length === 1) return matches[0] || null;
209
+ if (matches.length === 0) return null;
210
+ const labels = matches.map((record) => `${record.runtimeKey} (${record.sessionId})`).join(", ");
211
+ throw new Error(`Ambiguous runtime target. Matches: ${labels}`);
212
+ }
213
+ function resolveRuntimeRecord(records, identifier) {
214
+ const target = identifier.trim();
215
+ if (!target) {
216
+ throw new Error("Runtime target is required");
217
+ }
218
+ const exact = uniqueMatch(
219
+ records,
220
+ (record) => record.sessionId === target || normalizeValue(record.runtimeKey) === normalizeValue(target) || normalizeValue(record.displayName) === normalizeValue(target)
221
+ );
222
+ if (exact) return exact;
223
+ const prefix = uniqueMatch(
224
+ records,
225
+ (record) => record.sessionId.startsWith(target) || normalizeValue(record.runtimeKey).startsWith(normalizeValue(target))
226
+ );
227
+ if (prefix) return prefix;
228
+ throw new Error(`Unknown runtime target: ${target}`);
229
+ }
230
+ function formatRuntimeOwner(record) {
231
+ if (!record.writeOwner) return "none";
232
+ return `${record.writeOwner.ownerType}:${record.writeOwner.clientId}`;
233
+ }
234
+
235
+ // src/registry.ts
236
+ var SessionHostRegistry = class {
237
+ sessions = /* @__PURE__ */ new Map();
238
+ createSession(payload) {
239
+ const sessionId = payload.sessionId || (0, import_crypto.randomUUID)();
240
+ if (this.sessions.has(sessionId)) {
241
+ throw new Error(`Session already exists: ${sessionId}`);
242
+ }
243
+ const now = Date.now();
244
+ const initialClient = payload.clientId ? [{
245
+ clientId: payload.clientId,
246
+ type: payload.clientType || "daemon",
247
+ readOnly: false,
248
+ attachedAt: now,
249
+ lastSeenAt: now
250
+ }] : [];
251
+ const record = {
252
+ sessionId,
253
+ runtimeKey: buildRuntimeKey(
254
+ payload,
255
+ Array.from(this.sessions.values(), (state) => state.record.runtimeKey)
256
+ ),
257
+ displayName: buildRuntimeDisplayName(payload),
258
+ workspaceLabel: getWorkspaceLabel(payload.workspace),
259
+ transport: "pty",
260
+ providerType: payload.providerType,
261
+ category: payload.category,
262
+ workspace: payload.workspace,
263
+ launchCommand: payload.launchCommand,
264
+ createdAt: now,
265
+ lastActivityAt: now,
266
+ lifecycle: "starting",
267
+ writeOwner: null,
268
+ attachedClients: initialClient,
269
+ buffer: {
270
+ scrollbackBytes: 0,
271
+ snapshotSeq: 0
272
+ },
273
+ meta: payload.meta || {}
274
+ };
275
+ record.meta = {
276
+ sessionHostCols: payload.cols || 80,
277
+ sessionHostRows: payload.rows || 24,
278
+ ...record.meta
279
+ };
280
+ this.sessions.set(sessionId, {
281
+ record,
282
+ buffer: new SessionRingBuffer()
283
+ });
284
+ return this.cloneRecord(record);
285
+ }
286
+ restoreSession(record, snapshot) {
287
+ const cloned = this.cloneRecord(record);
288
+ this.sessions.set(cloned.sessionId, {
289
+ record: cloned,
290
+ buffer: (() => {
291
+ const buffer = new SessionRingBuffer();
292
+ if (snapshot) buffer.restore(snapshot);
293
+ return buffer;
294
+ })()
295
+ });
296
+ return this.cloneRecord(cloned);
297
+ }
298
+ listSessions() {
299
+ return Array.from(this.sessions.values()).map((state) => this.cloneRecord(state.record)).sort((a, b) => b.lastActivityAt - a.lastActivityAt);
300
+ }
301
+ getSession(sessionId) {
302
+ const state = this.sessions.get(sessionId);
303
+ return state ? this.cloneRecord(state.record) : null;
304
+ }
305
+ attachClient(payload) {
306
+ const state = this.requireSession(payload.sessionId);
307
+ const now = Date.now();
308
+ let removedDaemonOwner = false;
309
+ if (payload.clientType === "daemon") {
310
+ const staleDaemonClientIds = state.record.attachedClients.filter((client) => client.type === "daemon" && client.clientId !== payload.clientId).map((client) => client.clientId);
311
+ if (staleDaemonClientIds.length > 0) {
312
+ state.record.attachedClients = state.record.attachedClients.filter(
313
+ (client) => !(client.type === "daemon" && client.clientId !== payload.clientId)
314
+ );
315
+ if (state.record.writeOwner && staleDaemonClientIds.includes(state.record.writeOwner.clientId)) {
316
+ removedDaemonOwner = true;
317
+ }
318
+ }
319
+ }
320
+ const existing = state.record.attachedClients.find((client) => client.clientId === payload.clientId);
321
+ if (existing) {
322
+ existing.type = payload.clientType;
323
+ existing.readOnly = !!payload.readOnly;
324
+ existing.lastSeenAt = now;
325
+ } else {
326
+ state.record.attachedClients.push({
327
+ clientId: payload.clientId,
328
+ type: payload.clientType,
329
+ readOnly: !!payload.readOnly,
330
+ attachedAt: now,
331
+ lastSeenAt: now
332
+ });
333
+ }
334
+ if (removedDaemonOwner) {
335
+ state.record.writeOwner = null;
336
+ }
337
+ state.record.lastActivityAt = now;
338
+ return this.cloneRecord(state.record);
339
+ }
340
+ detachClient(payload) {
341
+ const state = this.requireSession(payload.sessionId);
342
+ state.record.attachedClients = state.record.attachedClients.filter((client) => client.clientId !== payload.clientId);
343
+ if (state.record.writeOwner?.clientId === payload.clientId) {
344
+ state.record.writeOwner = null;
345
+ }
346
+ state.record.lastActivityAt = Date.now();
347
+ return this.cloneRecord(state.record);
348
+ }
349
+ acquireWrite(payload) {
350
+ const state = this.requireSession(payload.sessionId);
351
+ if (state.record.writeOwner && state.record.writeOwner.clientId !== payload.clientId && !payload.force) {
352
+ throw new Error(`Write owned by ${state.record.writeOwner.clientId}`);
353
+ }
354
+ const attachedClient = state.record.attachedClients.find((client) => client.clientId === payload.clientId);
355
+ if (attachedClient) {
356
+ attachedClient.readOnly = false;
357
+ attachedClient.lastSeenAt = Date.now();
358
+ }
359
+ state.record.writeOwner = {
360
+ clientId: payload.clientId,
361
+ ownerType: payload.ownerType,
362
+ acquiredAt: Date.now()
363
+ };
364
+ state.record.lastActivityAt = Date.now();
365
+ return this.cloneRecord(state.record);
366
+ }
367
+ releaseWrite(payload) {
368
+ const state = this.requireSession(payload.sessionId);
369
+ const attachedClient = state.record.attachedClients.find((client) => client.clientId === payload.clientId);
370
+ if (attachedClient) {
371
+ attachedClient.readOnly = false;
372
+ attachedClient.lastSeenAt = Date.now();
373
+ }
374
+ if (state.record.writeOwner?.clientId === payload.clientId) {
375
+ state.record.writeOwner = null;
376
+ }
377
+ state.record.lastActivityAt = Date.now();
378
+ return this.cloneRecord(state.record);
379
+ }
380
+ appendOutput(sessionId, data) {
381
+ const state = this.requireSession(sessionId);
382
+ const seq = state.buffer.append(data);
383
+ state.record.buffer = state.buffer.getState();
384
+ state.record.lastActivityAt = Date.now();
385
+ return { record: this.cloneRecord(state.record), seq };
386
+ }
387
+ getSnapshot(sessionId, sinceSeq) {
388
+ const state = this.requireSession(sessionId);
389
+ state.record.buffer = state.buffer.getState();
390
+ return state.buffer.snapshot(sinceSeq);
391
+ }
392
+ clearBuffer(sessionId) {
393
+ const state = this.requireSession(sessionId);
394
+ state.buffer.clear();
395
+ state.record.buffer = state.buffer.getState();
396
+ state.record.lastActivityAt = Date.now();
397
+ return this.cloneRecord(state.record);
398
+ }
399
+ updateSessionMeta(sessionId, meta, replace = false) {
400
+ const state = this.requireSession(sessionId);
401
+ state.record.meta = replace ? { ...meta } : {
402
+ ...state.record.meta || {},
403
+ ...meta
404
+ };
405
+ state.record.lastActivityAt = Date.now();
406
+ return this.cloneRecord(state.record);
407
+ }
408
+ markStarted(sessionId, pid) {
409
+ const state = this.requireSession(sessionId);
410
+ state.record.lifecycle = "running";
411
+ state.record.startedAt = state.record.startedAt || Date.now();
412
+ if (typeof pid === "number") state.record.osPid = pid;
413
+ state.record.lastActivityAt = Date.now();
414
+ return this.cloneRecord(state.record);
415
+ }
416
+ markStopped(sessionId, lifecycle = "stopped") {
417
+ const state = this.requireSession(sessionId);
418
+ state.record.lifecycle = lifecycle;
419
+ state.record.lastActivityAt = Date.now();
420
+ return this.cloneRecord(state.record);
421
+ }
422
+ setLifecycle(sessionId, lifecycle) {
423
+ const state = this.requireSession(sessionId);
424
+ state.record.lifecycle = lifecycle;
425
+ state.record.lastActivityAt = Date.now();
426
+ return this.cloneRecord(state.record);
427
+ }
428
+ deleteSession(sessionId) {
429
+ return this.sessions.delete(sessionId);
430
+ }
431
+ requireSession(sessionId) {
432
+ const state = this.sessions.get(sessionId);
433
+ if (!state) throw new Error(`Unknown session: ${sessionId}`);
434
+ return state;
435
+ }
436
+ cloneRecord(record) {
437
+ return {
438
+ ...record,
439
+ launchCommand: {
440
+ ...record.launchCommand,
441
+ args: [...record.launchCommand.args],
442
+ env: record.launchCommand.env ? { ...record.launchCommand.env } : void 0
443
+ },
444
+ writeOwner: record.writeOwner ? { ...record.writeOwner } : null,
445
+ attachedClients: record.attachedClients.map((client) => ({ ...client })),
446
+ buffer: { ...record.buffer },
447
+ meta: { ...record.meta }
448
+ };
449
+ }
450
+ };
451
+
452
+ // src/ipc.ts
453
+ var os = __toESM(require("os"));
454
+ var path2 = __toESM(require("path"));
455
+ var net = __toESM(require("net"));
456
+ var import_crypto2 = require("crypto");
457
+ function getDefaultSessionHostEndpoint(appName = "adhdev") {
458
+ if (process.platform === "win32") {
459
+ return {
460
+ kind: "pipe",
461
+ path: `\\\\.\\pipe\\${appName}-session-host`
462
+ };
463
+ }
464
+ return {
465
+ kind: "unix",
466
+ path: path2.join(os.tmpdir(), `${appName}-session-host.sock`)
467
+ };
468
+ }
469
+ function serializeEnvelope(envelope) {
470
+ return `${JSON.stringify(envelope)}
471
+ `;
472
+ }
473
+ function createLineParser(onEnvelope) {
474
+ let buffer = "";
475
+ return (chunk) => {
476
+ buffer += chunk.toString();
477
+ let newlineIndex = buffer.indexOf("\n");
478
+ while (newlineIndex >= 0) {
479
+ const rawLine = buffer.slice(0, newlineIndex).trim();
480
+ buffer = buffer.slice(newlineIndex + 1);
481
+ if (rawLine) {
482
+ onEnvelope(JSON.parse(rawLine));
483
+ }
484
+ newlineIndex = buffer.indexOf("\n");
485
+ }
486
+ };
487
+ }
488
+ var SessionHostClient = class {
489
+ endpoint;
490
+ socket = null;
491
+ requestWaiters = /* @__PURE__ */ new Map();
492
+ eventListeners = /* @__PURE__ */ new Set();
493
+ constructor(options = {}) {
494
+ this.endpoint = options.endpoint || getDefaultSessionHostEndpoint(options.appName || "adhdev");
495
+ }
496
+ async connect() {
497
+ if (this.socket && !this.socket.destroyed) return;
498
+ if (this.socket) {
499
+ try {
500
+ this.socket.destroy();
501
+ } catch {
502
+ }
503
+ this.socket = null;
504
+ }
505
+ const socket = net.createConnection(this.endpoint.path);
506
+ this.socket = socket;
507
+ socket.on("data", createLineParser((envelope) => {
508
+ if (envelope.kind === "response") {
509
+ const waiter = this.requestWaiters.get(envelope.requestId);
510
+ if (waiter) {
511
+ this.requestWaiters.delete(envelope.requestId);
512
+ waiter.resolve(envelope.response);
513
+ }
514
+ return;
515
+ }
516
+ if (envelope.kind === "event") {
517
+ for (const listener of this.eventListeners) listener(envelope.event);
518
+ }
519
+ }));
520
+ socket.on("error", (error) => {
521
+ for (const waiter of this.requestWaiters.values()) {
522
+ waiter.reject(error);
523
+ }
524
+ this.requestWaiters.clear();
525
+ if (this.socket === socket) {
526
+ this.socket = null;
527
+ }
528
+ try {
529
+ socket.destroy();
530
+ } catch {
531
+ }
532
+ });
533
+ await new Promise((resolve2, reject) => {
534
+ socket.once("connect", () => resolve2());
535
+ socket.once("error", reject);
536
+ });
537
+ }
538
+ onEvent(listener) {
539
+ this.eventListeners.add(listener);
540
+ return () => {
541
+ this.eventListeners.delete(listener);
542
+ };
543
+ }
544
+ async request(request) {
545
+ await this.connect();
546
+ if (!this.socket) throw new Error("Session host socket unavailable");
547
+ const requestId = (0, import_crypto2.randomUUID)();
548
+ const envelope = {
549
+ kind: "request",
550
+ requestId,
551
+ request
552
+ };
553
+ const response = await new Promise((resolve2, reject) => {
554
+ const timeout = setTimeout(() => {
555
+ this.requestWaiters.delete(requestId);
556
+ reject(new Error(`Session host request timed out after 30s (${request.type})`));
557
+ }, 3e4);
558
+ this.requestWaiters.set(requestId, {
559
+ resolve: (value) => {
560
+ clearTimeout(timeout);
561
+ resolve2(value);
562
+ },
563
+ reject: (error) => {
564
+ clearTimeout(timeout);
565
+ reject(error);
566
+ }
567
+ });
568
+ this.socket?.write(serializeEnvelope(envelope));
569
+ });
570
+ return response;
571
+ }
572
+ async close() {
573
+ if (!this.socket) return;
574
+ const socket = this.socket;
575
+ this.socket = null;
576
+ for (const waiter of this.requestWaiters.values()) {
577
+ waiter.reject(new Error("Session host client closed"));
578
+ }
579
+ this.requestWaiters.clear();
580
+ await new Promise((resolve2) => {
581
+ let settled = false;
582
+ const done = () => {
583
+ if (settled) return;
584
+ settled = true;
585
+ resolve2();
586
+ };
587
+ socket.once("close", done);
588
+ socket.end();
589
+ socket.destroy();
590
+ setTimeout(done, 50);
591
+ });
592
+ }
593
+ };
594
+ function createResponseEnvelope(requestId, response) {
595
+ return {
596
+ kind: "response",
597
+ requestId,
598
+ response
599
+ };
600
+ }
601
+ function writeEnvelope(socket, envelope) {
602
+ socket.write(serializeEnvelope(envelope));
603
+ }
604
+
605
+ // src/spawn-env.ts
606
+ var os2 = __toESM(require("os"));
607
+ var path3 = __toESM(require("path"));
608
+ function sanitizeSpawnEnv(baseEnv, overrides) {
609
+ const env = {};
610
+ const source = { ...baseEnv, ...overrides || {} };
611
+ for (const [key, value] of Object.entries(source)) {
612
+ if (typeof value !== "string") continue;
613
+ env[key] = value;
614
+ }
615
+ for (const key of Object.keys(env)) {
616
+ if (key === "INIT_CWD" || key === "npm_command" || key === "npm_execpath" || key === "npm_node_execpath" || key.startsWith("npm_") || key.startsWith("npm_config_") || key.startsWith("npm_package_") || key.startsWith("npm_lifecycle_") || key.startsWith("PNPM_") || key.startsWith("YARN_") || key.startsWith("BUN_") || key.startsWith("VSCODE_") || key.startsWith("ELECTRON_")) {
617
+ delete env[key];
618
+ }
619
+ }
620
+ delete env.CODEX_THREAD_ID;
621
+ delete env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE;
622
+ applyTerminalColorEnv(env);
623
+ return env;
624
+ }
625
+ function applyTerminalColorEnv(env) {
626
+ if (env.NO_COLOR) return;
627
+ if (!env.TERM || env.TERM === "xterm-color") {
628
+ env.TERM = "xterm-256color";
629
+ }
630
+ if (!env.COLORTERM) env.COLORTERM = "truecolor";
631
+ if (process.platform === "win32") {
632
+ if (!env.FORCE_COLOR) env.FORCE_COLOR = "1";
633
+ if (!env.CLICOLOR) env.CLICOLOR = "1";
634
+ }
635
+ }
636
+ function ensureNodePtySpawnHelperPermissions(logFn) {
637
+ if (os2.platform() === "win32") return;
638
+ try {
639
+ const fs = require("fs");
640
+ const ptyDir = path3.resolve(path3.dirname(require.resolve("node-pty")), "..");
641
+ const platformArch = `${os2.platform()}-${os2.arch()}`;
642
+ const helper = path3.join(ptyDir, "prebuilds", platformArch, "spawn-helper");
643
+ if (fs.existsSync(helper)) {
644
+ const stat = fs.statSync(helper);
645
+ if (!(stat.mode & 73)) {
646
+ fs.chmodSync(helper, stat.mode | 493);
647
+ logFn?.(`Fixed spawn-helper permissions: ${helper}`);
648
+ }
649
+ }
650
+ } catch {
651
+ }
652
+ }
653
+ // Annotate the CommonJS export names for ESM import in node:
654
+ 0 && (module.exports = {
655
+ SessionHostClient,
656
+ SessionHostRegistry,
657
+ SessionRingBuffer,
658
+ applyTerminalColorEnv,
659
+ buildRuntimeDisplayName,
660
+ buildRuntimeKey,
661
+ createLineParser,
662
+ createResponseEnvelope,
663
+ ensureNodePtySpawnHelperPermissions,
664
+ formatRuntimeOwner,
665
+ getDefaultSessionHostEndpoint,
666
+ getSessionHostRecoveryLabel,
667
+ getSessionHostSurfaceKind,
668
+ getWorkspaceLabel,
669
+ isSessionHostLiveRuntime,
670
+ isSessionHostRecoverySnapshot,
671
+ resolveAttachableRuntimeRecord,
672
+ resolveRuntimeRecord,
673
+ sanitizeSpawnEnv,
674
+ writeEnvelope
675
+ });
676
+ //# sourceMappingURL=index.js.map