@adhdev/daemon-standalone 0.8.12 → 0.8.13

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.
package/dist/index.js CHANGED
@@ -9129,6 +9129,591 @@ ${h.join(`
9129
9129
  }
9130
9130
  });
9131
9131
 
9132
+ // ../session-host-core/dist/index.js
9133
+ var require_dist = __commonJS({
9134
+ "../session-host-core/dist/index.js"(exports2, module2) {
9135
+ "use strict";
9136
+ var __create2 = Object.create;
9137
+ var __defProp2 = Object.defineProperty;
9138
+ var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
9139
+ var __getOwnPropNames2 = Object.getOwnPropertyNames;
9140
+ var __getProtoOf2 = Object.getPrototypeOf;
9141
+ var __hasOwnProp2 = Object.prototype.hasOwnProperty;
9142
+ var __export2 = (target, all) => {
9143
+ for (var name in all)
9144
+ __defProp2(target, name, { get: all[name], enumerable: true });
9145
+ };
9146
+ var __copyProps2 = (to, from, except, desc) => {
9147
+ if (from && typeof from === "object" || typeof from === "function") {
9148
+ for (let key of __getOwnPropNames2(from))
9149
+ if (!__hasOwnProp2.call(to, key) && key !== except)
9150
+ __defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
9151
+ }
9152
+ return to;
9153
+ };
9154
+ var __toESM2 = (mod, isNodeMode, target) => (target = mod != null ? __create2(__getProtoOf2(mod)) : {}, __copyProps2(
9155
+ // If the importer is in node compatibility mode or this is not an ESM
9156
+ // file that has been converted to a CommonJS file using a Babel-
9157
+ // compatible transform (i.e. "__esModule" has not been set), then set
9158
+ // "default" to the CommonJS "module.exports" for node compatibility.
9159
+ isNodeMode || !mod || !mod.__esModule ? __defProp2(target, "default", { value: mod, enumerable: true }) : target,
9160
+ mod
9161
+ ));
9162
+ var __toCommonJS2 = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
9163
+ var index_exports = {};
9164
+ __export2(index_exports, {
9165
+ SessionHostClient: () => SessionHostClient2,
9166
+ SessionHostRegistry: () => SessionHostRegistry,
9167
+ SessionRingBuffer: () => SessionRingBuffer,
9168
+ applyTerminalColorEnv: () => applyTerminalColorEnv,
9169
+ buildRuntimeDisplayName: () => buildRuntimeDisplayName,
9170
+ buildRuntimeKey: () => buildRuntimeKey,
9171
+ createLineParser: () => createLineParser2,
9172
+ createResponseEnvelope: () => createResponseEnvelope,
9173
+ ensureNodePtySpawnHelperPermissions: () => ensureNodePtySpawnHelperPermissions,
9174
+ formatRuntimeOwner: () => formatRuntimeOwner,
9175
+ getDefaultSessionHostEndpoint: () => getDefaultSessionHostEndpoint2,
9176
+ getWorkspaceLabel: () => getWorkspaceLabel,
9177
+ resolveRuntimeRecord: () => resolveRuntimeRecord,
9178
+ sanitizeSpawnEnv: () => sanitizeSpawnEnv,
9179
+ writeEnvelope: () => writeEnvelope
9180
+ });
9181
+ module2.exports = __toCommonJS2(index_exports);
9182
+ var SessionRingBuffer = class {
9183
+ maxBytes;
9184
+ chunks = [];
9185
+ nextSeq = 1;
9186
+ totalBytes = 0;
9187
+ constructor(options = {}) {
9188
+ this.maxBytes = options.maxBytes ?? 512 * 1024;
9189
+ }
9190
+ append(data) {
9191
+ const normalized = typeof data === "string" ? data : String(data ?? "");
9192
+ const bytes = Buffer.byteLength(normalized, "utf8");
9193
+ const seq = this.nextSeq++;
9194
+ this.chunks.push({ seq, data: normalized, bytes });
9195
+ this.totalBytes += bytes;
9196
+ this.trim();
9197
+ return seq;
9198
+ }
9199
+ snapshot(sinceSeq) {
9200
+ const relevant = typeof sinceSeq === "number" ? this.chunks.filter((chunk) => chunk.seq > sinceSeq) : this.chunks;
9201
+ const text = relevant.map((chunk) => chunk.data).join("");
9202
+ const truncated = !!this.chunks[0] && typeof sinceSeq === "number" && sinceSeq < this.chunks[0].seq - 1;
9203
+ return {
9204
+ seq: this.nextSeq - 1,
9205
+ text,
9206
+ truncated
9207
+ };
9208
+ }
9209
+ getState() {
9210
+ return {
9211
+ scrollbackBytes: this.totalBytes,
9212
+ snapshotSeq: this.nextSeq - 1
9213
+ };
9214
+ }
9215
+ clear() {
9216
+ this.chunks = [];
9217
+ this.totalBytes = 0;
9218
+ this.nextSeq = 1;
9219
+ }
9220
+ restore(snapshot) {
9221
+ this.clear();
9222
+ const text = String(snapshot.text || "");
9223
+ if (!text) {
9224
+ this.nextSeq = Math.max(1, Number(snapshot.seq || 0) + 1);
9225
+ return;
9226
+ }
9227
+ const bytes = Buffer.byteLength(text, "utf8");
9228
+ const seq = Math.max(1, Number(snapshot.seq || 1));
9229
+ this.chunks = [{ seq, data: text, bytes }];
9230
+ this.totalBytes = bytes;
9231
+ this.nextSeq = seq + 1;
9232
+ this.trim();
9233
+ }
9234
+ trim() {
9235
+ while (this.totalBytes > this.maxBytes && this.chunks.length > 1) {
9236
+ const removed = this.chunks.shift();
9237
+ if (!removed) break;
9238
+ this.totalBytes -= removed.bytes;
9239
+ }
9240
+ }
9241
+ };
9242
+ var import_crypto2 = require("crypto");
9243
+ var path5 = __toESM2(require("path"));
9244
+ function normalizeSlug(input) {
9245
+ return input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
9246
+ }
9247
+ function normalizeValue(input) {
9248
+ return input.trim().toLowerCase();
9249
+ }
9250
+ function getWorkspaceLabel(workspace) {
9251
+ const trimmed = workspace.trim();
9252
+ if (!trimmed) return "workspace";
9253
+ const normalized = trimmed.replace(/[\\/]+$/, "");
9254
+ const base = path5.basename(normalized);
9255
+ return base || normalized;
9256
+ }
9257
+ function buildRuntimeDisplayName(payload) {
9258
+ const explicit = payload.displayName?.trim();
9259
+ if (explicit) return explicit;
9260
+ const workspaceLabel = getWorkspaceLabel(payload.workspace);
9261
+ const providerLabel = payload.providerType.trim() || "runtime";
9262
+ return `${providerLabel} @ ${workspaceLabel}`;
9263
+ }
9264
+ function buildRuntimeKey(payload, existingKeys) {
9265
+ const requested = payload.runtimeKey?.trim();
9266
+ const existing = new Set(Array.from(existingKeys, (key) => key.toLowerCase()));
9267
+ const displayName = buildRuntimeDisplayName(payload);
9268
+ const baseKey = normalizeSlug(requested || displayName || getWorkspaceLabel(payload.workspace) || payload.providerType || "runtime") || "runtime";
9269
+ if (!existing.has(baseKey)) return baseKey;
9270
+ let suffix = 2;
9271
+ let candidate = `${baseKey}-${suffix}`;
9272
+ while (existing.has(candidate)) {
9273
+ suffix += 1;
9274
+ candidate = `${baseKey}-${suffix}`;
9275
+ }
9276
+ return candidate;
9277
+ }
9278
+ function uniqueMatch(records, predicate) {
9279
+ const matches = records.filter(predicate);
9280
+ if (matches.length === 1) return matches[0] || null;
9281
+ if (matches.length === 0) return null;
9282
+ const labels = matches.map((record2) => `${record2.runtimeKey} (${record2.sessionId})`).join(", ");
9283
+ throw new Error(`Ambiguous runtime target. Matches: ${labels}`);
9284
+ }
9285
+ function resolveRuntimeRecord(records, identifier) {
9286
+ const target = identifier.trim();
9287
+ if (!target) {
9288
+ throw new Error("Runtime target is required");
9289
+ }
9290
+ const exact = uniqueMatch(
9291
+ records,
9292
+ (record2) => record2.sessionId === target || normalizeValue(record2.runtimeKey) === normalizeValue(target) || normalizeValue(record2.displayName) === normalizeValue(target)
9293
+ );
9294
+ if (exact) return exact;
9295
+ const prefix = uniqueMatch(
9296
+ records,
9297
+ (record2) => record2.sessionId.startsWith(target) || normalizeValue(record2.runtimeKey).startsWith(normalizeValue(target))
9298
+ );
9299
+ if (prefix) return prefix;
9300
+ throw new Error(`Unknown runtime target: ${target}`);
9301
+ }
9302
+ function formatRuntimeOwner(record2) {
9303
+ if (!record2.writeOwner) return "none";
9304
+ return `${record2.writeOwner.ownerType}:${record2.writeOwner.clientId}`;
9305
+ }
9306
+ var SessionHostRegistry = class {
9307
+ sessions = /* @__PURE__ */ new Map();
9308
+ createSession(payload) {
9309
+ const sessionId = payload.sessionId || (0, import_crypto2.randomUUID)();
9310
+ if (this.sessions.has(sessionId)) {
9311
+ throw new Error(`Session already exists: ${sessionId}`);
9312
+ }
9313
+ const now = Date.now();
9314
+ const initialClient = payload.clientId ? [{
9315
+ clientId: payload.clientId,
9316
+ type: payload.clientType || "daemon",
9317
+ readOnly: false,
9318
+ attachedAt: now,
9319
+ lastSeenAt: now
9320
+ }] : [];
9321
+ const record2 = {
9322
+ sessionId,
9323
+ runtimeKey: buildRuntimeKey(
9324
+ payload,
9325
+ Array.from(this.sessions.values(), (state) => state.record.runtimeKey)
9326
+ ),
9327
+ displayName: buildRuntimeDisplayName(payload),
9328
+ workspaceLabel: getWorkspaceLabel(payload.workspace),
9329
+ transport: "pty",
9330
+ providerType: payload.providerType,
9331
+ category: payload.category,
9332
+ workspace: payload.workspace,
9333
+ launchCommand: payload.launchCommand,
9334
+ createdAt: now,
9335
+ lastActivityAt: now,
9336
+ lifecycle: "starting",
9337
+ writeOwner: null,
9338
+ attachedClients: initialClient,
9339
+ buffer: {
9340
+ scrollbackBytes: 0,
9341
+ snapshotSeq: 0
9342
+ },
9343
+ meta: payload.meta || {}
9344
+ };
9345
+ record2.meta = {
9346
+ sessionHostCols: payload.cols || 80,
9347
+ sessionHostRows: payload.rows || 24,
9348
+ ...record2.meta
9349
+ };
9350
+ this.sessions.set(sessionId, {
9351
+ record: record2,
9352
+ buffer: new SessionRingBuffer()
9353
+ });
9354
+ return this.cloneRecord(record2);
9355
+ }
9356
+ restoreSession(record2, snapshot) {
9357
+ const cloned = this.cloneRecord(record2);
9358
+ this.sessions.set(cloned.sessionId, {
9359
+ record: cloned,
9360
+ buffer: (() => {
9361
+ const buffer = new SessionRingBuffer();
9362
+ if (snapshot) buffer.restore(snapshot);
9363
+ return buffer;
9364
+ })()
9365
+ });
9366
+ return this.cloneRecord(cloned);
9367
+ }
9368
+ listSessions() {
9369
+ return Array.from(this.sessions.values()).map((state) => this.cloneRecord(state.record)).sort((a, b2) => b2.lastActivityAt - a.lastActivityAt);
9370
+ }
9371
+ getSession(sessionId) {
9372
+ const state = this.sessions.get(sessionId);
9373
+ return state ? this.cloneRecord(state.record) : null;
9374
+ }
9375
+ attachClient(payload) {
9376
+ const state = this.requireSession(payload.sessionId);
9377
+ const now = Date.now();
9378
+ let removedDaemonOwner = false;
9379
+ if (payload.clientType === "daemon") {
9380
+ const staleDaemonClientIds = state.record.attachedClients.filter((client) => client.type === "daemon" && client.clientId !== payload.clientId).map((client) => client.clientId);
9381
+ if (staleDaemonClientIds.length > 0) {
9382
+ state.record.attachedClients = state.record.attachedClients.filter(
9383
+ (client) => !(client.type === "daemon" && client.clientId !== payload.clientId)
9384
+ );
9385
+ if (state.record.writeOwner && staleDaemonClientIds.includes(state.record.writeOwner.clientId)) {
9386
+ removedDaemonOwner = true;
9387
+ }
9388
+ }
9389
+ }
9390
+ const existing = state.record.attachedClients.find((client) => client.clientId === payload.clientId);
9391
+ if (existing) {
9392
+ existing.type = payload.clientType;
9393
+ existing.readOnly = !!payload.readOnly;
9394
+ existing.lastSeenAt = now;
9395
+ } else {
9396
+ state.record.attachedClients.push({
9397
+ clientId: payload.clientId,
9398
+ type: payload.clientType,
9399
+ readOnly: !!payload.readOnly,
9400
+ attachedAt: now,
9401
+ lastSeenAt: now
9402
+ });
9403
+ }
9404
+ if (removedDaemonOwner) {
9405
+ state.record.writeOwner = null;
9406
+ }
9407
+ state.record.lastActivityAt = now;
9408
+ return this.cloneRecord(state.record);
9409
+ }
9410
+ detachClient(payload) {
9411
+ const state = this.requireSession(payload.sessionId);
9412
+ state.record.attachedClients = state.record.attachedClients.filter((client) => client.clientId !== payload.clientId);
9413
+ if (state.record.writeOwner?.clientId === payload.clientId) {
9414
+ state.record.writeOwner = null;
9415
+ }
9416
+ state.record.lastActivityAt = Date.now();
9417
+ return this.cloneRecord(state.record);
9418
+ }
9419
+ acquireWrite(payload) {
9420
+ const state = this.requireSession(payload.sessionId);
9421
+ if (state.record.writeOwner && state.record.writeOwner.clientId !== payload.clientId && !payload.force) {
9422
+ throw new Error(`Write owned by ${state.record.writeOwner.clientId}`);
9423
+ }
9424
+ const attachedClient = state.record.attachedClients.find((client) => client.clientId === payload.clientId);
9425
+ if (attachedClient) {
9426
+ attachedClient.readOnly = false;
9427
+ attachedClient.lastSeenAt = Date.now();
9428
+ }
9429
+ state.record.writeOwner = {
9430
+ clientId: payload.clientId,
9431
+ ownerType: payload.ownerType,
9432
+ acquiredAt: Date.now()
9433
+ };
9434
+ state.record.lastActivityAt = Date.now();
9435
+ return this.cloneRecord(state.record);
9436
+ }
9437
+ releaseWrite(payload) {
9438
+ const state = this.requireSession(payload.sessionId);
9439
+ const attachedClient = state.record.attachedClients.find((client) => client.clientId === payload.clientId);
9440
+ if (attachedClient) {
9441
+ attachedClient.readOnly = false;
9442
+ attachedClient.lastSeenAt = Date.now();
9443
+ }
9444
+ if (state.record.writeOwner?.clientId === payload.clientId) {
9445
+ state.record.writeOwner = null;
9446
+ }
9447
+ state.record.lastActivityAt = Date.now();
9448
+ return this.cloneRecord(state.record);
9449
+ }
9450
+ appendOutput(sessionId, data) {
9451
+ const state = this.requireSession(sessionId);
9452
+ const seq = state.buffer.append(data);
9453
+ state.record.buffer = state.buffer.getState();
9454
+ state.record.lastActivityAt = Date.now();
9455
+ return { record: this.cloneRecord(state.record), seq };
9456
+ }
9457
+ getSnapshot(sessionId, sinceSeq) {
9458
+ const state = this.requireSession(sessionId);
9459
+ state.record.buffer = state.buffer.getState();
9460
+ return state.buffer.snapshot(sinceSeq);
9461
+ }
9462
+ clearBuffer(sessionId) {
9463
+ const state = this.requireSession(sessionId);
9464
+ state.buffer.clear();
9465
+ state.record.buffer = state.buffer.getState();
9466
+ state.record.lastActivityAt = Date.now();
9467
+ return this.cloneRecord(state.record);
9468
+ }
9469
+ updateSessionMeta(sessionId, meta3, replace = false) {
9470
+ const state = this.requireSession(sessionId);
9471
+ state.record.meta = replace ? { ...meta3 } : {
9472
+ ...state.record.meta || {},
9473
+ ...meta3
9474
+ };
9475
+ state.record.lastActivityAt = Date.now();
9476
+ return this.cloneRecord(state.record);
9477
+ }
9478
+ markStarted(sessionId, pid) {
9479
+ const state = this.requireSession(sessionId);
9480
+ state.record.lifecycle = "running";
9481
+ state.record.startedAt = state.record.startedAt || Date.now();
9482
+ if (typeof pid === "number") state.record.osPid = pid;
9483
+ state.record.lastActivityAt = Date.now();
9484
+ return this.cloneRecord(state.record);
9485
+ }
9486
+ markStopped(sessionId, lifecycle = "stopped") {
9487
+ const state = this.requireSession(sessionId);
9488
+ state.record.lifecycle = lifecycle;
9489
+ state.record.lastActivityAt = Date.now();
9490
+ return this.cloneRecord(state.record);
9491
+ }
9492
+ setLifecycle(sessionId, lifecycle) {
9493
+ const state = this.requireSession(sessionId);
9494
+ state.record.lifecycle = lifecycle;
9495
+ state.record.lastActivityAt = Date.now();
9496
+ return this.cloneRecord(state.record);
9497
+ }
9498
+ requireSession(sessionId) {
9499
+ const state = this.sessions.get(sessionId);
9500
+ if (!state) throw new Error(`Unknown session: ${sessionId}`);
9501
+ return state;
9502
+ }
9503
+ cloneRecord(record2) {
9504
+ return {
9505
+ ...record2,
9506
+ launchCommand: {
9507
+ ...record2.launchCommand,
9508
+ args: [...record2.launchCommand.args],
9509
+ env: record2.launchCommand.env ? { ...record2.launchCommand.env } : void 0
9510
+ },
9511
+ writeOwner: record2.writeOwner ? { ...record2.writeOwner } : null,
9512
+ attachedClients: record2.attachedClients.map((client) => ({ ...client })),
9513
+ buffer: { ...record2.buffer },
9514
+ meta: { ...record2.meta }
9515
+ };
9516
+ }
9517
+ };
9518
+ var os6 = __toESM2(require("os"));
9519
+ var path22 = __toESM2(require("path"));
9520
+ var net3 = __toESM2(require("net"));
9521
+ var import_crypto22 = require("crypto");
9522
+ function getDefaultSessionHostEndpoint2(appName = "adhdev") {
9523
+ if (process.platform === "win32") {
9524
+ return {
9525
+ kind: "pipe",
9526
+ path: `\\\\.\\pipe\\${appName}-session-host`
9527
+ };
9528
+ }
9529
+ return {
9530
+ kind: "unix",
9531
+ path: path22.join(os6.tmpdir(), `${appName}-session-host.sock`)
9532
+ };
9533
+ }
9534
+ function serializeEnvelope3(envelope) {
9535
+ return `${JSON.stringify(envelope)}
9536
+ `;
9537
+ }
9538
+ function createLineParser2(onEnvelope) {
9539
+ let buffer = "";
9540
+ return (chunk) => {
9541
+ buffer += chunk.toString();
9542
+ let newlineIndex = buffer.indexOf("\n");
9543
+ while (newlineIndex >= 0) {
9544
+ const rawLine = buffer.slice(0, newlineIndex).trim();
9545
+ buffer = buffer.slice(newlineIndex + 1);
9546
+ if (rawLine) {
9547
+ onEnvelope(JSON.parse(rawLine));
9548
+ }
9549
+ newlineIndex = buffer.indexOf("\n");
9550
+ }
9551
+ };
9552
+ }
9553
+ var SessionHostClient2 = class {
9554
+ endpoint;
9555
+ socket = null;
9556
+ requestWaiters = /* @__PURE__ */ new Map();
9557
+ eventListeners = /* @__PURE__ */ new Set();
9558
+ constructor(options = {}) {
9559
+ this.endpoint = options.endpoint || getDefaultSessionHostEndpoint2(options.appName || "adhdev");
9560
+ }
9561
+ async connect() {
9562
+ if (this.socket && !this.socket.destroyed) return;
9563
+ if (this.socket) {
9564
+ try {
9565
+ this.socket.destroy();
9566
+ } catch {
9567
+ }
9568
+ this.socket = null;
9569
+ }
9570
+ const socket = net3.createConnection(this.endpoint.path);
9571
+ this.socket = socket;
9572
+ socket.on("data", createLineParser2((envelope) => {
9573
+ if (envelope.kind === "response") {
9574
+ const waiter = this.requestWaiters.get(envelope.requestId);
9575
+ if (waiter) {
9576
+ this.requestWaiters.delete(envelope.requestId);
9577
+ waiter.resolve(envelope.response);
9578
+ }
9579
+ return;
9580
+ }
9581
+ if (envelope.kind === "event") {
9582
+ for (const listener of this.eventListeners) listener(envelope.event);
9583
+ }
9584
+ }));
9585
+ socket.on("error", (error48) => {
9586
+ for (const waiter of this.requestWaiters.values()) {
9587
+ waiter.reject(error48);
9588
+ }
9589
+ this.requestWaiters.clear();
9590
+ if (this.socket === socket) {
9591
+ this.socket = null;
9592
+ }
9593
+ try {
9594
+ socket.destroy();
9595
+ } catch {
9596
+ }
9597
+ });
9598
+ await new Promise((resolve22, reject) => {
9599
+ socket.once("connect", () => resolve22());
9600
+ socket.once("error", reject);
9601
+ });
9602
+ }
9603
+ onEvent(listener) {
9604
+ this.eventListeners.add(listener);
9605
+ return () => {
9606
+ this.eventListeners.delete(listener);
9607
+ };
9608
+ }
9609
+ async request(request) {
9610
+ await this.connect();
9611
+ if (!this.socket) throw new Error("Session host socket unavailable");
9612
+ const requestId = (0, import_crypto22.randomUUID)();
9613
+ const envelope = {
9614
+ kind: "request",
9615
+ requestId,
9616
+ request
9617
+ };
9618
+ const response = await new Promise((resolve22, reject) => {
9619
+ const timeout = setTimeout(() => {
9620
+ this.requestWaiters.delete(requestId);
9621
+ reject(new Error(`Session host request timed out after 30s (${request.type})`));
9622
+ }, 3e4);
9623
+ this.requestWaiters.set(requestId, {
9624
+ resolve: (value) => {
9625
+ clearTimeout(timeout);
9626
+ resolve22(value);
9627
+ },
9628
+ reject: (error48) => {
9629
+ clearTimeout(timeout);
9630
+ reject(error48);
9631
+ }
9632
+ });
9633
+ this.socket?.write(serializeEnvelope3(envelope));
9634
+ });
9635
+ return response;
9636
+ }
9637
+ async close() {
9638
+ if (!this.socket) return;
9639
+ const socket = this.socket;
9640
+ this.socket = null;
9641
+ for (const waiter of this.requestWaiters.values()) {
9642
+ waiter.reject(new Error("Session host client closed"));
9643
+ }
9644
+ this.requestWaiters.clear();
9645
+ await new Promise((resolve22) => {
9646
+ let settled = false;
9647
+ const done = () => {
9648
+ if (settled) return;
9649
+ settled = true;
9650
+ resolve22();
9651
+ };
9652
+ socket.once("close", done);
9653
+ socket.end();
9654
+ socket.destroy();
9655
+ setTimeout(done, 50);
9656
+ });
9657
+ }
9658
+ };
9659
+ function createResponseEnvelope(requestId, response) {
9660
+ return {
9661
+ kind: "response",
9662
+ requestId,
9663
+ response
9664
+ };
9665
+ }
9666
+ function writeEnvelope(socket, envelope) {
9667
+ socket.write(serializeEnvelope3(envelope));
9668
+ }
9669
+ var os22 = __toESM2(require("os"));
9670
+ var path32 = __toESM2(require("path"));
9671
+ function sanitizeSpawnEnv(baseEnv, overrides) {
9672
+ const env = {};
9673
+ const source = { ...baseEnv, ...overrides || {} };
9674
+ for (const [key, value] of Object.entries(source)) {
9675
+ if (typeof value !== "string") continue;
9676
+ env[key] = value;
9677
+ }
9678
+ for (const key of Object.keys(env)) {
9679
+ 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_")) {
9680
+ delete env[key];
9681
+ }
9682
+ }
9683
+ applyTerminalColorEnv(env);
9684
+ return env;
9685
+ }
9686
+ function applyTerminalColorEnv(env) {
9687
+ if (env.NO_COLOR) return;
9688
+ if (!env.TERM || env.TERM === "xterm-color") {
9689
+ env.TERM = "xterm-256color";
9690
+ }
9691
+ if (!env.COLORTERM) env.COLORTERM = "truecolor";
9692
+ if (process.platform === "win32") {
9693
+ if (!env.FORCE_COLOR) env.FORCE_COLOR = "1";
9694
+ if (!env.CLICOLOR) env.CLICOLOR = "1";
9695
+ }
9696
+ }
9697
+ function ensureNodePtySpawnHelperPermissions(logFn) {
9698
+ if (os22.platform() === "win32") return;
9699
+ try {
9700
+ const fs4 = require("fs");
9701
+ const ptyDir = path32.resolve(path32.dirname(require.resolve("node-pty")), "..");
9702
+ const platformArch = `${os22.platform()}-${os22.arch()}`;
9703
+ const helper = path32.join(ptyDir, "prebuilds", platformArch, "spawn-helper");
9704
+ if (fs4.existsSync(helper)) {
9705
+ const stat4 = fs4.statSync(helper);
9706
+ if (!(stat4.mode & 73)) {
9707
+ fs4.chmodSync(helper, stat4.mode | 493);
9708
+ logFn?.(`Fixed spawn-helper permissions: ${helper}`);
9709
+ }
9710
+ }
9711
+ } catch {
9712
+ }
9713
+ }
9714
+ }
9715
+ });
9716
+
9132
9717
  // ../../node_modules/zod/v4/core/core.js
9133
9718
  // @__NO_SIDE_EFFECTS__
9134
9719
  function $constructor(name, initializer3, params) {
@@ -27028,685 +27613,162 @@ var init_chokidar = __esm({
27028
27613
  writes.get(path5).lastChange = now2;
27029
27614
  }
27030
27615
  const pw = writes.get(path5);
27031
- const df = now2 - pw.lastChange;
27032
- if (df >= threshold) {
27033
- writes.delete(path5);
27034
- awfEmit(void 0, curStat);
27035
- } else {
27036
- timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
27037
- }
27038
- });
27039
- }
27040
- if (!writes.has(path5)) {
27041
- writes.set(path5, {
27042
- lastChange: now,
27043
- cancelWait: () => {
27044
- writes.delete(path5);
27045
- clearTimeout(timeoutHandler);
27046
- return event;
27047
- }
27048
- });
27049
- timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval);
27050
- }
27051
- }
27052
- /**
27053
- * Determines whether user has asked to ignore this path.
27054
- */
27055
- _isIgnored(path5, stats) {
27056
- if (this.options.atomic && DOT_RE.test(path5))
27057
- return true;
27058
- if (!this._userIgnored) {
27059
- const { cwd } = this.options;
27060
- const ign = this.options.ignored;
27061
- const ignored = (ign || []).map(normalizeIgnored(cwd));
27062
- const ignoredPaths = [...this._ignoredPaths];
27063
- const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
27064
- this._userIgnored = anymatch(list, void 0);
27065
- }
27066
- return this._userIgnored(path5, stats);
27067
- }
27068
- _isntIgnored(path5, stat4) {
27069
- return !this._isIgnored(path5, stat4);
27070
- }
27071
- /**
27072
- * Provides a set of common helpers and properties relating to symlink handling.
27073
- * @param path file or directory pattern being watched
27074
- */
27075
- _getWatchHelpers(path5) {
27076
- return new WatchHelper(path5, this.options.followSymlinks, this);
27077
- }
27078
- // Directory helpers
27079
- // -----------------
27080
- /**
27081
- * Provides directory tracking objects
27082
- * @param directory path of the directory
27083
- */
27084
- _getWatchedDir(directory) {
27085
- const dir = sp2.resolve(directory);
27086
- if (!this._watched.has(dir))
27087
- this._watched.set(dir, new DirEntry(dir, this._boundRemove));
27088
- return this._watched.get(dir);
27089
- }
27090
- // File helpers
27091
- // ------------
27092
- /**
27093
- * Check for read permissions: https://stackoverflow.com/a/11781404/1358405
27094
- */
27095
- _hasReadPermissions(stats) {
27096
- if (this.options.ignorePermissionErrors)
27097
- return true;
27098
- return Boolean(Number(stats.mode) & 256);
27099
- }
27100
- /**
27101
- * Handles emitting unlink events for
27102
- * files and directories, and via recursion, for
27103
- * files and directories within directories that are unlinked
27104
- * @param directory within which the following item is located
27105
- * @param item base path of item/directory
27106
- */
27107
- _remove(directory, item, isDirectory) {
27108
- const path5 = sp2.join(directory, item);
27109
- const fullPath = sp2.resolve(path5);
27110
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path5) || this._watched.has(fullPath);
27111
- if (!this._throttle("remove", path5, 100))
27112
- return;
27113
- if (!isDirectory && this._watched.size === 1) {
27114
- this.add(directory, item, true);
27115
- }
27116
- const wp = this._getWatchedDir(path5);
27117
- const nestedDirectoryChildren = wp.getChildren();
27118
- nestedDirectoryChildren.forEach((nested) => this._remove(path5, nested));
27119
- const parent = this._getWatchedDir(directory);
27120
- const wasTracked = parent.has(item);
27121
- parent.remove(item);
27122
- if (this._symlinkPaths.has(fullPath)) {
27123
- this._symlinkPaths.delete(fullPath);
27124
- }
27125
- let relPath = path5;
27126
- if (this.options.cwd)
27127
- relPath = sp2.relative(this.options.cwd, path5);
27128
- if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
27129
- const event = this._pendingWrites.get(relPath).cancelWait();
27130
- if (event === EVENTS.ADD)
27131
- return;
27132
- }
27133
- this._watched.delete(path5);
27134
- this._watched.delete(fullPath);
27135
- const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
27136
- if (wasTracked && !this._isIgnored(path5))
27137
- this._emit(eventName, path5);
27138
- this._closePath(path5);
27139
- }
27140
- /**
27141
- * Closes all watchers for a path
27142
- */
27143
- _closePath(path5) {
27144
- this._closeFile(path5);
27145
- const dir = sp2.dirname(path5);
27146
- this._getWatchedDir(dir).remove(sp2.basename(path5));
27147
- }
27148
- /**
27149
- * Closes only file-specific watchers
27150
- */
27151
- _closeFile(path5) {
27152
- const closers = this._closers.get(path5);
27153
- if (!closers)
27154
- return;
27155
- closers.forEach((closer) => closer());
27156
- this._closers.delete(path5);
27157
- }
27158
- _addPathCloser(path5, closer) {
27159
- if (!closer)
27160
- return;
27161
- let list = this._closers.get(path5);
27162
- if (!list) {
27163
- list = [];
27164
- this._closers.set(path5, list);
27165
- }
27166
- list.push(closer);
27167
- }
27168
- _readdirp(root, opts) {
27169
- if (this.closed)
27170
- return;
27171
- const options = { type: EVENTS.ALL, alwaysStat: true, lstat: true, ...opts, depth: 0 };
27172
- let stream = readdirp(root, options);
27173
- this._streams.add(stream);
27174
- stream.once(STR_CLOSE, () => {
27175
- stream = void 0;
27176
- });
27177
- stream.once(STR_END, () => {
27178
- if (stream) {
27179
- this._streams.delete(stream);
27180
- stream = void 0;
27181
- }
27182
- });
27183
- return stream;
27184
- }
27185
- };
27186
- chokidar_default = { watch, FSWatcher };
27187
- }
27188
- });
27189
-
27190
- // ../session-host-core/dist/index.js
27191
- var require_dist = __commonJS({
27192
- "../session-host-core/dist/index.js"(exports2, module2) {
27193
- "use strict";
27194
- var __create2 = Object.create;
27195
- var __defProp2 = Object.defineProperty;
27196
- var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
27197
- var __getOwnPropNames2 = Object.getOwnPropertyNames;
27198
- var __getProtoOf2 = Object.getPrototypeOf;
27199
- var __hasOwnProp2 = Object.prototype.hasOwnProperty;
27200
- var __export2 = (target, all) => {
27201
- for (var name in all)
27202
- __defProp2(target, name, { get: all[name], enumerable: true });
27203
- };
27204
- var __copyProps2 = (to, from, except, desc) => {
27205
- if (from && typeof from === "object" || typeof from === "function") {
27206
- for (let key of __getOwnPropNames2(from))
27207
- if (!__hasOwnProp2.call(to, key) && key !== except)
27208
- __defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
27209
- }
27210
- return to;
27211
- };
27212
- var __toESM2 = (mod, isNodeMode, target) => (target = mod != null ? __create2(__getProtoOf2(mod)) : {}, __copyProps2(
27213
- // If the importer is in node compatibility mode or this is not an ESM
27214
- // file that has been converted to a CommonJS file using a Babel-
27215
- // compatible transform (i.e. "__esModule" has not been set), then set
27216
- // "default" to the CommonJS "module.exports" for node compatibility.
27217
- isNodeMode || !mod || !mod.__esModule ? __defProp2(target, "default", { value: mod, enumerable: true }) : target,
27218
- mod
27219
- ));
27220
- var __toCommonJS2 = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
27221
- var index_exports = {};
27222
- __export2(index_exports, {
27223
- SessionHostClient: () => SessionHostClient2,
27224
- SessionHostRegistry: () => SessionHostRegistry,
27225
- SessionRingBuffer: () => SessionRingBuffer,
27226
- buildRuntimeDisplayName: () => buildRuntimeDisplayName,
27227
- buildRuntimeKey: () => buildRuntimeKey,
27228
- createLineParser: () => createLineParser2,
27229
- createResponseEnvelope: () => createResponseEnvelope,
27230
- formatRuntimeOwner: () => formatRuntimeOwner,
27231
- getDefaultSessionHostEndpoint: () => getDefaultSessionHostEndpoint2,
27232
- getWorkspaceLabel: () => getWorkspaceLabel,
27233
- resolveRuntimeRecord: () => resolveRuntimeRecord,
27234
- writeEnvelope: () => writeEnvelope
27235
- });
27236
- module2.exports = __toCommonJS2(index_exports);
27237
- var SessionRingBuffer = class {
27238
- maxBytes;
27239
- chunks = [];
27240
- nextSeq = 1;
27241
- totalBytes = 0;
27242
- constructor(options = {}) {
27243
- this.maxBytes = options.maxBytes ?? 512 * 1024;
27244
- }
27245
- append(data) {
27246
- const normalized = typeof data === "string" ? data : String(data ?? "");
27247
- const bytes = Buffer.byteLength(normalized, "utf8");
27248
- const seq = this.nextSeq++;
27249
- this.chunks.push({ seq, data: normalized, bytes });
27250
- this.totalBytes += bytes;
27251
- this.trim();
27252
- return seq;
27253
- }
27254
- snapshot(sinceSeq) {
27255
- const relevant = typeof sinceSeq === "number" ? this.chunks.filter((chunk) => chunk.seq > sinceSeq) : this.chunks;
27256
- const text = relevant.map((chunk) => chunk.data).join("");
27257
- const truncated = !!this.chunks[0] && typeof sinceSeq === "number" && sinceSeq < this.chunks[0].seq - 1;
27258
- return {
27259
- seq: this.nextSeq - 1,
27260
- text,
27261
- truncated
27262
- };
27263
- }
27264
- getState() {
27265
- return {
27266
- scrollbackBytes: this.totalBytes,
27267
- snapshotSeq: this.nextSeq - 1
27268
- };
27269
- }
27270
- clear() {
27271
- this.chunks = [];
27272
- this.totalBytes = 0;
27273
- this.nextSeq = 1;
27274
- }
27275
- restore(snapshot) {
27276
- this.clear();
27277
- const text = String(snapshot.text || "");
27278
- if (!text) {
27279
- this.nextSeq = Math.max(1, Number(snapshot.seq || 0) + 1);
27280
- return;
27281
- }
27282
- const bytes = Buffer.byteLength(text, "utf8");
27283
- const seq = Math.max(1, Number(snapshot.seq || 1));
27284
- this.chunks = [{ seq, data: text, bytes }];
27285
- this.totalBytes = bytes;
27286
- this.nextSeq = seq + 1;
27287
- this.trim();
27288
- }
27289
- trim() {
27290
- while (this.totalBytes > this.maxBytes && this.chunks.length > 1) {
27291
- const removed = this.chunks.shift();
27292
- if (!removed) break;
27293
- this.totalBytes -= removed.bytes;
27294
- }
27295
- }
27296
- };
27297
- var import_crypto2 = require("crypto");
27298
- var path5 = __toESM2(require("path"));
27299
- function normalizeSlug(input) {
27300
- return input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
27301
- }
27302
- function normalizeValue(input) {
27303
- return input.trim().toLowerCase();
27304
- }
27305
- function getWorkspaceLabel(workspace) {
27306
- const trimmed = workspace.trim();
27307
- if (!trimmed) return "workspace";
27308
- const normalized = trimmed.replace(/[\\/]+$/, "");
27309
- const base = path5.basename(normalized);
27310
- return base || normalized;
27311
- }
27312
- function buildRuntimeDisplayName(payload) {
27313
- const explicit = payload.displayName?.trim();
27314
- if (explicit) return explicit;
27315
- const workspaceLabel = getWorkspaceLabel(payload.workspace);
27316
- const providerLabel = payload.providerType.trim() || "runtime";
27317
- return `${providerLabel} @ ${workspaceLabel}`;
27318
- }
27319
- function buildRuntimeKey(payload, existingKeys) {
27320
- const requested = payload.runtimeKey?.trim();
27321
- const existing = new Set(Array.from(existingKeys, (key) => key.toLowerCase()));
27322
- const displayName = buildRuntimeDisplayName(payload);
27323
- const baseKey = normalizeSlug(requested || displayName || getWorkspaceLabel(payload.workspace) || payload.providerType || "runtime") || "runtime";
27324
- if (!existing.has(baseKey)) return baseKey;
27325
- let suffix = 2;
27326
- let candidate = `${baseKey}-${suffix}`;
27327
- while (existing.has(candidate)) {
27328
- suffix += 1;
27329
- candidate = `${baseKey}-${suffix}`;
27330
- }
27331
- return candidate;
27332
- }
27333
- function uniqueMatch(records, predicate) {
27334
- const matches = records.filter(predicate);
27335
- if (matches.length === 1) return matches[0] || null;
27336
- if (matches.length === 0) return null;
27337
- const labels = matches.map((record2) => `${record2.runtimeKey} (${record2.sessionId})`).join(", ");
27338
- throw new Error(`Ambiguous runtime target. Matches: ${labels}`);
27339
- }
27340
- function resolveRuntimeRecord(records, identifier) {
27341
- const target = identifier.trim();
27342
- if (!target) {
27343
- throw new Error("Runtime target is required");
27344
- }
27345
- const exact = uniqueMatch(
27346
- records,
27347
- (record2) => record2.sessionId === target || normalizeValue(record2.runtimeKey) === normalizeValue(target) || normalizeValue(record2.displayName) === normalizeValue(target)
27348
- );
27349
- if (exact) return exact;
27350
- const prefix = uniqueMatch(
27351
- records,
27352
- (record2) => record2.sessionId.startsWith(target) || normalizeValue(record2.runtimeKey).startsWith(normalizeValue(target))
27353
- );
27354
- if (prefix) return prefix;
27355
- throw new Error(`Unknown runtime target: ${target}`);
27356
- }
27357
- function formatRuntimeOwner(record2) {
27358
- if (!record2.writeOwner) return "none";
27359
- return `${record2.writeOwner.ownerType}:${record2.writeOwner.clientId}`;
27360
- }
27361
- var SessionHostRegistry = class {
27362
- sessions = /* @__PURE__ */ new Map();
27363
- createSession(payload) {
27364
- const sessionId = payload.sessionId || (0, import_crypto2.randomUUID)();
27365
- if (this.sessions.has(sessionId)) {
27366
- throw new Error(`Session already exists: ${sessionId}`);
27367
- }
27368
- const now = Date.now();
27369
- const initialClient = payload.clientId ? [{
27370
- clientId: payload.clientId,
27371
- type: payload.clientType || "daemon",
27372
- readOnly: false,
27373
- attachedAt: now,
27374
- lastSeenAt: now
27375
- }] : [];
27376
- const record2 = {
27377
- sessionId,
27378
- runtimeKey: buildRuntimeKey(
27379
- payload,
27380
- Array.from(this.sessions.values(), (state) => state.record.runtimeKey)
27381
- ),
27382
- displayName: buildRuntimeDisplayName(payload),
27383
- workspaceLabel: getWorkspaceLabel(payload.workspace),
27384
- transport: "pty",
27385
- providerType: payload.providerType,
27386
- category: payload.category,
27387
- workspace: payload.workspace,
27388
- launchCommand: payload.launchCommand,
27389
- createdAt: now,
27390
- lastActivityAt: now,
27391
- lifecycle: "starting",
27392
- writeOwner: null,
27393
- attachedClients: initialClient,
27394
- buffer: {
27395
- scrollbackBytes: 0,
27396
- snapshotSeq: 0
27397
- },
27398
- meta: payload.meta || {}
27399
- };
27400
- record2.meta = {
27401
- sessionHostCols: payload.cols || 80,
27402
- sessionHostRows: payload.rows || 24,
27403
- ...record2.meta
27404
- };
27405
- this.sessions.set(sessionId, {
27406
- record: record2,
27407
- buffer: new SessionRingBuffer()
27408
- });
27409
- return this.cloneRecord(record2);
27410
- }
27411
- restoreSession(record2, snapshot) {
27412
- const cloned = this.cloneRecord(record2);
27413
- this.sessions.set(cloned.sessionId, {
27414
- record: cloned,
27415
- buffer: (() => {
27416
- const buffer = new SessionRingBuffer();
27417
- if (snapshot) buffer.restore(snapshot);
27418
- return buffer;
27419
- })()
27420
- });
27421
- return this.cloneRecord(cloned);
27422
- }
27423
- listSessions() {
27424
- return Array.from(this.sessions.values()).map((state) => this.cloneRecord(state.record)).sort((a, b2) => b2.lastActivityAt - a.lastActivityAt);
27425
- }
27426
- getSession(sessionId) {
27427
- const state = this.sessions.get(sessionId);
27428
- return state ? this.cloneRecord(state.record) : null;
27429
- }
27430
- attachClient(payload) {
27431
- const state = this.requireSession(payload.sessionId);
27432
- const now = Date.now();
27433
- let removedDaemonOwner = false;
27434
- if (payload.clientType === "daemon") {
27435
- const staleDaemonClientIds = state.record.attachedClients.filter((client) => client.type === "daemon" && client.clientId !== payload.clientId).map((client) => client.clientId);
27436
- if (staleDaemonClientIds.length > 0) {
27437
- state.record.attachedClients = state.record.attachedClients.filter(
27438
- (client) => !(client.type === "daemon" && client.clientId !== payload.clientId)
27439
- );
27440
- if (state.record.writeOwner && staleDaemonClientIds.includes(state.record.writeOwner.clientId)) {
27441
- removedDaemonOwner = true;
27442
- }
27443
- }
27444
- }
27445
- const existing = state.record.attachedClients.find((client) => client.clientId === payload.clientId);
27446
- if (existing) {
27447
- existing.type = payload.clientType;
27448
- existing.readOnly = !!payload.readOnly;
27449
- existing.lastSeenAt = now;
27450
- } else {
27451
- state.record.attachedClients.push({
27452
- clientId: payload.clientId,
27453
- type: payload.clientType,
27454
- readOnly: !!payload.readOnly,
27455
- attachedAt: now,
27456
- lastSeenAt: now
27616
+ const df = now2 - pw.lastChange;
27617
+ if (df >= threshold) {
27618
+ writes.delete(path5);
27619
+ awfEmit(void 0, curStat);
27620
+ } else {
27621
+ timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
27622
+ }
27457
27623
  });
27458
27624
  }
27459
- if (removedDaemonOwner) {
27460
- state.record.writeOwner = null;
27461
- }
27462
- state.record.lastActivityAt = now;
27463
- return this.cloneRecord(state.record);
27464
- }
27465
- detachClient(payload) {
27466
- const state = this.requireSession(payload.sessionId);
27467
- state.record.attachedClients = state.record.attachedClients.filter((client) => client.clientId !== payload.clientId);
27468
- if (state.record.writeOwner?.clientId === payload.clientId) {
27469
- state.record.writeOwner = null;
27470
- }
27471
- state.record.lastActivityAt = Date.now();
27472
- return this.cloneRecord(state.record);
27473
- }
27474
- acquireWrite(payload) {
27475
- const state = this.requireSession(payload.sessionId);
27476
- if (state.record.writeOwner && state.record.writeOwner.clientId !== payload.clientId && !payload.force) {
27477
- throw new Error(`Write owned by ${state.record.writeOwner.clientId}`);
27478
- }
27479
- const attachedClient = state.record.attachedClients.find((client) => client.clientId === payload.clientId);
27480
- if (attachedClient) {
27481
- attachedClient.readOnly = false;
27482
- attachedClient.lastSeenAt = Date.now();
27625
+ if (!writes.has(path5)) {
27626
+ writes.set(path5, {
27627
+ lastChange: now,
27628
+ cancelWait: () => {
27629
+ writes.delete(path5);
27630
+ clearTimeout(timeoutHandler);
27631
+ return event;
27632
+ }
27633
+ });
27634
+ timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval);
27483
27635
  }
27484
- state.record.writeOwner = {
27485
- clientId: payload.clientId,
27486
- ownerType: payload.ownerType,
27487
- acquiredAt: Date.now()
27488
- };
27489
- state.record.lastActivityAt = Date.now();
27490
- return this.cloneRecord(state.record);
27491
27636
  }
27492
- releaseWrite(payload) {
27493
- const state = this.requireSession(payload.sessionId);
27494
- const attachedClient = state.record.attachedClients.find((client) => client.clientId === payload.clientId);
27495
- if (attachedClient) {
27496
- attachedClient.readOnly = false;
27497
- attachedClient.lastSeenAt = Date.now();
27498
- }
27499
- if (state.record.writeOwner?.clientId === payload.clientId) {
27500
- state.record.writeOwner = null;
27637
+ /**
27638
+ * Determines whether user has asked to ignore this path.
27639
+ */
27640
+ _isIgnored(path5, stats) {
27641
+ if (this.options.atomic && DOT_RE.test(path5))
27642
+ return true;
27643
+ if (!this._userIgnored) {
27644
+ const { cwd } = this.options;
27645
+ const ign = this.options.ignored;
27646
+ const ignored = (ign || []).map(normalizeIgnored(cwd));
27647
+ const ignoredPaths = [...this._ignoredPaths];
27648
+ const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
27649
+ this._userIgnored = anymatch(list, void 0);
27501
27650
  }
27502
- state.record.lastActivityAt = Date.now();
27503
- return this.cloneRecord(state.record);
27504
- }
27505
- appendOutput(sessionId, data) {
27506
- const state = this.requireSession(sessionId);
27507
- const seq = state.buffer.append(data);
27508
- state.record.buffer = state.buffer.getState();
27509
- state.record.lastActivityAt = Date.now();
27510
- return { record: this.cloneRecord(state.record), seq };
27511
- }
27512
- getSnapshot(sessionId, sinceSeq) {
27513
- const state = this.requireSession(sessionId);
27514
- state.record.buffer = state.buffer.getState();
27515
- return state.buffer.snapshot(sinceSeq);
27516
- }
27517
- clearBuffer(sessionId) {
27518
- const state = this.requireSession(sessionId);
27519
- state.buffer.clear();
27520
- state.record.buffer = state.buffer.getState();
27521
- state.record.lastActivityAt = Date.now();
27522
- return this.cloneRecord(state.record);
27523
- }
27524
- updateSessionMeta(sessionId, meta3, replace = false) {
27525
- const state = this.requireSession(sessionId);
27526
- state.record.meta = replace ? { ...meta3 } : {
27527
- ...state.record.meta || {},
27528
- ...meta3
27529
- };
27530
- state.record.lastActivityAt = Date.now();
27531
- return this.cloneRecord(state.record);
27532
- }
27533
- markStarted(sessionId, pid) {
27534
- const state = this.requireSession(sessionId);
27535
- state.record.lifecycle = "running";
27536
- state.record.startedAt = state.record.startedAt || Date.now();
27537
- if (typeof pid === "number") state.record.osPid = pid;
27538
- state.record.lastActivityAt = Date.now();
27539
- return this.cloneRecord(state.record);
27540
- }
27541
- markStopped(sessionId, lifecycle = "stopped") {
27542
- const state = this.requireSession(sessionId);
27543
- state.record.lifecycle = lifecycle;
27544
- state.record.lastActivityAt = Date.now();
27545
- return this.cloneRecord(state.record);
27651
+ return this._userIgnored(path5, stats);
27546
27652
  }
27547
- setLifecycle(sessionId, lifecycle) {
27548
- const state = this.requireSession(sessionId);
27549
- state.record.lifecycle = lifecycle;
27550
- state.record.lastActivityAt = Date.now();
27551
- return this.cloneRecord(state.record);
27653
+ _isntIgnored(path5, stat4) {
27654
+ return !this._isIgnored(path5, stat4);
27552
27655
  }
27553
- requireSession(sessionId) {
27554
- const state = this.sessions.get(sessionId);
27555
- if (!state) throw new Error(`Unknown session: ${sessionId}`);
27556
- return state;
27656
+ /**
27657
+ * Provides a set of common helpers and properties relating to symlink handling.
27658
+ * @param path file or directory pattern being watched
27659
+ */
27660
+ _getWatchHelpers(path5) {
27661
+ return new WatchHelper(path5, this.options.followSymlinks, this);
27557
27662
  }
27558
- cloneRecord(record2) {
27559
- return {
27560
- ...record2,
27561
- launchCommand: {
27562
- ...record2.launchCommand,
27563
- args: [...record2.launchCommand.args],
27564
- env: record2.launchCommand.env ? { ...record2.launchCommand.env } : void 0
27565
- },
27566
- writeOwner: record2.writeOwner ? { ...record2.writeOwner } : null,
27567
- attachedClients: record2.attachedClients.map((client) => ({ ...client })),
27568
- buffer: { ...record2.buffer },
27569
- meta: { ...record2.meta }
27570
- };
27663
+ // Directory helpers
27664
+ // -----------------
27665
+ /**
27666
+ * Provides directory tracking objects
27667
+ * @param directory path of the directory
27668
+ */
27669
+ _getWatchedDir(directory) {
27670
+ const dir = sp2.resolve(directory);
27671
+ if (!this._watched.has(dir))
27672
+ this._watched.set(dir, new DirEntry(dir, this._boundRemove));
27673
+ return this._watched.get(dir);
27571
27674
  }
27572
- };
27573
- var os6 = __toESM2(require("os"));
27574
- var path22 = __toESM2(require("path"));
27575
- var net3 = __toESM2(require("net"));
27576
- var import_crypto22 = require("crypto");
27577
- function getDefaultSessionHostEndpoint2(appName = "adhdev") {
27578
- if (process.platform === "win32") {
27579
- return {
27580
- kind: "pipe",
27581
- path: `\\\\.\\pipe\\${appName}-session-host`
27582
- };
27675
+ // File helpers
27676
+ // ------------
27677
+ /**
27678
+ * Check for read permissions: https://stackoverflow.com/a/11781404/1358405
27679
+ */
27680
+ _hasReadPermissions(stats) {
27681
+ if (this.options.ignorePermissionErrors)
27682
+ return true;
27683
+ return Boolean(Number(stats.mode) & 256);
27583
27684
  }
27584
- return {
27585
- kind: "unix",
27586
- path: path22.join(os6.tmpdir(), `${appName}-session-host.sock`)
27587
- };
27588
- }
27589
- function serializeEnvelope3(envelope) {
27590
- return `${JSON.stringify(envelope)}
27591
- `;
27592
- }
27593
- function createLineParser2(onEnvelope) {
27594
- let buffer = "";
27595
- return (chunk) => {
27596
- buffer += chunk.toString();
27597
- let newlineIndex = buffer.indexOf("\n");
27598
- while (newlineIndex >= 0) {
27599
- const rawLine = buffer.slice(0, newlineIndex).trim();
27600
- buffer = buffer.slice(newlineIndex + 1);
27601
- if (rawLine) {
27602
- onEnvelope(JSON.parse(rawLine));
27603
- }
27604
- newlineIndex = buffer.indexOf("\n");
27685
+ /**
27686
+ * Handles emitting unlink events for
27687
+ * files and directories, and via recursion, for
27688
+ * files and directories within directories that are unlinked
27689
+ * @param directory within which the following item is located
27690
+ * @param item base path of item/directory
27691
+ */
27692
+ _remove(directory, item, isDirectory) {
27693
+ const path5 = sp2.join(directory, item);
27694
+ const fullPath = sp2.resolve(path5);
27695
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path5) || this._watched.has(fullPath);
27696
+ if (!this._throttle("remove", path5, 100))
27697
+ return;
27698
+ if (!isDirectory && this._watched.size === 1) {
27699
+ this.add(directory, item, true);
27605
27700
  }
27606
- };
27607
- }
27608
- var SessionHostClient2 = class {
27609
- endpoint;
27610
- socket = null;
27611
- requestWaiters = /* @__PURE__ */ new Map();
27612
- eventListeners = /* @__PURE__ */ new Set();
27613
- constructor(options = {}) {
27614
- this.endpoint = options.endpoint || getDefaultSessionHostEndpoint2(options.appName || "adhdev");
27615
- }
27616
- async connect() {
27617
- if (this.socket && !this.socket.destroyed) return;
27618
- const socket = net3.createConnection(this.endpoint.path);
27619
- this.socket = socket;
27620
- socket.on("data", createLineParser2((envelope) => {
27621
- if (envelope.kind === "response") {
27622
- const waiter = this.requestWaiters.get(envelope.requestId);
27623
- if (waiter) {
27624
- this.requestWaiters.delete(envelope.requestId);
27625
- waiter.resolve(envelope.response);
27626
- }
27701
+ const wp = this._getWatchedDir(path5);
27702
+ const nestedDirectoryChildren = wp.getChildren();
27703
+ nestedDirectoryChildren.forEach((nested) => this._remove(path5, nested));
27704
+ const parent = this._getWatchedDir(directory);
27705
+ const wasTracked = parent.has(item);
27706
+ parent.remove(item);
27707
+ if (this._symlinkPaths.has(fullPath)) {
27708
+ this._symlinkPaths.delete(fullPath);
27709
+ }
27710
+ let relPath = path5;
27711
+ if (this.options.cwd)
27712
+ relPath = sp2.relative(this.options.cwd, path5);
27713
+ if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
27714
+ const event = this._pendingWrites.get(relPath).cancelWait();
27715
+ if (event === EVENTS.ADD)
27627
27716
  return;
27628
- }
27629
- if (envelope.kind === "event") {
27630
- for (const listener of this.eventListeners) listener(envelope.event);
27631
- }
27632
- }));
27633
- socket.on("error", (error48) => {
27634
- for (const waiter of this.requestWaiters.values()) {
27635
- waiter.reject(error48);
27636
- }
27637
- this.requestWaiters.clear();
27638
- });
27639
- await new Promise((resolve4, reject) => {
27640
- socket.once("connect", () => resolve4());
27641
- socket.once("error", reject);
27642
- });
27717
+ }
27718
+ this._watched.delete(path5);
27719
+ this._watched.delete(fullPath);
27720
+ const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
27721
+ if (wasTracked && !this._isIgnored(path5))
27722
+ this._emit(eventName, path5);
27723
+ this._closePath(path5);
27643
27724
  }
27644
- onEvent(listener) {
27645
- this.eventListeners.add(listener);
27646
- return () => {
27647
- this.eventListeners.delete(listener);
27648
- };
27725
+ /**
27726
+ * Closes all watchers for a path
27727
+ */
27728
+ _closePath(path5) {
27729
+ this._closeFile(path5);
27730
+ const dir = sp2.dirname(path5);
27731
+ this._getWatchedDir(dir).remove(sp2.basename(path5));
27649
27732
  }
27650
- async request(request) {
27651
- await this.connect();
27652
- if (!this.socket) throw new Error("Session host socket unavailable");
27653
- const requestId = (0, import_crypto22.randomUUID)();
27654
- const envelope = {
27655
- kind: "request",
27656
- requestId,
27657
- request
27658
- };
27659
- const response = await new Promise((resolve4, reject) => {
27660
- const timeout = setTimeout(() => {
27661
- this.requestWaiters.delete(requestId);
27662
- reject(new Error(`Session host request timed out after 30s (${request.type})`));
27663
- }, 3e4);
27664
- this.requestWaiters.set(requestId, {
27665
- resolve: (value) => {
27666
- clearTimeout(timeout);
27667
- resolve4(value);
27668
- },
27669
- reject: (error48) => {
27670
- clearTimeout(timeout);
27671
- reject(error48);
27672
- }
27673
- });
27674
- this.socket?.write(serializeEnvelope3(envelope));
27675
- });
27676
- return response;
27733
+ /**
27734
+ * Closes only file-specific watchers
27735
+ */
27736
+ _closeFile(path5) {
27737
+ const closers = this._closers.get(path5);
27738
+ if (!closers)
27739
+ return;
27740
+ closers.forEach((closer) => closer());
27741
+ this._closers.delete(path5);
27677
27742
  }
27678
- async close() {
27679
- if (!this.socket) return;
27680
- const socket = this.socket;
27681
- this.socket = null;
27682
- for (const waiter of this.requestWaiters.values()) {
27683
- waiter.reject(new Error("Session host client closed"));
27743
+ _addPathCloser(path5, closer) {
27744
+ if (!closer)
27745
+ return;
27746
+ let list = this._closers.get(path5);
27747
+ if (!list) {
27748
+ list = [];
27749
+ this._closers.set(path5, list);
27684
27750
  }
27685
- this.requestWaiters.clear();
27686
- await new Promise((resolve4) => {
27687
- let settled = false;
27688
- const done = () => {
27689
- if (settled) return;
27690
- settled = true;
27691
- resolve4();
27692
- };
27693
- socket.once("close", done);
27694
- socket.end();
27695
- socket.destroy();
27696
- setTimeout(done, 50);
27751
+ list.push(closer);
27752
+ }
27753
+ _readdirp(root, opts) {
27754
+ if (this.closed)
27755
+ return;
27756
+ const options = { type: EVENTS.ALL, alwaysStat: true, lstat: true, ...opts, depth: 0 };
27757
+ let stream = readdirp(root, options);
27758
+ this._streams.add(stream);
27759
+ stream.once(STR_CLOSE, () => {
27760
+ stream = void 0;
27761
+ });
27762
+ stream.once(STR_END, () => {
27763
+ if (stream) {
27764
+ this._streams.delete(stream);
27765
+ stream = void 0;
27766
+ }
27697
27767
  });
27768
+ return stream;
27698
27769
  }
27699
27770
  };
27700
- function createResponseEnvelope(requestId, response) {
27701
- return {
27702
- kind: "response",
27703
- requestId,
27704
- response
27705
- };
27706
- }
27707
- function writeEnvelope(socket, envelope) {
27708
- socket.write(serializeEnvelope3(envelope));
27709
- }
27771
+ chokidar_default = { watch, FSWatcher };
27710
27772
  }
27711
27773
  });
27712
27774
 
@@ -28370,6 +28432,13 @@ var require_dist2 = __commonJS({
28370
28432
  const key = `${preference}:${ghosttyAvailable}:${backendKind}`;
28371
28433
  if (loggedTerminalBackends.has(key)) return;
28372
28434
  loggedTerminalBackends.add(key);
28435
+ if (backendKind === "xterm" && preference !== "xterm" && !ghosttyAvailable) {
28436
+ LOG2.warn(
28437
+ "Terminal",
28438
+ `[terminal-screen] ghostty-vt unavailable; using xterm fallback (preference=${preference})`
28439
+ );
28440
+ return;
28441
+ }
28373
28442
  LOG2.info(
28374
28443
  "Terminal",
28375
28444
  `[terminal-screen] backend=${backendKind} preference=${preference} ghosttyAvailable=${ghosttyAvailable}`
@@ -28432,12 +28501,14 @@ var require_dist2 = __commonJS({
28432
28501
  };
28433
28502
  }
28434
28503
  });
28504
+ var os7;
28435
28505
  var pty;
28436
28506
  var NodePtyRuntimeTransport;
28437
28507
  var NodePtyTransportFactory;
28438
28508
  var init_pty_transport = __esm2({
28439
28509
  "src/cli-adapters/pty-transport.ts"() {
28440
28510
  "use strict";
28511
+ os7 = __toESM2(require("os"));
28441
28512
  try {
28442
28513
  pty = require("node-pty");
28443
28514
  } catch {
@@ -28474,11 +28545,21 @@ var require_dist2 = __commonJS({
28474
28545
  NodePtyTransportFactory = class {
28475
28546
  spawn(command, args, options) {
28476
28547
  if (!pty) throw new Error("node-pty is not installed");
28548
+ let cwd = options.cwd;
28549
+ if (cwd) {
28550
+ try {
28551
+ const fs15 = require("fs");
28552
+ const stat4 = fs15.statSync(cwd);
28553
+ if (!stat4.isDirectory()) cwd = os7.homedir();
28554
+ } catch {
28555
+ cwd = os7.homedir();
28556
+ }
28557
+ }
28477
28558
  const handle = pty.spawn(command, args, {
28478
28559
  name: "xterm-256color",
28479
28560
  cols: options.cols,
28480
28561
  rows: options.rows,
28481
- cwd: options.cwd,
28562
+ cwd,
28482
28563
  env: options.env
28483
28564
  });
28484
28565
  return new NodePtyRuntimeTransport(handle);
@@ -28486,6 +28567,13 @@ var require_dist2 = __commonJS({
28486
28567
  };
28487
28568
  }
28488
28569
  });
28570
+ var import_session_host_core2;
28571
+ var init_spawn_env = __esm2({
28572
+ "src/cli-adapters/spawn-env.ts"() {
28573
+ "use strict";
28574
+ import_session_host_core2 = require_dist();
28575
+ }
28576
+ });
28489
28577
  var provider_cli_adapter_exports = {};
28490
28578
  __export2(provider_cli_adapter_exports, {
28491
28579
  ProviderCliAdapter: () => ProviderCliAdapter,
@@ -28500,32 +28588,6 @@ var require_dist2 = __commonJS({
28500
28588
  function sanitizeTerminalText(str) {
28501
28589
  return stripTerminalNoise(stripAnsi(str));
28502
28590
  }
28503
- function applyPreferredTerminalColorEnv(env) {
28504
- if (env.NO_COLOR) return;
28505
- if (!env.TERM || env.TERM === "xterm-color") {
28506
- env.TERM = "xterm-256color";
28507
- }
28508
- if (!env.COLORTERM) env.COLORTERM = "truecolor";
28509
- if (process.platform === "win32") {
28510
- if (!env.FORCE_COLOR) env.FORCE_COLOR = "1";
28511
- if (!env.CLICOLOR) env.CLICOLOR = "1";
28512
- }
28513
- }
28514
- function buildCliSpawnEnv(baseEnv, overrides) {
28515
- const env = {};
28516
- const source = { ...baseEnv, ...overrides || {} };
28517
- for (const [key, value] of Object.entries(source)) {
28518
- if (typeof value !== "string") continue;
28519
- env[key] = value;
28520
- }
28521
- for (const key of Object.keys(env)) {
28522
- 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_")) {
28523
- delete env[key];
28524
- }
28525
- }
28526
- applyPreferredTerminalColorEnv(env);
28527
- return env;
28528
- }
28529
28591
  function computeTerminalQueryTail(buffer) {
28530
28592
  const prefixes = ["\x1B[6n", "\x1B[?6n"];
28531
28593
  const maxLength = prefixes.reduce((n, value) => Math.max(n, value.length), 0) - 1;
@@ -28539,7 +28601,7 @@ var require_dist2 = __commonJS({
28539
28601
  return "";
28540
28602
  }
28541
28603
  function findBinary(name) {
28542
- const isWin = os7.platform() === "win32";
28604
+ const isWin = os8.platform() === "win32";
28543
28605
  try {
28544
28606
  const cmd = isWin ? `where ${name}` : `which ${name}`;
28545
28607
  return (0, import_child_process4.execSync)(cmd, { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }).trim().split("\n")[0].trim();
@@ -28587,7 +28649,7 @@ var require_dist2 = __commonJS({
28587
28649
  }
28588
28650
  function shSingleQuote(arg) {
28589
28651
  if (/^[a-zA-Z0-9@%_+=:,./-]+$/.test(arg)) return arg;
28590
- if (os7.platform() === "win32") {
28652
+ if (os8.platform() === "win32") {
28591
28653
  return `"${arg.replace(/"/g, '""')}"`;
28592
28654
  }
28593
28655
  return `'${arg.replace(/'/g, `'\\''`)}'`;
@@ -28654,41 +28716,29 @@ var require_dist2 = __commonJS({
28654
28716
  }
28655
28717
  };
28656
28718
  }
28657
- var os7;
28719
+ var os8;
28658
28720
  var path7;
28659
28721
  var import_child_process4;
28660
28722
  var pty2;
28723
+ var buildCliSpawnEnv;
28661
28724
  var ProviderCliAdapter;
28662
28725
  var init_provider_cli_adapter = __esm2({
28663
28726
  "src/cli-adapters/provider-cli-adapter.ts"() {
28664
28727
  "use strict";
28665
- os7 = __toESM2(require("os"));
28728
+ os8 = __toESM2(require("os"));
28666
28729
  path7 = __toESM2(require("path"));
28667
28730
  import_child_process4 = require("child_process");
28668
28731
  init_logger();
28669
28732
  init_terminal_screen();
28670
28733
  init_pty_transport();
28734
+ init_spawn_env();
28671
28735
  try {
28672
28736
  pty2 = require("node-pty");
28673
- if (os7.platform() !== "win32") {
28674
- try {
28675
- const fs15 = require("fs");
28676
- const ptyDir = path7.resolve(path7.dirname(require.resolve("node-pty")), "..");
28677
- const platformArch = `${os7.platform()}-${os7.arch()}`;
28678
- const helper = path7.join(ptyDir, "prebuilds", platformArch, "spawn-helper");
28679
- if (fs15.existsSync(helper)) {
28680
- const stat4 = fs15.statSync(helper);
28681
- if (!(stat4.mode & 73)) {
28682
- fs15.chmodSync(helper, stat4.mode | 493);
28683
- LOG2.info("CLI", "[node-pty] Fixed spawn-helper permissions");
28684
- }
28685
- }
28686
- } catch {
28687
- }
28688
- }
28737
+ (0, import_session_host_core2.ensureNodePtySpawnHelperPermissions)((msg) => LOG2.info("CLI", msg));
28689
28738
  } catch {
28690
28739
  LOG2.error("CLI", "[ProviderCliAdapter] node-pty not found. Terminal features disabled.");
28691
28740
  }
28741
+ buildCliSpawnEnv = import_session_host_core2.sanitizeSpawnEnv;
28692
28742
  ProviderCliAdapter = class _ProviderCliAdapter {
28693
28743
  constructor(provider, workingDir, extraArgs = [], transportFactory = new NodePtyTransportFactory()) {
28694
28744
  this.extraArgs = extraArgs;
@@ -28696,7 +28746,7 @@ var require_dist2 = __commonJS({
28696
28746
  this.transportFactory = transportFactory;
28697
28747
  this.cliType = provider.type;
28698
28748
  this.cliName = provider.name;
28699
- this.workingDir = workingDir.startsWith("~") ? workingDir.replace(/^~/, os7.homedir()) : workingDir;
28749
+ this.workingDir = workingDir.startsWith("~") ? workingDir.replace(/^~/, os8.homedir()) : workingDir;
28700
28750
  const t = provider.timeouts || {};
28701
28751
  this.timeouts = {
28702
28752
  ptyFlush: t.ptyFlush ?? 50,
@@ -29003,7 +29053,7 @@ var require_dist2 = __commonJS({
29003
29053
  if (this.ptyProcess) return;
29004
29054
  const { spawn: spawnConfig } = this.provider;
29005
29055
  const binaryPath = findBinary(spawnConfig.command);
29006
- const isWin = os7.platform() === "win32";
29056
+ const isWin = os8.platform() === "win32";
29007
29057
  const allArgs = [...spawnConfig.args, ...this.extraArgs];
29008
29058
  LOG2.info("CLI", `[${this.cliType}] Spawning in ${this.workingDir}`);
29009
29059
  this.resetTraceSession();
@@ -29055,6 +29105,12 @@ var require_dist2 = __commonJS({
29055
29105
  shellArgs = ["-l", "-c", fullCmd];
29056
29106
  this.ptyProcess = this.transportFactory.spawn(shellCmd, shellArgs, ptyOpts);
29057
29107
  } else {
29108
+ if (isWin) {
29109
+ const hint = /error code 267|ERROR_DIRECTORY/i.test(msg) ? " (working directory does not exist or is not a directory)" : /error code 740|elevation/i.test(msg) ? " (requires administrator privileges)" : /error code 2|ENOENT|not found/i.test(msg) ? ` (executable not found: ${shellCmd})` : "";
29110
+ if (hint) {
29111
+ throw new Error(`Failed to spawn CLI${hint}: ${msg}`);
29112
+ }
29113
+ }
29058
29114
  throw err;
29059
29115
  }
29060
29116
  }
@@ -29229,7 +29285,7 @@ var require_dist2 = __commonJS({
29229
29285
  `[${this.cliType}] Waiting for interactive prompt: hasPrompt=${hasPrompt} stableMs=${stableMs} recentOutputMs=${recentlyOutput} status=${status} startup=${startupLikelyActive} screen=${JSON.stringify(this.summarizeTraceText(screenText, 220)).slice(0, 260)}`
29230
29286
  );
29231
29287
  }
29232
- await new Promise((resolve10) => setTimeout(resolve10, 50));
29288
+ await new Promise((resolve9) => setTimeout(resolve9, 50));
29233
29289
  }
29234
29290
  const finalScreenText = this.terminalScreen.getText() || "";
29235
29291
  LOG2.warn(
@@ -29616,7 +29672,7 @@ ${data.message || ""}`.trim();
29616
29672
  if (this.startupParseGate) {
29617
29673
  const deadline = Date.now() + 1e4;
29618
29674
  while (this.startupParseGate && Date.now() < deadline) {
29619
- await new Promise((resolve10) => setTimeout(resolve10, 50));
29675
+ await new Promise((resolve9) => setTimeout(resolve9, 50));
29620
29676
  }
29621
29677
  }
29622
29678
  if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
@@ -29807,7 +29863,8 @@ ${data.message || ""}`.trim();
29807
29863
  const payload = stopCommand.endsWith("\r") || stopCommand.endsWith("\n") ? stopCommand : `${stopCommand}${this.sendKey}`;
29808
29864
  this.ptyProcess.write(payload);
29809
29865
  };
29810
- if (wasProcessing) setTimeout(writeCommand, 250);
29866
+ const interruptGraceMs = typeof resume.interruptGraceMs === "number" ? Math.max(100, resume.interruptGraceMs) : 500;
29867
+ if (wasProcessing) setTimeout(writeCommand, interruptGraceMs);
29811
29868
  else writeCommand();
29812
29869
  } else {
29813
29870
  this.ptyProcess.write("");
@@ -29823,17 +29880,17 @@ ${data.message || ""}`.trim();
29823
29880
  }
29824
29881
  }
29825
29882
  waitForStopped(timeoutMs) {
29826
- return new Promise((resolve10) => {
29883
+ return new Promise((resolve9) => {
29827
29884
  const startedAt = Date.now();
29828
29885
  const timer = setInterval(() => {
29829
29886
  if (!this.ptyProcess || this.currentStatus === "stopped") {
29830
29887
  clearInterval(timer);
29831
- resolve10(true);
29888
+ resolve9(true);
29832
29889
  return;
29833
29890
  }
29834
29891
  if (Date.now() - startedAt >= timeoutMs) {
29835
29892
  clearInterval(timer);
29836
- resolve10(false);
29893
+ resolve9(false);
29837
29894
  }
29838
29895
  }, 100);
29839
29896
  });
@@ -29852,6 +29909,18 @@ ${data.message || ""}`.trim();
29852
29909
  clearTimeout(this.submitRetryTimer);
29853
29910
  this.submitRetryTimer = null;
29854
29911
  }
29912
+ if (this.responseTimeout) {
29913
+ clearTimeout(this.responseTimeout);
29914
+ this.responseTimeout = null;
29915
+ }
29916
+ if (this.idleTimeout) {
29917
+ clearTimeout(this.idleTimeout);
29918
+ this.idleTimeout = null;
29919
+ }
29920
+ if (this.pendingScriptStatusTimer) {
29921
+ clearTimeout(this.pendingScriptStatusTimer);
29922
+ this.pendingScriptStatusTimer = null;
29923
+ }
29855
29924
  if (this.pendingOutputParseTimer) {
29856
29925
  clearTimeout(this.pendingOutputParseTimer);
29857
29926
  this.pendingOutputParseTimer = null;
@@ -29893,6 +29962,18 @@ ${data.message || ""}`.trim();
29893
29962
  clearTimeout(this.submitRetryTimer);
29894
29963
  this.submitRetryTimer = null;
29895
29964
  }
29965
+ if (this.responseTimeout) {
29966
+ clearTimeout(this.responseTimeout);
29967
+ this.responseTimeout = null;
29968
+ }
29969
+ if (this.idleTimeout) {
29970
+ clearTimeout(this.idleTimeout);
29971
+ this.idleTimeout = null;
29972
+ }
29973
+ if (this.pendingScriptStatusTimer) {
29974
+ clearTimeout(this.pendingScriptStatusTimer);
29975
+ this.pendingScriptStatusTimer = null;
29976
+ }
29896
29977
  if (this.pendingOutputParseTimer) {
29897
29978
  clearTimeout(this.pendingOutputParseTimer);
29898
29979
  this.pendingOutputParseTimer = null;
@@ -30476,20 +30557,20 @@ ${data.message || ""}`.trim();
30476
30557
  return null;
30477
30558
  }
30478
30559
  async function detectIDEs() {
30479
- const os17 = (0, import_os22.platform)();
30560
+ const os18 = (0, import_os22.platform)();
30480
30561
  const results = [];
30481
30562
  for (const def of getMergedDefinitions()) {
30482
30563
  const cliPath = findCliCommand(def.cli);
30483
- const appPath = checkPathExists(def.paths[os17] || []);
30564
+ const appPath = checkPathExists(def.paths[os18] || []);
30484
30565
  const installed = !!(cliPath || appPath);
30485
30566
  let resolvedCli = cliPath;
30486
- if (!resolvedCli && appPath && os17 === "darwin") {
30567
+ if (!resolvedCli && appPath && os18 === "darwin") {
30487
30568
  const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
30488
30569
  if ((0, import_fs2.existsSync)(bundledCli)) resolvedCli = bundledCli;
30489
30570
  }
30490
- if (!resolvedCli && appPath && os17 === "win32") {
30491
- const { dirname: dirname7 } = await import("path");
30492
- const appDir = dirname7(appPath);
30571
+ if (!resolvedCli && appPath && os18 === "win32") {
30572
+ const { dirname: dirname6 } = await import("path");
30573
+ const appDir = dirname6(appPath);
30493
30574
  const candidates = [
30494
30575
  `${appDir}\\\\bin\\\\${def.cli}.cmd`,
30495
30576
  `${appDir}\\\\bin\\\\${def.cli}`,
@@ -30525,15 +30606,15 @@ ${data.message || ""}`.trim();
30525
30606
  return match ? match[1] : raw.split("\n")[0].slice(0, 100);
30526
30607
  }
30527
30608
  function execAsync(cmd, timeoutMs = 5e3) {
30528
- return new Promise((resolve10) => {
30609
+ return new Promise((resolve9) => {
30529
30610
  const child = (0, import_child_process22.exec)(cmd, { encoding: "utf-8", timeout: timeoutMs }, (err, stdout) => {
30530
30611
  if (err || !stdout?.trim()) {
30531
- resolve10(null);
30612
+ resolve9(null);
30532
30613
  } else {
30533
- resolve10(stdout.trim());
30614
+ resolve9(stdout.trim());
30534
30615
  }
30535
30616
  });
30536
- child.on("error", () => resolve10(null));
30617
+ child.on("error", () => resolve9(null));
30537
30618
  });
30538
30619
  }
30539
30620
  async function detectCLIs(providerLoader) {
@@ -30573,6 +30654,39 @@ ${data.message || ""}`.trim();
30573
30654
  }
30574
30655
  async function detectCLI(cliId, providerLoader) {
30575
30656
  const resolvedId = providerLoader ? providerLoader.resolveAlias(cliId) : cliId;
30657
+ if (providerLoader) {
30658
+ const cliList = providerLoader.getCliDetectionList();
30659
+ const target = cliList.find((c) => c.id === resolvedId);
30660
+ if (target) {
30661
+ const platform9 = os22.platform();
30662
+ const whichCmd = platform9 === "win32" ? "where" : "which";
30663
+ try {
30664
+ const pathResult = await execAsync(`${whichCmd} ${target.command}`);
30665
+ if (!pathResult) return null;
30666
+ const firstPath = pathResult.split("\n")[0];
30667
+ let version2;
30668
+ try {
30669
+ const versionCommands = [
30670
+ target.versionCommand,
30671
+ `${target.command} --version`,
30672
+ `${target.command} -V`,
30673
+ `${target.command} -v`
30674
+ ].filter((v2) => !!v2);
30675
+ for (const versionCommand of versionCommands) {
30676
+ const versionResult = await execAsync(versionCommand, 3e3);
30677
+ if (versionResult) {
30678
+ version2 = parseVersion(versionResult);
30679
+ break;
30680
+ }
30681
+ }
30682
+ } catch {
30683
+ }
30684
+ return { ...target, installed: true, version: version2, path: firstPath };
30685
+ } catch {
30686
+ return null;
30687
+ }
30688
+ }
30689
+ }
30576
30690
  const all = await detectCLIs(providerLoader);
30577
30691
  return all.find((c) => c.id === resolvedId && c.installed) || null;
30578
30692
  }
@@ -30696,7 +30810,7 @@ ${data.message || ""}`.trim();
30696
30810
  * Returns multiple entries if multiple IDE windows are open on same port
30697
30811
  */
30698
30812
  static listAllTargets(port) {
30699
- return new Promise((resolve10) => {
30813
+ return new Promise((resolve9) => {
30700
30814
  const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
30701
30815
  let data = "";
30702
30816
  res.on("data", (chunk) => data += chunk.toString());
@@ -30712,16 +30826,16 @@ ${data.message || ""}`.trim();
30712
30826
  (t) => !isNonMain(t.title || "") && t.url?.includes("workbench.html") && !t.url?.includes("agent")
30713
30827
  );
30714
30828
  const fallbackPages = pages.filter((t) => !isNonMain(t.title || ""));
30715
- resolve10(mainPages.length > 0 ? mainPages : fallbackPages);
30829
+ resolve9(mainPages.length > 0 ? mainPages : fallbackPages);
30716
30830
  } catch {
30717
- resolve10([]);
30831
+ resolve9([]);
30718
30832
  }
30719
30833
  });
30720
30834
  });
30721
- req.on("error", () => resolve10([]));
30835
+ req.on("error", () => resolve9([]));
30722
30836
  req.setTimeout(2e3, () => {
30723
30837
  req.destroy();
30724
- resolve10([]);
30838
+ resolve9([]);
30725
30839
  });
30726
30840
  });
30727
30841
  }
@@ -30761,7 +30875,7 @@ ${data.message || ""}`.trim();
30761
30875
  }
30762
30876
  }
30763
30877
  findTargetOnPort(port) {
30764
- return new Promise((resolve10) => {
30878
+ return new Promise((resolve9) => {
30765
30879
  const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
30766
30880
  let data = "";
30767
30881
  res.on("data", (chunk) => data += chunk.toString());
@@ -30772,7 +30886,7 @@ ${data.message || ""}`.trim();
30772
30886
  (t) => (t.type === "page" || t.type === "browser" || t.type === "Page") && t.webSocketDebuggerUrl
30773
30887
  );
30774
30888
  if (pages.length === 0) {
30775
- resolve10(targets.find((t) => t.webSocketDebuggerUrl) || null);
30889
+ resolve9(targets.find((t) => t.webSocketDebuggerUrl) || null);
30776
30890
  return;
30777
30891
  }
30778
30892
  const mainPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
@@ -30782,24 +30896,24 @@ ${data.message || ""}`.trim();
30782
30896
  const specific = list.find((t) => t.id === this._targetId);
30783
30897
  if (specific) {
30784
30898
  this._pageTitle = specific.title || "";
30785
- resolve10(specific);
30899
+ resolve9(specific);
30786
30900
  } else {
30787
30901
  this.log(`[CDP] Target ${this._targetId} not found in page list`);
30788
- resolve10(null);
30902
+ resolve9(null);
30789
30903
  }
30790
30904
  return;
30791
30905
  }
30792
30906
  this._pageTitle = list[0]?.title || "";
30793
- resolve10(list[0]);
30907
+ resolve9(list[0]);
30794
30908
  } catch {
30795
- resolve10(null);
30909
+ resolve9(null);
30796
30910
  }
30797
30911
  });
30798
30912
  });
30799
- req.on("error", () => resolve10(null));
30913
+ req.on("error", () => resolve9(null));
30800
30914
  req.setTimeout(2e3, () => {
30801
30915
  req.destroy();
30802
- resolve10(null);
30916
+ resolve9(null);
30803
30917
  });
30804
30918
  });
30805
30919
  }
@@ -30810,7 +30924,7 @@ ${data.message || ""}`.trim();
30810
30924
  this.extensionProviders = providers;
30811
30925
  }
30812
30926
  connectToTarget(wsUrl) {
30813
- return new Promise((resolve10) => {
30927
+ return new Promise((resolve9) => {
30814
30928
  this.ws = new import_ws2.default(wsUrl);
30815
30929
  this.ws.on("open", async () => {
30816
30930
  this._connected = true;
@@ -30820,17 +30934,17 @@ ${data.message || ""}`.trim();
30820
30934
  }
30821
30935
  this.connectBrowserWs().catch(() => {
30822
30936
  });
30823
- resolve10(true);
30937
+ resolve9(true);
30824
30938
  });
30825
30939
  this.ws.on("message", (data) => {
30826
30940
  try {
30827
30941
  const msg = JSON.parse(data.toString());
30828
30942
  if (msg.id && this.pending.has(msg.id)) {
30829
- const { resolve: resolve11, reject } = this.pending.get(msg.id);
30943
+ const { resolve: resolve10, reject } = this.pending.get(msg.id);
30830
30944
  this.pending.delete(msg.id);
30831
30945
  this.failureCount = 0;
30832
30946
  if (msg.error) reject(new Error(msg.error.message));
30833
- else resolve11(msg.result);
30947
+ else resolve10(msg.result);
30834
30948
  } else if (msg.method === "Runtime.executionContextCreated") {
30835
30949
  this.contexts.add(msg.params.context.id);
30836
30950
  } else if (msg.method === "Runtime.executionContextDestroyed") {
@@ -30853,7 +30967,7 @@ ${data.message || ""}`.trim();
30853
30967
  this.ws.on("error", (err) => {
30854
30968
  this.log(`[CDP] WebSocket error: ${err.message}`);
30855
30969
  this._connected = false;
30856
- resolve10(false);
30970
+ resolve9(false);
30857
30971
  });
30858
30972
  });
30859
30973
  }
@@ -30867,7 +30981,7 @@ ${data.message || ""}`.trim();
30867
30981
  return;
30868
30982
  }
30869
30983
  this.log(`[CDP] Connecting browser WS for target discovery...`);
30870
- await new Promise((resolve10, reject) => {
30984
+ await new Promise((resolve9, reject) => {
30871
30985
  this.browserWs = new import_ws2.default(browserWsUrl);
30872
30986
  this.browserWs.on("open", async () => {
30873
30987
  this._browserConnected = true;
@@ -30877,16 +30991,16 @@ ${data.message || ""}`.trim();
30877
30991
  } catch (e) {
30878
30992
  this.log(`[CDP] setDiscoverTargets failed: ${e.message}`);
30879
30993
  }
30880
- resolve10();
30994
+ resolve9();
30881
30995
  });
30882
30996
  this.browserWs.on("message", (data) => {
30883
30997
  try {
30884
30998
  const msg = JSON.parse(data.toString());
30885
30999
  if (msg.id && this.browserPending.has(msg.id)) {
30886
- const { resolve: resolve11, reject: reject2 } = this.browserPending.get(msg.id);
31000
+ const { resolve: resolve10, reject: reject2 } = this.browserPending.get(msg.id);
30887
31001
  this.browserPending.delete(msg.id);
30888
31002
  if (msg.error) reject2(new Error(msg.error.message));
30889
- else resolve11(msg.result);
31003
+ else resolve10(msg.result);
30890
31004
  }
30891
31005
  } catch {
30892
31006
  }
@@ -30906,31 +31020,31 @@ ${data.message || ""}`.trim();
30906
31020
  }
30907
31021
  }
30908
31022
  getBrowserWsUrl() {
30909
- return new Promise((resolve10) => {
31023
+ return new Promise((resolve9) => {
30910
31024
  const req = http.get(`http://127.0.0.1:${this.port}/json/version`, (res) => {
30911
31025
  let data = "";
30912
31026
  res.on("data", (chunk) => data += chunk.toString());
30913
31027
  res.on("end", () => {
30914
31028
  try {
30915
31029
  const info = JSON.parse(data);
30916
- resolve10(info.webSocketDebuggerUrl || null);
31030
+ resolve9(info.webSocketDebuggerUrl || null);
30917
31031
  } catch {
30918
- resolve10(null);
31032
+ resolve9(null);
30919
31033
  }
30920
31034
  });
30921
31035
  });
30922
- req.on("error", () => resolve10(null));
31036
+ req.on("error", () => resolve9(null));
30923
31037
  req.setTimeout(3e3, () => {
30924
31038
  req.destroy();
30925
- resolve10(null);
31039
+ resolve9(null);
30926
31040
  });
30927
31041
  });
30928
31042
  }
30929
31043
  sendBrowser(method, params = {}, timeoutMs = 15e3) {
30930
- return new Promise((resolve10, reject) => {
31044
+ return new Promise((resolve9, reject) => {
30931
31045
  if (!this.browserWs || !this._browserConnected) return reject(new Error("Browser WS not connected"));
30932
31046
  const id = this.browserMsgId++;
30933
- this.browserPending.set(id, { resolve: resolve10, reject });
31047
+ this.browserPending.set(id, { resolve: resolve9, reject });
30934
31048
  this.browserWs.send(JSON.stringify({ id, method, params }));
30935
31049
  setTimeout(() => {
30936
31050
  if (this.browserPending.has(id)) {
@@ -30970,11 +31084,11 @@ ${data.message || ""}`.trim();
30970
31084
  }
30971
31085
  // ─── CDP Protocol ────────────────────────────────────────
30972
31086
  sendInternal(method, params = {}, timeoutMs = 15e3) {
30973
- return new Promise((resolve10, reject) => {
31087
+ return new Promise((resolve9, reject) => {
30974
31088
  if (!this.ws || !this._connected) return reject(new Error("CDP not connected"));
30975
31089
  if (this.ws.readyState !== import_ws2.default.OPEN) return reject(new Error("WebSocket not open"));
30976
31090
  const id = this.msgId++;
30977
- this.pending.set(id, { resolve: resolve10, reject });
31091
+ this.pending.set(id, { resolve: resolve9, reject });
30978
31092
  this.ws.send(JSON.stringify({ id, method, params }));
30979
31093
  setTimeout(() => {
30980
31094
  if (this.pending.has(id)) {
@@ -31223,7 +31337,7 @@ ${data.message || ""}`.trim();
31223
31337
  const browserWs = this.browserWs;
31224
31338
  let msgId = this.browserMsgId;
31225
31339
  const sendWs = (method, params = {}, sessionId) => {
31226
- return new Promise((resolve10, reject) => {
31340
+ return new Promise((resolve9, reject) => {
31227
31341
  const mid = msgId++;
31228
31342
  this.browserMsgId = msgId;
31229
31343
  const handler = (raw) => {
@@ -31232,7 +31346,7 @@ ${data.message || ""}`.trim();
31232
31346
  if (msg.id === mid) {
31233
31347
  browserWs.removeListener("message", handler);
31234
31348
  if (msg.error) reject(new Error(msg.error.message || JSON.stringify(msg.error)));
31235
- else resolve10(msg.result);
31349
+ else resolve9(msg.result);
31236
31350
  }
31237
31351
  } catch {
31238
31352
  }
@@ -31423,14 +31537,14 @@ ${data.message || ""}`.trim();
31423
31537
  if (!ws2 || ws2.readyState !== import_ws2.default.OPEN) {
31424
31538
  throw new Error("CDP not connected");
31425
31539
  }
31426
- return new Promise((resolve10, reject) => {
31540
+ return new Promise((resolve9, reject) => {
31427
31541
  const id = getNextId();
31428
31542
  pendingMap.set(id, {
31429
31543
  resolve: (result) => {
31430
31544
  if (result?.result?.subtype === "error") {
31431
31545
  reject(new Error(result.result.description));
31432
31546
  } else {
31433
- resolve10(result?.result?.value);
31547
+ resolve9(result?.result?.value);
31434
31548
  }
31435
31549
  },
31436
31550
  reject
@@ -31462,10 +31576,10 @@ ${data.message || ""}`.trim();
31462
31576
  throw new Error("CDP not connected");
31463
31577
  }
31464
31578
  const sendViaSession = (method, params = {}) => {
31465
- return new Promise((resolve10, reject) => {
31579
+ return new Promise((resolve9, reject) => {
31466
31580
  const pendingMap = this._browserConnected ? this.browserPending : this.pending;
31467
31581
  const id = this._browserConnected ? this.browserMsgId++ : this.msgId++;
31468
- pendingMap.set(id, { resolve: resolve10, reject });
31582
+ pendingMap.set(id, { resolve: resolve9, reject });
31469
31583
  ws2.send(JSON.stringify({ id, sessionId, method, params }));
31470
31584
  setTimeout(() => {
31471
31585
  if (pendingMap.has(id)) {
@@ -35432,7 +35546,7 @@ ${data.message || ""}`.trim();
35432
35546
  try {
35433
35547
  const http3 = await import("http");
35434
35548
  const postData = JSON.stringify(body);
35435
- const result = await new Promise((resolve10, reject) => {
35549
+ const result = await new Promise((resolve9, reject) => {
35436
35550
  const req = http3.request({
35437
35551
  hostname: "127.0.0.1",
35438
35552
  port: 19280,
@@ -35444,9 +35558,9 @@ ${data.message || ""}`.trim();
35444
35558
  res.on("data", (chunk) => data += chunk);
35445
35559
  res.on("end", () => {
35446
35560
  try {
35447
- resolve10(JSON.parse(data));
35561
+ resolve9(JSON.parse(data));
35448
35562
  } catch {
35449
- resolve10({ raw: data });
35563
+ resolve9({ raw: data });
35450
35564
  }
35451
35565
  });
35452
35566
  });
@@ -35464,15 +35578,15 @@ ${data.message || ""}`.trim();
35464
35578
  if (!providerType) return { success: false, error: "providerType required" };
35465
35579
  try {
35466
35580
  const http3 = await import("http");
35467
- const result = await new Promise((resolve10, reject) => {
35581
+ const result = await new Promise((resolve9, reject) => {
35468
35582
  http3.get(`http://127.0.0.1:19280/api/providers/${providerType}/${endpoint}`, (res) => {
35469
35583
  let data = "";
35470
35584
  res.on("data", (chunk) => data += chunk);
35471
35585
  res.on("end", () => {
35472
35586
  try {
35473
- resolve10(JSON.parse(data));
35587
+ resolve9(JSON.parse(data));
35474
35588
  } catch {
35475
- resolve10({ raw: data });
35589
+ resolve9({ raw: data });
35476
35590
  }
35477
35591
  });
35478
35592
  }).on("error", reject);
@@ -35486,7 +35600,7 @@ ${data.message || ""}`.trim();
35486
35600
  try {
35487
35601
  const http3 = await import("http");
35488
35602
  const postData = JSON.stringify(args || {});
35489
- const result = await new Promise((resolve10, reject) => {
35603
+ const result = await new Promise((resolve9, reject) => {
35490
35604
  const req = http3.request({
35491
35605
  hostname: "127.0.0.1",
35492
35606
  port: 19280,
@@ -35498,9 +35612,9 @@ ${data.message || ""}`.trim();
35498
35612
  res.on("data", (chunk) => data += chunk);
35499
35613
  res.on("end", () => {
35500
35614
  try {
35501
- resolve10(JSON.parse(data));
35615
+ resolve9(JSON.parse(data));
35502
35616
  } catch {
35503
- resolve10({ raw: data });
35617
+ resolve9({ raw: data });
35504
35618
  }
35505
35619
  });
35506
35620
  });
@@ -35514,13 +35628,13 @@ ${data.message || ""}`.trim();
35514
35628
  }
35515
35629
  }
35516
35630
  };
35517
- var os9 = __toESM2(require("os"));
35631
+ var os10 = __toESM2(require("os"));
35518
35632
  var path9 = __toESM2(require("path"));
35519
35633
  var crypto4 = __toESM2(require("crypto"));
35520
35634
  var import_chalk = __toESM2(require("chalk"));
35521
35635
  init_provider_cli_adapter();
35522
35636
  init_config();
35523
- var os8 = __toESM2(require("os"));
35637
+ var os9 = __toESM2(require("os"));
35524
35638
  var path8 = __toESM2(require("path"));
35525
35639
  var crypto3 = __toESM2(require("crypto"));
35526
35640
  var fs5 = __toESM2(require("fs"));
@@ -35609,17 +35723,60 @@ ${data.message || ""}`.trim();
35609
35723
  async onTick() {
35610
35724
  if (this.providerSessionId) return;
35611
35725
  let probedSessionId = null;
35612
- if (this.type === "opencode-cli") {
35613
- probedSessionId = this.probeOpenCodeSessionId();
35614
- } else if (this.type === "codex-cli") {
35615
- probedSessionId = this.probeCodexSessionId();
35616
- } else if (this.type === "goose-cli") {
35617
- probedSessionId = this.probeGooseSessionId();
35726
+ const probeConfig = this.provider.sessionProbe;
35727
+ if (probeConfig) {
35728
+ probedSessionId = this.probeSessionIdFromConfig(probeConfig);
35729
+ } else {
35730
+ if (this.type === "opencode-cli") {
35731
+ probedSessionId = this.probeSessionIdFromConfig({
35732
+ dbPath: "~/.local/share/opencode/opencode.db",
35733
+ query: "select id from session where directory in ({dirs}) and time_created >= ? and time_archived is null order by time_updated desc limit 1",
35734
+ timestampFormat: "unix_ms"
35735
+ });
35736
+ } else if (this.type === "codex-cli") {
35737
+ probedSessionId = this.probeSessionIdFromConfig({
35738
+ dbPath: "~/.codex/state_5.sqlite",
35739
+ query: "select id from threads where cwd in ({dirs}) and created_at >= ? and archived = 0 order by created_at desc limit 1",
35740
+ timestampFormat: "unix_s"
35741
+ });
35742
+ } else if (this.type === "goose-cli") {
35743
+ probedSessionId = this.probeSessionIdFromConfig({
35744
+ dbPath: "~/.local/share/goose/sessions/sessions.db",
35745
+ query: "select id from sessions where working_dir in ({dirs}) and created_at >= ? order by updated_at desc limit 1",
35746
+ timestampFormat: "iso"
35747
+ });
35748
+ }
35618
35749
  }
35619
35750
  if (probedSessionId) {
35620
35751
  this.promoteProviderSessionId(probedSessionId);
35621
35752
  }
35622
35753
  }
35754
+ /**
35755
+ * Generic session ID probe using declarative ProviderSessionProbe config.
35756
+ * Replaces the previously duplicated probeOpenCode/Codex/Goose functions.
35757
+ */
35758
+ probeSessionIdFromConfig(probe) {
35759
+ const resolvedDbPath = probe.dbPath.replace(/^~/, os9.homedir());
35760
+ if (!fs5.existsSync(resolvedDbPath)) return null;
35761
+ const directories = this.getProbeDirectories();
35762
+ const minCreatedAt = Math.max(0, this.startedAt - 6e4);
35763
+ const tsFormat = probe.timestampFormat || "unix_ms";
35764
+ let timestampParam;
35765
+ if (tsFormat === "unix_s") {
35766
+ timestampParam = Math.floor(minCreatedAt / 1e3);
35767
+ } else if (tsFormat === "iso") {
35768
+ timestampParam = new Date(minCreatedAt).toISOString().slice(0, 19).replace("T", " ");
35769
+ } else {
35770
+ timestampParam = minCreatedAt;
35771
+ }
35772
+ const placeholders = this.buildSqlPlaceholderList(directories.length);
35773
+ const query = probe.query.replace("{dirs}", placeholders);
35774
+ try {
35775
+ return this.querySqliteText(resolvedDbPath, query, [...directories, timestampParam]);
35776
+ } catch {
35777
+ return null;
35778
+ }
35779
+ }
35623
35780
  getState() {
35624
35781
  const adapterStatus = this.adapter.getStatus();
35625
35782
  const parsedStatus = this.adapter.getScriptParsedStatus?.() || null;
@@ -35917,34 +36074,6 @@ ${data.message || ""}`.trim();
35917
36074
  });
35918
36075
  LOG2.info("CLI", `[${this.type}] discovered provider session id: ${nextSessionId}`);
35919
36076
  }
35920
- probeOpenCodeSessionId() {
35921
- const dbPath = path8.join(os8.homedir(), ".local", "share", "opencode", "opencode.db");
35922
- if (!fs5.existsSync(dbPath)) return null;
35923
- const minCreatedAt = Math.max(0, this.startedAt - 6e4);
35924
- const directories = this.getProbeDirectories();
35925
- const query = `select id from session where directory in (${this.buildSqlPlaceholderList(directories.length)}) and time_created >= ? and time_archived is null order by time_updated desc limit 1;`;
35926
- return this.querySqliteText(dbPath, query, [...directories, minCreatedAt]);
35927
- }
35928
- probeCodexSessionId() {
35929
- const dbPath = path8.join(os8.homedir(), ".codex", "state_5.sqlite");
35930
- if (!fs5.existsSync(dbPath)) return null;
35931
- const minCreatedAt = Math.max(0, Math.floor((this.startedAt - 6e4) / 1e3));
35932
- const directories = this.getProbeDirectories();
35933
- const query = `select id from threads where cwd in (${this.buildSqlPlaceholderList(directories.length)}) and created_at >= ? and archived = 0 order by created_at desc limit 1;`;
35934
- return this.querySqliteText(dbPath, query, [...directories, minCreatedAt]);
35935
- }
35936
- probeGooseSessionId() {
35937
- const dbPath = path8.join(os8.homedir(), ".local", "share", "goose", "sessions", "sessions.db");
35938
- if (!fs5.existsSync(dbPath)) return null;
35939
- const minCreatedAtIso = new Date(Math.max(0, this.startedAt - 6e4)).toISOString().slice(0, 19).replace("T", " ");
35940
- const directories = this.getProbeDirectories();
35941
- const query = `select id from sessions where working_dir in (${this.buildSqlPlaceholderList(directories.length)}) and created_at >= ? order by updated_at desc limit 1;`;
35942
- try {
35943
- return this.querySqliteText(dbPath, query, [...directories, minCreatedAtIso]);
35944
- } catch {
35945
- return null;
35946
- }
35947
- }
35948
36077
  getProbeDirectories() {
35949
36078
  const dirs = /* @__PURE__ */ new Set();
35950
36079
  const addDir = (value) => {
@@ -36410,13 +36539,13 @@ ${data.message || ""}`.trim();
36410
36539
  }
36411
36540
  this.currentStatus = "waiting_approval";
36412
36541
  this.detectStatusTransition();
36413
- const approved = await new Promise((resolve10) => {
36414
- this.permissionResolvers.push(resolve10);
36542
+ const approved = await new Promise((resolve9) => {
36543
+ this.permissionResolvers.push(resolve9);
36415
36544
  setTimeout(() => {
36416
- const idx = this.permissionResolvers.indexOf(resolve10);
36545
+ const idx = this.permissionResolvers.indexOf(resolve9);
36417
36546
  if (idx >= 0) {
36418
36547
  this.permissionResolvers.splice(idx, 1);
36419
- resolve10(false);
36548
+ resolve9(false);
36420
36549
  }
36421
36550
  }, 3e5);
36422
36551
  });
@@ -37121,7 +37250,7 @@ ${data.message || ""}`.trim();
37121
37250
  async startSession(cliType, workingDir, cliArgs, initialModel, options) {
37122
37251
  const trimmed = (workingDir || "").trim();
37123
37252
  if (!trimmed) throw new Error("working directory required");
37124
- const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os9.homedir()) : path9.resolve(trimmed);
37253
+ const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os10.homedir()) : path9.resolve(trimmed);
37125
37254
  const normalizedType = this.providerLoader.resolveAlias(cliType);
37126
37255
  const provider = this.providerLoader.getByAlias(cliType);
37127
37256
  const key = crypto4.randomUUID();
@@ -37205,7 +37334,19 @@ ${installInfo}`
37205
37334
  return { runtimeSessionId: sessionId };
37206
37335
  }
37207
37336
  const cliInfo = await detectCLI(cliType, this.providerLoader);
37208
- if (!cliInfo) throw new Error(`${cliType} not found`);
37337
+ if (!cliInfo) {
37338
+ const installHint = provider?.install || "";
37339
+ const displayName = provider?.displayName || provider?.name || cliType;
37340
+ const spawnCmd = provider?.spawn?.command || cliType;
37341
+ throw new Error(
37342
+ `${displayName} is not installed.
37343
+ Command '${spawnCmd}' not found on PATH.
37344
+ ` + (installHint ? `
37345
+ ${installHint}
37346
+ ` : "") + `
37347
+ Run 'adhdev doctor' for detailed diagnostics.`
37348
+ );
37349
+ }
37209
37350
  console.log(colorize("yellow", ` \u26A1 Starting CLI ${cliType} in ${resolvedDir}...`));
37210
37351
  if (provider) {
37211
37352
  console.log(colorize("cyan", ` \u{1F4E6} Using provider: ${provider.name} (${provider.type})`));
@@ -37521,8 +37662,9 @@ ${installInfo}`
37521
37662
  const dir = rdir.path;
37522
37663
  if (!cliType) throw new Error("cliType required");
37523
37664
  const found = this.findAdapter(cliType, { instanceKey: args?.targetSessionId, dir });
37665
+ const prevCliArgs = found ? found.adapter.extraArgs : void 0;
37524
37666
  if (found) await this.stopSession(found.key);
37525
- await this.startSession(cliType, dir);
37667
+ await this.startSession(cliType, dir, args?.cliArgs || prevCliArgs, args?.initialModel);
37526
37668
  return { success: true, restarted: true };
37527
37669
  }
37528
37670
  case "agent_command": {
@@ -37555,11 +37697,11 @@ ${installInfo}`
37555
37697
  };
37556
37698
  var import_child_process6 = require("child_process");
37557
37699
  var net3 = __toESM2(require("net"));
37558
- var os11 = __toESM2(require("os"));
37700
+ var os12 = __toESM2(require("os"));
37559
37701
  var path11 = __toESM2(require("path"));
37560
37702
  var fs6 = __toESM2(require("fs"));
37561
37703
  var path10 = __toESM2(require("path"));
37562
- var os10 = __toESM2(require("os"));
37704
+ var os11 = __toESM2(require("os"));
37563
37705
  var chokidar = __toESM2((init_chokidar(), __toCommonJS(chokidar_exports)));
37564
37706
  init_logger();
37565
37707
  var ProviderLoader = class _ProviderLoader {
@@ -37579,7 +37721,7 @@ ${installInfo}`
37579
37721
  static META_FILE = ".meta.json";
37580
37722
  constructor(options) {
37581
37723
  this.logFn = options?.logFn || LOG2.forComponent("Provider").asLogFn();
37582
- const defaultProvidersDir = path10.join(os10.homedir(), ".adhdev", "providers");
37724
+ const defaultProvidersDir = path10.join(os11.homedir(), ".adhdev", "providers");
37583
37725
  if (options?.userDir) {
37584
37726
  this.userDir = options.userDir;
37585
37727
  this.log(`Config 'providerDir' applied: ${this.userDir}`);
@@ -38136,7 +38278,7 @@ ${installInfo}`
38136
38278
  return { updated: false };
38137
38279
  }
38138
38280
  try {
38139
- const etag = await new Promise((resolve10, reject) => {
38281
+ const etag = await new Promise((resolve9, reject) => {
38140
38282
  const options = {
38141
38283
  method: "HEAD",
38142
38284
  hostname: "github.com",
@@ -38154,7 +38296,7 @@ ${installInfo}`
38154
38296
  headers: { "User-Agent": "adhdev-launcher" },
38155
38297
  timeout: 1e4
38156
38298
  }, (res2) => {
38157
- resolve10(res2.headers.etag || res2.headers["last-modified"] || "");
38299
+ resolve9(res2.headers.etag || res2.headers["last-modified"] || "");
38158
38300
  });
38159
38301
  req2.on("error", reject);
38160
38302
  req2.on("timeout", () => {
@@ -38163,7 +38305,7 @@ ${installInfo}`
38163
38305
  });
38164
38306
  req2.end();
38165
38307
  } else {
38166
- resolve10(res.headers.etag || res.headers["last-modified"] || "");
38308
+ resolve9(res.headers.etag || res.headers["last-modified"] || "");
38167
38309
  }
38168
38310
  });
38169
38311
  req.on("error", reject);
@@ -38179,8 +38321,8 @@ ${installInfo}`
38179
38321
  return { updated: false };
38180
38322
  }
38181
38323
  this.log("Downloading latest providers from GitHub...");
38182
- const tmpTar = path10.join(os10.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
38183
- const tmpExtract = path10.join(os10.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
38324
+ const tmpTar = path10.join(os11.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
38325
+ const tmpExtract = path10.join(os11.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
38184
38326
  await this.downloadFile(_ProviderLoader.GITHUB_TARBALL_URL, tmpTar);
38185
38327
  fs6.mkdirSync(tmpExtract, { recursive: true });
38186
38328
  execSync7(`tar -xzf "${tmpTar}" -C "${tmpExtract}"`, { timeout: 3e4 });
@@ -38227,7 +38369,7 @@ ${installInfo}`
38227
38369
  downloadFile(url2, destPath) {
38228
38370
  const https = require("https");
38229
38371
  const http3 = require("http");
38230
- return new Promise((resolve10, reject) => {
38372
+ return new Promise((resolve9, reject) => {
38231
38373
  const doRequest = (reqUrl, redirectCount = 0) => {
38232
38374
  if (redirectCount > 5) {
38233
38375
  reject(new Error("Too many redirects"));
@@ -38247,7 +38389,7 @@ ${installInfo}`
38247
38389
  res.pipe(ws2);
38248
38390
  ws2.on("finish", () => {
38249
38391
  ws2.close();
38250
- resolve10();
38392
+ resolve9();
38251
38393
  });
38252
38394
  ws2.on("error", reject);
38253
38395
  });
@@ -38610,17 +38752,17 @@ ${installInfo}`
38610
38752
  throw new Error("No free port found");
38611
38753
  }
38612
38754
  function checkPortFree(port) {
38613
- return new Promise((resolve10) => {
38755
+ return new Promise((resolve9) => {
38614
38756
  const server = net3.createServer();
38615
38757
  server.unref();
38616
- server.on("error", () => resolve10(false));
38758
+ server.on("error", () => resolve9(false));
38617
38759
  server.listen(port, "127.0.0.1", () => {
38618
- server.close(() => resolve10(true));
38760
+ server.close(() => resolve9(true));
38619
38761
  });
38620
38762
  });
38621
38763
  }
38622
38764
  async function isCdpActive(port) {
38623
- return new Promise((resolve10) => {
38765
+ return new Promise((resolve9) => {
38624
38766
  const req = require("http").get(`http://127.0.0.1:${port}/json/version`, {
38625
38767
  timeout: 2e3
38626
38768
  }, (res) => {
@@ -38629,21 +38771,21 @@ ${installInfo}`
38629
38771
  res.on("end", () => {
38630
38772
  try {
38631
38773
  const info = JSON.parse(data);
38632
- resolve10(!!info["WebKit-Version"] || !!info["Browser"]);
38774
+ resolve9(!!info["WebKit-Version"] || !!info["Browser"]);
38633
38775
  } catch {
38634
- resolve10(false);
38776
+ resolve9(false);
38635
38777
  }
38636
38778
  });
38637
38779
  });
38638
- req.on("error", () => resolve10(false));
38780
+ req.on("error", () => resolve9(false));
38639
38781
  req.on("timeout", () => {
38640
38782
  req.destroy();
38641
- resolve10(false);
38783
+ resolve9(false);
38642
38784
  });
38643
38785
  });
38644
38786
  }
38645
38787
  async function killIdeProcess(ideId) {
38646
- const plat = os11.platform();
38788
+ const plat = os12.platform();
38647
38789
  const appName = getMacAppIdentifiers()[ideId];
38648
38790
  const winProcesses = getWinProcessNames()[ideId];
38649
38791
  try {
@@ -38702,7 +38844,7 @@ ${installInfo}`
38702
38844
  }
38703
38845
  }
38704
38846
  function isIdeRunning(ideId) {
38705
- const plat = os11.platform();
38847
+ const plat = os12.platform();
38706
38848
  try {
38707
38849
  if (plat === "darwin") {
38708
38850
  const appName = getMacAppIdentifiers()[ideId];
@@ -38738,7 +38880,7 @@ ${installInfo}`
38738
38880
  }
38739
38881
  }
38740
38882
  function detectCurrentWorkspace(ideId) {
38741
- const plat = os11.platform();
38883
+ const plat = os12.platform();
38742
38884
  if (plat === "darwin") {
38743
38885
  try {
38744
38886
  const appName = getMacAppIdentifiers()[ideId];
@@ -38758,7 +38900,7 @@ ${installInfo}`
38758
38900
  const appName = appNameMap[ideId];
38759
38901
  if (appName) {
38760
38902
  const storagePath = path11.join(
38761
- process.env.APPDATA || path11.join(os11.homedir(), "AppData", "Roaming"),
38903
+ process.env.APPDATA || path11.join(os12.homedir(), "AppData", "Roaming"),
38762
38904
  appName,
38763
38905
  "storage.json"
38764
38906
  );
@@ -38780,7 +38922,7 @@ ${installInfo}`
38780
38922
  return void 0;
38781
38923
  }
38782
38924
  async function launchWithCdp(options = {}) {
38783
- const platform9 = os11.platform();
38925
+ const platform9 = os12.platform();
38784
38926
  let targetIde;
38785
38927
  const ides = await detectIDEs();
38786
38928
  if (options.ideId) {
@@ -38928,8 +39070,8 @@ ${installInfo}`
38928
39070
  init_logger();
38929
39071
  var fs7 = __toESM2(require("fs"));
38930
39072
  var path12 = __toESM2(require("path"));
38931
- var os12 = __toESM2(require("os"));
38932
- var LOG_DIR2 = process.platform === "win32" ? path12.join(process.env.LOCALAPPDATA || process.env.APPDATA || path12.join(os12.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path12.join(os12.homedir(), "Library", "Logs", "adhdev") : path12.join(os12.homedir(), ".local", "share", "adhdev", "logs");
39073
+ var os13 = __toESM2(require("os"));
39074
+ var LOG_DIR2 = process.platform === "win32" ? path12.join(process.env.LOCALAPPDATA || process.env.APPDATA || path12.join(os13.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path12.join(os13.homedir(), "Library", "Logs", "adhdev") : path12.join(os13.homedir(), ".local", "share", "adhdev", "logs");
38933
39075
  var MAX_FILE_SIZE = 5 * 1024 * 1024;
38934
39076
  var MAX_DAYS = 7;
38935
39077
  try {
@@ -39060,7 +39202,7 @@ ${installInfo}`
39060
39202
  }
39061
39203
  cleanOldFiles();
39062
39204
  init_logger();
39063
- var os13 = __toESM2(require("os"));
39205
+ var os14 = __toESM2(require("os"));
39064
39206
  init_config();
39065
39207
  init_terminal_screen();
39066
39208
  init_logger();
@@ -39176,16 +39318,16 @@ ${installInfo}`
39176
39318
  version: options.version,
39177
39319
  daemonMode: options.daemonMode,
39178
39320
  machine: {
39179
- hostname: os13.hostname(),
39180
- platform: os13.platform(),
39181
- arch: os13.arch(),
39182
- cpus: os13.cpus().length,
39321
+ hostname: os14.hostname(),
39322
+ platform: os14.platform(),
39323
+ arch: os14.arch(),
39324
+ cpus: os14.cpus().length,
39183
39325
  totalMem: memSnap.totalMem,
39184
39326
  freeMem: memSnap.freeMem,
39185
39327
  availableMem: memSnap.availableMem,
39186
- loadavg: os13.loadavg(),
39187
- uptime: os13.uptime(),
39188
- release: os13.release()
39328
+ loadavg: os14.loadavg(),
39329
+ uptime: os14.uptime(),
39330
+ release: os14.release()
39189
39331
  },
39190
39332
  machineNickname: options.machineNickname ?? cfg.machineNickname ?? null,
39191
39333
  timestamp: options.timestamp ?? Date.now(),
@@ -39203,11 +39345,11 @@ ${installInfo}`
39203
39345
  var import_child_process7 = require("child_process");
39204
39346
  var import_child_process8 = require("child_process");
39205
39347
  var fs8 = __toESM2(require("fs"));
39206
- var os14 = __toESM2(require("os"));
39348
+ var os15 = __toESM2(require("os"));
39207
39349
  var path13 = __toESM2(require("path"));
39208
39350
  var UPGRADE_HELPER_ENV = "ADHDEV_DAEMON_UPGRADE_HELPER";
39209
39351
  function getUpgradeLogPath() {
39210
- const home = os14.homedir();
39352
+ const home = os15.homedir();
39211
39353
  const dir = path13.join(home, ".adhdev");
39212
39354
  fs8.mkdirSync(dir, { recursive: true });
39213
39355
  return path13.join(dir, "daemon-upgrade.log");
@@ -39240,14 +39382,14 @@ ${installInfo}`
39240
39382
  while (Date.now() - start < timeoutMs) {
39241
39383
  try {
39242
39384
  process.kill(pid, 0);
39243
- await new Promise((resolve10) => setTimeout(resolve10, 250));
39385
+ await new Promise((resolve9) => setTimeout(resolve9, 250));
39244
39386
  } catch {
39245
39387
  return;
39246
39388
  }
39247
39389
  }
39248
39390
  }
39249
39391
  function stopSessionHostProcesses(appName) {
39250
- const pidFile = path13.join(os14.homedir(), ".adhdev", `${appName}-session-host.pid`);
39392
+ const pidFile = path13.join(os15.homedir(), ".adhdev", `${appName}-session-host.pid`);
39251
39393
  try {
39252
39394
  if (fs8.existsSync(pidFile)) {
39253
39395
  const pid = Number.parseInt(fs8.readFileSync(pidFile, "utf8").trim(), 10);
@@ -39276,7 +39418,7 @@ ${installInfo}`
39276
39418
  }
39277
39419
  }
39278
39420
  function removeDaemonPidFile() {
39279
- const pidFile = path13.join(os14.homedir(), ".adhdev", "daemon.pid");
39421
+ const pidFile = path13.join(os15.homedir(), ".adhdev", "daemon.pid");
39280
39422
  try {
39281
39423
  fs8.unlinkSync(pidFile);
39282
39424
  } catch {
@@ -40766,10 +40908,10 @@ ${installInfo}`
40766
40908
  };
40767
40909
  var fs10 = __toESM2(require("fs"));
40768
40910
  var path14 = __toESM2(require("path"));
40769
- var os15 = __toESM2(require("os"));
40911
+ var os16 = __toESM2(require("os"));
40770
40912
  var import_child_process9 = require("child_process");
40771
40913
  var import_os3 = require("os");
40772
- var ARCHIVE_PATH = path14.join(os15.homedir(), ".adhdev", "version-history.json");
40914
+ var ARCHIVE_PATH = path14.join(os16.homedir(), ".adhdev", "version-history.json");
40773
40915
  var MAX_ENTRIES_PER_PROVIDER = 20;
40774
40916
  var VersionArchive = class {
40775
40917
  history = {};
@@ -40856,7 +40998,7 @@ ${installInfo}`
40856
40998
  function checkPathExists2(paths) {
40857
40999
  for (const p of paths) {
40858
41000
  if (p.includes("*")) {
40859
- const home = os15.homedir();
41001
+ const home = os16.homedir();
40860
41002
  const resolved = p.replace(/\*/g, home.split(path14.sep).pop() || "");
40861
41003
  if (fs10.existsSync(resolved)) return resolved;
40862
41004
  } else {
@@ -42443,7 +42585,7 @@ async (params) => {
42443
42585
  return { target, instance, adapter };
42444
42586
  }
42445
42587
  function sleep(ms2) {
42446
- return new Promise((resolve10) => setTimeout(resolve10, ms2));
42588
+ return new Promise((resolve9) => setTimeout(resolve9, ms2));
42447
42589
  }
42448
42590
  async function waitForCliReady(ctx, type, instanceId, timeoutMs) {
42449
42591
  const startedAt = Date.now();
@@ -43172,7 +43314,7 @@ async (params) => {
43172
43314
  }
43173
43315
  var fs13 = __toESM2(require("fs"));
43174
43316
  var path17 = __toESM2(require("path"));
43175
- var os16 = __toESM2(require("os"));
43317
+ var os17 = __toESM2(require("os"));
43176
43318
  function getAutoImplPid(ctx) {
43177
43319
  const proc = ctx.autoImplProcess;
43178
43320
  return proc && typeof proc.pid === "number" && proc.pid > 0 ? proc.pid : null;
@@ -43375,7 +43517,7 @@ async (params) => {
43375
43517
  });
43376
43518
  const referenceScripts = loadAutoImplReferenceScripts(ctx, resolvedReference);
43377
43519
  const prompt = buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domContext, referenceScripts, comment, resolvedReference, verification);
43378
- const tmpDir = path17.join(os16.tmpdir(), "adhdev-autoimpl");
43520
+ const tmpDir = path17.join(os17.tmpdir(), "adhdev-autoimpl");
43379
43521
  if (!fs13.existsSync(tmpDir)) fs13.mkdirSync(tmpDir, { recursive: true });
43380
43522
  const promptFile = path17.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
43381
43523
  fs13.writeFileSync(promptFile, prompt, "utf-8");
@@ -43529,7 +43671,7 @@ async (params) => {
43529
43671
  const interactiveFlags = ["--yolo", "--interactive", "-i"];
43530
43672
  const baseArgs = [...spawn4.args || []].filter((a) => !interactiveFlags.includes(a));
43531
43673
  let shellCmd;
43532
- const isWin = os16.platform() === "win32";
43674
+ const isWin = os17.platform() === "win32";
43533
43675
  const escapeArg = (a) => isWin ? `"${a.replace(/"/g, '""')}"` : `'${a.replace(/'/g, "'\\''")}'`;
43534
43676
  if (command === "claude") {
43535
43677
  const args = [...baseArgs, "--dangerously-skip-permissions"];
@@ -43573,7 +43715,7 @@ async (params) => {
43573
43715
  try {
43574
43716
  const pty3 = require("node-pty");
43575
43717
  ctx.log(`Auto-implement spawn (PTY): ${shellCmd}`);
43576
- const isWin2 = os16.platform() === "win32";
43718
+ const isWin2 = os17.platform() === "win32";
43577
43719
  child = pty3.spawn(isWin2 ? "cmd.exe" : process.env.SHELL || "/bin/zsh", [isWin2 ? "/c" : "-c", shellCmd], {
43578
43720
  name: "xterm-256color",
43579
43721
  cols: 120,
@@ -44609,15 +44751,15 @@ data: ${JSON.stringify(msg.data)}
44609
44751
  this.json(res, 500, { error: e.message });
44610
44752
  }
44611
44753
  });
44612
- return new Promise((resolve10, reject) => {
44754
+ return new Promise((resolve9, reject) => {
44613
44755
  this.server.listen(port, "127.0.0.1", () => {
44614
44756
  this.log(`Dev server listening on http://127.0.0.1:${port}`);
44615
- resolve10();
44757
+ resolve9();
44616
44758
  });
44617
44759
  this.server.on("error", (e) => {
44618
44760
  if (e.code === "EADDRINUSE") {
44619
44761
  this.log(`Port ${port} in use, skipping dev server`);
44620
- resolve10();
44762
+ resolve9();
44621
44763
  } else {
44622
44764
  reject(e);
44623
44765
  }
@@ -44700,20 +44842,20 @@ data: ${JSON.stringify(msg.data)}
44700
44842
  child.stderr?.on("data", (d) => {
44701
44843
  stderr += d.toString().slice(0, 2e3);
44702
44844
  });
44703
- await new Promise((resolve10) => {
44845
+ await new Promise((resolve9) => {
44704
44846
  const timer = setTimeout(() => {
44705
44847
  child.kill();
44706
- resolve10();
44848
+ resolve9();
44707
44849
  }, 3e3);
44708
44850
  child.on("exit", () => {
44709
44851
  clearTimeout(timer);
44710
- resolve10();
44852
+ resolve9();
44711
44853
  });
44712
44854
  child.stdout?.once("data", () => {
44713
44855
  setTimeout(() => {
44714
44856
  child.kill();
44715
44857
  clearTimeout(timer);
44716
- resolve10();
44858
+ resolve9();
44717
44859
  }, 500);
44718
44860
  });
44719
44861
  });
@@ -45222,14 +45364,14 @@ data: ${JSON.stringify(msg.data)}
45222
45364
  child.stderr?.on("data", (d) => {
45223
45365
  stderr += d.toString();
45224
45366
  });
45225
- await new Promise((resolve10) => {
45367
+ await new Promise((resolve9) => {
45226
45368
  const timer = setTimeout(() => {
45227
45369
  child.kill();
45228
- resolve10();
45370
+ resolve9();
45229
45371
  }, timeout);
45230
45372
  child.on("exit", () => {
45231
45373
  clearTimeout(timer);
45232
- resolve10();
45374
+ resolve9();
45233
45375
  });
45234
45376
  });
45235
45377
  const elapsed = Date.now() - start;
@@ -45904,14 +46046,14 @@ data: ${JSON.stringify(msg.data)}
45904
46046
  res.end(JSON.stringify(data, null, 2));
45905
46047
  }
45906
46048
  async readBody(req) {
45907
- return new Promise((resolve10) => {
46049
+ return new Promise((resolve9) => {
45908
46050
  let body = "";
45909
46051
  req.on("data", (chunk) => body += chunk);
45910
46052
  req.on("end", () => {
45911
46053
  try {
45912
- resolve10(JSON.parse(body));
46054
+ resolve9(JSON.parse(body));
45913
46055
  } catch {
45914
- resolve10({});
46056
+ resolve9({});
45915
46057
  }
45916
46058
  });
45917
46059
  });
@@ -45980,12 +46122,12 @@ data: ${JSON.stringify(msg.data)}
45980
46122
  };
45981
46123
  init_provider_cli_adapter();
45982
46124
  init_pty_transport();
45983
- var import_session_host_core2 = require_dist();
46125
+ var import_session_host_core22 = require_dist();
45984
46126
  init_logger();
45985
46127
  var SessionHostRuntimeTransport = class {
45986
46128
  constructor(options) {
45987
46129
  this.options = options;
45988
- this.client = new import_session_host_core2.SessionHostClient({
46130
+ this.client = new import_session_host_core22.SessionHostClient({
45989
46131
  endpoint: options.endpoint,
45990
46132
  appName: options.appName
45991
46133
  });
@@ -46352,11 +46494,11 @@ data: ${JSON.stringify(msg.data)}
46352
46494
  });
46353
46495
  }
46354
46496
  };
46355
- var import_session_host_core22 = require_dist();
46497
+ var import_session_host_core3 = require_dist();
46356
46498
  var STARTUP_TIMEOUT_MS = 8e3;
46357
46499
  var STARTUP_POLL_MS = 200;
46358
46500
  async function canConnect(endpoint) {
46359
- const client = new import_session_host_core22.SessionHostClient({ endpoint });
46501
+ const client = new import_session_host_core3.SessionHostClient({ endpoint });
46360
46502
  try {
46361
46503
  await client.connect();
46362
46504
  await client.close();
@@ -46369,19 +46511,19 @@ data: ${JSON.stringify(msg.data)}
46369
46511
  const deadline = Date.now() + timeoutMs;
46370
46512
  while (Date.now() < deadline) {
46371
46513
  if (await canConnect(endpoint)) return;
46372
- await new Promise((resolve10) => setTimeout(resolve10, STARTUP_POLL_MS));
46514
+ await new Promise((resolve9) => setTimeout(resolve9, STARTUP_POLL_MS));
46373
46515
  }
46374
46516
  throw new Error(`Session host did not become ready within ${timeoutMs}ms`);
46375
46517
  }
46376
46518
  async function ensureSessionHostReady2(options) {
46377
- const endpoint = (0, import_session_host_core22.getDefaultSessionHostEndpoint)(options.appName || "adhdev");
46519
+ const endpoint = (0, import_session_host_core3.getDefaultSessionHostEndpoint)(options.appName || "adhdev");
46378
46520
  if (await canConnect(endpoint)) return endpoint;
46379
46521
  options.spawnHost();
46380
46522
  await waitForReady(endpoint, options.timeoutMs);
46381
46523
  return endpoint;
46382
46524
  }
46383
46525
  async function listHostedCliRuntimes2(endpoint) {
46384
- const client = new import_session_host_core22.SessionHostClient({ endpoint });
46526
+ const client = new import_session_host_core3.SessionHostClient({ endpoint });
46385
46527
  try {
46386
46528
  const response = await client.request({
46387
46529
  type: "list_sessions",
@@ -46523,10 +46665,10 @@ data: ${JSON.stringify(msg.data)}
46523
46665
  const buffer = Buffer.from(await res.arrayBuffer());
46524
46666
  const fs15 = await import("fs");
46525
46667
  fs15.writeFileSync(vsixPath, buffer);
46526
- return new Promise((resolve10) => {
46668
+ return new Promise((resolve9) => {
46527
46669
  const cmd = `"${ide.cliCommand}" --install-extension "${vsixPath}" --force`;
46528
46670
  (0, import_child_process10.exec)(cmd, { timeout: 6e4 }, (error48, _stdout, stderr) => {
46529
- resolve10({
46671
+ resolve9({
46530
46672
  extensionId: extension.id,
46531
46673
  marketplaceId: extension.marketplaceId,
46532
46674
  success: !error48,
@@ -46539,11 +46681,11 @@ data: ${JSON.stringify(msg.data)}
46539
46681
  } catch (e) {
46540
46682
  }
46541
46683
  }
46542
- return new Promise((resolve10) => {
46684
+ return new Promise((resolve9) => {
46543
46685
  const cmd = `"${ide.cliCommand}" --install-extension ${extension.marketplaceId} --force`;
46544
46686
  (0, import_child_process10.exec)(cmd, { timeout: 6e4 }, (error48, stdout, stderr) => {
46545
46687
  if (error48) {
46546
- resolve10({
46688
+ resolve9({
46547
46689
  extensionId: extension.id,
46548
46690
  marketplaceId: extension.marketplaceId,
46549
46691
  success: false,
@@ -46551,7 +46693,7 @@ data: ${JSON.stringify(msg.data)}
46551
46693
  error: stderr || error48.message
46552
46694
  });
46553
46695
  } else {
46554
- resolve10({
46696
+ resolve9({
46555
46697
  extensionId: extension.id,
46556
46698
  marketplaceId: extension.marketplaceId,
46557
46699
  success: true,
@@ -47029,6 +47171,13 @@ var SessionHostClient = class {
47029
47171
  }
47030
47172
  async connect() {
47031
47173
  if (this.socket && !this.socket.destroyed) return;
47174
+ if (this.socket) {
47175
+ try {
47176
+ this.socket.destroy();
47177
+ } catch {
47178
+ }
47179
+ this.socket = null;
47180
+ }
47032
47181
  const socket = net.createConnection(this.endpoint.path);
47033
47182
  this.socket = socket;
47034
47183
  socket.on("data", createLineParser((envelope) => {
@@ -47049,9 +47198,16 @@ var SessionHostClient = class {
47049
47198
  waiter.reject(error48);
47050
47199
  }
47051
47200
  this.requestWaiters.clear();
47201
+ if (this.socket === socket) {
47202
+ this.socket = null;
47203
+ }
47204
+ try {
47205
+ socket.destroy();
47206
+ } catch {
47207
+ }
47052
47208
  });
47053
- await new Promise((resolve4, reject) => {
47054
- socket.once("connect", () => resolve4());
47209
+ await new Promise((resolve22, reject) => {
47210
+ socket.once("connect", () => resolve22());
47055
47211
  socket.once("error", reject);
47056
47212
  });
47057
47213
  }
@@ -47070,7 +47226,7 @@ var SessionHostClient = class {
47070
47226
  requestId,
47071
47227
  request
47072
47228
  };
47073
- const response = await new Promise((resolve4, reject) => {
47229
+ const response = await new Promise((resolve22, reject) => {
47074
47230
  const timeout = setTimeout(() => {
47075
47231
  this.requestWaiters.delete(requestId);
47076
47232
  reject(new Error(`Session host request timed out after 30s (${request.type})`));
@@ -47078,7 +47234,7 @@ var SessionHostClient = class {
47078
47234
  this.requestWaiters.set(requestId, {
47079
47235
  resolve: (value) => {
47080
47236
  clearTimeout(timeout);
47081
- resolve4(value);
47237
+ resolve22(value);
47082
47238
  },
47083
47239
  reject: (error48) => {
47084
47240
  clearTimeout(timeout);
@@ -47097,12 +47253,12 @@ var SessionHostClient = class {
47097
47253
  waiter.reject(new Error("Session host client closed"));
47098
47254
  }
47099
47255
  this.requestWaiters.clear();
47100
- await new Promise((resolve4) => {
47256
+ await new Promise((resolve22) => {
47101
47257
  let settled = false;
47102
47258
  const done = () => {
47103
47259
  if (settled) return;
47104
47260
  settled = true;
47105
- resolve4();
47261
+ resolve22();
47106
47262
  };
47107
47263
  socket.once("close", done);
47108
47264
  socket.end();