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