@adhdev/daemon-standalone 0.8.12 → 0.8.14

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) {
@@ -27022,691 +27607,168 @@ var init_chokidar = __esm({
27022
27607
  if (err && err.code !== "ENOENT")
27023
27608
  awfEmit(err);
27024
27609
  return;
27025
- }
27026
- const now2 = Number(/* @__PURE__ */ new Date());
27027
- if (prevStat && curStat.size !== prevStat.size) {
27028
- writes.get(path5).lastChange = now2;
27029
- }
27030
- 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
27457
- });
27458
- }
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}`);
27610
+ }
27611
+ const now2 = Number(/* @__PURE__ */ new Date());
27612
+ if (prevStat && curStat.size !== prevStat.size) {
27613
+ writes.get(path5).lastChange = now2;
27614
+ }
27615
+ const pw = writes.get(path5);
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
+ }
27623
+ });
27478
27624
  }
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();
@@ -29011,13 +29061,16 @@ var require_dist2 = __commonJS({
29011
29061
  let shellArgs;
29012
29062
  const useShellUnix = !isWin && (!!spawnConfig.shell || !path7.isAbsolute(binaryPath) || isScriptBinary(binaryPath) || !looksLikeMachOOrElf(binaryPath));
29013
29063
  const isCmdShim = isWin && /\.(cmd|bat)$/i.test(binaryPath);
29014
- const useShell = isWin ? !!spawnConfig.shell || isCmdShim : useShellUnix;
29064
+ const useShellWin = isCmdShim || !path7.isAbsolute(binaryPath) || isScriptBinary(binaryPath);
29065
+ const useShell = isWin ? useShellWin : useShellUnix;
29015
29066
  if (useShell) {
29016
29067
  if (!spawnConfig.shell && !isWin) {
29017
29068
  LOG2.info("CLI", `[${this.cliType}] Using login shell (script shim or non-native binary)`);
29018
29069
  }
29019
29070
  if (isCmdShim) {
29020
29071
  LOG2.info("CLI", `[${this.cliType}] Using cmd.exe shell for .cmd/.bat shim: ${binaryPath}`);
29072
+ } else if (isWin) {
29073
+ LOG2.info("CLI", `[${this.cliType}] Using cmd.exe shell on Windows: ${binaryPath}`);
29021
29074
  }
29022
29075
  shellCmd = isWin ? "cmd.exe" : process.env.SHELL || "/bin/zsh";
29023
29076
  if (isWin) {
@@ -29027,6 +29080,9 @@ var require_dist2 = __commonJS({
29027
29080
  shellArgs = ["-l", "-c", fullCmd];
29028
29081
  }
29029
29082
  } else {
29083
+ if (isWin && spawnConfig.shell) {
29084
+ LOG2.info("CLI", `[${this.cliType}] Spawning Windows binary directly without cmd.exe: ${binaryPath}`);
29085
+ }
29030
29086
  shellCmd = binaryPath;
29031
29087
  shellArgs = allArgs;
29032
29088
  }
@@ -29055,6 +29111,12 @@ var require_dist2 = __commonJS({
29055
29111
  shellArgs = ["-l", "-c", fullCmd];
29056
29112
  this.ptyProcess = this.transportFactory.spawn(shellCmd, shellArgs, ptyOpts);
29057
29113
  } else {
29114
+ if (isWin) {
29115
+ 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})` : "";
29116
+ if (hint) {
29117
+ throw new Error(`Failed to spawn CLI${hint}: ${msg}`);
29118
+ }
29119
+ }
29058
29120
  throw err;
29059
29121
  }
29060
29122
  }
@@ -29229,7 +29291,7 @@ var require_dist2 = __commonJS({
29229
29291
  `[${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
29292
  );
29231
29293
  }
29232
- await new Promise((resolve10) => setTimeout(resolve10, 50));
29294
+ await new Promise((resolve9) => setTimeout(resolve9, 50));
29233
29295
  }
29234
29296
  const finalScreenText = this.terminalScreen.getText() || "";
29235
29297
  LOG2.warn(
@@ -29616,7 +29678,7 @@ ${data.message || ""}`.trim();
29616
29678
  if (this.startupParseGate) {
29617
29679
  const deadline = Date.now() + 1e4;
29618
29680
  while (this.startupParseGate && Date.now() < deadline) {
29619
- await new Promise((resolve10) => setTimeout(resolve10, 50));
29681
+ await new Promise((resolve9) => setTimeout(resolve9, 50));
29620
29682
  }
29621
29683
  }
29622
29684
  if (!this.ready) throw new Error(`${this.cliName} not ready (status: ${this.currentStatus})`);
@@ -29807,7 +29869,8 @@ ${data.message || ""}`.trim();
29807
29869
  const payload = stopCommand.endsWith("\r") || stopCommand.endsWith("\n") ? stopCommand : `${stopCommand}${this.sendKey}`;
29808
29870
  this.ptyProcess.write(payload);
29809
29871
  };
29810
- if (wasProcessing) setTimeout(writeCommand, 250);
29872
+ const interruptGraceMs = typeof resume.interruptGraceMs === "number" ? Math.max(100, resume.interruptGraceMs) : 500;
29873
+ if (wasProcessing) setTimeout(writeCommand, interruptGraceMs);
29811
29874
  else writeCommand();
29812
29875
  } else {
29813
29876
  this.ptyProcess.write("");
@@ -29823,17 +29886,17 @@ ${data.message || ""}`.trim();
29823
29886
  }
29824
29887
  }
29825
29888
  waitForStopped(timeoutMs) {
29826
- return new Promise((resolve10) => {
29889
+ return new Promise((resolve9) => {
29827
29890
  const startedAt = Date.now();
29828
29891
  const timer = setInterval(() => {
29829
29892
  if (!this.ptyProcess || this.currentStatus === "stopped") {
29830
29893
  clearInterval(timer);
29831
- resolve10(true);
29894
+ resolve9(true);
29832
29895
  return;
29833
29896
  }
29834
29897
  if (Date.now() - startedAt >= timeoutMs) {
29835
29898
  clearInterval(timer);
29836
- resolve10(false);
29899
+ resolve9(false);
29837
29900
  }
29838
29901
  }, 100);
29839
29902
  });
@@ -29852,6 +29915,18 @@ ${data.message || ""}`.trim();
29852
29915
  clearTimeout(this.submitRetryTimer);
29853
29916
  this.submitRetryTimer = null;
29854
29917
  }
29918
+ if (this.responseTimeout) {
29919
+ clearTimeout(this.responseTimeout);
29920
+ this.responseTimeout = null;
29921
+ }
29922
+ if (this.idleTimeout) {
29923
+ clearTimeout(this.idleTimeout);
29924
+ this.idleTimeout = null;
29925
+ }
29926
+ if (this.pendingScriptStatusTimer) {
29927
+ clearTimeout(this.pendingScriptStatusTimer);
29928
+ this.pendingScriptStatusTimer = null;
29929
+ }
29855
29930
  if (this.pendingOutputParseTimer) {
29856
29931
  clearTimeout(this.pendingOutputParseTimer);
29857
29932
  this.pendingOutputParseTimer = null;
@@ -29893,6 +29968,18 @@ ${data.message || ""}`.trim();
29893
29968
  clearTimeout(this.submitRetryTimer);
29894
29969
  this.submitRetryTimer = null;
29895
29970
  }
29971
+ if (this.responseTimeout) {
29972
+ clearTimeout(this.responseTimeout);
29973
+ this.responseTimeout = null;
29974
+ }
29975
+ if (this.idleTimeout) {
29976
+ clearTimeout(this.idleTimeout);
29977
+ this.idleTimeout = null;
29978
+ }
29979
+ if (this.pendingScriptStatusTimer) {
29980
+ clearTimeout(this.pendingScriptStatusTimer);
29981
+ this.pendingScriptStatusTimer = null;
29982
+ }
29896
29983
  if (this.pendingOutputParseTimer) {
29897
29984
  clearTimeout(this.pendingOutputParseTimer);
29898
29985
  this.pendingOutputParseTimer = null;
@@ -30476,20 +30563,20 @@ ${data.message || ""}`.trim();
30476
30563
  return null;
30477
30564
  }
30478
30565
  async function detectIDEs() {
30479
- const os17 = (0, import_os22.platform)();
30566
+ const os18 = (0, import_os22.platform)();
30480
30567
  const results = [];
30481
30568
  for (const def of getMergedDefinitions()) {
30482
30569
  const cliPath = findCliCommand(def.cli);
30483
- const appPath = checkPathExists(def.paths[os17] || []);
30570
+ const appPath = checkPathExists(def.paths[os18] || []);
30484
30571
  const installed = !!(cliPath || appPath);
30485
30572
  let resolvedCli = cliPath;
30486
- if (!resolvedCli && appPath && os17 === "darwin") {
30573
+ if (!resolvedCli && appPath && os18 === "darwin") {
30487
30574
  const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
30488
30575
  if ((0, import_fs2.existsSync)(bundledCli)) resolvedCli = bundledCli;
30489
30576
  }
30490
- if (!resolvedCli && appPath && os17 === "win32") {
30491
- const { dirname: dirname7 } = await import("path");
30492
- const appDir = dirname7(appPath);
30577
+ if (!resolvedCli && appPath && os18 === "win32") {
30578
+ const { dirname: dirname6 } = await import("path");
30579
+ const appDir = dirname6(appPath);
30493
30580
  const candidates = [
30494
30581
  `${appDir}\\\\bin\\\\${def.cli}.cmd`,
30495
30582
  `${appDir}\\\\bin\\\\${def.cli}`,
@@ -30525,15 +30612,15 @@ ${data.message || ""}`.trim();
30525
30612
  return match ? match[1] : raw.split("\n")[0].slice(0, 100);
30526
30613
  }
30527
30614
  function execAsync(cmd, timeoutMs = 5e3) {
30528
- return new Promise((resolve10) => {
30615
+ return new Promise((resolve9) => {
30529
30616
  const child = (0, import_child_process22.exec)(cmd, { encoding: "utf-8", timeout: timeoutMs }, (err, stdout) => {
30530
30617
  if (err || !stdout?.trim()) {
30531
- resolve10(null);
30618
+ resolve9(null);
30532
30619
  } else {
30533
- resolve10(stdout.trim());
30620
+ resolve9(stdout.trim());
30534
30621
  }
30535
30622
  });
30536
- child.on("error", () => resolve10(null));
30623
+ child.on("error", () => resolve9(null));
30537
30624
  });
30538
30625
  }
30539
30626
  async function detectCLIs(providerLoader) {
@@ -30573,6 +30660,39 @@ ${data.message || ""}`.trim();
30573
30660
  }
30574
30661
  async function detectCLI(cliId, providerLoader) {
30575
30662
  const resolvedId = providerLoader ? providerLoader.resolveAlias(cliId) : cliId;
30663
+ if (providerLoader) {
30664
+ const cliList = providerLoader.getCliDetectionList();
30665
+ const target = cliList.find((c) => c.id === resolvedId);
30666
+ if (target) {
30667
+ const platform9 = os22.platform();
30668
+ const whichCmd = platform9 === "win32" ? "where" : "which";
30669
+ try {
30670
+ const pathResult = await execAsync(`${whichCmd} ${target.command}`);
30671
+ if (!pathResult) return null;
30672
+ const firstPath = pathResult.split("\n")[0];
30673
+ let version2;
30674
+ try {
30675
+ const versionCommands = [
30676
+ target.versionCommand,
30677
+ `${target.command} --version`,
30678
+ `${target.command} -V`,
30679
+ `${target.command} -v`
30680
+ ].filter((v2) => !!v2);
30681
+ for (const versionCommand of versionCommands) {
30682
+ const versionResult = await execAsync(versionCommand, 3e3);
30683
+ if (versionResult) {
30684
+ version2 = parseVersion(versionResult);
30685
+ break;
30686
+ }
30687
+ }
30688
+ } catch {
30689
+ }
30690
+ return { ...target, installed: true, version: version2, path: firstPath };
30691
+ } catch {
30692
+ return null;
30693
+ }
30694
+ }
30695
+ }
30576
30696
  const all = await detectCLIs(providerLoader);
30577
30697
  return all.find((c) => c.id === resolvedId && c.installed) || null;
30578
30698
  }
@@ -30696,7 +30816,7 @@ ${data.message || ""}`.trim();
30696
30816
  * Returns multiple entries if multiple IDE windows are open on same port
30697
30817
  */
30698
30818
  static listAllTargets(port) {
30699
- return new Promise((resolve10) => {
30819
+ return new Promise((resolve9) => {
30700
30820
  const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
30701
30821
  let data = "";
30702
30822
  res.on("data", (chunk) => data += chunk.toString());
@@ -30712,16 +30832,16 @@ ${data.message || ""}`.trim();
30712
30832
  (t) => !isNonMain(t.title || "") && t.url?.includes("workbench.html") && !t.url?.includes("agent")
30713
30833
  );
30714
30834
  const fallbackPages = pages.filter((t) => !isNonMain(t.title || ""));
30715
- resolve10(mainPages.length > 0 ? mainPages : fallbackPages);
30835
+ resolve9(mainPages.length > 0 ? mainPages : fallbackPages);
30716
30836
  } catch {
30717
- resolve10([]);
30837
+ resolve9([]);
30718
30838
  }
30719
30839
  });
30720
30840
  });
30721
- req.on("error", () => resolve10([]));
30841
+ req.on("error", () => resolve9([]));
30722
30842
  req.setTimeout(2e3, () => {
30723
30843
  req.destroy();
30724
- resolve10([]);
30844
+ resolve9([]);
30725
30845
  });
30726
30846
  });
30727
30847
  }
@@ -30761,7 +30881,7 @@ ${data.message || ""}`.trim();
30761
30881
  }
30762
30882
  }
30763
30883
  findTargetOnPort(port) {
30764
- return new Promise((resolve10) => {
30884
+ return new Promise((resolve9) => {
30765
30885
  const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
30766
30886
  let data = "";
30767
30887
  res.on("data", (chunk) => data += chunk.toString());
@@ -30772,7 +30892,7 @@ ${data.message || ""}`.trim();
30772
30892
  (t) => (t.type === "page" || t.type === "browser" || t.type === "Page") && t.webSocketDebuggerUrl
30773
30893
  );
30774
30894
  if (pages.length === 0) {
30775
- resolve10(targets.find((t) => t.webSocketDebuggerUrl) || null);
30895
+ resolve9(targets.find((t) => t.webSocketDebuggerUrl) || null);
30776
30896
  return;
30777
30897
  }
30778
30898
  const mainPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
@@ -30782,24 +30902,24 @@ ${data.message || ""}`.trim();
30782
30902
  const specific = list.find((t) => t.id === this._targetId);
30783
30903
  if (specific) {
30784
30904
  this._pageTitle = specific.title || "";
30785
- resolve10(specific);
30905
+ resolve9(specific);
30786
30906
  } else {
30787
30907
  this.log(`[CDP] Target ${this._targetId} not found in page list`);
30788
- resolve10(null);
30908
+ resolve9(null);
30789
30909
  }
30790
30910
  return;
30791
30911
  }
30792
30912
  this._pageTitle = list[0]?.title || "";
30793
- resolve10(list[0]);
30913
+ resolve9(list[0]);
30794
30914
  } catch {
30795
- resolve10(null);
30915
+ resolve9(null);
30796
30916
  }
30797
30917
  });
30798
30918
  });
30799
- req.on("error", () => resolve10(null));
30919
+ req.on("error", () => resolve9(null));
30800
30920
  req.setTimeout(2e3, () => {
30801
30921
  req.destroy();
30802
- resolve10(null);
30922
+ resolve9(null);
30803
30923
  });
30804
30924
  });
30805
30925
  }
@@ -30810,7 +30930,7 @@ ${data.message || ""}`.trim();
30810
30930
  this.extensionProviders = providers;
30811
30931
  }
30812
30932
  connectToTarget(wsUrl) {
30813
- return new Promise((resolve10) => {
30933
+ return new Promise((resolve9) => {
30814
30934
  this.ws = new import_ws2.default(wsUrl);
30815
30935
  this.ws.on("open", async () => {
30816
30936
  this._connected = true;
@@ -30820,17 +30940,17 @@ ${data.message || ""}`.trim();
30820
30940
  }
30821
30941
  this.connectBrowserWs().catch(() => {
30822
30942
  });
30823
- resolve10(true);
30943
+ resolve9(true);
30824
30944
  });
30825
30945
  this.ws.on("message", (data) => {
30826
30946
  try {
30827
30947
  const msg = JSON.parse(data.toString());
30828
30948
  if (msg.id && this.pending.has(msg.id)) {
30829
- const { resolve: resolve11, reject } = this.pending.get(msg.id);
30949
+ const { resolve: resolve10, reject } = this.pending.get(msg.id);
30830
30950
  this.pending.delete(msg.id);
30831
30951
  this.failureCount = 0;
30832
30952
  if (msg.error) reject(new Error(msg.error.message));
30833
- else resolve11(msg.result);
30953
+ else resolve10(msg.result);
30834
30954
  } else if (msg.method === "Runtime.executionContextCreated") {
30835
30955
  this.contexts.add(msg.params.context.id);
30836
30956
  } else if (msg.method === "Runtime.executionContextDestroyed") {
@@ -30853,7 +30973,7 @@ ${data.message || ""}`.trim();
30853
30973
  this.ws.on("error", (err) => {
30854
30974
  this.log(`[CDP] WebSocket error: ${err.message}`);
30855
30975
  this._connected = false;
30856
- resolve10(false);
30976
+ resolve9(false);
30857
30977
  });
30858
30978
  });
30859
30979
  }
@@ -30867,7 +30987,7 @@ ${data.message || ""}`.trim();
30867
30987
  return;
30868
30988
  }
30869
30989
  this.log(`[CDP] Connecting browser WS for target discovery...`);
30870
- await new Promise((resolve10, reject) => {
30990
+ await new Promise((resolve9, reject) => {
30871
30991
  this.browserWs = new import_ws2.default(browserWsUrl);
30872
30992
  this.browserWs.on("open", async () => {
30873
30993
  this._browserConnected = true;
@@ -30877,16 +30997,16 @@ ${data.message || ""}`.trim();
30877
30997
  } catch (e) {
30878
30998
  this.log(`[CDP] setDiscoverTargets failed: ${e.message}`);
30879
30999
  }
30880
- resolve10();
31000
+ resolve9();
30881
31001
  });
30882
31002
  this.browserWs.on("message", (data) => {
30883
31003
  try {
30884
31004
  const msg = JSON.parse(data.toString());
30885
31005
  if (msg.id && this.browserPending.has(msg.id)) {
30886
- const { resolve: resolve11, reject: reject2 } = this.browserPending.get(msg.id);
31006
+ const { resolve: resolve10, reject: reject2 } = this.browserPending.get(msg.id);
30887
31007
  this.browserPending.delete(msg.id);
30888
31008
  if (msg.error) reject2(new Error(msg.error.message));
30889
- else resolve11(msg.result);
31009
+ else resolve10(msg.result);
30890
31010
  }
30891
31011
  } catch {
30892
31012
  }
@@ -30906,31 +31026,31 @@ ${data.message || ""}`.trim();
30906
31026
  }
30907
31027
  }
30908
31028
  getBrowserWsUrl() {
30909
- return new Promise((resolve10) => {
31029
+ return new Promise((resolve9) => {
30910
31030
  const req = http.get(`http://127.0.0.1:${this.port}/json/version`, (res) => {
30911
31031
  let data = "";
30912
31032
  res.on("data", (chunk) => data += chunk.toString());
30913
31033
  res.on("end", () => {
30914
31034
  try {
30915
31035
  const info = JSON.parse(data);
30916
- resolve10(info.webSocketDebuggerUrl || null);
31036
+ resolve9(info.webSocketDebuggerUrl || null);
30917
31037
  } catch {
30918
- resolve10(null);
31038
+ resolve9(null);
30919
31039
  }
30920
31040
  });
30921
31041
  });
30922
- req.on("error", () => resolve10(null));
31042
+ req.on("error", () => resolve9(null));
30923
31043
  req.setTimeout(3e3, () => {
30924
31044
  req.destroy();
30925
- resolve10(null);
31045
+ resolve9(null);
30926
31046
  });
30927
31047
  });
30928
31048
  }
30929
31049
  sendBrowser(method, params = {}, timeoutMs = 15e3) {
30930
- return new Promise((resolve10, reject) => {
31050
+ return new Promise((resolve9, reject) => {
30931
31051
  if (!this.browserWs || !this._browserConnected) return reject(new Error("Browser WS not connected"));
30932
31052
  const id = this.browserMsgId++;
30933
- this.browserPending.set(id, { resolve: resolve10, reject });
31053
+ this.browserPending.set(id, { resolve: resolve9, reject });
30934
31054
  this.browserWs.send(JSON.stringify({ id, method, params }));
30935
31055
  setTimeout(() => {
30936
31056
  if (this.browserPending.has(id)) {
@@ -30970,11 +31090,11 @@ ${data.message || ""}`.trim();
30970
31090
  }
30971
31091
  // ─── CDP Protocol ────────────────────────────────────────
30972
31092
  sendInternal(method, params = {}, timeoutMs = 15e3) {
30973
- return new Promise((resolve10, reject) => {
31093
+ return new Promise((resolve9, reject) => {
30974
31094
  if (!this.ws || !this._connected) return reject(new Error("CDP not connected"));
30975
31095
  if (this.ws.readyState !== import_ws2.default.OPEN) return reject(new Error("WebSocket not open"));
30976
31096
  const id = this.msgId++;
30977
- this.pending.set(id, { resolve: resolve10, reject });
31097
+ this.pending.set(id, { resolve: resolve9, reject });
30978
31098
  this.ws.send(JSON.stringify({ id, method, params }));
30979
31099
  setTimeout(() => {
30980
31100
  if (this.pending.has(id)) {
@@ -31223,7 +31343,7 @@ ${data.message || ""}`.trim();
31223
31343
  const browserWs = this.browserWs;
31224
31344
  let msgId = this.browserMsgId;
31225
31345
  const sendWs = (method, params = {}, sessionId) => {
31226
- return new Promise((resolve10, reject) => {
31346
+ return new Promise((resolve9, reject) => {
31227
31347
  const mid = msgId++;
31228
31348
  this.browserMsgId = msgId;
31229
31349
  const handler = (raw) => {
@@ -31232,7 +31352,7 @@ ${data.message || ""}`.trim();
31232
31352
  if (msg.id === mid) {
31233
31353
  browserWs.removeListener("message", handler);
31234
31354
  if (msg.error) reject(new Error(msg.error.message || JSON.stringify(msg.error)));
31235
- else resolve10(msg.result);
31355
+ else resolve9(msg.result);
31236
31356
  }
31237
31357
  } catch {
31238
31358
  }
@@ -31423,14 +31543,14 @@ ${data.message || ""}`.trim();
31423
31543
  if (!ws2 || ws2.readyState !== import_ws2.default.OPEN) {
31424
31544
  throw new Error("CDP not connected");
31425
31545
  }
31426
- return new Promise((resolve10, reject) => {
31546
+ return new Promise((resolve9, reject) => {
31427
31547
  const id = getNextId();
31428
31548
  pendingMap.set(id, {
31429
31549
  resolve: (result) => {
31430
31550
  if (result?.result?.subtype === "error") {
31431
31551
  reject(new Error(result.result.description));
31432
31552
  } else {
31433
- resolve10(result?.result?.value);
31553
+ resolve9(result?.result?.value);
31434
31554
  }
31435
31555
  },
31436
31556
  reject
@@ -31462,10 +31582,10 @@ ${data.message || ""}`.trim();
31462
31582
  throw new Error("CDP not connected");
31463
31583
  }
31464
31584
  const sendViaSession = (method, params = {}) => {
31465
- return new Promise((resolve10, reject) => {
31585
+ return new Promise((resolve9, reject) => {
31466
31586
  const pendingMap = this._browserConnected ? this.browserPending : this.pending;
31467
31587
  const id = this._browserConnected ? this.browserMsgId++ : this.msgId++;
31468
- pendingMap.set(id, { resolve: resolve10, reject });
31588
+ pendingMap.set(id, { resolve: resolve9, reject });
31469
31589
  ws2.send(JSON.stringify({ id, sessionId, method, params }));
31470
31590
  setTimeout(() => {
31471
31591
  if (pendingMap.has(id)) {
@@ -34965,8 +35085,24 @@ ${data.message || ""}`.trim();
34965
35085
  return { success: false, error: "Failed to save setting" };
34966
35086
  }
34967
35087
  init_config();
35088
+ function loadWorkspaceConfig() {
35089
+ try {
35090
+ return loadConfig2();
35091
+ } catch (e) {
35092
+ return { error: `Could not load config: ${e?.message || "unknown error"}` };
35093
+ }
35094
+ }
35095
+ function persistWorkspaceConfig(config2) {
35096
+ try {
35097
+ saveConfig(config2);
35098
+ return { ok: true };
35099
+ } catch (e) {
35100
+ return { error: `Could not save config: ${e?.message || "unknown error"}` };
35101
+ }
35102
+ }
34968
35103
  function handleWorkspaceList() {
34969
- const config2 = loadConfig2();
35104
+ const config2 = loadWorkspaceConfig();
35105
+ if ("error" in config2) return { success: false, error: config2.error };
34970
35106
  const state = getWorkspaceState2(config2);
34971
35107
  return {
34972
35108
  success: true,
@@ -34980,31 +35116,37 @@ ${data.message || ""}`.trim();
34980
35116
  const label = (args?.label || "").trim() || void 0;
34981
35117
  const createIfMissing = args?.createIfMissing === true;
34982
35118
  if (!rawPath) return { success: false, error: "path required" };
34983
- const config2 = loadConfig2();
35119
+ const config2 = loadWorkspaceConfig();
35120
+ if ("error" in config2) return { success: false, error: config2.error };
34984
35121
  const result = addWorkspaceEntry(config2, rawPath, label, { createIfMissing });
34985
35122
  if ("error" in result) return { success: false, error: result.error };
34986
- saveConfig(result.config);
35123
+ const saveResult = persistWorkspaceConfig(result.config);
35124
+ if ("error" in saveResult) return { success: false, error: saveResult.error };
34987
35125
  const state = getWorkspaceState2(result.config);
34988
35126
  return { success: true, entry: result.entry, ...state };
34989
35127
  }
34990
35128
  function handleWorkspaceRemove(args) {
34991
35129
  const id = (args?.id || "").trim();
34992
35130
  if (!id) return { success: false, error: "id required" };
34993
- const config2 = loadConfig2();
35131
+ const config2 = loadWorkspaceConfig();
35132
+ if ("error" in config2) return { success: false, error: config2.error };
34994
35133
  const removed = (config2.workspaces || []).find((w) => w.id === id);
34995
35134
  const result = removeWorkspaceEntry(config2, id);
34996
35135
  if ("error" in result) return { success: false, error: result.error };
34997
- saveConfig(result.config);
35136
+ const saveResult = persistWorkspaceConfig(result.config);
35137
+ if ("error" in saveResult) return { success: false, error: saveResult.error };
34998
35138
  const state = getWorkspaceState2(result.config);
34999
35139
  return { success: true, removedId: id, ...state };
35000
35140
  }
35001
35141
  function handleWorkspaceSetDefault(args) {
35002
35142
  const clear = args?.clear === true || args?.id === null || args?.id === "";
35003
35143
  if (clear) {
35004
- const config22 = loadConfig2();
35144
+ const config22 = loadWorkspaceConfig();
35145
+ if ("error" in config22) return { success: false, error: config22.error };
35005
35146
  const result2 = setDefaultWorkspaceId(config22, null);
35006
35147
  if ("error" in result2) return { success: false, error: result2.error };
35007
- saveConfig(result2.config);
35148
+ const saveResult2 = persistWorkspaceConfig(result2.config);
35149
+ if ("error" in saveResult2) return { success: false, error: saveResult2.error };
35008
35150
  const state2 = getWorkspaceState2(result2.config);
35009
35151
  return {
35010
35152
  success: true,
@@ -35016,7 +35158,9 @@ ${data.message || ""}`.trim();
35016
35158
  if (!pathArg && !idArg) {
35017
35159
  return { success: false, error: "id or path required (or clear: true)" };
35018
35160
  }
35019
- let config2 = loadConfig2();
35161
+ const configResult = loadWorkspaceConfig();
35162
+ if ("error" in configResult) return { success: false, error: configResult.error };
35163
+ let config2 = configResult;
35020
35164
  let nextId;
35021
35165
  if (pathArg) {
35022
35166
  let w = findWorkspaceByPath(config2, pathArg);
@@ -35032,7 +35176,8 @@ ${data.message || ""}`.trim();
35032
35176
  }
35033
35177
  const result = setDefaultWorkspaceId(config2, nextId);
35034
35178
  if ("error" in result) return { success: false, error: result.error };
35035
- saveConfig(result.config);
35179
+ const saveResult = persistWorkspaceConfig(result.config);
35180
+ if ("error" in saveResult) return { success: false, error: saveResult.error };
35036
35181
  const state = getWorkspaceState2(result.config);
35037
35182
  return { success: true, ...state };
35038
35183
  }
@@ -35432,7 +35577,7 @@ ${data.message || ""}`.trim();
35432
35577
  try {
35433
35578
  const http3 = await import("http");
35434
35579
  const postData = JSON.stringify(body);
35435
- const result = await new Promise((resolve10, reject) => {
35580
+ const result = await new Promise((resolve9, reject) => {
35436
35581
  const req = http3.request({
35437
35582
  hostname: "127.0.0.1",
35438
35583
  port: 19280,
@@ -35444,9 +35589,9 @@ ${data.message || ""}`.trim();
35444
35589
  res.on("data", (chunk) => data += chunk);
35445
35590
  res.on("end", () => {
35446
35591
  try {
35447
- resolve10(JSON.parse(data));
35592
+ resolve9(JSON.parse(data));
35448
35593
  } catch {
35449
- resolve10({ raw: data });
35594
+ resolve9({ raw: data });
35450
35595
  }
35451
35596
  });
35452
35597
  });
@@ -35464,15 +35609,15 @@ ${data.message || ""}`.trim();
35464
35609
  if (!providerType) return { success: false, error: "providerType required" };
35465
35610
  try {
35466
35611
  const http3 = await import("http");
35467
- const result = await new Promise((resolve10, reject) => {
35612
+ const result = await new Promise((resolve9, reject) => {
35468
35613
  http3.get(`http://127.0.0.1:19280/api/providers/${providerType}/${endpoint}`, (res) => {
35469
35614
  let data = "";
35470
35615
  res.on("data", (chunk) => data += chunk);
35471
35616
  res.on("end", () => {
35472
35617
  try {
35473
- resolve10(JSON.parse(data));
35618
+ resolve9(JSON.parse(data));
35474
35619
  } catch {
35475
- resolve10({ raw: data });
35620
+ resolve9({ raw: data });
35476
35621
  }
35477
35622
  });
35478
35623
  }).on("error", reject);
@@ -35486,7 +35631,7 @@ ${data.message || ""}`.trim();
35486
35631
  try {
35487
35632
  const http3 = await import("http");
35488
35633
  const postData = JSON.stringify(args || {});
35489
- const result = await new Promise((resolve10, reject) => {
35634
+ const result = await new Promise((resolve9, reject) => {
35490
35635
  const req = http3.request({
35491
35636
  hostname: "127.0.0.1",
35492
35637
  port: 19280,
@@ -35498,9 +35643,9 @@ ${data.message || ""}`.trim();
35498
35643
  res.on("data", (chunk) => data += chunk);
35499
35644
  res.on("end", () => {
35500
35645
  try {
35501
- resolve10(JSON.parse(data));
35646
+ resolve9(JSON.parse(data));
35502
35647
  } catch {
35503
- resolve10({ raw: data });
35648
+ resolve9({ raw: data });
35504
35649
  }
35505
35650
  });
35506
35651
  });
@@ -35514,13 +35659,13 @@ ${data.message || ""}`.trim();
35514
35659
  }
35515
35660
  }
35516
35661
  };
35517
- var os9 = __toESM2(require("os"));
35662
+ var os10 = __toESM2(require("os"));
35518
35663
  var path9 = __toESM2(require("path"));
35519
35664
  var crypto4 = __toESM2(require("crypto"));
35520
35665
  var import_chalk = __toESM2(require("chalk"));
35521
35666
  init_provider_cli_adapter();
35522
35667
  init_config();
35523
- var os8 = __toESM2(require("os"));
35668
+ var os9 = __toESM2(require("os"));
35524
35669
  var path8 = __toESM2(require("path"));
35525
35670
  var crypto3 = __toESM2(require("crypto"));
35526
35671
  var fs5 = __toESM2(require("fs"));
@@ -35609,17 +35754,60 @@ ${data.message || ""}`.trim();
35609
35754
  async onTick() {
35610
35755
  if (this.providerSessionId) return;
35611
35756
  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();
35757
+ const probeConfig = this.provider.sessionProbe;
35758
+ if (probeConfig) {
35759
+ probedSessionId = this.probeSessionIdFromConfig(probeConfig);
35760
+ } else {
35761
+ if (this.type === "opencode-cli") {
35762
+ probedSessionId = this.probeSessionIdFromConfig({
35763
+ dbPath: "~/.local/share/opencode/opencode.db",
35764
+ query: "select id from session where directory in ({dirs}) and time_created >= ? and time_archived is null order by time_updated desc limit 1",
35765
+ timestampFormat: "unix_ms"
35766
+ });
35767
+ } else if (this.type === "codex-cli") {
35768
+ probedSessionId = this.probeSessionIdFromConfig({
35769
+ dbPath: "~/.codex/state_5.sqlite",
35770
+ query: "select id from threads where cwd in ({dirs}) and created_at >= ? and archived = 0 order by created_at desc limit 1",
35771
+ timestampFormat: "unix_s"
35772
+ });
35773
+ } else if (this.type === "goose-cli") {
35774
+ probedSessionId = this.probeSessionIdFromConfig({
35775
+ dbPath: "~/.local/share/goose/sessions/sessions.db",
35776
+ query: "select id from sessions where working_dir in ({dirs}) and created_at >= ? order by updated_at desc limit 1",
35777
+ timestampFormat: "iso"
35778
+ });
35779
+ }
35618
35780
  }
35619
35781
  if (probedSessionId) {
35620
35782
  this.promoteProviderSessionId(probedSessionId);
35621
35783
  }
35622
35784
  }
35785
+ /**
35786
+ * Generic session ID probe using declarative ProviderSessionProbe config.
35787
+ * Replaces the previously duplicated probeOpenCode/Codex/Goose functions.
35788
+ */
35789
+ probeSessionIdFromConfig(probe) {
35790
+ const resolvedDbPath = probe.dbPath.replace(/^~/, os9.homedir());
35791
+ if (!fs5.existsSync(resolvedDbPath)) return null;
35792
+ const directories = this.getProbeDirectories();
35793
+ const minCreatedAt = Math.max(0, this.startedAt - 6e4);
35794
+ const tsFormat = probe.timestampFormat || "unix_ms";
35795
+ let timestampParam;
35796
+ if (tsFormat === "unix_s") {
35797
+ timestampParam = Math.floor(minCreatedAt / 1e3);
35798
+ } else if (tsFormat === "iso") {
35799
+ timestampParam = new Date(minCreatedAt).toISOString().slice(0, 19).replace("T", " ");
35800
+ } else {
35801
+ timestampParam = minCreatedAt;
35802
+ }
35803
+ const placeholders = this.buildSqlPlaceholderList(directories.length);
35804
+ const query = probe.query.replace("{dirs}", placeholders);
35805
+ try {
35806
+ return this.querySqliteText(resolvedDbPath, query, [...directories, timestampParam]);
35807
+ } catch {
35808
+ return null;
35809
+ }
35810
+ }
35623
35811
  getState() {
35624
35812
  const adapterStatus = this.adapter.getStatus();
35625
35813
  const parsedStatus = this.adapter.getScriptParsedStatus?.() || null;
@@ -35917,34 +36105,6 @@ ${data.message || ""}`.trim();
35917
36105
  });
35918
36106
  LOG2.info("CLI", `[${this.type}] discovered provider session id: ${nextSessionId}`);
35919
36107
  }
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
36108
  getProbeDirectories() {
35949
36109
  const dirs = /* @__PURE__ */ new Set();
35950
36110
  const addDir = (value) => {
@@ -36410,13 +36570,13 @@ ${data.message || ""}`.trim();
36410
36570
  }
36411
36571
  this.currentStatus = "waiting_approval";
36412
36572
  this.detectStatusTransition();
36413
- const approved = await new Promise((resolve10) => {
36414
- this.permissionResolvers.push(resolve10);
36573
+ const approved = await new Promise((resolve9) => {
36574
+ this.permissionResolvers.push(resolve9);
36415
36575
  setTimeout(() => {
36416
- const idx = this.permissionResolvers.indexOf(resolve10);
36576
+ const idx = this.permissionResolvers.indexOf(resolve9);
36417
36577
  if (idx >= 0) {
36418
36578
  this.permissionResolvers.splice(idx, 1);
36419
- resolve10(false);
36579
+ resolve9(false);
36420
36580
  }
36421
36581
  }, 3e5);
36422
36582
  });
@@ -37121,7 +37281,7 @@ ${data.message || ""}`.trim();
37121
37281
  async startSession(cliType, workingDir, cliArgs, initialModel, options) {
37122
37282
  const trimmed = (workingDir || "").trim();
37123
37283
  if (!trimmed) throw new Error("working directory required");
37124
- const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os9.homedir()) : path9.resolve(trimmed);
37284
+ const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os10.homedir()) : path9.resolve(trimmed);
37125
37285
  const normalizedType = this.providerLoader.resolveAlias(cliType);
37126
37286
  const provider = this.providerLoader.getByAlias(cliType);
37127
37287
  const key = crypto4.randomUUID();
@@ -37205,7 +37365,19 @@ ${installInfo}`
37205
37365
  return { runtimeSessionId: sessionId };
37206
37366
  }
37207
37367
  const cliInfo = await detectCLI(cliType, this.providerLoader);
37208
- if (!cliInfo) throw new Error(`${cliType} not found`);
37368
+ if (!cliInfo) {
37369
+ const installHint = provider?.install || "";
37370
+ const displayName = provider?.displayName || provider?.name || cliType;
37371
+ const spawnCmd = provider?.spawn?.command || cliType;
37372
+ throw new Error(
37373
+ `${displayName} is not installed.
37374
+ Command '${spawnCmd}' not found on PATH.
37375
+ ` + (installHint ? `
37376
+ ${installHint}
37377
+ ` : "") + `
37378
+ Run 'adhdev doctor' for detailed diagnostics.`
37379
+ );
37380
+ }
37209
37381
  console.log(colorize("yellow", ` \u26A1 Starting CLI ${cliType} in ${resolvedDir}...`));
37210
37382
  if (provider) {
37211
37383
  console.log(colorize("cyan", ` \u{1F4E6} Using provider: ${provider.name} (${provider.type})`));
@@ -37521,8 +37693,9 @@ ${installInfo}`
37521
37693
  const dir = rdir.path;
37522
37694
  if (!cliType) throw new Error("cliType required");
37523
37695
  const found = this.findAdapter(cliType, { instanceKey: args?.targetSessionId, dir });
37696
+ const prevCliArgs = found ? found.adapter.extraArgs : void 0;
37524
37697
  if (found) await this.stopSession(found.key);
37525
- await this.startSession(cliType, dir);
37698
+ await this.startSession(cliType, dir, args?.cliArgs || prevCliArgs, args?.initialModel);
37526
37699
  return { success: true, restarted: true };
37527
37700
  }
37528
37701
  case "agent_command": {
@@ -37555,11 +37728,11 @@ ${installInfo}`
37555
37728
  };
37556
37729
  var import_child_process6 = require("child_process");
37557
37730
  var net3 = __toESM2(require("net"));
37558
- var os11 = __toESM2(require("os"));
37731
+ var os12 = __toESM2(require("os"));
37559
37732
  var path11 = __toESM2(require("path"));
37560
37733
  var fs6 = __toESM2(require("fs"));
37561
37734
  var path10 = __toESM2(require("path"));
37562
- var os10 = __toESM2(require("os"));
37735
+ var os11 = __toESM2(require("os"));
37563
37736
  var chokidar = __toESM2((init_chokidar(), __toCommonJS(chokidar_exports)));
37564
37737
  init_logger();
37565
37738
  var ProviderLoader = class _ProviderLoader {
@@ -37579,7 +37752,7 @@ ${installInfo}`
37579
37752
  static META_FILE = ".meta.json";
37580
37753
  constructor(options) {
37581
37754
  this.logFn = options?.logFn || LOG2.forComponent("Provider").asLogFn();
37582
- const defaultProvidersDir = path10.join(os10.homedir(), ".adhdev", "providers");
37755
+ const defaultProvidersDir = path10.join(os11.homedir(), ".adhdev", "providers");
37583
37756
  if (options?.userDir) {
37584
37757
  this.userDir = options.userDir;
37585
37758
  this.log(`Config 'providerDir' applied: ${this.userDir}`);
@@ -38136,7 +38309,7 @@ ${installInfo}`
38136
38309
  return { updated: false };
38137
38310
  }
38138
38311
  try {
38139
- const etag = await new Promise((resolve10, reject) => {
38312
+ const etag = await new Promise((resolve9, reject) => {
38140
38313
  const options = {
38141
38314
  method: "HEAD",
38142
38315
  hostname: "github.com",
@@ -38154,7 +38327,7 @@ ${installInfo}`
38154
38327
  headers: { "User-Agent": "adhdev-launcher" },
38155
38328
  timeout: 1e4
38156
38329
  }, (res2) => {
38157
- resolve10(res2.headers.etag || res2.headers["last-modified"] || "");
38330
+ resolve9(res2.headers.etag || res2.headers["last-modified"] || "");
38158
38331
  });
38159
38332
  req2.on("error", reject);
38160
38333
  req2.on("timeout", () => {
@@ -38163,7 +38336,7 @@ ${installInfo}`
38163
38336
  });
38164
38337
  req2.end();
38165
38338
  } else {
38166
- resolve10(res.headers.etag || res.headers["last-modified"] || "");
38339
+ resolve9(res.headers.etag || res.headers["last-modified"] || "");
38167
38340
  }
38168
38341
  });
38169
38342
  req.on("error", reject);
@@ -38179,8 +38352,8 @@ ${installInfo}`
38179
38352
  return { updated: false };
38180
38353
  }
38181
38354
  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()}`);
38355
+ const tmpTar = path10.join(os11.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
38356
+ const tmpExtract = path10.join(os11.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
38184
38357
  await this.downloadFile(_ProviderLoader.GITHUB_TARBALL_URL, tmpTar);
38185
38358
  fs6.mkdirSync(tmpExtract, { recursive: true });
38186
38359
  execSync7(`tar -xzf "${tmpTar}" -C "${tmpExtract}"`, { timeout: 3e4 });
@@ -38227,7 +38400,7 @@ ${installInfo}`
38227
38400
  downloadFile(url2, destPath) {
38228
38401
  const https = require("https");
38229
38402
  const http3 = require("http");
38230
- return new Promise((resolve10, reject) => {
38403
+ return new Promise((resolve9, reject) => {
38231
38404
  const doRequest = (reqUrl, redirectCount = 0) => {
38232
38405
  if (redirectCount > 5) {
38233
38406
  reject(new Error("Too many redirects"));
@@ -38247,7 +38420,7 @@ ${installInfo}`
38247
38420
  res.pipe(ws2);
38248
38421
  ws2.on("finish", () => {
38249
38422
  ws2.close();
38250
- resolve10();
38423
+ resolve9();
38251
38424
  });
38252
38425
  ws2.on("error", reject);
38253
38426
  });
@@ -38610,17 +38783,17 @@ ${installInfo}`
38610
38783
  throw new Error("No free port found");
38611
38784
  }
38612
38785
  function checkPortFree(port) {
38613
- return new Promise((resolve10) => {
38786
+ return new Promise((resolve9) => {
38614
38787
  const server = net3.createServer();
38615
38788
  server.unref();
38616
- server.on("error", () => resolve10(false));
38789
+ server.on("error", () => resolve9(false));
38617
38790
  server.listen(port, "127.0.0.1", () => {
38618
- server.close(() => resolve10(true));
38791
+ server.close(() => resolve9(true));
38619
38792
  });
38620
38793
  });
38621
38794
  }
38622
38795
  async function isCdpActive(port) {
38623
- return new Promise((resolve10) => {
38796
+ return new Promise((resolve9) => {
38624
38797
  const req = require("http").get(`http://127.0.0.1:${port}/json/version`, {
38625
38798
  timeout: 2e3
38626
38799
  }, (res) => {
@@ -38629,21 +38802,21 @@ ${installInfo}`
38629
38802
  res.on("end", () => {
38630
38803
  try {
38631
38804
  const info = JSON.parse(data);
38632
- resolve10(!!info["WebKit-Version"] || !!info["Browser"]);
38805
+ resolve9(!!info["WebKit-Version"] || !!info["Browser"]);
38633
38806
  } catch {
38634
- resolve10(false);
38807
+ resolve9(false);
38635
38808
  }
38636
38809
  });
38637
38810
  });
38638
- req.on("error", () => resolve10(false));
38811
+ req.on("error", () => resolve9(false));
38639
38812
  req.on("timeout", () => {
38640
38813
  req.destroy();
38641
- resolve10(false);
38814
+ resolve9(false);
38642
38815
  });
38643
38816
  });
38644
38817
  }
38645
38818
  async function killIdeProcess(ideId) {
38646
- const plat = os11.platform();
38819
+ const plat = os12.platform();
38647
38820
  const appName = getMacAppIdentifiers()[ideId];
38648
38821
  const winProcesses = getWinProcessNames()[ideId];
38649
38822
  try {
@@ -38702,7 +38875,7 @@ ${installInfo}`
38702
38875
  }
38703
38876
  }
38704
38877
  function isIdeRunning(ideId) {
38705
- const plat = os11.platform();
38878
+ const plat = os12.platform();
38706
38879
  try {
38707
38880
  if (plat === "darwin") {
38708
38881
  const appName = getMacAppIdentifiers()[ideId];
@@ -38738,7 +38911,7 @@ ${installInfo}`
38738
38911
  }
38739
38912
  }
38740
38913
  function detectCurrentWorkspace(ideId) {
38741
- const plat = os11.platform();
38914
+ const plat = os12.platform();
38742
38915
  if (plat === "darwin") {
38743
38916
  try {
38744
38917
  const appName = getMacAppIdentifiers()[ideId];
@@ -38758,7 +38931,7 @@ ${installInfo}`
38758
38931
  const appName = appNameMap[ideId];
38759
38932
  if (appName) {
38760
38933
  const storagePath = path11.join(
38761
- process.env.APPDATA || path11.join(os11.homedir(), "AppData", "Roaming"),
38934
+ process.env.APPDATA || path11.join(os12.homedir(), "AppData", "Roaming"),
38762
38935
  appName,
38763
38936
  "storage.json"
38764
38937
  );
@@ -38780,7 +38953,7 @@ ${installInfo}`
38780
38953
  return void 0;
38781
38954
  }
38782
38955
  async function launchWithCdp(options = {}) {
38783
- const platform9 = os11.platform();
38956
+ const platform9 = os12.platform();
38784
38957
  let targetIde;
38785
38958
  const ides = await detectIDEs();
38786
38959
  if (options.ideId) {
@@ -38928,8 +39101,8 @@ ${installInfo}`
38928
39101
  init_logger();
38929
39102
  var fs7 = __toESM2(require("fs"));
38930
39103
  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");
39104
+ var os13 = __toESM2(require("os"));
39105
+ 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
39106
  var MAX_FILE_SIZE = 5 * 1024 * 1024;
38934
39107
  var MAX_DAYS = 7;
38935
39108
  try {
@@ -39060,7 +39233,7 @@ ${installInfo}`
39060
39233
  }
39061
39234
  cleanOldFiles();
39062
39235
  init_logger();
39063
- var os13 = __toESM2(require("os"));
39236
+ var os14 = __toESM2(require("os"));
39064
39237
  init_config();
39065
39238
  init_terminal_screen();
39066
39239
  init_logger();
@@ -39176,16 +39349,16 @@ ${installInfo}`
39176
39349
  version: options.version,
39177
39350
  daemonMode: options.daemonMode,
39178
39351
  machine: {
39179
- hostname: os13.hostname(),
39180
- platform: os13.platform(),
39181
- arch: os13.arch(),
39182
- cpus: os13.cpus().length,
39352
+ hostname: os14.hostname(),
39353
+ platform: os14.platform(),
39354
+ arch: os14.arch(),
39355
+ cpus: os14.cpus().length,
39183
39356
  totalMem: memSnap.totalMem,
39184
39357
  freeMem: memSnap.freeMem,
39185
39358
  availableMem: memSnap.availableMem,
39186
- loadavg: os13.loadavg(),
39187
- uptime: os13.uptime(),
39188
- release: os13.release()
39359
+ loadavg: os14.loadavg(),
39360
+ uptime: os14.uptime(),
39361
+ release: os14.release()
39189
39362
  },
39190
39363
  machineNickname: options.machineNickname ?? cfg.machineNickname ?? null,
39191
39364
  timestamp: options.timestamp ?? Date.now(),
@@ -39203,11 +39376,11 @@ ${installInfo}`
39203
39376
  var import_child_process7 = require("child_process");
39204
39377
  var import_child_process8 = require("child_process");
39205
39378
  var fs8 = __toESM2(require("fs"));
39206
- var os14 = __toESM2(require("os"));
39379
+ var os15 = __toESM2(require("os"));
39207
39380
  var path13 = __toESM2(require("path"));
39208
39381
  var UPGRADE_HELPER_ENV = "ADHDEV_DAEMON_UPGRADE_HELPER";
39209
39382
  function getUpgradeLogPath() {
39210
- const home = os14.homedir();
39383
+ const home = os15.homedir();
39211
39384
  const dir = path13.join(home, ".adhdev");
39212
39385
  fs8.mkdirSync(dir, { recursive: true });
39213
39386
  return path13.join(dir, "daemon-upgrade.log");
@@ -39240,14 +39413,14 @@ ${installInfo}`
39240
39413
  while (Date.now() - start < timeoutMs) {
39241
39414
  try {
39242
39415
  process.kill(pid, 0);
39243
- await new Promise((resolve10) => setTimeout(resolve10, 250));
39416
+ await new Promise((resolve9) => setTimeout(resolve9, 250));
39244
39417
  } catch {
39245
39418
  return;
39246
39419
  }
39247
39420
  }
39248
39421
  }
39249
39422
  function stopSessionHostProcesses(appName) {
39250
- const pidFile = path13.join(os14.homedir(), ".adhdev", `${appName}-session-host.pid`);
39423
+ const pidFile = path13.join(os15.homedir(), ".adhdev", `${appName}-session-host.pid`);
39251
39424
  try {
39252
39425
  if (fs8.existsSync(pidFile)) {
39253
39426
  const pid = Number.parseInt(fs8.readFileSync(pidFile, "utf8").trim(), 10);
@@ -39276,7 +39449,7 @@ ${installInfo}`
39276
39449
  }
39277
39450
  }
39278
39451
  function removeDaemonPidFile() {
39279
- const pidFile = path13.join(os14.homedir(), ".adhdev", "daemon.pid");
39452
+ const pidFile = path13.join(os15.homedir(), ".adhdev", "daemon.pid");
39280
39453
  try {
39281
39454
  fs8.unlinkSync(pidFile);
39282
39455
  } catch {
@@ -40766,10 +40939,10 @@ ${installInfo}`
40766
40939
  };
40767
40940
  var fs10 = __toESM2(require("fs"));
40768
40941
  var path14 = __toESM2(require("path"));
40769
- var os15 = __toESM2(require("os"));
40942
+ var os16 = __toESM2(require("os"));
40770
40943
  var import_child_process9 = require("child_process");
40771
40944
  var import_os3 = require("os");
40772
- var ARCHIVE_PATH = path14.join(os15.homedir(), ".adhdev", "version-history.json");
40945
+ var ARCHIVE_PATH = path14.join(os16.homedir(), ".adhdev", "version-history.json");
40773
40946
  var MAX_ENTRIES_PER_PROVIDER = 20;
40774
40947
  var VersionArchive = class {
40775
40948
  history = {};
@@ -40856,7 +41029,7 @@ ${installInfo}`
40856
41029
  function checkPathExists2(paths) {
40857
41030
  for (const p of paths) {
40858
41031
  if (p.includes("*")) {
40859
- const home = os15.homedir();
41032
+ const home = os16.homedir();
40860
41033
  const resolved = p.replace(/\*/g, home.split(path14.sep).pop() || "");
40861
41034
  if (fs10.existsSync(resolved)) return resolved;
40862
41035
  } else {
@@ -42443,7 +42616,7 @@ async (params) => {
42443
42616
  return { target, instance, adapter };
42444
42617
  }
42445
42618
  function sleep(ms2) {
42446
- return new Promise((resolve10) => setTimeout(resolve10, ms2));
42619
+ return new Promise((resolve9) => setTimeout(resolve9, ms2));
42447
42620
  }
42448
42621
  async function waitForCliReady(ctx, type, instanceId, timeoutMs) {
42449
42622
  const startedAt = Date.now();
@@ -43172,7 +43345,7 @@ async (params) => {
43172
43345
  }
43173
43346
  var fs13 = __toESM2(require("fs"));
43174
43347
  var path17 = __toESM2(require("path"));
43175
- var os16 = __toESM2(require("os"));
43348
+ var os17 = __toESM2(require("os"));
43176
43349
  function getAutoImplPid(ctx) {
43177
43350
  const proc = ctx.autoImplProcess;
43178
43351
  return proc && typeof proc.pid === "number" && proc.pid > 0 ? proc.pid : null;
@@ -43375,7 +43548,7 @@ async (params) => {
43375
43548
  });
43376
43549
  const referenceScripts = loadAutoImplReferenceScripts(ctx, resolvedReference);
43377
43550
  const prompt = buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domContext, referenceScripts, comment, resolvedReference, verification);
43378
- const tmpDir = path17.join(os16.tmpdir(), "adhdev-autoimpl");
43551
+ const tmpDir = path17.join(os17.tmpdir(), "adhdev-autoimpl");
43379
43552
  if (!fs13.existsSync(tmpDir)) fs13.mkdirSync(tmpDir, { recursive: true });
43380
43553
  const promptFile = path17.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
43381
43554
  fs13.writeFileSync(promptFile, prompt, "utf-8");
@@ -43529,7 +43702,7 @@ async (params) => {
43529
43702
  const interactiveFlags = ["--yolo", "--interactive", "-i"];
43530
43703
  const baseArgs = [...spawn4.args || []].filter((a) => !interactiveFlags.includes(a));
43531
43704
  let shellCmd;
43532
- const isWin = os16.platform() === "win32";
43705
+ const isWin = os17.platform() === "win32";
43533
43706
  const escapeArg = (a) => isWin ? `"${a.replace(/"/g, '""')}"` : `'${a.replace(/'/g, "'\\''")}'`;
43534
43707
  if (command === "claude") {
43535
43708
  const args = [...baseArgs, "--dangerously-skip-permissions"];
@@ -43573,7 +43746,7 @@ async (params) => {
43573
43746
  try {
43574
43747
  const pty3 = require("node-pty");
43575
43748
  ctx.log(`Auto-implement spawn (PTY): ${shellCmd}`);
43576
- const isWin2 = os16.platform() === "win32";
43749
+ const isWin2 = os17.platform() === "win32";
43577
43750
  child = pty3.spawn(isWin2 ? "cmd.exe" : process.env.SHELL || "/bin/zsh", [isWin2 ? "/c" : "-c", shellCmd], {
43578
43751
  name: "xterm-256color",
43579
43752
  cols: 120,
@@ -44609,15 +44782,15 @@ data: ${JSON.stringify(msg.data)}
44609
44782
  this.json(res, 500, { error: e.message });
44610
44783
  }
44611
44784
  });
44612
- return new Promise((resolve10, reject) => {
44785
+ return new Promise((resolve9, reject) => {
44613
44786
  this.server.listen(port, "127.0.0.1", () => {
44614
44787
  this.log(`Dev server listening on http://127.0.0.1:${port}`);
44615
- resolve10();
44788
+ resolve9();
44616
44789
  });
44617
44790
  this.server.on("error", (e) => {
44618
44791
  if (e.code === "EADDRINUSE") {
44619
44792
  this.log(`Port ${port} in use, skipping dev server`);
44620
- resolve10();
44793
+ resolve9();
44621
44794
  } else {
44622
44795
  reject(e);
44623
44796
  }
@@ -44700,20 +44873,20 @@ data: ${JSON.stringify(msg.data)}
44700
44873
  child.stderr?.on("data", (d) => {
44701
44874
  stderr += d.toString().slice(0, 2e3);
44702
44875
  });
44703
- await new Promise((resolve10) => {
44876
+ await new Promise((resolve9) => {
44704
44877
  const timer = setTimeout(() => {
44705
44878
  child.kill();
44706
- resolve10();
44879
+ resolve9();
44707
44880
  }, 3e3);
44708
44881
  child.on("exit", () => {
44709
44882
  clearTimeout(timer);
44710
- resolve10();
44883
+ resolve9();
44711
44884
  });
44712
44885
  child.stdout?.once("data", () => {
44713
44886
  setTimeout(() => {
44714
44887
  child.kill();
44715
44888
  clearTimeout(timer);
44716
- resolve10();
44889
+ resolve9();
44717
44890
  }, 500);
44718
44891
  });
44719
44892
  });
@@ -45222,14 +45395,14 @@ data: ${JSON.stringify(msg.data)}
45222
45395
  child.stderr?.on("data", (d) => {
45223
45396
  stderr += d.toString();
45224
45397
  });
45225
- await new Promise((resolve10) => {
45398
+ await new Promise((resolve9) => {
45226
45399
  const timer = setTimeout(() => {
45227
45400
  child.kill();
45228
- resolve10();
45401
+ resolve9();
45229
45402
  }, timeout);
45230
45403
  child.on("exit", () => {
45231
45404
  clearTimeout(timer);
45232
- resolve10();
45405
+ resolve9();
45233
45406
  });
45234
45407
  });
45235
45408
  const elapsed = Date.now() - start;
@@ -45904,14 +46077,14 @@ data: ${JSON.stringify(msg.data)}
45904
46077
  res.end(JSON.stringify(data, null, 2));
45905
46078
  }
45906
46079
  async readBody(req) {
45907
- return new Promise((resolve10) => {
46080
+ return new Promise((resolve9) => {
45908
46081
  let body = "";
45909
46082
  req.on("data", (chunk) => body += chunk);
45910
46083
  req.on("end", () => {
45911
46084
  try {
45912
- resolve10(JSON.parse(body));
46085
+ resolve9(JSON.parse(body));
45913
46086
  } catch {
45914
- resolve10({});
46087
+ resolve9({});
45915
46088
  }
45916
46089
  });
45917
46090
  });
@@ -45980,12 +46153,12 @@ data: ${JSON.stringify(msg.data)}
45980
46153
  };
45981
46154
  init_provider_cli_adapter();
45982
46155
  init_pty_transport();
45983
- var import_session_host_core2 = require_dist();
46156
+ var import_session_host_core22 = require_dist();
45984
46157
  init_logger();
45985
46158
  var SessionHostRuntimeTransport = class {
45986
46159
  constructor(options) {
45987
46160
  this.options = options;
45988
- this.client = new import_session_host_core2.SessionHostClient({
46161
+ this.client = new import_session_host_core22.SessionHostClient({
45989
46162
  endpoint: options.endpoint,
45990
46163
  appName: options.appName
45991
46164
  });
@@ -46352,11 +46525,11 @@ data: ${JSON.stringify(msg.data)}
46352
46525
  });
46353
46526
  }
46354
46527
  };
46355
- var import_session_host_core22 = require_dist();
46528
+ var import_session_host_core3 = require_dist();
46356
46529
  var STARTUP_TIMEOUT_MS = 8e3;
46357
46530
  var STARTUP_POLL_MS = 200;
46358
46531
  async function canConnect(endpoint) {
46359
- const client = new import_session_host_core22.SessionHostClient({ endpoint });
46532
+ const client = new import_session_host_core3.SessionHostClient({ endpoint });
46360
46533
  try {
46361
46534
  await client.connect();
46362
46535
  await client.close();
@@ -46369,19 +46542,19 @@ data: ${JSON.stringify(msg.data)}
46369
46542
  const deadline = Date.now() + timeoutMs;
46370
46543
  while (Date.now() < deadline) {
46371
46544
  if (await canConnect(endpoint)) return;
46372
- await new Promise((resolve10) => setTimeout(resolve10, STARTUP_POLL_MS));
46545
+ await new Promise((resolve9) => setTimeout(resolve9, STARTUP_POLL_MS));
46373
46546
  }
46374
46547
  throw new Error(`Session host did not become ready within ${timeoutMs}ms`);
46375
46548
  }
46376
46549
  async function ensureSessionHostReady2(options) {
46377
- const endpoint = (0, import_session_host_core22.getDefaultSessionHostEndpoint)(options.appName || "adhdev");
46550
+ const endpoint = (0, import_session_host_core3.getDefaultSessionHostEndpoint)(options.appName || "adhdev");
46378
46551
  if (await canConnect(endpoint)) return endpoint;
46379
46552
  options.spawnHost();
46380
46553
  await waitForReady(endpoint, options.timeoutMs);
46381
46554
  return endpoint;
46382
46555
  }
46383
46556
  async function listHostedCliRuntimes2(endpoint) {
46384
- const client = new import_session_host_core22.SessionHostClient({ endpoint });
46557
+ const client = new import_session_host_core3.SessionHostClient({ endpoint });
46385
46558
  try {
46386
46559
  const response = await client.request({
46387
46560
  type: "list_sessions",
@@ -46523,10 +46696,10 @@ data: ${JSON.stringify(msg.data)}
46523
46696
  const buffer = Buffer.from(await res.arrayBuffer());
46524
46697
  const fs15 = await import("fs");
46525
46698
  fs15.writeFileSync(vsixPath, buffer);
46526
- return new Promise((resolve10) => {
46699
+ return new Promise((resolve9) => {
46527
46700
  const cmd = `"${ide.cliCommand}" --install-extension "${vsixPath}" --force`;
46528
46701
  (0, import_child_process10.exec)(cmd, { timeout: 6e4 }, (error48, _stdout, stderr) => {
46529
- resolve10({
46702
+ resolve9({
46530
46703
  extensionId: extension.id,
46531
46704
  marketplaceId: extension.marketplaceId,
46532
46705
  success: !error48,
@@ -46539,11 +46712,11 @@ data: ${JSON.stringify(msg.data)}
46539
46712
  } catch (e) {
46540
46713
  }
46541
46714
  }
46542
- return new Promise((resolve10) => {
46715
+ return new Promise((resolve9) => {
46543
46716
  const cmd = `"${ide.cliCommand}" --install-extension ${extension.marketplaceId} --force`;
46544
46717
  (0, import_child_process10.exec)(cmd, { timeout: 6e4 }, (error48, stdout, stderr) => {
46545
46718
  if (error48) {
46546
- resolve10({
46719
+ resolve9({
46547
46720
  extensionId: extension.id,
46548
46721
  marketplaceId: extension.marketplaceId,
46549
46722
  success: false,
@@ -46551,7 +46724,7 @@ data: ${JSON.stringify(msg.data)}
46551
46724
  error: stderr || error48.message
46552
46725
  });
46553
46726
  } else {
46554
- resolve10({
46727
+ resolve9({
46555
46728
  extensionId: extension.id,
46556
46729
  marketplaceId: extension.marketplaceId,
46557
46730
  success: true,
@@ -47029,6 +47202,13 @@ var SessionHostClient = class {
47029
47202
  }
47030
47203
  async connect() {
47031
47204
  if (this.socket && !this.socket.destroyed) return;
47205
+ if (this.socket) {
47206
+ try {
47207
+ this.socket.destroy();
47208
+ } catch {
47209
+ }
47210
+ this.socket = null;
47211
+ }
47032
47212
  const socket = net.createConnection(this.endpoint.path);
47033
47213
  this.socket = socket;
47034
47214
  socket.on("data", createLineParser((envelope) => {
@@ -47049,9 +47229,16 @@ var SessionHostClient = class {
47049
47229
  waiter.reject(error48);
47050
47230
  }
47051
47231
  this.requestWaiters.clear();
47232
+ if (this.socket === socket) {
47233
+ this.socket = null;
47234
+ }
47235
+ try {
47236
+ socket.destroy();
47237
+ } catch {
47238
+ }
47052
47239
  });
47053
- await new Promise((resolve4, reject) => {
47054
- socket.once("connect", () => resolve4());
47240
+ await new Promise((resolve22, reject) => {
47241
+ socket.once("connect", () => resolve22());
47055
47242
  socket.once("error", reject);
47056
47243
  });
47057
47244
  }
@@ -47070,7 +47257,7 @@ var SessionHostClient = class {
47070
47257
  requestId,
47071
47258
  request
47072
47259
  };
47073
- const response = await new Promise((resolve4, reject) => {
47260
+ const response = await new Promise((resolve22, reject) => {
47074
47261
  const timeout = setTimeout(() => {
47075
47262
  this.requestWaiters.delete(requestId);
47076
47263
  reject(new Error(`Session host request timed out after 30s (${request.type})`));
@@ -47078,7 +47265,7 @@ var SessionHostClient = class {
47078
47265
  this.requestWaiters.set(requestId, {
47079
47266
  resolve: (value) => {
47080
47267
  clearTimeout(timeout);
47081
- resolve4(value);
47268
+ resolve22(value);
47082
47269
  },
47083
47270
  reject: (error48) => {
47084
47271
  clearTimeout(timeout);
@@ -47097,12 +47284,12 @@ var SessionHostClient = class {
47097
47284
  waiter.reject(new Error("Session host client closed"));
47098
47285
  }
47099
47286
  this.requestWaiters.clear();
47100
- await new Promise((resolve4) => {
47287
+ await new Promise((resolve22) => {
47101
47288
  let settled = false;
47102
47289
  const done = () => {
47103
47290
  if (settled) return;
47104
47291
  settled = true;
47105
- resolve4();
47292
+ resolve22();
47106
47293
  };
47107
47294
  socket.once("close", done);
47108
47295
  socket.end();