@adhdev/daemon-core 0.5.7 → 0.5.16
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.d.ts +460 -330
- package/dist/index.js +543 -101
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/providers/_builtin/cli/claude-cli/provider.json +34 -24
- package/providers/_builtin/cli/gemini-cli/provider.json +24 -17
- package/providers/_builtin/ide/antigravity/provider.json +21 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/resolve_action.js +19 -24
- package/providers/_builtin/ide/antigravity/scripts/1.107/read_chat.js +67 -5
- package/providers/_builtin/ide/antigravity/scripts/1.107/resolve_action.js +19 -24
- package/providers/_builtin/ide/cursor/scripts/0.49/read_chat.js +271 -32
- package/providers/_builtin/registry.json +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +96 -25
- package/src/commands/router.ts +132 -33
- package/src/daemon/dev-server.ts +285 -0
- package/src/daemon-core.ts +7 -6
- package/src/detection/ide-detector.ts +5 -5
- package/src/index.ts +24 -7
- package/src/launch.ts +2 -2
- package/src/providers/acp-provider-instance.ts +54 -56
- package/src/providers/cli-provider-instance.ts +32 -4
- package/src/providers/contracts.ts +5 -22
- package/src/providers/ide-provider-instance.ts +8 -10
- package/src/providers/provider-instance.ts +70 -33
- package/src/shared-types.ts +203 -0
- package/src/status/reporter.ts +31 -22
- package/src/types.ts +26 -110
package/dist/index.js
CHANGED
|
@@ -396,7 +396,9 @@ __export(index_exports, {
|
|
|
396
396
|
installExtensions: () => installExtensions,
|
|
397
397
|
installGlobalInterceptor: () => installGlobalInterceptor,
|
|
398
398
|
isExtensionInstalled: () => isExtensionInstalled,
|
|
399
|
+
isIdeRunning: () => isIdeRunning,
|
|
399
400
|
isSetupComplete: () => isSetupComplete,
|
|
401
|
+
killIdeProcess: () => killIdeProcess,
|
|
400
402
|
launchIDE: () => launchIDE,
|
|
401
403
|
launchWithCdp: () => launchWithCdp,
|
|
402
404
|
loadConfig: () => loadConfig,
|
|
@@ -499,13 +501,11 @@ function getIdeVersion(cliCommand) {
|
|
|
499
501
|
}
|
|
500
502
|
}
|
|
501
503
|
function checkPathExists(paths) {
|
|
504
|
+
const home = (0, import_os2.homedir)();
|
|
502
505
|
for (const p of paths) {
|
|
503
506
|
if (p.includes("*")) {
|
|
504
|
-
const
|
|
505
|
-
const
|
|
506
|
-
const suffix = p.substring(starIdx + 1);
|
|
507
|
-
const homeNormalized = home.replace(/\//g, "\\\\");
|
|
508
|
-
const resolved = homeNormalized + suffix;
|
|
507
|
+
const username = home.split(/[\\/]/).pop() || "";
|
|
508
|
+
const resolved = p.replace("*", username);
|
|
509
509
|
if ((0, import_fs2.existsSync)(resolved)) return resolved;
|
|
510
510
|
} else {
|
|
511
511
|
if ((0, import_fs2.existsSync)(p)) return p;
|
|
@@ -2340,8 +2340,7 @@ var IdeProviderInstance = class {
|
|
|
2340
2340
|
// IDE meta
|
|
2341
2341
|
ideVersion = "";
|
|
2342
2342
|
instanceId;
|
|
2343
|
-
|
|
2344
|
-
activeFile = null;
|
|
2343
|
+
workspace = "";
|
|
2345
2344
|
// ─── Child Extension Instances ────────────────────
|
|
2346
2345
|
extensions = /* @__PURE__ */ new Map();
|
|
2347
2346
|
constructor(provider, instanceKey) {
|
|
@@ -2402,8 +2401,7 @@ var IdeProviderInstance = class {
|
|
|
2402
2401
|
activeModal: this.cachedChat.activeModal || null,
|
|
2403
2402
|
inputContent: this.cachedChat.inputContent || ""
|
|
2404
2403
|
} : null,
|
|
2405
|
-
|
|
2406
|
-
activeFile: this.activeFile,
|
|
2404
|
+
workspace: this.workspace || null,
|
|
2407
2405
|
extensions: extensionStates,
|
|
2408
2406
|
cdpConnected: cdp?.isConnected || false,
|
|
2409
2407
|
currentModel: this.cachedChat?.model || void 0,
|
|
@@ -2420,11 +2418,6 @@ var IdeProviderInstance = class {
|
|
|
2420
2418
|
} else if (event === "cdp_disconnected") {
|
|
2421
2419
|
this.cachedChat = null;
|
|
2422
2420
|
this.currentStatus = "idle";
|
|
2423
|
-
} else if (event === "extension_data") {
|
|
2424
|
-
if (data?.workspaceFolders) this.workspaceFolders = data.workspaceFolders;
|
|
2425
|
-
if (data?.activeFile) this.activeFile = data.activeFile;
|
|
2426
|
-
if (data?.ideVersion) this.ideVersion = data.ideVersion;
|
|
2427
|
-
if (data?.instanceId) this.instanceId = data.instanceId;
|
|
2428
2421
|
} else if (event === "stream_update") {
|
|
2429
2422
|
const extType = data?.extensionType;
|
|
2430
2423
|
if (extType && this.extensions.has(extType)) {
|
|
@@ -2480,6 +2473,10 @@ var IdeProviderInstance = class {
|
|
|
2480
2473
|
getExtensionInstances() {
|
|
2481
2474
|
return [...this.extensions.values()];
|
|
2482
2475
|
}
|
|
2476
|
+
/** Set workspace from daemon launch context */
|
|
2477
|
+
setWorkspace(workspace) {
|
|
2478
|
+
this.workspace = workspace;
|
|
2479
|
+
}
|
|
2483
2480
|
// ─── CDP readChat ───────────────────────────────
|
|
2484
2481
|
async readChat() {
|
|
2485
2482
|
const { cdp } = this.context;
|
|
@@ -6033,8 +6030,8 @@ var DaemonCommandRouter = class {
|
|
|
6033
6030
|
if (!targetType) throw new Error("cliType or ideType required");
|
|
6034
6031
|
const isIde = this.deps.cdpManagers.has(targetType) || this.deps.providerLoader.getMeta(targetType)?.category === "ide";
|
|
6035
6032
|
if (isIde) {
|
|
6036
|
-
await this.stopIde(targetType);
|
|
6037
|
-
const launchResult = await this.executeDaemonCommand("launch_ide", { ideType: targetType, enableCdp: true });
|
|
6033
|
+
await this.stopIde(targetType, true);
|
|
6034
|
+
const launchResult = await this.executeDaemonCommand("launch_ide", { ideType: targetType, enableCdp: true, workspace: args?.workspace });
|
|
6038
6035
|
return { success: true, restarted: true, ideType: targetType, launch: launchResult };
|
|
6039
6036
|
}
|
|
6040
6037
|
return this.deps.cliManager.handleCliCommand(cmd, args);
|
|
@@ -6043,15 +6040,16 @@ var DaemonCommandRouter = class {
|
|
|
6043
6040
|
case "stop_ide": {
|
|
6044
6041
|
const ideType = args?.ideType;
|
|
6045
6042
|
if (!ideType) throw new Error("ideType required");
|
|
6046
|
-
|
|
6047
|
-
|
|
6043
|
+
const killProcess = args?.killProcess !== false;
|
|
6044
|
+
await this.stopIde(ideType, killProcess);
|
|
6045
|
+
return { success: true, ideType, stopped: true, processKilled: killProcess };
|
|
6048
6046
|
}
|
|
6049
6047
|
// ─── IDE restart ───
|
|
6050
6048
|
case "restart_ide": {
|
|
6051
6049
|
const ideType = args?.ideType;
|
|
6052
6050
|
if (!ideType) throw new Error("ideType required");
|
|
6053
|
-
await this.stopIde(ideType);
|
|
6054
|
-
const launchResult = await this.executeDaemonCommand("launch_ide", { ideType, enableCdp: true });
|
|
6051
|
+
await this.stopIde(ideType, true);
|
|
6052
|
+
const launchResult = await this.executeDaemonCommand("launch_ide", { ideType, enableCdp: true, workspace: args?.workspace });
|
|
6055
6053
|
return { success: true, ideType, restarted: true, launch: launchResult };
|
|
6056
6054
|
}
|
|
6057
6055
|
// ─── IDE launch + CDP connect ───
|
|
@@ -6097,10 +6095,48 @@ var DaemonCommandRouter = class {
|
|
|
6097
6095
|
}
|
|
6098
6096
|
return { success: result.success, ...result };
|
|
6099
6097
|
}
|
|
6100
|
-
// ───
|
|
6098
|
+
// ─── Detect IDEs ───
|
|
6101
6099
|
case "detect_ides": {
|
|
6102
|
-
|
|
6103
|
-
|
|
6100
|
+
const results = await detectIDEs();
|
|
6101
|
+
this.deps.detectedIdes.value = results;
|
|
6102
|
+
return { success: true, detectedInfo: results };
|
|
6103
|
+
}
|
|
6104
|
+
// ─── Set User Name ───
|
|
6105
|
+
case "set_user_name": {
|
|
6106
|
+
const name = args?.userName;
|
|
6107
|
+
if (!name || typeof name !== "string") throw new Error("userName required");
|
|
6108
|
+
updateConfig({ userName: name });
|
|
6109
|
+
return { success: true, userName: name };
|
|
6110
|
+
}
|
|
6111
|
+
// ─── Daemon Self-Upgrade ───
|
|
6112
|
+
case "daemon_upgrade": {
|
|
6113
|
+
LOG.info("Upgrade", "Remote upgrade requested from dashboard");
|
|
6114
|
+
try {
|
|
6115
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
6116
|
+
const latest = execSync7("npm view adhdev version", { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
6117
|
+
LOG.info("Upgrade", `Latest available: v${latest}`);
|
|
6118
|
+
execSync7("npm install -g adhdev@latest", {
|
|
6119
|
+
encoding: "utf-8",
|
|
6120
|
+
timeout: 6e4,
|
|
6121
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
6122
|
+
});
|
|
6123
|
+
LOG.info("Upgrade", `\u2705 Upgraded to v${latest}`);
|
|
6124
|
+
setTimeout(() => {
|
|
6125
|
+
LOG.info("Upgrade", "Restarting daemon with new version...");
|
|
6126
|
+
const { spawn: spawn3 } = require("child_process");
|
|
6127
|
+
const child = spawn3(process.execPath, [process.argv[1], "daemon", "-p", "19222"], {
|
|
6128
|
+
detached: true,
|
|
6129
|
+
stdio: "ignore",
|
|
6130
|
+
env: { ...process.env }
|
|
6131
|
+
});
|
|
6132
|
+
child.unref();
|
|
6133
|
+
process.exit(0);
|
|
6134
|
+
}, 3e3);
|
|
6135
|
+
return { success: true, upgraded: true, version: latest };
|
|
6136
|
+
} catch (e) {
|
|
6137
|
+
LOG.error("Upgrade", `Failed: ${e.message}`);
|
|
6138
|
+
return { success: false, error: e.message };
|
|
6139
|
+
}
|
|
6104
6140
|
}
|
|
6105
6141
|
// ─── Machine Settings ───
|
|
6106
6142
|
case "set_machine_nickname": {
|
|
@@ -6108,38 +6144,85 @@ var DaemonCommandRouter = class {
|
|
|
6108
6144
|
updateConfig({ machineNickname: nickname || null });
|
|
6109
6145
|
return { success: true };
|
|
6110
6146
|
}
|
|
6147
|
+
default:
|
|
6148
|
+
break;
|
|
6111
6149
|
}
|
|
6112
6150
|
return null;
|
|
6113
6151
|
}
|
|
6114
6152
|
/**
|
|
6115
|
-
* IDE stop: CDP disconnect +
|
|
6153
|
+
* IDE stop: CDP disconnect + InstanceManager cleanup + optionally kill OS process
|
|
6116
6154
|
*/
|
|
6117
|
-
async stopIde(ideType) {
|
|
6118
|
-
const
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
}
|
|
6155
|
+
async stopIde(ideType, killProcess = false) {
|
|
6156
|
+
const cdpKeysToRemove = [];
|
|
6157
|
+
for (const key of this.deps.cdpManagers.keys()) {
|
|
6158
|
+
if (key === ideType || key.startsWith(`${ideType}_`)) {
|
|
6159
|
+
cdpKeysToRemove.push(key);
|
|
6160
|
+
}
|
|
6161
|
+
}
|
|
6162
|
+
for (const key of cdpKeysToRemove) {
|
|
6163
|
+
const cdp = this.deps.cdpManagers.get(key);
|
|
6164
|
+
if (cdp) {
|
|
6165
|
+
try {
|
|
6166
|
+
cdp.disconnect();
|
|
6167
|
+
} catch {
|
|
6168
|
+
}
|
|
6169
|
+
this.deps.cdpManagers.delete(key);
|
|
6170
|
+
LOG.info("StopIDE", `CDP disconnected: ${key}`);
|
|
6171
|
+
}
|
|
6172
|
+
}
|
|
6173
|
+
const keysToRemove = [];
|
|
6174
|
+
for (const key of this.deps.instanceManager.instances?.keys?.() || []) {
|
|
6175
|
+
if (key === `ide:${ideType}` || typeof key === "string" && key.startsWith(`ide:${ideType}_`)) {
|
|
6176
|
+
keysToRemove.push(key);
|
|
6177
|
+
}
|
|
6178
|
+
}
|
|
6179
|
+
for (const instanceKey of keysToRemove) {
|
|
6180
|
+
const ideInstance = this.deps.instanceManager.getInstance(instanceKey);
|
|
6181
|
+
if (ideInstance) {
|
|
6182
|
+
if (ideInstance.getInstanceId) {
|
|
6183
|
+
this.deps.instanceIdMap.delete(ideInstance.getInstanceId());
|
|
6184
|
+
}
|
|
6185
|
+
if (ideInstance.getExtensionInstances) {
|
|
6186
|
+
for (const ext of ideInstance.getExtensionInstances()) {
|
|
6187
|
+
if (ext.getInstanceId) this.deps.instanceIdMap.delete(ext.getInstanceId());
|
|
6188
|
+
}
|
|
6189
|
+
}
|
|
6190
|
+
this.deps.instanceManager.removeInstance(instanceKey);
|
|
6191
|
+
LOG.info("StopIDE", `Instance removed: ${instanceKey}`);
|
|
6123
6192
|
}
|
|
6124
|
-
this.deps.cdpManagers.delete(ideType);
|
|
6125
|
-
LOG.info("StopIDE", `CDP disconnected: ${ideType}`);
|
|
6126
6193
|
}
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
if (ideInstance
|
|
6131
|
-
|
|
6194
|
+
if (keysToRemove.length === 0) {
|
|
6195
|
+
const instanceKey = `ide:${ideType}`;
|
|
6196
|
+
const ideInstance = this.deps.instanceManager.getInstance(instanceKey);
|
|
6197
|
+
if (ideInstance) {
|
|
6198
|
+
if (ideInstance.getInstanceId) {
|
|
6199
|
+
this.deps.instanceIdMap.delete(ideInstance.getInstanceId());
|
|
6200
|
+
}
|
|
6201
|
+
if (ideInstance.getExtensionInstances) {
|
|
6202
|
+
for (const ext of ideInstance.getExtensionInstances()) {
|
|
6203
|
+
if (ext.getInstanceId) this.deps.instanceIdMap.delete(ext.getInstanceId());
|
|
6204
|
+
}
|
|
6205
|
+
}
|
|
6206
|
+
this.deps.instanceManager.removeInstance(instanceKey);
|
|
6207
|
+
LOG.info("StopIDE", `Instance removed: ${instanceKey}`);
|
|
6132
6208
|
}
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6209
|
+
}
|
|
6210
|
+
if (killProcess) {
|
|
6211
|
+
const running = isIdeRunning(ideType);
|
|
6212
|
+
if (running) {
|
|
6213
|
+
LOG.info("StopIDE", `Killing IDE process: ${ideType}`);
|
|
6214
|
+
const killed = await killIdeProcess(ideType);
|
|
6215
|
+
if (killed) {
|
|
6216
|
+
LOG.info("StopIDE", `\u2705 Process killed: ${ideType}`);
|
|
6217
|
+
} else {
|
|
6218
|
+
LOG.warn("StopIDE", `\u26A0 Could not kill process: ${ideType} (may need manual intervention)`);
|
|
6136
6219
|
}
|
|
6220
|
+
} else {
|
|
6221
|
+
LOG.info("StopIDE", `Process not running: ${ideType}`);
|
|
6137
6222
|
}
|
|
6138
|
-
this.deps.instanceManager.removeInstance(instanceKey);
|
|
6139
|
-
LOG.info("StopIDE", `Instance removed: ${instanceKey}`);
|
|
6140
6223
|
}
|
|
6141
6224
|
this.deps.onStatusChange?.();
|
|
6142
|
-
LOG.info("StopIDE", `IDE stopped: ${ideType}`);
|
|
6225
|
+
LOG.info("StopIDE", `IDE stopped: ${ideType} (processKill=${killProcess})`);
|
|
6143
6226
|
}
|
|
6144
6227
|
};
|
|
6145
6228
|
|
|
@@ -6233,7 +6316,7 @@ var DaemonStatusReporter = class {
|
|
|
6233
6316
|
const acpStates = allStates.filter((s) => s.category === "acp");
|
|
6234
6317
|
const ideSummary = ideStates.map((s) => {
|
|
6235
6318
|
const msgs = s.activeChat?.messages?.length || 0;
|
|
6236
|
-
const exts =
|
|
6319
|
+
const exts = s.extensions.length;
|
|
6237
6320
|
return `${s.type}(${s.status},${msgs}msg,${exts}ext${s.currentModel ? ",model=" + s.currentModel : ""})`;
|
|
6238
6321
|
}).join(", ");
|
|
6239
6322
|
const cliSummary = cliStates.map((s) => `${s.type}(${s.status})`).join(", ");
|
|
@@ -6253,13 +6336,12 @@ var DaemonStatusReporter = class {
|
|
|
6253
6336
|
ideType: s.type,
|
|
6254
6337
|
ideVersion: "",
|
|
6255
6338
|
instanceId: s.instanceId,
|
|
6256
|
-
|
|
6257
|
-
activeFile: s.activeFile || null,
|
|
6339
|
+
workspace: s.workspace || null,
|
|
6258
6340
|
terminals: 0,
|
|
6259
6341
|
aiAgents: [],
|
|
6260
6342
|
activeChat: s.activeChat,
|
|
6261
6343
|
chats: [],
|
|
6262
|
-
agentStreams:
|
|
6344
|
+
agentStreams: s.extensions.map((ext) => ({
|
|
6263
6345
|
agentType: ext.type,
|
|
6264
6346
|
agentName: ext.name,
|
|
6265
6347
|
extensionId: ext.type,
|
|
@@ -6268,7 +6350,7 @@ var DaemonStatusReporter = class {
|
|
|
6268
6350
|
inputContent: ext.activeChat?.inputContent || "",
|
|
6269
6351
|
activeModal: ext.activeChat?.activeModal || null
|
|
6270
6352
|
})),
|
|
6271
|
-
cdpConnected: s.cdpConnected
|
|
6353
|
+
cdpConnected: s.cdpConnected,
|
|
6272
6354
|
currentModel: s.currentModel,
|
|
6273
6355
|
currentPlan: s.currentPlan,
|
|
6274
6356
|
currentAutoApprove: s.currentAutoApprove
|
|
@@ -6279,8 +6361,8 @@ var DaemonStatusReporter = class {
|
|
|
6279
6361
|
cliType: s.type,
|
|
6280
6362
|
cliName: s.name,
|
|
6281
6363
|
status: s.status,
|
|
6282
|
-
mode: s.mode
|
|
6283
|
-
|
|
6364
|
+
mode: s.mode,
|
|
6365
|
+
workspace: s.workspace || "",
|
|
6284
6366
|
activeChat: s.activeChat
|
|
6285
6367
|
}));
|
|
6286
6368
|
const managedAcps = acpStates.map((s) => ({
|
|
@@ -6288,19 +6370,22 @@ var DaemonStatusReporter = class {
|
|
|
6288
6370
|
acpType: s.type,
|
|
6289
6371
|
acpName: s.name,
|
|
6290
6372
|
status: s.status,
|
|
6291
|
-
mode:
|
|
6292
|
-
|
|
6373
|
+
mode: s.mode,
|
|
6374
|
+
workspace: s.workspace || "",
|
|
6293
6375
|
activeChat: s.activeChat,
|
|
6294
6376
|
currentModel: s.currentModel,
|
|
6295
6377
|
currentPlan: s.currentPlan,
|
|
6296
6378
|
acpConfigOptions: s.acpConfigOptions,
|
|
6297
|
-
acpModes: s.acpModes
|
|
6379
|
+
acpModes: s.acpModes,
|
|
6380
|
+
errorMessage: s.errorMessage,
|
|
6381
|
+
errorReason: s.errorReason
|
|
6298
6382
|
}));
|
|
6299
6383
|
const cfg = loadConfig();
|
|
6300
6384
|
const wsState = getWorkspaceState(cfg);
|
|
6301
6385
|
const memSnap = getHostMemorySnapshot();
|
|
6302
6386
|
const payload = {
|
|
6303
6387
|
daemonMode: true,
|
|
6388
|
+
version: this.deps.daemonVersion || "unknown",
|
|
6304
6389
|
machineNickname: cfg.machineNickname || null,
|
|
6305
6390
|
workspaces: wsState.workspaces,
|
|
6306
6391
|
defaultWorkspaceId: wsState.defaultWorkspaceId,
|
|
@@ -6326,7 +6411,6 @@ var DaemonStatusReporter = class {
|
|
|
6326
6411
|
peers: p2p?.connectedPeerCount || 0,
|
|
6327
6412
|
screenshotActive: p2p?.screenshotActive || false
|
|
6328
6413
|
},
|
|
6329
|
-
cdpConnected: [...cdpManagers.values()].some((c) => c.isConnected),
|
|
6330
6414
|
connectedExtensions: [],
|
|
6331
6415
|
detectedIdes: this.deps.detectedIdes || [],
|
|
6332
6416
|
availableProviders: this.deps.providerLoader.getAll().map((p) => ({
|
|
@@ -6368,7 +6452,6 @@ var DaemonStatusReporter = class {
|
|
|
6368
6452
|
acpName: a.acpName
|
|
6369
6453
|
})),
|
|
6370
6454
|
p2p: payload.p2p,
|
|
6371
|
-
cdpConnected: payload.cdpConnected,
|
|
6372
6455
|
timestamp: now
|
|
6373
6456
|
};
|
|
6374
6457
|
serverConn.sendMessage("status_report", wsPayload);
|
|
@@ -6507,12 +6590,14 @@ var FALLBACK_PROMPT = [
|
|
|
6507
6590
|
/Type your message/i,
|
|
6508
6591
|
/^>\s*$/m,
|
|
6509
6592
|
// '>' alone on its own line
|
|
6510
|
-
/[
|
|
6511
|
-
|
|
6512
|
-
|
|
6593
|
+
/[›❯]\s*[\r\n]/,
|
|
6594
|
+
// prompt char followed by line ending (ANSI-stripped may not have $ at end)
|
|
6595
|
+
/[›❯]\s*$/m,
|
|
6596
|
+
// prompt char at end of line (multiline)
|
|
6597
|
+
/for\s*shortcuts/i,
|
|
6598
|
+
// Claude Code prompt (ANSI strip may remove spaces → 'forshortcuts')
|
|
6599
|
+
/\?\s*for\s*help/i,
|
|
6513
6600
|
/Press enter/i
|
|
6514
|
-
// NOTE: removed /^[\s\u2500-\u257f]*>\s*$/m — the box-drawing char range is too wide and
|
|
6515
|
-
// can match dialog-clearing ANSI output, causing false prompt detection in approval state.
|
|
6516
6601
|
];
|
|
6517
6602
|
var FALLBACK_GENERATING = [
|
|
6518
6603
|
/[\u2800-\u28ff]/,
|
|
@@ -6524,13 +6609,18 @@ var FALLBACK_GENERATING = [
|
|
|
6524
6609
|
// Specific Claude Code status
|
|
6525
6610
|
];
|
|
6526
6611
|
var FALLBACK_APPROVAL = [
|
|
6527
|
-
/Allow\s
|
|
6528
|
-
|
|
6612
|
+
/Allow\s*once/i,
|
|
6613
|
+
// ANSI strip may remove spaces
|
|
6614
|
+
/Always\s*allow/i,
|
|
6529
6615
|
/\(y\/n\)/i,
|
|
6530
6616
|
/\[Y\/n\]/i,
|
|
6531
|
-
/Run\s
|
|
6532
|
-
|
|
6533
|
-
//
|
|
6617
|
+
/Run\s*this\s*command/i,
|
|
6618
|
+
/Allow\s*tool/i,
|
|
6619
|
+
// Claude Code v2 approval
|
|
6620
|
+
/Yes,?\s*don'?t\s*ask/i,
|
|
6621
|
+
// "Yes, don't ask again" (Claude Code)
|
|
6622
|
+
/Deny/i
|
|
6623
|
+
// Deny button presence = approval dialog
|
|
6534
6624
|
];
|
|
6535
6625
|
function defaultCleanOutput(raw, _lastUserInput) {
|
|
6536
6626
|
return stripAnsi(raw).trim();
|
|
@@ -6565,6 +6655,8 @@ var ProviderCliAdapter = class {
|
|
|
6565
6655
|
maxResponse: t.maxResponse ?? 3e5,
|
|
6566
6656
|
shutdownGrace: t.shutdownGrace ?? 1e3
|
|
6567
6657
|
};
|
|
6658
|
+
const rawKeys = provider.approvalKeys;
|
|
6659
|
+
this.approvalKeys = rawKeys && typeof rawKeys === "object" ? rawKeys : {};
|
|
6568
6660
|
}
|
|
6569
6661
|
cliType;
|
|
6570
6662
|
cliName;
|
|
@@ -6596,8 +6688,20 @@ var ProviderCliAdapter = class {
|
|
|
6596
6688
|
approvalExitTimeout = null;
|
|
6597
6689
|
// Resize redraw suppression
|
|
6598
6690
|
resizeSuppressUntil = 0;
|
|
6691
|
+
// Debug: status transition history
|
|
6692
|
+
statusHistory = [];
|
|
6693
|
+
setStatus(status, trigger) {
|
|
6694
|
+
const prev = this.currentStatus;
|
|
6695
|
+
if (prev === status) return;
|
|
6696
|
+
this.currentStatus = status;
|
|
6697
|
+
this.statusHistory.push({ status, at: Date.now(), trigger });
|
|
6698
|
+
if (this.statusHistory.length > 50) this.statusHistory.shift();
|
|
6699
|
+
LOG.info("CLI", `[${this.cliType}] status: ${prev} \u2192 ${status}${trigger ? ` (${trigger})` : ""}`);
|
|
6700
|
+
}
|
|
6599
6701
|
// Resolved timeouts (provider defaults + overrides)
|
|
6600
6702
|
timeouts;
|
|
6703
|
+
// Provider approval key mapping (e.g. { 0: '1', 1: '2', 2: '3' }) — loaded from provider.json
|
|
6704
|
+
approvalKeys;
|
|
6601
6705
|
// ─── Lifecycle ─────────────────────────────────
|
|
6602
6706
|
setServerConn(serverConn) {
|
|
6603
6707
|
this.serverConn = serverConn;
|
|
@@ -6677,11 +6781,11 @@ var ProviderCliAdapter = class {
|
|
|
6677
6781
|
this.ptyProcess.onExit(({ exitCode }) => {
|
|
6678
6782
|
LOG.info("CLI", `[${this.cliType}] Exit code ${exitCode}`);
|
|
6679
6783
|
this.ptyProcess = null;
|
|
6680
|
-
this.
|
|
6784
|
+
this.setStatus("stopped", "pty_exit");
|
|
6681
6785
|
this.ready = false;
|
|
6682
6786
|
this.onStatusChange?.();
|
|
6683
6787
|
});
|
|
6684
|
-
this.
|
|
6788
|
+
this.setStatus("starting", "spawn");
|
|
6685
6789
|
this.onStatusChange?.();
|
|
6686
6790
|
}
|
|
6687
6791
|
// ─── Output state machine ────────────────────────────
|
|
@@ -6699,9 +6803,13 @@ var ProviderCliAdapter = class {
|
|
|
6699
6803
|
this.recentOutputBuffer = (this.recentOutputBuffer + cleanData).slice(-1e3);
|
|
6700
6804
|
if (!this.ready) {
|
|
6701
6805
|
this.startupBuffer += cleanData;
|
|
6806
|
+
LOG.info("CLI", `[${this.cliType}] startup chunk (${cleanData.length} chars): ${cleanData.slice(0, 200).replace(/\n/g, "\\n")}`);
|
|
6702
6807
|
const dialogPatterns = [
|
|
6703
6808
|
/Do you want to connect/i,
|
|
6704
|
-
/Do you trust the files/i
|
|
6809
|
+
/Do you trust the files/i,
|
|
6810
|
+
/Quick safety check/i,
|
|
6811
|
+
/Is this a project/i,
|
|
6812
|
+
/Enter to confirm/i
|
|
6705
6813
|
];
|
|
6706
6814
|
if (dialogPatterns.some((p) => p.test(this.startupBuffer))) {
|
|
6707
6815
|
setTimeout(() => this.ptyProcess?.write("\r"), this.timeouts.dialogAccept);
|
|
@@ -6710,7 +6818,7 @@ var ProviderCliAdapter = class {
|
|
|
6710
6818
|
}
|
|
6711
6819
|
if (patterns.prompt.some((p) => p.test(this.startupBuffer))) {
|
|
6712
6820
|
this.ready = true;
|
|
6713
|
-
this.
|
|
6821
|
+
this.setStatus("idle", "prompt_matched");
|
|
6714
6822
|
LOG.info("CLI", `[${this.cliType}] \u2713 Ready`);
|
|
6715
6823
|
this.onStatusChange?.();
|
|
6716
6824
|
}
|
|
@@ -6721,12 +6829,12 @@ var ProviderCliAdapter = class {
|
|
|
6721
6829
|
if (this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown) return;
|
|
6722
6830
|
const ctxLines = this.recentOutputBuffer.split("\n").map((l) => l.trim()).filter((l) => l && !/^[─═╭╮╰╯│]+$/.test(l));
|
|
6723
6831
|
this.isWaitingForResponse = true;
|
|
6724
|
-
this.
|
|
6832
|
+
this.setStatus("waiting_approval", "approval_pattern");
|
|
6725
6833
|
this.recentOutputBuffer = "";
|
|
6726
6834
|
this.approvalTransitionBuffer = "";
|
|
6727
6835
|
this.activeModal = {
|
|
6728
6836
|
message: ctxLines.slice(-5).join(" ").slice(0, 200) || "Approval required",
|
|
6729
|
-
buttons: ["Allow once", "Always allow", "Deny"]
|
|
6837
|
+
buttons: this.cliType === "claude-cli" ? ["Yes (y)", "Always allow (a)", "Deny (Esc)"] : ["Allow once", "Always allow", "Deny"]
|
|
6730
6838
|
};
|
|
6731
6839
|
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
6732
6840
|
if (this.approvalExitTimeout) clearTimeout(this.approvalExitTimeout);
|
|
@@ -6738,7 +6846,7 @@ var ProviderCliAdapter = class {
|
|
|
6738
6846
|
this.recentOutputBuffer = "";
|
|
6739
6847
|
this.approvalTransitionBuffer = "";
|
|
6740
6848
|
this.approvalExitTimeout = null;
|
|
6741
|
-
this.
|
|
6849
|
+
this.setStatus(this.isWaitingForResponse ? "generating" : "idle", "approval_cleared");
|
|
6742
6850
|
this.onStatusChange?.();
|
|
6743
6851
|
}
|
|
6744
6852
|
}, 6e4);
|
|
@@ -6754,7 +6862,7 @@ var ProviderCliAdapter = class {
|
|
|
6754
6862
|
clearTimeout(this.approvalExitTimeout);
|
|
6755
6863
|
this.approvalExitTimeout = null;
|
|
6756
6864
|
}
|
|
6757
|
-
this.
|
|
6865
|
+
this.setStatus("generating", "approval_gen_resume");
|
|
6758
6866
|
this.activeModal = null;
|
|
6759
6867
|
this.recentOutputBuffer = "";
|
|
6760
6868
|
this.approvalTransitionBuffer = "";
|
|
@@ -6777,7 +6885,7 @@ var ProviderCliAdapter = class {
|
|
|
6777
6885
|
if (patterns.generating.some((p) => p.test(cleanData))) {
|
|
6778
6886
|
this.isWaitingForResponse = true;
|
|
6779
6887
|
this.responseBuffer = "";
|
|
6780
|
-
this.
|
|
6888
|
+
this.setStatus("generating", "autonomous_gen");
|
|
6781
6889
|
this.onStatusChange?.();
|
|
6782
6890
|
}
|
|
6783
6891
|
}
|
|
@@ -6786,7 +6894,7 @@ var ProviderCliAdapter = class {
|
|
|
6786
6894
|
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
6787
6895
|
const stillGenerating = patterns.generating.some((p) => p.test(cleanData));
|
|
6788
6896
|
if (stillGenerating) {
|
|
6789
|
-
this.
|
|
6897
|
+
this.setStatus("generating", "still_generating");
|
|
6790
6898
|
this.idleTimeout = setTimeout(() => {
|
|
6791
6899
|
if (this.isWaitingForResponse) this.finishResponse();
|
|
6792
6900
|
}, this.timeouts.generatingIdle);
|
|
@@ -6833,7 +6941,7 @@ var ProviderCliAdapter = class {
|
|
|
6833
6941
|
this.responseBuffer = "";
|
|
6834
6942
|
this.isWaitingForResponse = false;
|
|
6835
6943
|
this.activeModal = null;
|
|
6836
|
-
this.
|
|
6944
|
+
this.setStatus("idle", "response_finished");
|
|
6837
6945
|
this.onStatusChange?.();
|
|
6838
6946
|
}
|
|
6839
6947
|
// ─── Public API (CliAdapter interface) ──────────
|
|
@@ -6852,7 +6960,7 @@ var ProviderCliAdapter = class {
|
|
|
6852
6960
|
this.messages.push({ role: "user", content: text, timestamp: Date.now() });
|
|
6853
6961
|
this.isWaitingForResponse = true;
|
|
6854
6962
|
this.responseBuffer = "";
|
|
6855
|
-
this.
|
|
6963
|
+
this.setStatus("generating", "sendMessage");
|
|
6856
6964
|
this.onStatusChange?.();
|
|
6857
6965
|
this.ptyProcess.write(text + "\r");
|
|
6858
6966
|
this.responseTimeout = setTimeout(() => {
|
|
@@ -6880,7 +6988,7 @@ var ProviderCliAdapter = class {
|
|
|
6880
6988
|
} catch {
|
|
6881
6989
|
}
|
|
6882
6990
|
this.ptyProcess = null;
|
|
6883
|
-
this.
|
|
6991
|
+
this.setStatus("stopped", "stop_cmd");
|
|
6884
6992
|
this.ready = false;
|
|
6885
6993
|
this.onStatusChange?.();
|
|
6886
6994
|
}, this.timeouts.shutdownGrace);
|
|
@@ -6906,9 +7014,13 @@ var ProviderCliAdapter = class {
|
|
|
6906
7014
|
*/
|
|
6907
7015
|
resolveModal(buttonIndex) {
|
|
6908
7016
|
if (!this.ptyProcess || this.currentStatus !== "waiting_approval") return;
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
7017
|
+
if (buttonIndex in this.approvalKeys) {
|
|
7018
|
+
this.ptyProcess.write(this.approvalKeys[buttonIndex]);
|
|
7019
|
+
} else {
|
|
7020
|
+
const DOWN = "\x1B[B";
|
|
7021
|
+
const keys = DOWN.repeat(Math.max(0, buttonIndex)) + "\r";
|
|
7022
|
+
this.ptyProcess.write(keys);
|
|
7023
|
+
}
|
|
6912
7024
|
}
|
|
6913
7025
|
resize(cols, rows) {
|
|
6914
7026
|
if (this.ptyProcess) {
|
|
@@ -6919,6 +7031,44 @@ var ProviderCliAdapter = class {
|
|
|
6919
7031
|
}
|
|
6920
7032
|
}
|
|
6921
7033
|
}
|
|
7034
|
+
/**
|
|
7035
|
+
* Full debug state — exposes all internal buffers, status, and patterns for debugging.
|
|
7036
|
+
* Used by DevServer /api/cli/debug endpoint.
|
|
7037
|
+
*/
|
|
7038
|
+
getDebugState() {
|
|
7039
|
+
return {
|
|
7040
|
+
type: this.cliType,
|
|
7041
|
+
name: this.cliName,
|
|
7042
|
+
status: this.currentStatus,
|
|
7043
|
+
ready: this.ready,
|
|
7044
|
+
workingDir: this.workingDir,
|
|
7045
|
+
messages: this.messages.slice(-20),
|
|
7046
|
+
messageCount: this.messages.length,
|
|
7047
|
+
// Buffers
|
|
7048
|
+
startupBuffer: this.startupBuffer.slice(-500),
|
|
7049
|
+
recentOutputBuffer: this.recentOutputBuffer.slice(-500),
|
|
7050
|
+
responseBuffer: this.responseBuffer.slice(-500),
|
|
7051
|
+
approvalTransitionBuffer: this.approvalTransitionBuffer.slice(-500),
|
|
7052
|
+
// State
|
|
7053
|
+
isWaitingForResponse: this.isWaitingForResponse,
|
|
7054
|
+
activeModal: this.activeModal,
|
|
7055
|
+
lastApprovalResolvedAt: this.lastApprovalResolvedAt,
|
|
7056
|
+
resizeSuppressUntil: this.resizeSuppressUntil,
|
|
7057
|
+
// Provider patterns (serialized)
|
|
7058
|
+
patterns: {
|
|
7059
|
+
prompt: this.provider.patterns.prompt.map((p) => p.toString()),
|
|
7060
|
+
generating: this.provider.patterns.generating.map((p) => p.toString()),
|
|
7061
|
+
approval: this.provider.patterns.approval.map((p) => p.toString()),
|
|
7062
|
+
ready: this.provider.patterns.ready.map((p) => p.toString())
|
|
7063
|
+
},
|
|
7064
|
+
// Status history
|
|
7065
|
+
statusHistory: this.statusHistory.slice(-30),
|
|
7066
|
+
// Timeouts config
|
|
7067
|
+
timeouts: this.timeouts,
|
|
7068
|
+
// PTY alive
|
|
7069
|
+
ptyAlive: !!this.ptyProcess
|
|
7070
|
+
};
|
|
7071
|
+
}
|
|
6922
7072
|
};
|
|
6923
7073
|
|
|
6924
7074
|
// src/commands/cli-manager.ts
|
|
@@ -6947,6 +7097,8 @@ var CliProviderInstance = class {
|
|
|
6947
7097
|
generatingStartedAt = 0;
|
|
6948
7098
|
settings = {};
|
|
6949
7099
|
monitor;
|
|
7100
|
+
generatingDebounceTimer = null;
|
|
7101
|
+
generatingDebouncePending = null;
|
|
6950
7102
|
historyWriter;
|
|
6951
7103
|
instanceId;
|
|
6952
7104
|
// ─── Lifecycle ─────────────────────────────────
|
|
@@ -7013,7 +7165,7 @@ var CliProviderInstance = class {
|
|
|
7013
7165
|
activeModal: adapterStatus.activeModal,
|
|
7014
7166
|
inputContent: ""
|
|
7015
7167
|
},
|
|
7016
|
-
|
|
7168
|
+
workspace: this.workingDir,
|
|
7017
7169
|
instanceId: this.instanceId,
|
|
7018
7170
|
lastUpdated: Date.now(),
|
|
7019
7171
|
settings: this.settings,
|
|
@@ -7042,8 +7194,24 @@ var CliProviderInstance = class {
|
|
|
7042
7194
|
LOG.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
|
|
7043
7195
|
if (this.lastStatus === "idle" && newStatus === "generating") {
|
|
7044
7196
|
this.generatingStartedAt = now;
|
|
7045
|
-
this.
|
|
7197
|
+
if (this.generatingDebounceTimer) clearTimeout(this.generatingDebounceTimer);
|
|
7198
|
+
this.generatingDebouncePending = { chatTitle, timestamp: now };
|
|
7199
|
+
this.generatingDebounceTimer = setTimeout(() => {
|
|
7200
|
+
if (this.generatingDebouncePending) {
|
|
7201
|
+
this.pushEvent({ event: "agent:generating_started", ...this.generatingDebouncePending });
|
|
7202
|
+
this.generatingDebouncePending = null;
|
|
7203
|
+
}
|
|
7204
|
+
this.generatingDebounceTimer = null;
|
|
7205
|
+
}, 1e3);
|
|
7046
7206
|
} else if (newStatus === "waiting_approval") {
|
|
7207
|
+
if (this.generatingDebouncePending) {
|
|
7208
|
+
if (this.generatingDebounceTimer) {
|
|
7209
|
+
clearTimeout(this.generatingDebounceTimer);
|
|
7210
|
+
this.generatingDebounceTimer = null;
|
|
7211
|
+
}
|
|
7212
|
+
this.pushEvent({ event: "agent:generating_started", ...this.generatingDebouncePending });
|
|
7213
|
+
this.generatingDebouncePending = null;
|
|
7214
|
+
}
|
|
7047
7215
|
if (!this.generatingStartedAt) this.generatingStartedAt = now;
|
|
7048
7216
|
const modal = adapterStatus.activeModal;
|
|
7049
7217
|
LOG.info("CLI", `[${this.type}] approval modal: "${modal?.message?.slice(0, 80) ?? "none"}"`);
|
|
@@ -7056,10 +7224,24 @@ var CliProviderInstance = class {
|
|
|
7056
7224
|
});
|
|
7057
7225
|
} else if (newStatus === "idle" && (this.lastStatus === "generating" || this.lastStatus === "waiting_approval")) {
|
|
7058
7226
|
const duration = this.generatingStartedAt ? Math.round((now - this.generatingStartedAt) / 1e3) : 0;
|
|
7059
|
-
|
|
7060
|
-
|
|
7227
|
+
if (this.generatingDebouncePending) {
|
|
7228
|
+
LOG.info("CLI", `[${this.type}] suppressed short generating (${now - this.generatingStartedAt}ms)`);
|
|
7229
|
+
if (this.generatingDebounceTimer) {
|
|
7230
|
+
clearTimeout(this.generatingDebounceTimer);
|
|
7231
|
+
this.generatingDebounceTimer = null;
|
|
7232
|
+
}
|
|
7233
|
+
this.generatingDebouncePending = null;
|
|
7234
|
+
} else {
|
|
7235
|
+
LOG.info("CLI", `[${this.type}] completed in ${duration}s`);
|
|
7236
|
+
this.pushEvent({ event: "agent:generating_completed", chatTitle, duration, timestamp: now });
|
|
7237
|
+
}
|
|
7061
7238
|
this.generatingStartedAt = 0;
|
|
7062
7239
|
} else if (newStatus === "stopped") {
|
|
7240
|
+
if (this.generatingDebounceTimer) {
|
|
7241
|
+
clearTimeout(this.generatingDebounceTimer);
|
|
7242
|
+
this.generatingDebounceTimer = null;
|
|
7243
|
+
}
|
|
7244
|
+
this.generatingDebouncePending = null;
|
|
7063
7245
|
this.pushEvent({ event: "agent:stopped", chatTitle, timestamp: now });
|
|
7064
7246
|
}
|
|
7065
7247
|
this.lastStatus = newStatus;
|
|
@@ -7210,7 +7392,7 @@ var AcpProviderInstance = class {
|
|
|
7210
7392
|
} : null,
|
|
7211
7393
|
inputContent: ""
|
|
7212
7394
|
},
|
|
7213
|
-
|
|
7395
|
+
workspace: this.workingDir,
|
|
7214
7396
|
currentModel: this.currentModel,
|
|
7215
7397
|
currentPlan: this.currentMode,
|
|
7216
7398
|
instanceId: this.instanceId,
|
|
@@ -7221,8 +7403,8 @@ var AcpProviderInstance = class {
|
|
|
7221
7403
|
acpConfigOptions: this.configOptions,
|
|
7222
7404
|
acpModes: this.availableModes,
|
|
7223
7405
|
// Error details for dashboard display
|
|
7224
|
-
errorMessage: this.errorMessage,
|
|
7225
|
-
errorReason: this.errorReason
|
|
7406
|
+
errorMessage: this.errorMessage || void 0,
|
|
7407
|
+
errorReason: this.errorReason || void 0
|
|
7226
7408
|
};
|
|
7227
7409
|
}
|
|
7228
7410
|
onEvent(event, data) {
|
|
@@ -7705,29 +7887,27 @@ var AcpProviderInstance = class {
|
|
|
7705
7887
|
handleSessionUpdate(params) {
|
|
7706
7888
|
if (!params) return;
|
|
7707
7889
|
const update = params.update;
|
|
7708
|
-
this.log.debug(`[${this.type}] sessionUpdate: ${update.sessionUpdate}
|
|
7890
|
+
this.log.debug(`[${this.type}] sessionUpdate: ${update.sessionUpdate}`);
|
|
7709
7891
|
switch (update.sessionUpdate) {
|
|
7710
7892
|
case "agent_message_chunk": {
|
|
7711
7893
|
const content = update.content;
|
|
7712
|
-
if (content
|
|
7894
|
+
if (content.type === "text") {
|
|
7713
7895
|
this.partialContent += content.text;
|
|
7714
|
-
} else if (content
|
|
7896
|
+
} else if (content.type === "image") {
|
|
7715
7897
|
this.partialBlocks.push({
|
|
7716
7898
|
type: "image",
|
|
7717
|
-
data: content.data
|
|
7718
|
-
mimeType: content.mimeType
|
|
7719
|
-
uri: content.uri
|
|
7899
|
+
data: content.data,
|
|
7900
|
+
mimeType: content.mimeType
|
|
7720
7901
|
});
|
|
7721
|
-
} else if (content
|
|
7902
|
+
} else if (content.type === "resource_link") {
|
|
7722
7903
|
this.partialBlocks.push({
|
|
7723
7904
|
type: "resource_link",
|
|
7724
|
-
uri: content.uri
|
|
7905
|
+
uri: content.uri,
|
|
7725
7906
|
name: content.name || "resource",
|
|
7726
|
-
title: content.title,
|
|
7727
|
-
mimeType: content.mimeType
|
|
7728
|
-
size: content.size
|
|
7907
|
+
title: content.title ?? void 0,
|
|
7908
|
+
mimeType: content.mimeType ?? void 0
|
|
7729
7909
|
});
|
|
7730
|
-
} else if (content
|
|
7910
|
+
} else if (content.type === "resource") {
|
|
7731
7911
|
this.partialBlocks.push({
|
|
7732
7912
|
type: "resource",
|
|
7733
7913
|
resource: content.resource
|
|
@@ -7899,7 +8079,7 @@ var AcpProviderInstance = class {
|
|
|
7899
8079
|
return { ...b, text: b.text.slice(0, -3) };
|
|
7900
8080
|
}
|
|
7901
8081
|
return b;
|
|
7902
|
-
}).filter((b) => b.type !== "text" || b.text.trim());
|
|
8082
|
+
}).filter((b) => b.type !== "text" || b.type === "text" && b.text.trim());
|
|
7903
8083
|
if (finalBlocks.length > 0) {
|
|
7904
8084
|
this.messages.push({
|
|
7905
8085
|
role: "assistant",
|
|
@@ -9460,6 +9640,8 @@ var DevServer = class _DevServer {
|
|
|
9460
9640
|
server = null;
|
|
9461
9641
|
providerLoader;
|
|
9462
9642
|
cdpManagers;
|
|
9643
|
+
instanceManager;
|
|
9644
|
+
cliManager;
|
|
9463
9645
|
logFn;
|
|
9464
9646
|
sseClients = [];
|
|
9465
9647
|
watchScriptPath = null;
|
|
@@ -9469,9 +9651,13 @@ var DevServer = class _DevServer {
|
|
|
9469
9651
|
autoImplProcess = null;
|
|
9470
9652
|
autoImplSSEClients = [];
|
|
9471
9653
|
autoImplStatus = { running: false, type: null, progress: [] };
|
|
9654
|
+
// CLI debug SSE
|
|
9655
|
+
cliSSEClients = [];
|
|
9472
9656
|
constructor(options) {
|
|
9473
9657
|
this.providerLoader = options.providerLoader;
|
|
9474
9658
|
this.cdpManagers = options.cdpManagers;
|
|
9659
|
+
this.instanceManager = options.instanceManager || null;
|
|
9660
|
+
this.cliManager = options.cliManager || null;
|
|
9475
9661
|
this.logFn = options.logFn || LOG.forComponent("DevServer").asLogFn();
|
|
9476
9662
|
}
|
|
9477
9663
|
log(msg) {
|
|
@@ -9498,6 +9684,15 @@ var DevServer = class _DevServer {
|
|
|
9498
9684
|
{ method: "POST", pattern: "/api/watch/stop", handler: (q, s) => this.handleWatchStop(q, s) },
|
|
9499
9685
|
{ method: "GET", pattern: "/api/watch/events", handler: (q, s) => this.handleSSE(q, s) },
|
|
9500
9686
|
{ method: "POST", pattern: "/api/scaffold", handler: (q, s) => this.handleScaffold(q, s) },
|
|
9687
|
+
// CLI Debug routes
|
|
9688
|
+
{ method: "GET", pattern: "/api/cli/status", handler: (q, s) => this.handleCliStatus(q, s) },
|
|
9689
|
+
{ method: "POST", pattern: "/api/cli/launch", handler: (q, s) => this.handleCliLaunch(q, s) },
|
|
9690
|
+
{ method: "POST", pattern: "/api/cli/send", handler: (q, s) => this.handleCliSend(q, s) },
|
|
9691
|
+
{ method: "POST", pattern: "/api/cli/resolve", handler: (q, s) => this.handleCliResolve(q, s) },
|
|
9692
|
+
{ method: "POST", pattern: "/api/cli/raw", handler: (q, s) => this.handleCliRaw(q, s) },
|
|
9693
|
+
{ method: "POST", pattern: "/api/cli/stop", handler: (q, s) => this.handleCliStop(q, s) },
|
|
9694
|
+
{ method: "GET", pattern: "/api/cli/events", handler: (q, s) => this.handleCliSSE(q, s) },
|
|
9695
|
+
{ method: "GET", pattern: /^\/api\/cli\/debug\/([^/]+)$/, handler: (q, s, p) => this.handleCliDebug(p[0], q, s) },
|
|
9501
9696
|
// Dynamic routes (provider :type param)
|
|
9502
9697
|
{ method: "POST", pattern: /^\/api\/providers\/([^/]+)\/script$/, handler: (q, s, p) => this.handleRunScript(p[0], q, s) },
|
|
9503
9698
|
{ method: "GET", pattern: /^\/api\/providers\/([^/]+)\/files$/, handler: (q, s, p) => this.handleListFiles(p[0], q, s) },
|
|
@@ -11764,6 +11959,251 @@ data: ${JSON.stringify(msg.data)}
|
|
|
11764
11959
|
});
|
|
11765
11960
|
});
|
|
11766
11961
|
}
|
|
11962
|
+
// ─── CLI Debug Handlers ──────────────────────────────
|
|
11963
|
+
/** GET /api/cli/status — list all running CLI/ACP instances with state */
|
|
11964
|
+
async handleCliStatus(_req, res) {
|
|
11965
|
+
if (!this.instanceManager) {
|
|
11966
|
+
this.json(res, 503, { error: "InstanceManager not available (daemon not fully initialized)" });
|
|
11967
|
+
return;
|
|
11968
|
+
}
|
|
11969
|
+
const allStates = this.instanceManager.collectAllStates();
|
|
11970
|
+
const cliStates = allStates.filter((s) => s.category === "cli" || s.category === "acp");
|
|
11971
|
+
const result = cliStates.map((s) => ({
|
|
11972
|
+
instanceId: s.instanceId,
|
|
11973
|
+
type: s.type,
|
|
11974
|
+
name: s.name,
|
|
11975
|
+
category: s.category,
|
|
11976
|
+
status: s.status,
|
|
11977
|
+
mode: s.mode,
|
|
11978
|
+
workspace: s.workspace,
|
|
11979
|
+
messageCount: s.activeChat?.messages?.length || 0,
|
|
11980
|
+
lastMessage: s.activeChat?.messages?.slice(-1)[0] || null,
|
|
11981
|
+
activeModal: s.activeChat?.activeModal || null,
|
|
11982
|
+
pendingEvents: s.pendingEvents || [],
|
|
11983
|
+
currentModel: s.currentModel,
|
|
11984
|
+
settings: s.settings
|
|
11985
|
+
}));
|
|
11986
|
+
this.json(res, 200, { instances: result, count: result.length });
|
|
11987
|
+
}
|
|
11988
|
+
/** POST /api/cli/launch — launch a CLI agent { type, workingDir?, args? } */
|
|
11989
|
+
async handleCliLaunch(req, res) {
|
|
11990
|
+
if (!this.cliManager) {
|
|
11991
|
+
this.json(res, 503, { error: "CliManager not available" });
|
|
11992
|
+
return;
|
|
11993
|
+
}
|
|
11994
|
+
const body = await this.readBody(req);
|
|
11995
|
+
const { type, workingDir, args } = body;
|
|
11996
|
+
if (!type) {
|
|
11997
|
+
this.json(res, 400, { error: "type required (e.g. claude-cli, gemini-cli)" });
|
|
11998
|
+
return;
|
|
11999
|
+
}
|
|
12000
|
+
try {
|
|
12001
|
+
await this.cliManager.startSession(type, workingDir || process.cwd(), args || []);
|
|
12002
|
+
this.json(res, 200, { launched: true, type, workspace: workingDir || process.cwd() });
|
|
12003
|
+
} catch (e) {
|
|
12004
|
+
this.json(res, 500, { error: `Launch failed: ${e.message}` });
|
|
12005
|
+
}
|
|
12006
|
+
}
|
|
12007
|
+
/** POST /api/cli/send — send message to a running CLI { type, text } */
|
|
12008
|
+
async handleCliSend(req, res) {
|
|
12009
|
+
if (!this.instanceManager) {
|
|
12010
|
+
this.json(res, 503, { error: "InstanceManager not available" });
|
|
12011
|
+
return;
|
|
12012
|
+
}
|
|
12013
|
+
const body = await this.readBody(req);
|
|
12014
|
+
const { type, text, instanceId } = body;
|
|
12015
|
+
if (!text) {
|
|
12016
|
+
this.json(res, 400, { error: "text required" });
|
|
12017
|
+
return;
|
|
12018
|
+
}
|
|
12019
|
+
const allStates = this.instanceManager.collectAllStates();
|
|
12020
|
+
const target = allStates.find(
|
|
12021
|
+
(s) => (s.category === "cli" || s.category === "acp") && (instanceId ? s.instanceId === instanceId : s.type === type)
|
|
12022
|
+
);
|
|
12023
|
+
if (!target) {
|
|
12024
|
+
this.json(res, 404, { error: `No running instance found for: ${type || instanceId}` });
|
|
12025
|
+
return;
|
|
12026
|
+
}
|
|
12027
|
+
try {
|
|
12028
|
+
this.instanceManager.sendEvent(target.instanceId, "send_message", { text });
|
|
12029
|
+
this.json(res, 200, { sent: true, type: target.type, instanceId: target.instanceId });
|
|
12030
|
+
} catch (e) {
|
|
12031
|
+
this.json(res, 500, { error: `Send failed: ${e.message}` });
|
|
12032
|
+
}
|
|
12033
|
+
}
|
|
12034
|
+
/** POST /api/cli/stop — stop a running CLI { type } */
|
|
12035
|
+
async handleCliStop(req, res) {
|
|
12036
|
+
if (!this.instanceManager) {
|
|
12037
|
+
this.json(res, 503, { error: "InstanceManager not available" });
|
|
12038
|
+
return;
|
|
12039
|
+
}
|
|
12040
|
+
const body = await this.readBody(req);
|
|
12041
|
+
const { type, instanceId } = body;
|
|
12042
|
+
const allStates = this.instanceManager.collectAllStates();
|
|
12043
|
+
const target = allStates.find(
|
|
12044
|
+
(s) => (s.category === "cli" || s.category === "acp") && (instanceId ? s.instanceId === instanceId : s.type === type)
|
|
12045
|
+
);
|
|
12046
|
+
if (!target) {
|
|
12047
|
+
this.json(res, 404, { error: `No running instance found for: ${type || instanceId}` });
|
|
12048
|
+
return;
|
|
12049
|
+
}
|
|
12050
|
+
try {
|
|
12051
|
+
this.instanceManager.removeInstance(target.instanceId);
|
|
12052
|
+
this.json(res, 200, { stopped: true, type: target.type, instanceId: target.instanceId });
|
|
12053
|
+
} catch (e) {
|
|
12054
|
+
this.json(res, 500, { error: `Stop failed: ${e.message}` });
|
|
12055
|
+
}
|
|
12056
|
+
}
|
|
12057
|
+
/** GET /api/cli/events — SSE stream of CLI status events */
|
|
12058
|
+
handleCliSSE(_req, res) {
|
|
12059
|
+
res.writeHead(200, {
|
|
12060
|
+
"Content-Type": "text/event-stream",
|
|
12061
|
+
"Cache-Control": "no-cache",
|
|
12062
|
+
"Connection": "keep-alive",
|
|
12063
|
+
"Access-Control-Allow-Origin": "*"
|
|
12064
|
+
});
|
|
12065
|
+
res.write('data: {"type":"connected"}\n\n');
|
|
12066
|
+
this.cliSSEClients.push(res);
|
|
12067
|
+
if (this.cliSSEClients.length === 1 && this.instanceManager) {
|
|
12068
|
+
this.instanceManager.onEvent((event) => {
|
|
12069
|
+
this.sendCliSSE(event);
|
|
12070
|
+
});
|
|
12071
|
+
}
|
|
12072
|
+
if (this.instanceManager) {
|
|
12073
|
+
const allStates = this.instanceManager.collectAllStates();
|
|
12074
|
+
const cliStates = allStates.filter((s) => s.category === "cli" || s.category === "acp");
|
|
12075
|
+
for (const s of cliStates) {
|
|
12076
|
+
this.sendCliSSE({ event: "snapshot", providerType: s.type, status: s.status, instanceId: s.instanceId });
|
|
12077
|
+
}
|
|
12078
|
+
}
|
|
12079
|
+
_req.on("close", () => {
|
|
12080
|
+
this.cliSSEClients = this.cliSSEClients.filter((c) => c !== res);
|
|
12081
|
+
});
|
|
12082
|
+
}
|
|
12083
|
+
sendCliSSE(data) {
|
|
12084
|
+
const msg = `data: ${JSON.stringify({ ...data, timestamp: Date.now() })}
|
|
12085
|
+
|
|
12086
|
+
`;
|
|
12087
|
+
for (const client of this.cliSSEClients) {
|
|
12088
|
+
try {
|
|
12089
|
+
client.write(msg);
|
|
12090
|
+
} catch {
|
|
12091
|
+
}
|
|
12092
|
+
}
|
|
12093
|
+
}
|
|
12094
|
+
/** GET /api/cli/debug/:type — full internal debug state of a CLI adapter */
|
|
12095
|
+
async handleCliDebug(type, _req, res) {
|
|
12096
|
+
if (!this.instanceManager) {
|
|
12097
|
+
this.json(res, 503, { error: "InstanceManager not available" });
|
|
12098
|
+
return;
|
|
12099
|
+
}
|
|
12100
|
+
const allStates = this.instanceManager.collectAllStates();
|
|
12101
|
+
const target = allStates.find(
|
|
12102
|
+
(s) => (s.category === "cli" || s.category === "acp") && s.type === type
|
|
12103
|
+
);
|
|
12104
|
+
if (!target) {
|
|
12105
|
+
this.json(res, 404, { error: `No running instance for: ${type}`, available: allStates.filter((s) => s.category === "cli" || s.category === "acp").map((s) => s.type) });
|
|
12106
|
+
return;
|
|
12107
|
+
}
|
|
12108
|
+
const instance = this.instanceManager.getInstance(target.instanceId);
|
|
12109
|
+
if (!instance) {
|
|
12110
|
+
this.json(res, 404, { error: `Instance not found: ${target.instanceId}` });
|
|
12111
|
+
return;
|
|
12112
|
+
}
|
|
12113
|
+
try {
|
|
12114
|
+
const adapter = instance.getAdapter?.() || instance.adapter;
|
|
12115
|
+
if (adapter && typeof adapter.getDebugState === "function") {
|
|
12116
|
+
const debugState = adapter.getDebugState();
|
|
12117
|
+
this.json(res, 200, {
|
|
12118
|
+
instanceId: target.instanceId,
|
|
12119
|
+
providerState: {
|
|
12120
|
+
type: target.type,
|
|
12121
|
+
name: target.name,
|
|
12122
|
+
status: target.status,
|
|
12123
|
+
mode: "mode" in target ? target.mode : void 0
|
|
12124
|
+
},
|
|
12125
|
+
debug: debugState
|
|
12126
|
+
});
|
|
12127
|
+
} else {
|
|
12128
|
+
this.json(res, 200, {
|
|
12129
|
+
instanceId: target.instanceId,
|
|
12130
|
+
providerState: target,
|
|
12131
|
+
debug: null,
|
|
12132
|
+
message: "No debug state available (adapter.getDebugState not found)"
|
|
12133
|
+
});
|
|
12134
|
+
}
|
|
12135
|
+
} catch (e) {
|
|
12136
|
+
this.json(res, 500, { error: `Debug state failed: ${e.message}` });
|
|
12137
|
+
}
|
|
12138
|
+
}
|
|
12139
|
+
/** POST /api/cli/resolve — resolve an approval modal { type, buttonIndex } */
|
|
12140
|
+
async handleCliResolve(req, res) {
|
|
12141
|
+
const body = await this.readBody(req);
|
|
12142
|
+
const { type, buttonIndex, instanceId } = body;
|
|
12143
|
+
if (buttonIndex === void 0 || buttonIndex === null) {
|
|
12144
|
+
this.json(res, 400, { error: "buttonIndex required (0=Yes, 1=Always, 2=Deny)" });
|
|
12145
|
+
return;
|
|
12146
|
+
}
|
|
12147
|
+
if (!this.cliManager) {
|
|
12148
|
+
this.json(res, 503, { error: "CliManager not available" });
|
|
12149
|
+
return;
|
|
12150
|
+
}
|
|
12151
|
+
let adapter = null;
|
|
12152
|
+
for (const [, a] of this.cliManager.adapters) {
|
|
12153
|
+
if (type && a.cliType === type) {
|
|
12154
|
+
adapter = a;
|
|
12155
|
+
break;
|
|
12156
|
+
}
|
|
12157
|
+
}
|
|
12158
|
+
if (!adapter) {
|
|
12159
|
+
this.json(res, 404, { error: `No running adapter for: ${type || instanceId}` });
|
|
12160
|
+
return;
|
|
12161
|
+
}
|
|
12162
|
+
try {
|
|
12163
|
+
if (typeof adapter.resolveModal === "function") {
|
|
12164
|
+
adapter.resolveModal(buttonIndex);
|
|
12165
|
+
this.json(res, 200, { resolved: true, type, buttonIndex });
|
|
12166
|
+
} else {
|
|
12167
|
+
this.json(res, 400, { error: "resolveModal not available on this adapter" });
|
|
12168
|
+
}
|
|
12169
|
+
} catch (e) {
|
|
12170
|
+
this.json(res, 500, { error: `Resolve failed: ${e.message}` });
|
|
12171
|
+
}
|
|
12172
|
+
}
|
|
12173
|
+
/** POST /api/cli/raw — send raw keystrokes to PTY { type, keys } */
|
|
12174
|
+
async handleCliRaw(req, res) {
|
|
12175
|
+
const body = await this.readBody(req);
|
|
12176
|
+
const { type, keys, instanceId } = body;
|
|
12177
|
+
if (!keys) {
|
|
12178
|
+
this.json(res, 400, { error: "keys required (raw string to send to PTY)" });
|
|
12179
|
+
return;
|
|
12180
|
+
}
|
|
12181
|
+
if (!this.cliManager) {
|
|
12182
|
+
this.json(res, 503, { error: "CliManager not available" });
|
|
12183
|
+
return;
|
|
12184
|
+
}
|
|
12185
|
+
let adapter = null;
|
|
12186
|
+
for (const [, a] of this.cliManager.adapters) {
|
|
12187
|
+
if (type && a.cliType === type) {
|
|
12188
|
+
adapter = a;
|
|
12189
|
+
break;
|
|
12190
|
+
}
|
|
12191
|
+
}
|
|
12192
|
+
if (!adapter) {
|
|
12193
|
+
this.json(res, 404, { error: `No running adapter for: ${type || instanceId}` });
|
|
12194
|
+
return;
|
|
12195
|
+
}
|
|
12196
|
+
try {
|
|
12197
|
+
if (typeof adapter.writeRaw === "function") {
|
|
12198
|
+
adapter.writeRaw(keys);
|
|
12199
|
+
this.json(res, 200, { sent: true, type, keysLength: keys.length });
|
|
12200
|
+
} else {
|
|
12201
|
+
this.json(res, 400, { error: "writeRaw not available on this adapter" });
|
|
12202
|
+
}
|
|
12203
|
+
} catch (e) {
|
|
12204
|
+
this.json(res, 500, { error: `Raw send failed: ${e.message}` });
|
|
12205
|
+
}
|
|
12206
|
+
}
|
|
11767
12207
|
};
|
|
11768
12208
|
|
|
11769
12209
|
// src/installer.ts
|
|
@@ -12113,7 +12553,9 @@ async function shutdownDaemonComponents(components) {
|
|
|
12113
12553
|
installExtensions,
|
|
12114
12554
|
installGlobalInterceptor,
|
|
12115
12555
|
isExtensionInstalled,
|
|
12556
|
+
isIdeRunning,
|
|
12116
12557
|
isSetupComplete,
|
|
12558
|
+
killIdeProcess,
|
|
12117
12559
|
launchIDE,
|
|
12118
12560
|
launchWithCdp,
|
|
12119
12561
|
loadConfig,
|