@adhdev/daemon-core 0.8.27 → 0.8.29
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/agent-stream/manager.d.ts +1 -1
- package/dist/agent-stream/provider-adapter.d.ts +5 -0
- package/dist/commands/handler.d.ts +1 -0
- package/dist/commands/router.d.ts +5 -0
- package/dist/commands/stream-commands.d.ts +1 -1
- package/dist/config/chat-history.d.ts +12 -0
- package/dist/detection/cli-detector.d.ts +6 -2
- package/dist/detection/ide-detector.d.ts +2 -1
- package/dist/index.js +987 -377
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +985 -375
- package/dist/index.mjs.map +1 -1
- package/dist/providers/acp-provider-instance.d.ts +1 -1
- package/dist/providers/cli-provider-instance.d.ts +1 -0
- package/dist/providers/provider-loader.d.ts +26 -0
- package/dist/shared-types.d.ts +2 -0
- package/dist/status/snapshot.d.ts +8 -0
- package/node_modules/@adhdev/session-host-core/dist/index.d.mts +24 -1
- package/node_modules/@adhdev/session-host-core/dist/index.d.ts +24 -1
- package/node_modules/@adhdev/session-host-core/dist/index.js +6 -1
- package/node_modules/@adhdev/session-host-core/dist/index.js.map +1 -1
- package/node_modules/@adhdev/session-host-core/dist/index.mjs +6 -1
- package/node_modules/@adhdev/session-host-core/dist/index.mjs.map +1 -1
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/agent-stream/manager.ts +2 -2
- package/src/agent-stream/provider-adapter.ts +111 -4
- package/src/boot/daemon-lifecycle.ts +28 -1
- package/src/cli-adapters/provider-cli-adapter.ts +17 -3
- package/src/commands/chat-commands.ts +89 -10
- package/src/commands/cli-manager.ts +16 -2
- package/src/commands/handler.ts +1 -0
- package/src/commands/router.ts +23 -1
- package/src/commands/stream-commands.ts +6 -3
- package/src/config/chat-history.ts +269 -18
- package/src/detection/cli-detector.ts +72 -29
- package/src/detection/ide-detector.ts +24 -8
- package/src/launch.ts +1 -1
- package/src/providers/acp-provider-instance.ts +19 -10
- package/src/providers/cli-provider-instance.ts +17 -2
- package/src/providers/provider-loader.ts +144 -11
- package/src/shared-types.ts +2 -0
- package/src/status/snapshot.ts +19 -1
package/dist/index.js
CHANGED
|
@@ -251,13 +251,13 @@ function getDaemonLogDir() {
|
|
|
251
251
|
return LOG_DIR;
|
|
252
252
|
}
|
|
253
253
|
function getCurrentDaemonLogPath(date = /* @__PURE__ */ new Date()) {
|
|
254
|
-
return
|
|
254
|
+
return path6.join(LOG_DIR, `daemon-${date.toISOString().slice(0, 10)}.log`);
|
|
255
255
|
}
|
|
256
256
|
function checkDateRotation() {
|
|
257
257
|
const today = getDateStr();
|
|
258
258
|
if (today !== currentDate) {
|
|
259
259
|
currentDate = today;
|
|
260
|
-
currentLogFile =
|
|
260
|
+
currentLogFile = path6.join(LOG_DIR, `daemon-${currentDate}.log`);
|
|
261
261
|
cleanOldLogs();
|
|
262
262
|
}
|
|
263
263
|
}
|
|
@@ -271,7 +271,7 @@ function cleanOldLogs() {
|
|
|
271
271
|
const dateMatch = file.match(/daemon-(\d{4}-\d{2}-\d{2})/);
|
|
272
272
|
if (dateMatch && dateMatch[1] < cutoffStr) {
|
|
273
273
|
try {
|
|
274
|
-
fs2.unlinkSync(
|
|
274
|
+
fs2.unlinkSync(path6.join(LOG_DIR, file));
|
|
275
275
|
} catch {
|
|
276
276
|
}
|
|
277
277
|
}
|
|
@@ -388,17 +388,17 @@ function installGlobalInterceptor() {
|
|
|
388
388
|
writeToFile(`Log file: ${currentLogFile}`);
|
|
389
389
|
writeToFile(`Log level: ${currentLevel}`);
|
|
390
390
|
}
|
|
391
|
-
var fs2,
|
|
391
|
+
var fs2, path6, os4, LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH;
|
|
392
392
|
var init_logger = __esm({
|
|
393
393
|
"src/logging/logger.ts"() {
|
|
394
394
|
"use strict";
|
|
395
395
|
fs2 = __toESM(require("fs"));
|
|
396
|
-
|
|
396
|
+
path6 = __toESM(require("path"));
|
|
397
397
|
os4 = __toESM(require("os"));
|
|
398
398
|
LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
399
399
|
LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
|
|
400
400
|
currentLevel = "info";
|
|
401
|
-
LOG_DIR = process.platform === "win32" ?
|
|
401
|
+
LOG_DIR = process.platform === "win32" ? path6.join(process.env.LOCALAPPDATA || process.env.APPDATA || path6.join(os4.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path6.join(os4.homedir(), "Library", "Logs", "adhdev") : path6.join(os4.homedir(), ".local", "share", "adhdev", "logs");
|
|
402
402
|
MAX_LOG_SIZE = 5 * 1024 * 1024;
|
|
403
403
|
MAX_LOG_DAYS = 7;
|
|
404
404
|
try {
|
|
@@ -406,16 +406,16 @@ var init_logger = __esm({
|
|
|
406
406
|
} catch {
|
|
407
407
|
}
|
|
408
408
|
currentDate = getDateStr();
|
|
409
|
-
currentLogFile =
|
|
409
|
+
currentLogFile = path6.join(LOG_DIR, `daemon-${currentDate}.log`);
|
|
410
410
|
cleanOldLogs();
|
|
411
411
|
try {
|
|
412
|
-
const oldLog =
|
|
412
|
+
const oldLog = path6.join(LOG_DIR, "daemon.log");
|
|
413
413
|
if (fs2.existsSync(oldLog)) {
|
|
414
414
|
const stat = fs2.statSync(oldLog);
|
|
415
415
|
const oldDate = stat.mtime.toISOString().slice(0, 10);
|
|
416
|
-
fs2.renameSync(oldLog,
|
|
416
|
+
fs2.renameSync(oldLog, path6.join(LOG_DIR, `daemon-${oldDate}.log`));
|
|
417
417
|
}
|
|
418
|
-
const oldLogBackup =
|
|
418
|
+
const oldLogBackup = path6.join(LOG_DIR, "daemon.log.old");
|
|
419
419
|
if (fs2.existsSync(oldLogBackup)) {
|
|
420
420
|
fs2.unlinkSync(oldLogBackup);
|
|
421
421
|
}
|
|
@@ -447,7 +447,7 @@ var init_logger = __esm({
|
|
|
447
447
|
}
|
|
448
448
|
};
|
|
449
449
|
interceptorInstalled = false;
|
|
450
|
-
LOG_PATH =
|
|
450
|
+
LOG_PATH = path6.join(LOG_DIR, `daemon-${getDateStr()}.log`);
|
|
451
451
|
}
|
|
452
452
|
});
|
|
453
453
|
|
|
@@ -872,16 +872,22 @@ function computeTerminalQueryTail(buffer) {
|
|
|
872
872
|
return "";
|
|
873
873
|
}
|
|
874
874
|
function findBinary(name) {
|
|
875
|
+
const trimmed = String(name || "").trim();
|
|
876
|
+
if (!trimmed) return trimmed;
|
|
877
|
+
const expanded = trimmed.startsWith("~") ? path9.join(os8.homedir(), trimmed.slice(1)) : trimmed;
|
|
878
|
+
if (path9.isAbsolute(expanded) || expanded.includes("/") || expanded.includes("\\")) {
|
|
879
|
+
return path9.isAbsolute(expanded) ? expanded : path9.resolve(expanded);
|
|
880
|
+
}
|
|
875
881
|
const isWin = os8.platform() === "win32";
|
|
876
882
|
try {
|
|
877
|
-
const cmd = isWin ? `where ${
|
|
883
|
+
const cmd = isWin ? `where ${trimmed}` : `which ${trimmed}`;
|
|
878
884
|
return (0, import_child_process4.execSync)(cmd, { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }).trim().split("\n")[0].trim();
|
|
879
885
|
} catch {
|
|
880
|
-
return isWin ? `${
|
|
886
|
+
return isWin ? `${trimmed}.cmd` : trimmed;
|
|
881
887
|
}
|
|
882
888
|
}
|
|
883
889
|
function isScriptBinary(binaryPath) {
|
|
884
|
-
if (!
|
|
890
|
+
if (!path9.isAbsolute(binaryPath)) return false;
|
|
885
891
|
try {
|
|
886
892
|
const fs15 = require("fs");
|
|
887
893
|
const resolved = fs15.realpathSync(binaryPath);
|
|
@@ -897,7 +903,7 @@ function isScriptBinary(binaryPath) {
|
|
|
897
903
|
}
|
|
898
904
|
}
|
|
899
905
|
function looksLikeMachOOrElf(filePath) {
|
|
900
|
-
if (!
|
|
906
|
+
if (!path9.isAbsolute(filePath)) return false;
|
|
901
907
|
try {
|
|
902
908
|
const fs15 = require("fs");
|
|
903
909
|
const resolved = fs15.realpathSync(filePath);
|
|
@@ -1021,12 +1027,12 @@ function normalizeCliProviderForRuntime(raw) {
|
|
|
1021
1027
|
}
|
|
1022
1028
|
};
|
|
1023
1029
|
}
|
|
1024
|
-
var os8,
|
|
1030
|
+
var os8, path9, import_child_process4, buildCliSpawnEnv, ProviderCliAdapter;
|
|
1025
1031
|
var init_provider_cli_adapter = __esm({
|
|
1026
1032
|
"src/cli-adapters/provider-cli-adapter.ts"() {
|
|
1027
1033
|
"use strict";
|
|
1028
1034
|
os8 = __toESM(require("os"));
|
|
1029
|
-
|
|
1035
|
+
path9 = __toESM(require("path"));
|
|
1030
1036
|
import_child_process4 = require("child_process");
|
|
1031
1037
|
init_logger();
|
|
1032
1038
|
init_terminal_screen();
|
|
@@ -1381,16 +1387,17 @@ var init_provider_cli_adapter = __esm({
|
|
|
1381
1387
|
async spawn() {
|
|
1382
1388
|
if (this.ptyProcess) return;
|
|
1383
1389
|
const { spawn: spawnConfig } = this.provider;
|
|
1384
|
-
const
|
|
1390
|
+
const configuredCommand = typeof this.runtimeSettings.executablePath === "string" && this.runtimeSettings.executablePath.trim() ? this.runtimeSettings.executablePath.trim() : spawnConfig.command;
|
|
1391
|
+
const binaryPath = findBinary(configuredCommand);
|
|
1385
1392
|
const isWin = os8.platform() === "win32";
|
|
1386
1393
|
const allArgs = [...spawnConfig.args, ...this.extraArgs];
|
|
1387
1394
|
LOG.info("CLI", `[${this.cliType}] Spawning in ${this.workingDir}`);
|
|
1388
1395
|
this.resetTraceSession();
|
|
1389
1396
|
let shellCmd;
|
|
1390
1397
|
let shellArgs;
|
|
1391
|
-
const useShellUnix = !isWin && (!!spawnConfig.shell || !
|
|
1398
|
+
const useShellUnix = !isWin && (!!spawnConfig.shell || !path9.isAbsolute(binaryPath) || isScriptBinary(binaryPath) || !looksLikeMachOOrElf(binaryPath));
|
|
1392
1399
|
const isCmdShim = isWin && /\.(cmd|bat)$/i.test(binaryPath);
|
|
1393
|
-
const useShellWin = !!spawnConfig.shell || isCmdShim || !
|
|
1400
|
+
const useShellWin = !!spawnConfig.shell || isCmdShim || !path9.isAbsolute(binaryPath) || isScriptBinary(binaryPath);
|
|
1394
1401
|
const useShell = isWin ? useShellWin : useShellUnix;
|
|
1395
1402
|
if (useShell) {
|
|
1396
1403
|
if (!spawnConfig.shell && !isWin) {
|
|
@@ -1635,6 +1642,9 @@ var init_provider_cli_adapter = __esm({
|
|
|
1635
1642
|
looksLikeVisibleIdlePrompt(screenText) {
|
|
1636
1643
|
const text = String(screenText || "");
|
|
1637
1644
|
if (!text.trim()) return false;
|
|
1645
|
+
if (this.cliType === "codex-cli" && /(^|\n)\s*[❯›>]\s+(?:Find and fix a bug in @filename|Improve documentation in @filename|Use \/skills|Write tests for @filename|Explain this codebase|Summarize recent commits|Implement \{feature\}|Run \/review on my current changes)(?:\n|$)/im.test(text)) {
|
|
1646
|
+
return true;
|
|
1647
|
+
}
|
|
1638
1648
|
return /(^|\n)\s*[❯›>]\s*(?:\n|$)/m.test(text) || /⏎\s+send/i.test(text) || /\?\s*for\s*shortcuts/i.test(text) || /Type your message(?:\s+or\s+@path\/to\/file)?/i.test(text) || /workspace\s*\(\/directory\)/i.test(text) || /for\s*shortcuts/i.test(text);
|
|
1639
1649
|
}
|
|
1640
1650
|
findLastMatchingLineIndex(lines, predicate) {
|
|
@@ -1764,7 +1774,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
1764
1774
|
`[${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)}`
|
|
1765
1775
|
);
|
|
1766
1776
|
}
|
|
1767
|
-
await new Promise((
|
|
1777
|
+
await new Promise((resolve12) => setTimeout(resolve12, 50));
|
|
1768
1778
|
}
|
|
1769
1779
|
const finalScreenText = this.terminalScreen.getText() || "";
|
|
1770
1780
|
LOG.warn(
|
|
@@ -2275,7 +2285,7 @@ ${data.message || ""}`.trim();
|
|
|
2275
2285
|
const deadline = Date.now() + 1e4;
|
|
2276
2286
|
while (this.startupParseGate && Date.now() < deadline) {
|
|
2277
2287
|
this.resolveStartupState("send_wait");
|
|
2278
|
-
await new Promise((
|
|
2288
|
+
await new Promise((resolve12) => setTimeout(resolve12, 50));
|
|
2279
2289
|
}
|
|
2280
2290
|
}
|
|
2281
2291
|
await this.waitForInteractivePrompt();
|
|
@@ -2500,17 +2510,17 @@ ${data.message || ""}`.trim();
|
|
|
2500
2510
|
}
|
|
2501
2511
|
}
|
|
2502
2512
|
waitForStopped(timeoutMs) {
|
|
2503
|
-
return new Promise((
|
|
2513
|
+
return new Promise((resolve12) => {
|
|
2504
2514
|
const startedAt = Date.now();
|
|
2505
2515
|
const timer = setInterval(() => {
|
|
2506
2516
|
if (!this.ptyProcess || this.currentStatus === "stopped") {
|
|
2507
2517
|
clearInterval(timer);
|
|
2508
|
-
|
|
2518
|
+
resolve12(true);
|
|
2509
2519
|
return;
|
|
2510
2520
|
}
|
|
2511
2521
|
if (Date.now() - startedAt >= timeoutMs) {
|
|
2512
2522
|
clearInterval(timer);
|
|
2513
|
-
|
|
2523
|
+
resolve12(false);
|
|
2514
2524
|
}
|
|
2515
2525
|
}, 100);
|
|
2516
2526
|
});
|
|
@@ -3209,6 +3219,7 @@ function resetState() {
|
|
|
3209
3219
|
var import_child_process = require("child_process");
|
|
3210
3220
|
var import_fs3 = require("fs");
|
|
3211
3221
|
var import_os2 = require("os");
|
|
3222
|
+
var path4 = __toESM(require("path"));
|
|
3212
3223
|
var BUILTIN_IDE_DEFINITIONS = [];
|
|
3213
3224
|
var registeredIDEs = /* @__PURE__ */ new Map();
|
|
3214
3225
|
function registerIDEDefinition(def) {
|
|
@@ -3225,9 +3236,16 @@ function getMergedDefinitions() {
|
|
|
3225
3236
|
return [...merged.values()];
|
|
3226
3237
|
}
|
|
3227
3238
|
function findCliCommand(command) {
|
|
3239
|
+
const trimmed = String(command || "").trim();
|
|
3240
|
+
if (!trimmed) return null;
|
|
3241
|
+
if (path4.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
|
|
3242
|
+
const candidate = trimmed.startsWith("~") ? path4.join((0, import_os2.homedir)(), trimmed.slice(1)) : trimmed;
|
|
3243
|
+
const resolved = path4.isAbsolute(candidate) ? candidate : path4.resolve(candidate);
|
|
3244
|
+
return (0, import_fs3.existsSync)(resolved) ? resolved : null;
|
|
3245
|
+
}
|
|
3228
3246
|
try {
|
|
3229
3247
|
const result = (0, import_child_process.execSync)(
|
|
3230
|
-
(0, import_os2.platform)() === "win32" ? `where ${
|
|
3248
|
+
(0, import_os2.platform)() === "win32" ? `where ${trimmed}` : `which ${trimmed}`,
|
|
3231
3249
|
{ encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }
|
|
3232
3250
|
).trim();
|
|
3233
3251
|
return result.split("\n")[0] || null;
|
|
@@ -3250,23 +3268,23 @@ function getIdeVersion(cliCommand) {
|
|
|
3250
3268
|
function checkPathExists(paths) {
|
|
3251
3269
|
const home = (0, import_os2.homedir)();
|
|
3252
3270
|
for (const p of paths) {
|
|
3253
|
-
|
|
3271
|
+
const normalized = p.startsWith("~") ? path4.join(home, p.slice(1)) : p;
|
|
3272
|
+
if (normalized.includes("*")) {
|
|
3254
3273
|
const username = home.split(/[\\/]/).pop() || "";
|
|
3255
|
-
const resolved =
|
|
3274
|
+
const resolved = normalized.replace("*", username);
|
|
3256
3275
|
if ((0, import_fs3.existsSync)(resolved)) return resolved;
|
|
3257
3276
|
} else {
|
|
3258
|
-
if ((0, import_fs3.existsSync)(
|
|
3277
|
+
if ((0, import_fs3.existsSync)(normalized)) return normalized;
|
|
3259
3278
|
}
|
|
3260
3279
|
}
|
|
3261
3280
|
return null;
|
|
3262
3281
|
}
|
|
3263
|
-
async function detectIDEs() {
|
|
3282
|
+
async function detectIDEs(providerLoader) {
|
|
3264
3283
|
const os18 = (0, import_os2.platform)();
|
|
3265
3284
|
const results = [];
|
|
3266
3285
|
for (const def of getMergedDefinitions()) {
|
|
3267
|
-
const cliPath = findCliCommand(def.cli);
|
|
3268
|
-
const appPath = checkPathExists(def.paths[os18] || []);
|
|
3269
|
-
const installed = !!(cliPath || appPath);
|
|
3286
|
+
const cliPath = findCliCommand(providerLoader?.getIdeCliCommand(def.id, def.cli) || def.cli);
|
|
3287
|
+
const appPath = checkPathExists(providerLoader?.getIdePathCandidates(def.id, def.paths[os18] || []) || []);
|
|
3270
3288
|
let resolvedCli = cliPath;
|
|
3271
3289
|
if (!resolvedCli && appPath && os18 === "darwin") {
|
|
3272
3290
|
const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
|
|
@@ -3289,6 +3307,7 @@ async function detectIDEs() {
|
|
|
3289
3307
|
}
|
|
3290
3308
|
}
|
|
3291
3309
|
}
|
|
3310
|
+
const installed = os18 === "darwin" ? !!(resolvedCli || appPath) : !!resolvedCli;
|
|
3292
3311
|
const version = resolvedCli ? getIdeVersion(resolvedCli) : null;
|
|
3293
3312
|
results.push({
|
|
3294
3313
|
id: def.id,
|
|
@@ -3307,48 +3326,77 @@ async function detectIDEs() {
|
|
|
3307
3326
|
// src/detection/cli-detector.ts
|
|
3308
3327
|
var import_child_process2 = require("child_process");
|
|
3309
3328
|
var os2 = __toESM(require("os"));
|
|
3329
|
+
var path5 = __toESM(require("path"));
|
|
3330
|
+
var import_fs4 = require("fs");
|
|
3310
3331
|
function parseVersion(raw) {
|
|
3311
3332
|
const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
|
|
3312
3333
|
return match ? match[1] : raw.split("\n")[0].slice(0, 100);
|
|
3313
3334
|
}
|
|
3335
|
+
function shellQuote(value) {
|
|
3336
|
+
if (/^[a-zA-Z0-9_./:@%+=,-]+$/.test(value)) return value;
|
|
3337
|
+
return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
3338
|
+
}
|
|
3339
|
+
function expandHome(value) {
|
|
3340
|
+
const trimmed = value.trim();
|
|
3341
|
+
if (!trimmed.startsWith("~")) return trimmed;
|
|
3342
|
+
return path5.join(os2.homedir(), trimmed.slice(1));
|
|
3343
|
+
}
|
|
3344
|
+
function isExplicitCommandPath(command) {
|
|
3345
|
+
const trimmed = command.trim();
|
|
3346
|
+
return path5.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
|
|
3347
|
+
}
|
|
3348
|
+
function resolveCommandPath(command) {
|
|
3349
|
+
const trimmed = command.trim();
|
|
3350
|
+
if (!trimmed) return null;
|
|
3351
|
+
if (isExplicitCommandPath(trimmed)) {
|
|
3352
|
+
const expanded = expandHome(trimmed);
|
|
3353
|
+
const candidate = path5.isAbsolute(expanded) ? expanded : path5.resolve(expanded);
|
|
3354
|
+
return (0, import_fs4.existsSync)(candidate) ? candidate : null;
|
|
3355
|
+
}
|
|
3356
|
+
return null;
|
|
3357
|
+
}
|
|
3314
3358
|
function execAsync(cmd, timeoutMs = 5e3) {
|
|
3315
|
-
return new Promise((
|
|
3359
|
+
return new Promise((resolve12) => {
|
|
3316
3360
|
const child = (0, import_child_process2.exec)(cmd, { encoding: "utf-8", timeout: timeoutMs }, (err, stdout) => {
|
|
3317
3361
|
if (err || !stdout?.trim()) {
|
|
3318
|
-
|
|
3362
|
+
resolve12(null);
|
|
3319
3363
|
} else {
|
|
3320
|
-
|
|
3364
|
+
resolve12(stdout.trim());
|
|
3321
3365
|
}
|
|
3322
3366
|
});
|
|
3323
|
-
child.on("error", () =>
|
|
3367
|
+
child.on("error", () => resolve12(null));
|
|
3324
3368
|
});
|
|
3325
3369
|
}
|
|
3326
|
-
async function detectCLIs(providerLoader) {
|
|
3370
|
+
async function detectCLIs(providerLoader, options) {
|
|
3327
3371
|
const platform9 = os2.platform();
|
|
3328
3372
|
const whichCmd = platform9 === "win32" ? "where" : "which";
|
|
3373
|
+
const includeVersion = options?.includeVersion !== false;
|
|
3329
3374
|
const cliList = providerLoader ? providerLoader.getCliDetectionList() : [];
|
|
3330
3375
|
const results = await Promise.all(
|
|
3331
3376
|
cliList.map(async (cli) => {
|
|
3332
3377
|
try {
|
|
3333
|
-
const
|
|
3378
|
+
const explicitPath = resolveCommandPath(cli.command);
|
|
3379
|
+
const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(cli.command)}`);
|
|
3334
3380
|
if (!pathResult) return { ...cli, installed: false };
|
|
3335
|
-
const firstPath = pathResult.split("\n")[0];
|
|
3381
|
+
const firstPath = explicitPath || pathResult.split("\n")[0];
|
|
3336
3382
|
let version;
|
|
3337
|
-
|
|
3383
|
+
if (includeVersion) {
|
|
3338
3384
|
const versionCommands = [
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3385
|
+
`"${firstPath}" --version`,
|
|
3386
|
+
`"${firstPath}" -V`,
|
|
3387
|
+
`"${firstPath}" -v`,
|
|
3388
|
+
cli.versionCommand
|
|
3343
3389
|
].filter((v) => !!v);
|
|
3344
|
-
|
|
3345
|
-
const
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3390
|
+
try {
|
|
3391
|
+
for (const versionCommand of versionCommands) {
|
|
3392
|
+
const versionResult = await execAsync(versionCommand, 3e3);
|
|
3393
|
+
if (versionResult) {
|
|
3394
|
+
version = parseVersion(versionResult);
|
|
3395
|
+
break;
|
|
3396
|
+
}
|
|
3349
3397
|
}
|
|
3398
|
+
} catch {
|
|
3350
3399
|
}
|
|
3351
|
-
} catch {
|
|
3352
3400
|
}
|
|
3353
3401
|
return { ...cli, installed: true, version, path: firstPath };
|
|
3354
3402
|
} catch {
|
|
@@ -3358,7 +3406,7 @@ async function detectCLIs(providerLoader) {
|
|
|
3358
3406
|
);
|
|
3359
3407
|
return results;
|
|
3360
3408
|
}
|
|
3361
|
-
async function detectCLI(cliId, providerLoader) {
|
|
3409
|
+
async function detectCLI(cliId, providerLoader, options) {
|
|
3362
3410
|
const resolvedId = providerLoader ? providerLoader.resolveAlias(cliId) : cliId;
|
|
3363
3411
|
if (providerLoader) {
|
|
3364
3412
|
const cliList = providerLoader.getCliDetectionList();
|
|
@@ -3367,25 +3415,28 @@ async function detectCLI(cliId, providerLoader) {
|
|
|
3367
3415
|
const platform9 = os2.platform();
|
|
3368
3416
|
const whichCmd = platform9 === "win32" ? "where" : "which";
|
|
3369
3417
|
try {
|
|
3370
|
-
const
|
|
3418
|
+
const explicitPath = resolveCommandPath(target.command);
|
|
3419
|
+
const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(target.command)}`);
|
|
3371
3420
|
if (!pathResult) return null;
|
|
3372
|
-
const firstPath = pathResult.split("\n")[0];
|
|
3421
|
+
const firstPath = explicitPath || pathResult.split("\n")[0];
|
|
3373
3422
|
let version;
|
|
3374
|
-
|
|
3423
|
+
if (options?.includeVersion !== false) {
|
|
3375
3424
|
const versionCommands = [
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3425
|
+
`"${firstPath}" --version`,
|
|
3426
|
+
`"${firstPath}" -V`,
|
|
3427
|
+
`"${firstPath}" -v`,
|
|
3428
|
+
target.versionCommand
|
|
3380
3429
|
].filter((v) => !!v);
|
|
3381
|
-
|
|
3382
|
-
const
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3430
|
+
try {
|
|
3431
|
+
for (const versionCommand of versionCommands) {
|
|
3432
|
+
const versionResult = await execAsync(versionCommand, 3e3);
|
|
3433
|
+
if (versionResult) {
|
|
3434
|
+
version = parseVersion(versionResult);
|
|
3435
|
+
break;
|
|
3436
|
+
}
|
|
3386
3437
|
}
|
|
3438
|
+
} catch {
|
|
3387
3439
|
}
|
|
3388
|
-
} catch {
|
|
3389
3440
|
}
|
|
3390
3441
|
return { ...target, installed: true, version, path: firstPath };
|
|
3391
3442
|
} catch {
|
|
@@ -3393,7 +3444,7 @@ async function detectCLI(cliId, providerLoader) {
|
|
|
3393
3444
|
}
|
|
3394
3445
|
}
|
|
3395
3446
|
}
|
|
3396
|
-
const all = await detectCLIs(providerLoader);
|
|
3447
|
+
const all = await detectCLIs(providerLoader, options);
|
|
3397
3448
|
return all.find((c) => c.id === resolvedId && c.installed) || null;
|
|
3398
3449
|
}
|
|
3399
3450
|
|
|
@@ -3520,7 +3571,7 @@ var DaemonCdpManager = class {
|
|
|
3520
3571
|
* Returns multiple entries if multiple IDE windows are open on same port
|
|
3521
3572
|
*/
|
|
3522
3573
|
static listAllTargets(port) {
|
|
3523
|
-
return new Promise((
|
|
3574
|
+
return new Promise((resolve12) => {
|
|
3524
3575
|
const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
|
|
3525
3576
|
let data = "";
|
|
3526
3577
|
res.on("data", (chunk) => data += chunk.toString());
|
|
@@ -3536,16 +3587,16 @@ var DaemonCdpManager = class {
|
|
|
3536
3587
|
(t) => !isNonMain(t.title || "") && t.url?.includes("workbench.html") && !t.url?.includes("agent")
|
|
3537
3588
|
);
|
|
3538
3589
|
const fallbackPages = pages.filter((t) => !isNonMain(t.title || ""));
|
|
3539
|
-
|
|
3590
|
+
resolve12(mainPages.length > 0 ? mainPages : fallbackPages);
|
|
3540
3591
|
} catch {
|
|
3541
|
-
|
|
3592
|
+
resolve12([]);
|
|
3542
3593
|
}
|
|
3543
3594
|
});
|
|
3544
3595
|
});
|
|
3545
|
-
req.on("error", () =>
|
|
3596
|
+
req.on("error", () => resolve12([]));
|
|
3546
3597
|
req.setTimeout(2e3, () => {
|
|
3547
3598
|
req.destroy();
|
|
3548
|
-
|
|
3599
|
+
resolve12([]);
|
|
3549
3600
|
});
|
|
3550
3601
|
});
|
|
3551
3602
|
}
|
|
@@ -3585,7 +3636,7 @@ var DaemonCdpManager = class {
|
|
|
3585
3636
|
}
|
|
3586
3637
|
}
|
|
3587
3638
|
findTargetOnPort(port) {
|
|
3588
|
-
return new Promise((
|
|
3639
|
+
return new Promise((resolve12) => {
|
|
3589
3640
|
const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
|
|
3590
3641
|
let data = "";
|
|
3591
3642
|
res.on("data", (chunk) => data += chunk.toString());
|
|
@@ -3596,7 +3647,7 @@ var DaemonCdpManager = class {
|
|
|
3596
3647
|
(t) => (t.type === "page" || t.type === "browser" || t.type === "Page") && t.webSocketDebuggerUrl
|
|
3597
3648
|
);
|
|
3598
3649
|
if (pages.length === 0) {
|
|
3599
|
-
|
|
3650
|
+
resolve12(targets.find((t) => t.webSocketDebuggerUrl) || null);
|
|
3600
3651
|
return;
|
|
3601
3652
|
}
|
|
3602
3653
|
const mainPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
|
|
@@ -3606,24 +3657,24 @@ var DaemonCdpManager = class {
|
|
|
3606
3657
|
const specific = list.find((t) => t.id === this._targetId);
|
|
3607
3658
|
if (specific) {
|
|
3608
3659
|
this._pageTitle = specific.title || "";
|
|
3609
|
-
|
|
3660
|
+
resolve12(specific);
|
|
3610
3661
|
} else {
|
|
3611
3662
|
this.log(`[CDP] Target ${this._targetId} not found in page list`);
|
|
3612
|
-
|
|
3663
|
+
resolve12(null);
|
|
3613
3664
|
}
|
|
3614
3665
|
return;
|
|
3615
3666
|
}
|
|
3616
3667
|
this._pageTitle = list[0]?.title || "";
|
|
3617
|
-
|
|
3668
|
+
resolve12(list[0]);
|
|
3618
3669
|
} catch {
|
|
3619
|
-
|
|
3670
|
+
resolve12(null);
|
|
3620
3671
|
}
|
|
3621
3672
|
});
|
|
3622
3673
|
});
|
|
3623
|
-
req.on("error", () =>
|
|
3674
|
+
req.on("error", () => resolve12(null));
|
|
3624
3675
|
req.setTimeout(2e3, () => {
|
|
3625
3676
|
req.destroy();
|
|
3626
|
-
|
|
3677
|
+
resolve12(null);
|
|
3627
3678
|
});
|
|
3628
3679
|
});
|
|
3629
3680
|
}
|
|
@@ -3634,7 +3685,7 @@ var DaemonCdpManager = class {
|
|
|
3634
3685
|
this.extensionProviders = providers;
|
|
3635
3686
|
}
|
|
3636
3687
|
connectToTarget(wsUrl) {
|
|
3637
|
-
return new Promise((
|
|
3688
|
+
return new Promise((resolve12) => {
|
|
3638
3689
|
this.ws = new import_ws.default(wsUrl);
|
|
3639
3690
|
this.ws.on("open", async () => {
|
|
3640
3691
|
this._connected = true;
|
|
@@ -3644,17 +3695,17 @@ var DaemonCdpManager = class {
|
|
|
3644
3695
|
}
|
|
3645
3696
|
this.connectBrowserWs().catch(() => {
|
|
3646
3697
|
});
|
|
3647
|
-
|
|
3698
|
+
resolve12(true);
|
|
3648
3699
|
});
|
|
3649
3700
|
this.ws.on("message", (data) => {
|
|
3650
3701
|
try {
|
|
3651
3702
|
const msg = JSON.parse(data.toString());
|
|
3652
3703
|
if (msg.id && this.pending.has(msg.id)) {
|
|
3653
|
-
const { resolve:
|
|
3704
|
+
const { resolve: resolve13, reject } = this.pending.get(msg.id);
|
|
3654
3705
|
this.pending.delete(msg.id);
|
|
3655
3706
|
this.failureCount = 0;
|
|
3656
3707
|
if (msg.error) reject(new Error(msg.error.message));
|
|
3657
|
-
else
|
|
3708
|
+
else resolve13(msg.result);
|
|
3658
3709
|
} else if (msg.method === "Runtime.executionContextCreated") {
|
|
3659
3710
|
this.contexts.add(msg.params.context.id);
|
|
3660
3711
|
} else if (msg.method === "Runtime.executionContextDestroyed") {
|
|
@@ -3677,7 +3728,7 @@ var DaemonCdpManager = class {
|
|
|
3677
3728
|
this.ws.on("error", (err) => {
|
|
3678
3729
|
this.log(`[CDP] WebSocket error: ${err.message}`);
|
|
3679
3730
|
this._connected = false;
|
|
3680
|
-
|
|
3731
|
+
resolve12(false);
|
|
3681
3732
|
});
|
|
3682
3733
|
});
|
|
3683
3734
|
}
|
|
@@ -3691,7 +3742,7 @@ var DaemonCdpManager = class {
|
|
|
3691
3742
|
return;
|
|
3692
3743
|
}
|
|
3693
3744
|
this.log(`[CDP] Connecting browser WS for target discovery...`);
|
|
3694
|
-
await new Promise((
|
|
3745
|
+
await new Promise((resolve12, reject) => {
|
|
3695
3746
|
this.browserWs = new import_ws.default(browserWsUrl);
|
|
3696
3747
|
this.browserWs.on("open", async () => {
|
|
3697
3748
|
this._browserConnected = true;
|
|
@@ -3701,16 +3752,16 @@ var DaemonCdpManager = class {
|
|
|
3701
3752
|
} catch (e) {
|
|
3702
3753
|
this.log(`[CDP] setDiscoverTargets failed: ${e.message}`);
|
|
3703
3754
|
}
|
|
3704
|
-
|
|
3755
|
+
resolve12();
|
|
3705
3756
|
});
|
|
3706
3757
|
this.browserWs.on("message", (data) => {
|
|
3707
3758
|
try {
|
|
3708
3759
|
const msg = JSON.parse(data.toString());
|
|
3709
3760
|
if (msg.id && this.browserPending.has(msg.id)) {
|
|
3710
|
-
const { resolve:
|
|
3761
|
+
const { resolve: resolve13, reject: reject2 } = this.browserPending.get(msg.id);
|
|
3711
3762
|
this.browserPending.delete(msg.id);
|
|
3712
3763
|
if (msg.error) reject2(new Error(msg.error.message));
|
|
3713
|
-
else
|
|
3764
|
+
else resolve13(msg.result);
|
|
3714
3765
|
}
|
|
3715
3766
|
} catch {
|
|
3716
3767
|
}
|
|
@@ -3730,31 +3781,31 @@ var DaemonCdpManager = class {
|
|
|
3730
3781
|
}
|
|
3731
3782
|
}
|
|
3732
3783
|
getBrowserWsUrl() {
|
|
3733
|
-
return new Promise((
|
|
3784
|
+
return new Promise((resolve12) => {
|
|
3734
3785
|
const req = http.get(`http://127.0.0.1:${this.port}/json/version`, (res) => {
|
|
3735
3786
|
let data = "";
|
|
3736
3787
|
res.on("data", (chunk) => data += chunk.toString());
|
|
3737
3788
|
res.on("end", () => {
|
|
3738
3789
|
try {
|
|
3739
3790
|
const info = JSON.parse(data);
|
|
3740
|
-
|
|
3791
|
+
resolve12(info.webSocketDebuggerUrl || null);
|
|
3741
3792
|
} catch {
|
|
3742
|
-
|
|
3793
|
+
resolve12(null);
|
|
3743
3794
|
}
|
|
3744
3795
|
});
|
|
3745
3796
|
});
|
|
3746
|
-
req.on("error", () =>
|
|
3797
|
+
req.on("error", () => resolve12(null));
|
|
3747
3798
|
req.setTimeout(3e3, () => {
|
|
3748
3799
|
req.destroy();
|
|
3749
|
-
|
|
3800
|
+
resolve12(null);
|
|
3750
3801
|
});
|
|
3751
3802
|
});
|
|
3752
3803
|
}
|
|
3753
3804
|
sendBrowser(method, params = {}, timeoutMs = 15e3) {
|
|
3754
|
-
return new Promise((
|
|
3805
|
+
return new Promise((resolve12, reject) => {
|
|
3755
3806
|
if (!this.browserWs || !this._browserConnected) return reject(new Error("Browser WS not connected"));
|
|
3756
3807
|
const id = this.browserMsgId++;
|
|
3757
|
-
this.browserPending.set(id, { resolve:
|
|
3808
|
+
this.browserPending.set(id, { resolve: resolve12, reject });
|
|
3758
3809
|
this.browserWs.send(JSON.stringify({ id, method, params }));
|
|
3759
3810
|
setTimeout(() => {
|
|
3760
3811
|
if (this.browserPending.has(id)) {
|
|
@@ -3794,11 +3845,11 @@ var DaemonCdpManager = class {
|
|
|
3794
3845
|
}
|
|
3795
3846
|
// ─── CDP Protocol ────────────────────────────────────────
|
|
3796
3847
|
sendInternal(method, params = {}, timeoutMs = 15e3) {
|
|
3797
|
-
return new Promise((
|
|
3848
|
+
return new Promise((resolve12, reject) => {
|
|
3798
3849
|
if (!this.ws || !this._connected) return reject(new Error("CDP not connected"));
|
|
3799
3850
|
if (this.ws.readyState !== import_ws.default.OPEN) return reject(new Error("WebSocket not open"));
|
|
3800
3851
|
const id = this.msgId++;
|
|
3801
|
-
this.pending.set(id, { resolve:
|
|
3852
|
+
this.pending.set(id, { resolve: resolve12, reject });
|
|
3802
3853
|
this.ws.send(JSON.stringify({ id, method, params }));
|
|
3803
3854
|
setTimeout(() => {
|
|
3804
3855
|
if (this.pending.has(id)) {
|
|
@@ -4047,7 +4098,7 @@ var DaemonCdpManager = class {
|
|
|
4047
4098
|
const browserWs = this.browserWs;
|
|
4048
4099
|
let msgId = this.browserMsgId;
|
|
4049
4100
|
const sendWs = (method, params = {}, sessionId) => {
|
|
4050
|
-
return new Promise((
|
|
4101
|
+
return new Promise((resolve12, reject) => {
|
|
4051
4102
|
const mid = msgId++;
|
|
4052
4103
|
this.browserMsgId = msgId;
|
|
4053
4104
|
const handler = (raw) => {
|
|
@@ -4056,7 +4107,7 @@ var DaemonCdpManager = class {
|
|
|
4056
4107
|
if (msg.id === mid) {
|
|
4057
4108
|
browserWs.removeListener("message", handler);
|
|
4058
4109
|
if (msg.error) reject(new Error(msg.error.message || JSON.stringify(msg.error)));
|
|
4059
|
-
else
|
|
4110
|
+
else resolve12(msg.result);
|
|
4060
4111
|
}
|
|
4061
4112
|
} catch {
|
|
4062
4113
|
}
|
|
@@ -4247,14 +4298,14 @@ var DaemonCdpManager = class {
|
|
|
4247
4298
|
if (!ws || ws.readyState !== import_ws.default.OPEN) {
|
|
4248
4299
|
throw new Error("CDP not connected");
|
|
4249
4300
|
}
|
|
4250
|
-
return new Promise((
|
|
4301
|
+
return new Promise((resolve12, reject) => {
|
|
4251
4302
|
const id = getNextId();
|
|
4252
4303
|
pendingMap.set(id, {
|
|
4253
4304
|
resolve: (result) => {
|
|
4254
4305
|
if (result?.result?.subtype === "error") {
|
|
4255
4306
|
reject(new Error(result.result.description));
|
|
4256
4307
|
} else {
|
|
4257
|
-
|
|
4308
|
+
resolve12(result?.result?.value);
|
|
4258
4309
|
}
|
|
4259
4310
|
},
|
|
4260
4311
|
reject
|
|
@@ -4286,10 +4337,10 @@ var DaemonCdpManager = class {
|
|
|
4286
4337
|
throw new Error("CDP not connected");
|
|
4287
4338
|
}
|
|
4288
4339
|
const sendViaSession = (method, params = {}) => {
|
|
4289
|
-
return new Promise((
|
|
4340
|
+
return new Promise((resolve12, reject) => {
|
|
4290
4341
|
const pendingMap = this._browserConnected ? this.browserPending : this.pending;
|
|
4291
4342
|
const id = this._browserConnected ? this.browserMsgId++ : this.msgId++;
|
|
4292
|
-
pendingMap.set(id, { resolve:
|
|
4343
|
+
pendingMap.set(id, { resolve: resolve12, reject });
|
|
4293
4344
|
ws.send(JSON.stringify({ id, sessionId, method, params }));
|
|
4294
4345
|
setTimeout(() => {
|
|
4295
4346
|
if (pendingMap.has(id)) {
|
|
@@ -4917,15 +4968,76 @@ function normalizeControlValue(value) {
|
|
|
4917
4968
|
|
|
4918
4969
|
// src/config/chat-history.ts
|
|
4919
4970
|
var fs3 = __toESM(require("fs"));
|
|
4920
|
-
var
|
|
4971
|
+
var path7 = __toESM(require("path"));
|
|
4921
4972
|
var os5 = __toESM(require("os"));
|
|
4922
|
-
var HISTORY_DIR =
|
|
4973
|
+
var HISTORY_DIR = path7.join(os5.homedir(), ".adhdev", "history");
|
|
4923
4974
|
var RETAIN_DAYS = 30;
|
|
4975
|
+
var CODEX_STARTER_PROMPT_RE = /^(?:[›❯]\s*)?(?:Find and fix a bug in @filename|Improve documentation in @filename|Write tests for @filename|Explain this codebase|Summarize recent commits|Implement \{feature\}|Use \/skills(?: to list available skills)?|Run \/review on my current changes)$/i;
|
|
4976
|
+
function normalizeHistoryComparable(text) {
|
|
4977
|
+
return String(text || "").replace(/\s+/g, " ").trim();
|
|
4978
|
+
}
|
|
4979
|
+
function cleanupHistoryContent(agentType, role, content) {
|
|
4980
|
+
let value = String(content || "").replace(/\r\n/g, "\n").trim();
|
|
4981
|
+
if (!value) return "";
|
|
4982
|
+
if (agentType === "codex-cli" && role === "assistant") {
|
|
4983
|
+
const filtered = value.split("\n").filter((line) => !CODEX_STARTER_PROMPT_RE.test(line.trim())).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
4984
|
+
value = filtered;
|
|
4985
|
+
}
|
|
4986
|
+
return value;
|
|
4987
|
+
}
|
|
4988
|
+
function buildHistoryMessageHash(agentType, message) {
|
|
4989
|
+
if (message.historyDedupKey) return message.historyDedupKey;
|
|
4990
|
+
const cleaned = cleanupHistoryContent(agentType, message.role, message.content);
|
|
4991
|
+
return `${message.kind || "standard"}:${message.role}:${message.receivedAt || 0}:${normalizeHistoryComparable(cleaned)}`;
|
|
4992
|
+
}
|
|
4993
|
+
function buildHistoryMessageSignature(agentType, message) {
|
|
4994
|
+
const cleaned = cleanupHistoryContent(agentType, message.role, message.content);
|
|
4995
|
+
return `${message.kind || "standard"}:${message.role}:${normalizeHistoryComparable(cleaned)}`;
|
|
4996
|
+
}
|
|
4997
|
+
function isAdjacentHistoryDuplicate(agentType, previous, next) {
|
|
4998
|
+
if (!previous || !next) return false;
|
|
4999
|
+
return buildHistoryMessageSignature(agentType, previous) === buildHistoryMessageSignature(agentType, next);
|
|
5000
|
+
}
|
|
5001
|
+
function collapseReplayAssistantTurns(agentType, messages) {
|
|
5002
|
+
if (agentType !== "codex-cli") return messages;
|
|
5003
|
+
const collapsed = [];
|
|
5004
|
+
let sawAssistantSinceLastUser = false;
|
|
5005
|
+
for (const message of messages) {
|
|
5006
|
+
if (message.role === "user") {
|
|
5007
|
+
sawAssistantSinceLastUser = false;
|
|
5008
|
+
collapsed.push(message);
|
|
5009
|
+
continue;
|
|
5010
|
+
}
|
|
5011
|
+
if (message.role === "assistant") {
|
|
5012
|
+
if (sawAssistantSinceLastUser) continue;
|
|
5013
|
+
sawAssistantSinceLastUser = true;
|
|
5014
|
+
collapsed.push(message);
|
|
5015
|
+
continue;
|
|
5016
|
+
}
|
|
5017
|
+
collapsed.push(message);
|
|
5018
|
+
}
|
|
5019
|
+
return collapsed;
|
|
5020
|
+
}
|
|
5021
|
+
function sanitizeHistoryMessage(agentType, message) {
|
|
5022
|
+
if (!message || message.role !== "user" && message.role !== "assistant" && message.role !== "system") {
|
|
5023
|
+
return null;
|
|
5024
|
+
}
|
|
5025
|
+
const content = cleanupHistoryContent(agentType, message.role, message.content);
|
|
5026
|
+
if (!content) return null;
|
|
5027
|
+
return {
|
|
5028
|
+
...message,
|
|
5029
|
+
content
|
|
5030
|
+
};
|
|
5031
|
+
}
|
|
4924
5032
|
var ChatHistoryWriter = class {
|
|
4925
5033
|
/** Last seen message count per agent (deduplication) */
|
|
4926
5034
|
lastSeenCounts = /* @__PURE__ */ new Map();
|
|
4927
5035
|
/** Last seen message hash per agent (deduplication) */
|
|
4928
5036
|
lastSeenHashes = /* @__PURE__ */ new Map();
|
|
5037
|
+
/** Last appended normalized message signature per agent/session */
|
|
5038
|
+
lastSeenSignatures = /* @__PURE__ */ new Map();
|
|
5039
|
+
/** Last appended normalized non-system turn signature per agent/session */
|
|
5040
|
+
lastSeenTurnSignatures = /* @__PURE__ */ new Map();
|
|
4929
5041
|
rotated = false;
|
|
4930
5042
|
/**
|
|
4931
5043
|
* Append new messages to history
|
|
@@ -4947,14 +5059,36 @@ var ChatHistoryWriter = class {
|
|
|
4947
5059
|
}
|
|
4948
5060
|
const newMessages = [];
|
|
4949
5061
|
for (const msg of messages) {
|
|
4950
|
-
const
|
|
5062
|
+
const role = msg.role;
|
|
5063
|
+
if (role !== "user" && role !== "assistant" && role !== "system") continue;
|
|
5064
|
+
const content = cleanupHistoryContent(agentType, role, msg.content || "");
|
|
5065
|
+
if (!content) continue;
|
|
5066
|
+
const receivedAt = msg.receivedAt || Date.now();
|
|
5067
|
+
const hash = buildHistoryMessageHash(agentType, {
|
|
5068
|
+
role,
|
|
5069
|
+
content,
|
|
5070
|
+
receivedAt,
|
|
5071
|
+
kind: typeof msg.kind === "string" ? msg.kind : void 0,
|
|
5072
|
+
historyDedupKey: msg.historyDedupKey
|
|
5073
|
+
});
|
|
5074
|
+
const signature = buildHistoryMessageSignature(agentType, {
|
|
5075
|
+
role,
|
|
5076
|
+
content,
|
|
5077
|
+
kind: typeof msg.kind === "string" ? msg.kind : void 0
|
|
5078
|
+
});
|
|
4951
5079
|
if (seenHashes.has(hash)) continue;
|
|
5080
|
+
if (this.lastSeenSignatures.get(dedupKey) === signature) continue;
|
|
5081
|
+
if (role !== "system" && this.lastSeenTurnSignatures.get(dedupKey) === signature) continue;
|
|
4952
5082
|
seenHashes.add(hash);
|
|
5083
|
+
this.lastSeenSignatures.set(dedupKey, signature);
|
|
5084
|
+
if (role !== "system") {
|
|
5085
|
+
this.lastSeenTurnSignatures.set(dedupKey, signature);
|
|
5086
|
+
}
|
|
4953
5087
|
newMessages.push({
|
|
4954
|
-
ts: new Date(
|
|
4955
|
-
receivedAt
|
|
4956
|
-
role
|
|
4957
|
-
content
|
|
5088
|
+
ts: new Date(receivedAt).toISOString(),
|
|
5089
|
+
receivedAt,
|
|
5090
|
+
role,
|
|
5091
|
+
content,
|
|
4958
5092
|
kind: typeof msg.kind === "string" ? msg.kind : void 0,
|
|
4959
5093
|
senderName: typeof msg.senderName === "string" ? msg.senderName : void 0,
|
|
4960
5094
|
agent: agentType,
|
|
@@ -4964,16 +5098,18 @@ var ChatHistoryWriter = class {
|
|
|
4964
5098
|
});
|
|
4965
5099
|
}
|
|
4966
5100
|
if (newMessages.length === 0) return;
|
|
4967
|
-
const dir =
|
|
5101
|
+
const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
|
|
4968
5102
|
fs3.mkdirSync(dir, { recursive: true });
|
|
4969
5103
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4970
5104
|
const filePrefix = effectiveHistoryKey ? `${this.sanitize(effectiveHistoryKey)}_` : "";
|
|
4971
|
-
const filePath =
|
|
5105
|
+
const filePath = path7.join(dir, `${filePrefix}${date}.jsonl`);
|
|
4972
5106
|
const lines = newMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
4973
5107
|
fs3.appendFileSync(filePath, lines, "utf-8");
|
|
4974
5108
|
const prevCount = this.lastSeenCounts.get(dedupKey) || 0;
|
|
4975
5109
|
if (messages.length < prevCount * 0.5 && prevCount > 3) {
|
|
4976
5110
|
seenHashes.clear();
|
|
5111
|
+
this.lastSeenSignatures.delete(dedupKey);
|
|
5112
|
+
this.lastSeenTurnSignatures.delete(dedupKey);
|
|
4977
5113
|
for (const msg of messages) {
|
|
4978
5114
|
seenHashes.add(msg.historyDedupKey || `${msg.kind || "standard"}:${msg.role}:${(msg.content || "").slice(0, 50)}`);
|
|
4979
5115
|
}
|
|
@@ -4987,6 +5123,54 @@ var ChatHistoryWriter = class {
|
|
|
4987
5123
|
} catch {
|
|
4988
5124
|
}
|
|
4989
5125
|
}
|
|
5126
|
+
seedSessionHistory(agentType, messages = [], historySessionId, instanceId) {
|
|
5127
|
+
const effectiveHistoryKey = historySessionId || instanceId;
|
|
5128
|
+
const dedupKey = effectiveHistoryKey ? `${agentType}:${effectiveHistoryKey}` : agentType;
|
|
5129
|
+
const seenHashes = /* @__PURE__ */ new Set();
|
|
5130
|
+
for (const raw of messages) {
|
|
5131
|
+
const role = raw?.role;
|
|
5132
|
+
if (role !== "user" && role !== "assistant" && role !== "system") continue;
|
|
5133
|
+
const content = cleanupHistoryContent(agentType, role, raw?.content || "");
|
|
5134
|
+
if (!content) continue;
|
|
5135
|
+
seenHashes.add(buildHistoryMessageHash(agentType, {
|
|
5136
|
+
role,
|
|
5137
|
+
content,
|
|
5138
|
+
receivedAt: raw?.receivedAt || 0,
|
|
5139
|
+
kind: typeof raw?.kind === "string" ? raw.kind : void 0,
|
|
5140
|
+
historyDedupKey: raw?.historyDedupKey
|
|
5141
|
+
}));
|
|
5142
|
+
}
|
|
5143
|
+
this.lastSeenHashes.set(dedupKey, seenHashes);
|
|
5144
|
+
this.lastSeenCounts.set(dedupKey, messages.length);
|
|
5145
|
+
const lastMessage = [...messages].reverse().find((raw) => {
|
|
5146
|
+
const role = raw?.role;
|
|
5147
|
+
if (role !== "user" && role !== "assistant" && role !== "system") return false;
|
|
5148
|
+
return !!cleanupHistoryContent(agentType, role, raw?.content || "");
|
|
5149
|
+
});
|
|
5150
|
+
const lastTurnMessage = [...messages].reverse().find((raw) => {
|
|
5151
|
+
const role = raw?.role;
|
|
5152
|
+
if (role !== "user" && role !== "assistant") return false;
|
|
5153
|
+
return !!cleanupHistoryContent(agentType, role, raw?.content || "");
|
|
5154
|
+
});
|
|
5155
|
+
if (lastMessage) {
|
|
5156
|
+
this.lastSeenSignatures.set(dedupKey, buildHistoryMessageSignature(agentType, {
|
|
5157
|
+
role: lastMessage.role,
|
|
5158
|
+
content: lastMessage.content,
|
|
5159
|
+
kind: typeof lastMessage.kind === "string" ? lastMessage.kind : void 0
|
|
5160
|
+
}));
|
|
5161
|
+
} else {
|
|
5162
|
+
this.lastSeenSignatures.delete(dedupKey);
|
|
5163
|
+
}
|
|
5164
|
+
if (lastTurnMessage) {
|
|
5165
|
+
this.lastSeenTurnSignatures.set(dedupKey, buildHistoryMessageSignature(agentType, {
|
|
5166
|
+
role: lastTurnMessage.role,
|
|
5167
|
+
content: lastTurnMessage.content,
|
|
5168
|
+
kind: typeof lastTurnMessage.kind === "string" ? lastTurnMessage.kind : void 0
|
|
5169
|
+
}));
|
|
5170
|
+
} else {
|
|
5171
|
+
this.lastSeenTurnSignatures.delete(dedupKey);
|
|
5172
|
+
}
|
|
5173
|
+
}
|
|
4990
5174
|
appendSystemMarker(agentType, content, options = {}) {
|
|
4991
5175
|
this.appendNewMessages(
|
|
4992
5176
|
agentType,
|
|
@@ -5017,19 +5201,29 @@ var ChatHistoryWriter = class {
|
|
|
5017
5201
|
this.lastSeenHashes.set(toDedupKey, nextHashes);
|
|
5018
5202
|
this.lastSeenHashes.delete(fromDedupKey);
|
|
5019
5203
|
}
|
|
5204
|
+
const fromSignature = this.lastSeenSignatures.get(fromDedupKey);
|
|
5205
|
+
if (fromSignature) {
|
|
5206
|
+
this.lastSeenSignatures.set(toDedupKey, fromSignature);
|
|
5207
|
+
this.lastSeenSignatures.delete(fromDedupKey);
|
|
5208
|
+
}
|
|
5209
|
+
const fromTurnSignature = this.lastSeenTurnSignatures.get(fromDedupKey);
|
|
5210
|
+
if (fromTurnSignature) {
|
|
5211
|
+
this.lastSeenTurnSignatures.set(toDedupKey, fromTurnSignature);
|
|
5212
|
+
this.lastSeenTurnSignatures.delete(fromDedupKey);
|
|
5213
|
+
}
|
|
5020
5214
|
const fromCount = this.lastSeenCounts.get(fromDedupKey);
|
|
5021
5215
|
if (typeof fromCount === "number") {
|
|
5022
5216
|
this.lastSeenCounts.set(toDedupKey, Math.max(fromCount, this.lastSeenCounts.get(toDedupKey) || 0));
|
|
5023
5217
|
this.lastSeenCounts.delete(fromDedupKey);
|
|
5024
5218
|
}
|
|
5025
|
-
const dir =
|
|
5219
|
+
const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
|
|
5026
5220
|
if (!fs3.existsSync(dir)) return;
|
|
5027
5221
|
const fromPrefix = `${this.sanitize(fromId)}_`;
|
|
5028
5222
|
const toPrefix = `${this.sanitize(toId)}_`;
|
|
5029
5223
|
const files = fs3.readdirSync(dir).filter((file) => file.startsWith(fromPrefix) && file.endsWith(".jsonl"));
|
|
5030
5224
|
for (const file of files) {
|
|
5031
|
-
const sourcePath =
|
|
5032
|
-
const targetPath =
|
|
5225
|
+
const sourcePath = path7.join(dir, file);
|
|
5226
|
+
const targetPath = path7.join(dir, `${toPrefix}${file.slice(fromPrefix.length)}`);
|
|
5033
5227
|
const sourceLines = fs3.readFileSync(sourcePath, "utf-8").split("\n").filter(Boolean);
|
|
5034
5228
|
const rewritten = sourceLines.map((line) => {
|
|
5035
5229
|
try {
|
|
@@ -5058,10 +5252,61 @@ var ChatHistoryWriter = class {
|
|
|
5058
5252
|
} catch {
|
|
5059
5253
|
}
|
|
5060
5254
|
}
|
|
5255
|
+
compactHistorySession(agentType, historySessionId) {
|
|
5256
|
+
const sessionId = String(historySessionId || "").trim();
|
|
5257
|
+
if (!sessionId) return;
|
|
5258
|
+
try {
|
|
5259
|
+
const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
|
|
5260
|
+
if (!fs3.existsSync(dir)) return;
|
|
5261
|
+
const prefix = `${this.sanitize(sessionId)}_`;
|
|
5262
|
+
const files = fs3.readdirSync(dir).filter((file) => file.startsWith(prefix) && file.endsWith(".jsonl")).sort();
|
|
5263
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5264
|
+
for (const file of files) {
|
|
5265
|
+
const filePath = path7.join(dir, file);
|
|
5266
|
+
const lines = fs3.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
5267
|
+
const next = [];
|
|
5268
|
+
for (const line of lines) {
|
|
5269
|
+
let parsed = null;
|
|
5270
|
+
try {
|
|
5271
|
+
parsed = JSON.parse(line);
|
|
5272
|
+
} catch {
|
|
5273
|
+
parsed = null;
|
|
5274
|
+
}
|
|
5275
|
+
if (!parsed || parsed.historySessionId !== sessionId) continue;
|
|
5276
|
+
const sanitized = sanitizeHistoryMessage(agentType, parsed);
|
|
5277
|
+
if (!sanitized) continue;
|
|
5278
|
+
const hash = buildHistoryMessageHash(agentType, sanitized);
|
|
5279
|
+
if (seen.has(hash)) continue;
|
|
5280
|
+
seen.add(hash);
|
|
5281
|
+
next.push(sanitized);
|
|
5282
|
+
}
|
|
5283
|
+
next.sort((a, b) => a.receivedAt - b.receivedAt);
|
|
5284
|
+
const dedupedAdjacent = [];
|
|
5285
|
+
let lastTurn = null;
|
|
5286
|
+
for (const entry of next) {
|
|
5287
|
+
const previous = dedupedAdjacent[dedupedAdjacent.length - 1];
|
|
5288
|
+
if (isAdjacentHistoryDuplicate(agentType, previous, entry)) continue;
|
|
5289
|
+
if (entry.role !== "system" && isAdjacentHistoryDuplicate(agentType, lastTurn, entry)) continue;
|
|
5290
|
+
dedupedAdjacent.push(entry);
|
|
5291
|
+
if (entry.role !== "system") lastTurn = entry;
|
|
5292
|
+
}
|
|
5293
|
+
const collapsed = collapseReplayAssistantTurns(agentType, dedupedAdjacent);
|
|
5294
|
+
if (collapsed.length === 0) {
|
|
5295
|
+
fs3.unlinkSync(filePath);
|
|
5296
|
+
continue;
|
|
5297
|
+
}
|
|
5298
|
+
fs3.writeFileSync(filePath, `${collapsed.map((entry) => JSON.stringify(entry)).join("\n")}
|
|
5299
|
+
`, "utf-8");
|
|
5300
|
+
}
|
|
5301
|
+
} catch {
|
|
5302
|
+
}
|
|
5303
|
+
}
|
|
5061
5304
|
/** Called when agent session is explicitly changed */
|
|
5062
5305
|
onSessionChange(agentType) {
|
|
5063
5306
|
this.lastSeenHashes.delete(agentType);
|
|
5064
5307
|
this.lastSeenCounts.delete(agentType);
|
|
5308
|
+
this.lastSeenSignatures.delete(agentType);
|
|
5309
|
+
this.lastSeenTurnSignatures.delete(agentType);
|
|
5065
5310
|
}
|
|
5066
5311
|
/** Delete history files older than 30 days */
|
|
5067
5312
|
async rotateOldFiles() {
|
|
@@ -5070,10 +5315,10 @@ var ChatHistoryWriter = class {
|
|
|
5070
5315
|
const cutoff = Date.now() - RETAIN_DAYS * 24 * 60 * 60 * 1e3;
|
|
5071
5316
|
const agentDirs = fs3.readdirSync(HISTORY_DIR, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
5072
5317
|
for (const dir of agentDirs) {
|
|
5073
|
-
const dirPath =
|
|
5318
|
+
const dirPath = path7.join(HISTORY_DIR, dir.name);
|
|
5074
5319
|
const files = fs3.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl") || f.endsWith(".terminal.log"));
|
|
5075
5320
|
for (const file of files) {
|
|
5076
|
-
const filePath =
|
|
5321
|
+
const filePath = path7.join(dirPath, file);
|
|
5077
5322
|
const stat = fs3.statSync(filePath);
|
|
5078
5323
|
if (stat.mtimeMs < cutoff) {
|
|
5079
5324
|
fs3.unlinkSync(filePath);
|
|
@@ -5091,7 +5336,7 @@ var ChatHistoryWriter = class {
|
|
|
5091
5336
|
function readChatHistory(agentType, offset = 0, limit = 30, historySessionId) {
|
|
5092
5337
|
try {
|
|
5093
5338
|
const sanitized = agentType.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
5094
|
-
const dir =
|
|
5339
|
+
const dir = path7.join(HISTORY_DIR, sanitized);
|
|
5095
5340
|
if (!fs3.existsSync(dir)) return { messages: [], hasMore: false };
|
|
5096
5341
|
const sanitizedInstance = historySessionId?.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
5097
5342
|
const files = fs3.readdirSync(dir).filter((f) => {
|
|
@@ -5102,23 +5347,37 @@ function readChatHistory(agentType, offset = 0, limit = 30, historySessionId) {
|
|
|
5102
5347
|
return true;
|
|
5103
5348
|
}).sort().reverse();
|
|
5104
5349
|
const allMessages = [];
|
|
5105
|
-
const
|
|
5350
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5106
5351
|
for (const file of files) {
|
|
5107
|
-
|
|
5108
|
-
const filePath = path5.join(dir, file);
|
|
5352
|
+
const filePath = path7.join(dir, file);
|
|
5109
5353
|
const content = fs3.readFileSync(filePath, "utf-8");
|
|
5110
5354
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
5111
|
-
for (let i =
|
|
5112
|
-
if (allMessages.length >= needed) break;
|
|
5355
|
+
for (let i = 0; i < lines.length; i++) {
|
|
5113
5356
|
try {
|
|
5114
|
-
|
|
5357
|
+
const parsed = JSON.parse(lines[i]);
|
|
5358
|
+
const sanitizedMessage = sanitizeHistoryMessage(agentType, parsed);
|
|
5359
|
+
if (!sanitizedMessage) continue;
|
|
5360
|
+
const hash = buildHistoryMessageHash(agentType, sanitizedMessage);
|
|
5361
|
+
if (seen.has(hash)) continue;
|
|
5362
|
+
seen.add(hash);
|
|
5363
|
+
allMessages.push(sanitizedMessage);
|
|
5115
5364
|
} catch {
|
|
5116
5365
|
}
|
|
5117
5366
|
}
|
|
5118
5367
|
}
|
|
5119
|
-
|
|
5120
|
-
const
|
|
5121
|
-
|
|
5368
|
+
allMessages.sort((a, b) => a.receivedAt - b.receivedAt);
|
|
5369
|
+
const chronological = [];
|
|
5370
|
+
let lastTurn = null;
|
|
5371
|
+
for (const message of allMessages) {
|
|
5372
|
+
const previous = chronological[chronological.length - 1];
|
|
5373
|
+
if (isAdjacentHistoryDuplicate(agentType, previous, message)) continue;
|
|
5374
|
+
if (message.role !== "system" && isAdjacentHistoryDuplicate(agentType, lastTurn, message)) continue;
|
|
5375
|
+
chronological.push(message);
|
|
5376
|
+
if (message.role !== "system") lastTurn = message;
|
|
5377
|
+
}
|
|
5378
|
+
const collapsed = collapseReplayAssistantTurns(agentType, chronological);
|
|
5379
|
+
const sliced = collapsed.slice(offset, offset + limit);
|
|
5380
|
+
const hasMore = collapsed.length > offset + limit;
|
|
5122
5381
|
return { messages: sliced, hasMore };
|
|
5123
5382
|
} catch {
|
|
5124
5383
|
return { messages: [], hasMore: false };
|
|
@@ -5127,7 +5386,7 @@ function readChatHistory(agentType, offset = 0, limit = 30, historySessionId) {
|
|
|
5127
5386
|
function listSavedHistorySessions(agentType, options = {}) {
|
|
5128
5387
|
try {
|
|
5129
5388
|
const sanitized = agentType.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
5130
|
-
const dir =
|
|
5389
|
+
const dir = path7.join(HISTORY_DIR, sanitized);
|
|
5131
5390
|
if (!fs3.existsSync(dir)) return { sessions: [], hasMore: false };
|
|
5132
5391
|
const groupedFiles = /* @__PURE__ */ new Map();
|
|
5133
5392
|
const filePattern = /^([A-Za-z0-9_-]+)_\d{4}-\d{2}-\d{2}\.jsonl$/;
|
|
@@ -5148,7 +5407,7 @@ function listSavedHistorySessions(agentType, options = {}) {
|
|
|
5148
5407
|
let sessionTitle = "";
|
|
5149
5408
|
let preview = "";
|
|
5150
5409
|
for (const file of files.sort()) {
|
|
5151
|
-
const filePath =
|
|
5410
|
+
const filePath = path7.join(dir, file);
|
|
5152
5411
|
const content = fs3.readFileSync(filePath, "utf-8");
|
|
5153
5412
|
const lines = content.split("\n").filter(Boolean);
|
|
5154
5413
|
for (const line of lines) {
|
|
@@ -6972,6 +7231,46 @@ function didProviderConfirmSend(result) {
|
|
|
6972
7231
|
if (!parsed || typeof parsed !== "object") return false;
|
|
6973
7232
|
return parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true;
|
|
6974
7233
|
}
|
|
7234
|
+
async function readExtensionChatState(h) {
|
|
7235
|
+
try {
|
|
7236
|
+
const evalResult = await h.evaluateProviderScript("readChat", void 0, 5e4);
|
|
7237
|
+
if (!evalResult?.result) return null;
|
|
7238
|
+
const parsed = parseMaybeJson(evalResult.result);
|
|
7239
|
+
return parsed && typeof parsed === "object" ? parsed : null;
|
|
7240
|
+
} catch {
|
|
7241
|
+
return null;
|
|
7242
|
+
}
|
|
7243
|
+
}
|
|
7244
|
+
function getStateMessageCount(state) {
|
|
7245
|
+
return Array.isArray(state?.messages) ? state.messages.length : 0;
|
|
7246
|
+
}
|
|
7247
|
+
function getStateLastSignature(state) {
|
|
7248
|
+
const messages = Array.isArray(state?.messages) ? state.messages : [];
|
|
7249
|
+
const last = messages[messages.length - 1];
|
|
7250
|
+
if (!last) return "";
|
|
7251
|
+
return `${last.role || ""}:${String(last.content || "").replace(/\s+/g, " ").trim()}`;
|
|
7252
|
+
}
|
|
7253
|
+
async function getStableExtensionBaseline(h) {
|
|
7254
|
+
const first = await readExtensionChatState(h);
|
|
7255
|
+
if (getStateMessageCount(first) > 0 || getStateLastSignature(first)) return first;
|
|
7256
|
+
await new Promise((resolve12) => setTimeout(resolve12, 150));
|
|
7257
|
+
const second = await readExtensionChatState(h);
|
|
7258
|
+
return getStateMessageCount(second) >= getStateMessageCount(first) ? second : first;
|
|
7259
|
+
}
|
|
7260
|
+
async function verifyExtensionSendObserved(h, before) {
|
|
7261
|
+
const beforeCount = getStateMessageCount(before);
|
|
7262
|
+
const beforeSignature = getStateLastSignature(before);
|
|
7263
|
+
for (let attempt = 0; attempt < 12; attempt += 1) {
|
|
7264
|
+
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
7265
|
+
const state = await readExtensionChatState(h);
|
|
7266
|
+
if (state?.status === "waiting_approval") return true;
|
|
7267
|
+
const afterCount = getStateMessageCount(state);
|
|
7268
|
+
const afterSignature = getStateLastSignature(state);
|
|
7269
|
+
if (afterCount > beforeCount) return true;
|
|
7270
|
+
if (afterSignature && afterSignature !== beforeSignature) return true;
|
|
7271
|
+
}
|
|
7272
|
+
return false;
|
|
7273
|
+
}
|
|
6975
7274
|
async function handleChatHistory(h, args) {
|
|
6976
7275
|
const { agentType, offset, limit } = args;
|
|
6977
7276
|
const historySessionId = getHistorySessionId(h, args);
|
|
@@ -7152,12 +7451,17 @@ async function handleSendChat(h, args) {
|
|
|
7152
7451
|
if (isExtensionTransport(transport)) {
|
|
7153
7452
|
_log(`Extension: ${provider?.type || "unknown_extension"}`);
|
|
7154
7453
|
try {
|
|
7454
|
+
const beforeState = await getStableExtensionBaseline(h);
|
|
7155
7455
|
const evalResult = await h.evaluateProviderScript("sendMessage", { message: text }, 3e4);
|
|
7156
7456
|
if (evalResult?.result) {
|
|
7157
7457
|
const parsed = parseMaybeJson(evalResult.result);
|
|
7158
7458
|
if (didProviderConfirmSend(parsed)) {
|
|
7159
|
-
|
|
7160
|
-
|
|
7459
|
+
const observed = await verifyExtensionSendObserved(h, beforeState);
|
|
7460
|
+
if (observed) {
|
|
7461
|
+
_log(`Extension script sent OK`);
|
|
7462
|
+
return _logSendSuccess("extension-script");
|
|
7463
|
+
}
|
|
7464
|
+
_log(`Extension script reported send but no chat-state change was observed`);
|
|
7161
7465
|
}
|
|
7162
7466
|
if (parsed?.needsTypeAndSend) {
|
|
7163
7467
|
_log(`Extension needsTypeAndSend \u2192 AgentStreamManager`);
|
|
@@ -7323,6 +7627,14 @@ async function handleListChats(h, args) {
|
|
|
7323
7627
|
} catch {
|
|
7324
7628
|
}
|
|
7325
7629
|
}
|
|
7630
|
+
if (parsed?.sessions && Array.isArray(parsed.sessions)) {
|
|
7631
|
+
LOG.info("Command", `[list_chats] OK: ${parsed.sessions.length} chats`);
|
|
7632
|
+
return { success: true, chats: parsed.sessions };
|
|
7633
|
+
}
|
|
7634
|
+
if (parsed?.chats && Array.isArray(parsed.chats)) {
|
|
7635
|
+
LOG.info("Command", `[list_chats] OK: ${parsed.chats.length} chats`);
|
|
7636
|
+
return { success: true, chats: parsed.chats };
|
|
7637
|
+
}
|
|
7326
7638
|
if (Array.isArray(parsed)) {
|
|
7327
7639
|
LOG.info("Command", `[list_chats] OK: ${parsed.length} chats`);
|
|
7328
7640
|
return { success: true, chats: parsed };
|
|
@@ -7392,7 +7704,13 @@ async function handleSwitchChat(h, args) {
|
|
|
7392
7704
|
} catch (e) {
|
|
7393
7705
|
return { success: false, error: `webviewSwitchSession failed: ${e.message}` };
|
|
7394
7706
|
}
|
|
7395
|
-
const
|
|
7707
|
+
const switchParams = {
|
|
7708
|
+
sessionId,
|
|
7709
|
+
title: sessionId,
|
|
7710
|
+
id: sessionId,
|
|
7711
|
+
SESSION_ID: JSON.stringify(sessionId)
|
|
7712
|
+
};
|
|
7713
|
+
const script = h.getProviderScript("switchSession", switchParams) || h.getProviderScript("switch_session", switchParams);
|
|
7396
7714
|
if (!script) return { success: false, error: "switch_session script not available" };
|
|
7397
7715
|
try {
|
|
7398
7716
|
const raw = await cdp.evaluate(script, 15e3);
|
|
@@ -7471,8 +7789,8 @@ async function handleSetMode(h, args) {
|
|
|
7471
7789
|
const adapter = getTargetedCliAdapter(h, args, provider?.type);
|
|
7472
7790
|
if (adapter) {
|
|
7473
7791
|
const acpInstance = adapter._acpInstance;
|
|
7474
|
-
if (acpInstance && typeof acpInstance.
|
|
7475
|
-
acpInstance.
|
|
7792
|
+
if (acpInstance && typeof acpInstance.setMode === "function") {
|
|
7793
|
+
await acpInstance.setMode(mode);
|
|
7476
7794
|
return { success: true, mode };
|
|
7477
7795
|
}
|
|
7478
7796
|
}
|
|
@@ -7529,9 +7847,9 @@ async function handleChangeModel(h, args) {
|
|
|
7529
7847
|
LOG.info("Command", `[change_model] ACP adapter found: ${!!adapter}, type=${adapter?.cliType}, hasAcpInstance=${!!adapter?._acpInstance}`);
|
|
7530
7848
|
if (adapter) {
|
|
7531
7849
|
const acpInstance = adapter._acpInstance;
|
|
7532
|
-
if (acpInstance && typeof acpInstance.
|
|
7533
|
-
acpInstance.
|
|
7534
|
-
LOG.info("Command", `[change_model]
|
|
7850
|
+
if (acpInstance && typeof acpInstance.setConfigOption === "function") {
|
|
7851
|
+
await acpInstance.setConfigOption("model", model);
|
|
7852
|
+
LOG.info("Command", `[change_model] Updated ACP model to ${model}`);
|
|
7535
7853
|
return { success: true, model };
|
|
7536
7854
|
}
|
|
7537
7855
|
}
|
|
@@ -7648,9 +7966,21 @@ async function handleResolveAction(h, args) {
|
|
|
7648
7966
|
return { success: true, buttonIndex, button: buttons[buttonIndex] ?? button };
|
|
7649
7967
|
}
|
|
7650
7968
|
if (isExtensionTransport(transport) && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
|
|
7651
|
-
const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action);
|
|
7969
|
+
const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action, button);
|
|
7652
7970
|
return { success: ok };
|
|
7653
7971
|
}
|
|
7972
|
+
if (transport === "acp") {
|
|
7973
|
+
const adapter = getTargetedCliAdapter(h, args, provider?.type);
|
|
7974
|
+
const acpInstance = adapter?._acpInstance;
|
|
7975
|
+
if (!acpInstance) return { success: false, error: "ACP instance not found" };
|
|
7976
|
+
try {
|
|
7977
|
+
await acpInstance.resolvePermission(action === "approve" || action === "accept" || action === "always");
|
|
7978
|
+
LOG.info("Command", `[resolveAction] ACP \u2192 ${action}`);
|
|
7979
|
+
return { success: true, action };
|
|
7980
|
+
} catch (e) {
|
|
7981
|
+
return { success: false, error: e?.message || "ACP resolve action failed" };
|
|
7982
|
+
}
|
|
7983
|
+
}
|
|
7654
7984
|
if (provider?.scripts?.webviewResolveAction || provider?.scripts?.webview_resolve_action) {
|
|
7655
7985
|
const script = h.getProviderScript("webviewResolveAction", { action, button, buttonText: button }) || h.getProviderScript("webview_resolve_action", { action, button, buttonText: button });
|
|
7656
7986
|
if (script) {
|
|
@@ -7729,7 +8059,7 @@ async function handleResolveAction(h, args) {
|
|
|
7729
8059
|
|
|
7730
8060
|
// src/commands/cdp-commands.ts
|
|
7731
8061
|
var fs4 = __toESM(require("fs"));
|
|
7732
|
-
var
|
|
8062
|
+
var path8 = __toESM(require("path"));
|
|
7733
8063
|
var os6 = __toESM(require("os"));
|
|
7734
8064
|
var KEY_TO_VK = {
|
|
7735
8065
|
Backspace: 8,
|
|
@@ -7981,25 +8311,25 @@ function resolveSafePath(requestedPath) {
|
|
|
7981
8311
|
const inputPath = rawPath || ".";
|
|
7982
8312
|
const home = os6.homedir();
|
|
7983
8313
|
if (inputPath.startsWith("~")) {
|
|
7984
|
-
return
|
|
8314
|
+
return path8.resolve(path8.join(home, inputPath.slice(1)));
|
|
7985
8315
|
}
|
|
7986
8316
|
if (process.platform === "win32") {
|
|
7987
8317
|
const normalized = normalizeWindowsRequestedPath(inputPath);
|
|
7988
|
-
if (
|
|
7989
|
-
return
|
|
8318
|
+
if (path8.win32.isAbsolute(normalized)) {
|
|
8319
|
+
return path8.win32.normalize(normalized);
|
|
7990
8320
|
}
|
|
7991
|
-
return
|
|
8321
|
+
return path8.win32.resolve(normalized);
|
|
7992
8322
|
}
|
|
7993
|
-
if (
|
|
7994
|
-
return
|
|
8323
|
+
if (path8.isAbsolute(inputPath)) {
|
|
8324
|
+
return path8.normalize(inputPath);
|
|
7995
8325
|
}
|
|
7996
|
-
return
|
|
8326
|
+
return path8.resolve(inputPath);
|
|
7997
8327
|
}
|
|
7998
8328
|
function listDirectoryEntriesSafe(dirPath) {
|
|
7999
8329
|
const entries = fs4.readdirSync(dirPath, { withFileTypes: true });
|
|
8000
8330
|
const files = [];
|
|
8001
8331
|
for (const entry of entries) {
|
|
8002
|
-
const entryPath =
|
|
8332
|
+
const entryPath = path8.join(dirPath, entry.name);
|
|
8003
8333
|
try {
|
|
8004
8334
|
if (entry.isDirectory()) {
|
|
8005
8335
|
files.push({ name: entry.name, type: "directory" });
|
|
@@ -8038,7 +8368,7 @@ async function handleFileRead(h, args) {
|
|
|
8038
8368
|
async function handleFileWrite(h, args) {
|
|
8039
8369
|
try {
|
|
8040
8370
|
const filePath = resolveSafePath(args?.path);
|
|
8041
|
-
fs4.mkdirSync(
|
|
8371
|
+
fs4.mkdirSync(path8.dirname(filePath), { recursive: true });
|
|
8042
8372
|
fs4.writeFileSync(filePath, args?.content || "", "utf-8");
|
|
8043
8373
|
return { success: true, path: filePath };
|
|
8044
8374
|
} catch (e) {
|
|
@@ -8126,7 +8456,7 @@ function handleGetProviderSettings(h, args) {
|
|
|
8126
8456
|
}
|
|
8127
8457
|
return { success: true, settings: allSettings, values: allValues };
|
|
8128
8458
|
}
|
|
8129
|
-
function handleSetProviderSetting(h, args) {
|
|
8459
|
+
async function handleSetProviderSetting(h, args) {
|
|
8130
8460
|
const loader = h.ctx.providerLoader;
|
|
8131
8461
|
const { providerType, key, value } = args || {};
|
|
8132
8462
|
if (!providerType || !key || value === void 0) {
|
|
@@ -8139,6 +8469,7 @@ function handleSetProviderSetting(h, args) {
|
|
|
8139
8469
|
const updated = h.ctx.instanceManager.updateInstanceSettings(providerType, allSettings);
|
|
8140
8470
|
LOG.info("Command", `[set_provider_setting] ${providerType}.${key}=${JSON.stringify(value)} \u2192 ${updated} instance(s) updated`);
|
|
8141
8471
|
}
|
|
8472
|
+
await h.ctx.onProviderSettingChanged?.(providerType, key, value);
|
|
8142
8473
|
return { success: true, providerType, key, value };
|
|
8143
8474
|
}
|
|
8144
8475
|
return { success: false, error: `Failed to set ${providerType}.${key} \u2014 invalid key, value, or not a public setting` };
|
|
@@ -8176,10 +8507,10 @@ function getCliScriptCommand(payload) {
|
|
|
8176
8507
|
}
|
|
8177
8508
|
const command = payload.command;
|
|
8178
8509
|
if (!command || typeof command !== "object") return null;
|
|
8179
|
-
if (command.type !== "send_message") return null;
|
|
8510
|
+
if (command.type !== "send_message" && command.type !== "pty_write") return null;
|
|
8180
8511
|
const text = typeof command.text === "string" ? command.text.trim() : typeof command.message === "string" ? command.message.trim() : "";
|
|
8181
8512
|
if (!text) return null;
|
|
8182
|
-
return { type:
|
|
8513
|
+
return { type: command.type, text };
|
|
8183
8514
|
}
|
|
8184
8515
|
function applyProviderPatch(h, args, payload) {
|
|
8185
8516
|
if (!payload || typeof payload !== "object") return;
|
|
@@ -8220,6 +8551,8 @@ async function executeProviderScript(h, args, scriptName) {
|
|
|
8220
8551
|
const cliCommand = getCliScriptCommand(parsed.payload);
|
|
8221
8552
|
if (cliCommand?.type === "send_message" && cliCommand.text) {
|
|
8222
8553
|
await adapter.sendMessage(cliCommand.text);
|
|
8554
|
+
} else if (cliCommand?.type === "pty_write" && cliCommand.text && adapter.writeRaw) {
|
|
8555
|
+
adapter.writeRaw(cliCommand.text + "\r");
|
|
8223
8556
|
}
|
|
8224
8557
|
applyProviderPatch(h, args, parsed.payload);
|
|
8225
8558
|
return { success: true, ...parsed.payload && typeof parsed.payload === "object" ? parsed.payload : { result: parsed.payload } };
|
|
@@ -8898,7 +9231,7 @@ var DaemonCommandHandler = class {
|
|
|
8898
9231
|
try {
|
|
8899
9232
|
const http3 = await import("http");
|
|
8900
9233
|
const postData = JSON.stringify(body);
|
|
8901
|
-
const result = await new Promise((
|
|
9234
|
+
const result = await new Promise((resolve12, reject) => {
|
|
8902
9235
|
const req = http3.request({
|
|
8903
9236
|
hostname: "127.0.0.1",
|
|
8904
9237
|
port: 19280,
|
|
@@ -8910,9 +9243,9 @@ var DaemonCommandHandler = class {
|
|
|
8910
9243
|
res.on("data", (chunk) => data += chunk);
|
|
8911
9244
|
res.on("end", () => {
|
|
8912
9245
|
try {
|
|
8913
|
-
|
|
9246
|
+
resolve12(JSON.parse(data));
|
|
8914
9247
|
} catch {
|
|
8915
|
-
|
|
9248
|
+
resolve12({ raw: data });
|
|
8916
9249
|
}
|
|
8917
9250
|
});
|
|
8918
9251
|
});
|
|
@@ -8930,15 +9263,15 @@ var DaemonCommandHandler = class {
|
|
|
8930
9263
|
if (!providerType) return { success: false, error: "providerType required" };
|
|
8931
9264
|
try {
|
|
8932
9265
|
const http3 = await import("http");
|
|
8933
|
-
const result = await new Promise((
|
|
9266
|
+
const result = await new Promise((resolve12, reject) => {
|
|
8934
9267
|
http3.get(`http://127.0.0.1:19280/api/providers/${providerType}/${endpoint}`, (res) => {
|
|
8935
9268
|
let data = "";
|
|
8936
9269
|
res.on("data", (chunk) => data += chunk);
|
|
8937
9270
|
res.on("end", () => {
|
|
8938
9271
|
try {
|
|
8939
|
-
|
|
9272
|
+
resolve12(JSON.parse(data));
|
|
8940
9273
|
} catch {
|
|
8941
|
-
|
|
9274
|
+
resolve12({ raw: data });
|
|
8942
9275
|
}
|
|
8943
9276
|
});
|
|
8944
9277
|
}).on("error", reject);
|
|
@@ -8952,7 +9285,7 @@ var DaemonCommandHandler = class {
|
|
|
8952
9285
|
try {
|
|
8953
9286
|
const http3 = await import("http");
|
|
8954
9287
|
const postData = JSON.stringify(args || {});
|
|
8955
|
-
const result = await new Promise((
|
|
9288
|
+
const result = await new Promise((resolve12, reject) => {
|
|
8956
9289
|
const req = http3.request({
|
|
8957
9290
|
hostname: "127.0.0.1",
|
|
8958
9291
|
port: 19280,
|
|
@@ -8964,9 +9297,9 @@ var DaemonCommandHandler = class {
|
|
|
8964
9297
|
res.on("data", (chunk) => data += chunk);
|
|
8965
9298
|
res.on("end", () => {
|
|
8966
9299
|
try {
|
|
8967
|
-
|
|
9300
|
+
resolve12(JSON.parse(data));
|
|
8968
9301
|
} catch {
|
|
8969
|
-
|
|
9302
|
+
resolve12({ raw: data });
|
|
8970
9303
|
}
|
|
8971
9304
|
});
|
|
8972
9305
|
});
|
|
@@ -8983,7 +9316,7 @@ var DaemonCommandHandler = class {
|
|
|
8983
9316
|
|
|
8984
9317
|
// src/commands/cli-manager.ts
|
|
8985
9318
|
var os10 = __toESM(require("os"));
|
|
8986
|
-
var
|
|
9319
|
+
var path11 = __toESM(require("path"));
|
|
8987
9320
|
var crypto4 = __toESM(require("crypto"));
|
|
8988
9321
|
var import_chalk = __toESM(require("chalk"));
|
|
8989
9322
|
init_provider_cli_adapter();
|
|
@@ -8991,7 +9324,7 @@ init_config();
|
|
|
8991
9324
|
|
|
8992
9325
|
// src/providers/cli-provider-instance.ts
|
|
8993
9326
|
var os9 = __toESM(require("os"));
|
|
8994
|
-
var
|
|
9327
|
+
var path10 = __toESM(require("path"));
|
|
8995
9328
|
var crypto3 = __toESM(require("crypto"));
|
|
8996
9329
|
var fs5 = __toESM(require("fs"));
|
|
8997
9330
|
var import_node_module = require("module");
|
|
@@ -9000,7 +9333,7 @@ init_logger();
|
|
|
9000
9333
|
var CachedDatabaseSync = null;
|
|
9001
9334
|
function getDatabaseSync() {
|
|
9002
9335
|
if (CachedDatabaseSync) return CachedDatabaseSync;
|
|
9003
|
-
const requireFn = typeof require === "function" ? require : (0, import_node_module.createRequire)(
|
|
9336
|
+
const requireFn = typeof require === "function" ? require : (0, import_node_module.createRequire)(path10.join(process.cwd(), "__adhdev_sqlite_loader__.js"));
|
|
9004
9337
|
const sqliteModule = requireFn(`node:${"sqlite"}`);
|
|
9005
9338
|
CachedDatabaseSync = sqliteModule.DatabaseSync;
|
|
9006
9339
|
if (!CachedDatabaseSync) {
|
|
@@ -9040,6 +9373,7 @@ var CliProviderInstance = class {
|
|
|
9040
9373
|
historyWriter;
|
|
9041
9374
|
runtimeMessages = [];
|
|
9042
9375
|
instanceId;
|
|
9376
|
+
suppressIdleHistoryReplay = false;
|
|
9043
9377
|
presentationMode;
|
|
9044
9378
|
providerSessionId;
|
|
9045
9379
|
launchMode;
|
|
@@ -9067,7 +9401,15 @@ var CliProviderInstance = class {
|
|
|
9067
9401
|
await this.adapter.spawn();
|
|
9068
9402
|
this.maybeAppendRuntimeRecoveryMessage(this.adapter.getRuntimeMetadata());
|
|
9069
9403
|
if (this.providerSessionId) {
|
|
9404
|
+
this.historyWriter.compactHistorySession(this.type, this.providerSessionId);
|
|
9070
9405
|
const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
|
|
9406
|
+
this.historyWriter.seedSessionHistory(
|
|
9407
|
+
this.type,
|
|
9408
|
+
restoredHistory.messages,
|
|
9409
|
+
this.providerSessionId,
|
|
9410
|
+
this.instanceId
|
|
9411
|
+
);
|
|
9412
|
+
this.suppressIdleHistoryReplay = restoredHistory.messages.length > 0;
|
|
9071
9413
|
if (restoredHistory.messages.length > 0) {
|
|
9072
9414
|
this.adapter.seedCommittedMessages(
|
|
9073
9415
|
restoredHistory.messages.map((message) => ({
|
|
@@ -9111,7 +9453,7 @@ var CliProviderInstance = class {
|
|
|
9111
9453
|
} else if (this.type === "codex-cli") {
|
|
9112
9454
|
probedSessionId = this.probeSessionIdFromConfig({
|
|
9113
9455
|
dbPath: "~/.codex/state_5.sqlite",
|
|
9114
|
-
query: "select id from threads where cwd in ({dirs}) and
|
|
9456
|
+
query: "select id from threads where cwd in ({dirs}) and updated_at >= ? and archived = 0 order by updated_at desc limit 1",
|
|
9115
9457
|
timestampFormat: "unix_s"
|
|
9116
9458
|
});
|
|
9117
9459
|
} else if (this.type === "goose-cli") {
|
|
@@ -9171,6 +9513,7 @@ var CliProviderInstance = class {
|
|
|
9171
9513
|
const mergedMessages = this.mergeConversationMessages(parsedMessages);
|
|
9172
9514
|
const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
9173
9515
|
if (parsedMessages.length > 0) {
|
|
9516
|
+
const shouldSkipReplayPersist = this.suppressIdleHistoryReplay && adapterStatus.status === "idle" && parsedStatus?.status === "idle";
|
|
9174
9517
|
let messagesToSave = parsedMessages;
|
|
9175
9518
|
if (parsedStatus?.status === "generating" || parsedStatus?.status === "long_generating") {
|
|
9176
9519
|
const lastIdx = messagesToSave.length - 1;
|
|
@@ -9178,7 +9521,7 @@ var CliProviderInstance = class {
|
|
|
9178
9521
|
messagesToSave = messagesToSave.slice(0, lastIdx);
|
|
9179
9522
|
}
|
|
9180
9523
|
}
|
|
9181
|
-
if (messagesToSave.length > 0) {
|
|
9524
|
+
if (!shouldSkipReplayPersist && messagesToSave.length > 0) {
|
|
9182
9525
|
this.historyWriter.appendNewMessages(
|
|
9183
9526
|
this.type,
|
|
9184
9527
|
messagesToSave,
|
|
@@ -9273,6 +9616,7 @@ var CliProviderInstance = class {
|
|
|
9273
9616
|
if (newStatus !== this.lastStatus) {
|
|
9274
9617
|
LOG.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
|
|
9275
9618
|
if (this.lastStatus === "idle" && newStatus === "generating") {
|
|
9619
|
+
this.suppressIdleHistoryReplay = false;
|
|
9276
9620
|
if (this.completedDebouncePending) {
|
|
9277
9621
|
LOG.info("CLI", `[${this.type}] cancelled pending completed (resumed generating)`);
|
|
9278
9622
|
if (this.completedDebounceTimer) {
|
|
@@ -9292,6 +9636,7 @@ var CliProviderInstance = class {
|
|
|
9292
9636
|
this.generatingDebounceTimer = null;
|
|
9293
9637
|
}, 1e3);
|
|
9294
9638
|
} else if (newStatus === "waiting_approval") {
|
|
9639
|
+
this.suppressIdleHistoryReplay = false;
|
|
9295
9640
|
if (this.generatingDebouncePending) {
|
|
9296
9641
|
if (this.generatingDebounceTimer) {
|
|
9297
9642
|
clearTimeout(this.generatingDebounceTimer);
|
|
@@ -9829,8 +10174,9 @@ var AcpProviderInstance = class {
|
|
|
9829
10174
|
async setConfigOption(category, value) {
|
|
9830
10175
|
const opt = this.configOptions.find((c) => c.category === category);
|
|
9831
10176
|
if (!opt) {
|
|
9832
|
-
|
|
9833
|
-
|
|
10177
|
+
const message = `[${this.type}] No config option for category: ${category}`;
|
|
10178
|
+
this.log.warn(message);
|
|
10179
|
+
throw new Error(message);
|
|
9834
10180
|
}
|
|
9835
10181
|
if (this.useStaticConfig) {
|
|
9836
10182
|
opt.currentValue = value;
|
|
@@ -9842,8 +10188,9 @@ var AcpProviderInstance = class {
|
|
|
9842
10188
|
return;
|
|
9843
10189
|
}
|
|
9844
10190
|
if (!this.connection || !this.sessionId) {
|
|
9845
|
-
|
|
9846
|
-
|
|
10191
|
+
const message = `[${this.type}] Cannot set config: no active connection/session`;
|
|
10192
|
+
this.log.warn(message);
|
|
10193
|
+
throw new Error(message);
|
|
9847
10194
|
}
|
|
9848
10195
|
try {
|
|
9849
10196
|
this.log.info(`[${this.type}] Sending session/set_config_option: configId=${opt.configId} value=${value} sessionId=${this.sessionId}`);
|
|
@@ -9857,7 +10204,9 @@ var AcpProviderInstance = class {
|
|
|
9857
10204
|
if (result?.configOptions) this.parseConfigOptions(result.configOptions);
|
|
9858
10205
|
this.log.info(`[${this.type}] Config ${category} set to: ${value} | response: ${JSON.stringify(result)?.slice(0, 300)}`);
|
|
9859
10206
|
} catch (e) {
|
|
9860
|
-
|
|
10207
|
+
const message = e?.message || "Unknown ACP config error";
|
|
10208
|
+
this.log.warn(`[${this.type}] set_config_option failed: ${message}`);
|
|
10209
|
+
throw new Error(message);
|
|
9861
10210
|
}
|
|
9862
10211
|
}
|
|
9863
10212
|
async setMode(modeId) {
|
|
@@ -9873,8 +10222,9 @@ var AcpProviderInstance = class {
|
|
|
9873
10222
|
return;
|
|
9874
10223
|
}
|
|
9875
10224
|
if (!this.connection || !this.sessionId) {
|
|
9876
|
-
|
|
9877
|
-
|
|
10225
|
+
const message = `[${this.type}] Cannot set mode: no active connection/session`;
|
|
10226
|
+
this.log.warn(message);
|
|
10227
|
+
throw new Error(message);
|
|
9878
10228
|
}
|
|
9879
10229
|
try {
|
|
9880
10230
|
await this.connection.setSessionMode({
|
|
@@ -9884,7 +10234,9 @@ var AcpProviderInstance = class {
|
|
|
9884
10234
|
this.currentMode = modeId;
|
|
9885
10235
|
this.log.info(`[${this.type}] Mode set to: ${modeId}`);
|
|
9886
10236
|
} catch (e) {
|
|
9887
|
-
|
|
10237
|
+
const message = e?.message || "Unknown ACP mode error";
|
|
10238
|
+
this.log.warn(`[${this.type}] set_mode failed: ${message}`);
|
|
10239
|
+
throw new Error(message);
|
|
9888
10240
|
}
|
|
9889
10241
|
}
|
|
9890
10242
|
/** Static config: kill process and restart with new args */
|
|
@@ -9932,7 +10284,7 @@ var AcpProviderInstance = class {
|
|
|
9932
10284
|
if (!spawnConfig) {
|
|
9933
10285
|
throw new Error(`[ACP:${this.type}] No spawn config defined`);
|
|
9934
10286
|
}
|
|
9935
|
-
const command = spawnConfig.command;
|
|
10287
|
+
const command = typeof this.settings.executablePath === "string" && this.settings.executablePath.trim() ? this.settings.executablePath.trim() : spawnConfig.command;
|
|
9936
10288
|
let baseArgs = spawnConfig.args || [];
|
|
9937
10289
|
if (this.provider.spawnArgBuilder && Object.keys(this.selectedConfig).length > 0) {
|
|
9938
10290
|
baseArgs = this.provider.spawnArgBuilder(this.selectedConfig);
|
|
@@ -10048,13 +10400,13 @@ var AcpProviderInstance = class {
|
|
|
10048
10400
|
}
|
|
10049
10401
|
this.currentStatus = "waiting_approval";
|
|
10050
10402
|
this.detectStatusTransition();
|
|
10051
|
-
const approved = await new Promise((
|
|
10052
|
-
this.permissionResolvers.push(
|
|
10403
|
+
const approved = await new Promise((resolve12) => {
|
|
10404
|
+
this.permissionResolvers.push(resolve12);
|
|
10053
10405
|
setTimeout(() => {
|
|
10054
|
-
const idx = this.permissionResolvers.indexOf(
|
|
10406
|
+
const idx = this.permissionResolvers.indexOf(resolve12);
|
|
10055
10407
|
if (idx >= 0) {
|
|
10056
10408
|
this.permissionResolvers.splice(idx, 1);
|
|
10057
|
-
|
|
10409
|
+
resolve12(false);
|
|
10058
10410
|
}
|
|
10059
10411
|
}, 3e5);
|
|
10060
10412
|
});
|
|
@@ -10761,7 +11113,7 @@ var DaemonCliManager = class {
|
|
|
10761
11113
|
async startSession(cliType, workingDir, cliArgs, initialModel, options) {
|
|
10762
11114
|
const trimmed = (workingDir || "").trim();
|
|
10763
11115
|
if (!trimmed) throw new Error("working directory required");
|
|
10764
|
-
const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os10.homedir()) :
|
|
11116
|
+
const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os10.homedir()) : path11.resolve(trimmed);
|
|
10765
11117
|
const normalizedType = this.providerLoader.resolveAlias(cliType);
|
|
10766
11118
|
const provider = this.providerLoader.getByAlias(cliType);
|
|
10767
11119
|
const key = crypto4.randomUUID();
|
|
@@ -10848,10 +11200,10 @@ ${installInfo}`
|
|
|
10848
11200
|
if (!cliInfo) {
|
|
10849
11201
|
const installHint = provider?.install || "";
|
|
10850
11202
|
const displayName = provider?.displayName || provider?.name || cliType;
|
|
10851
|
-
const spawnCmd = provider?.spawn?.command || cliType;
|
|
11203
|
+
const spawnCmd = this.providerLoader.getSpawnCommand(normalizedType, provider?.spawn?.command || cliType);
|
|
10852
11204
|
throw new Error(
|
|
10853
11205
|
`${displayName} is not installed.
|
|
10854
|
-
Command '${spawnCmd}' not
|
|
11206
|
+
Command '${spawnCmd}' is not available.
|
|
10855
11207
|
` + (installHint ? `
|
|
10856
11208
|
${installHint}
|
|
10857
11209
|
` : "") + `
|
|
@@ -10997,6 +11349,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
10997
11349
|
if (!instanceManager) return 0;
|
|
10998
11350
|
const sessions = records || await this.deps.listHostedCliRuntimes?.() || [];
|
|
10999
11351
|
let restored = 0;
|
|
11352
|
+
const restoredBindings = /* @__PURE__ */ new Set();
|
|
11000
11353
|
for (const record of sessions) {
|
|
11001
11354
|
if (!record?.runtimeId || !record?.cliType || !record?.workspace) continue;
|
|
11002
11355
|
if (this.adapters.has(record.runtimeId) || instanceManager.getInstance(record.runtimeId)) continue;
|
|
@@ -11010,6 +11363,18 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
11010
11363
|
record.cliArgs,
|
|
11011
11364
|
record.providerSessionId
|
|
11012
11365
|
);
|
|
11366
|
+
const bindingKey = [
|
|
11367
|
+
normalizedType,
|
|
11368
|
+
record.workspace,
|
|
11369
|
+
sessionBinding.providerSessionId || record.runtimeId
|
|
11370
|
+
].join("::");
|
|
11371
|
+
if (restoredBindings.has(bindingKey)) {
|
|
11372
|
+
LOG.info(
|
|
11373
|
+
"CLI",
|
|
11374
|
+
`\u21B7 Skipping duplicate hosted runtime restore: ${record.runtimeKey || record.runtimeId} (${normalizedType} @ ${record.workspace}) binding=${sessionBinding.providerSessionId || "runtime"}`
|
|
11375
|
+
);
|
|
11376
|
+
continue;
|
|
11377
|
+
}
|
|
11013
11378
|
try {
|
|
11014
11379
|
await this.registerCliInstance(
|
|
11015
11380
|
record.runtimeId,
|
|
@@ -11025,6 +11390,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
11025
11390
|
launchMode: "manual"
|
|
11026
11391
|
}
|
|
11027
11392
|
);
|
|
11393
|
+
restoredBindings.add(bindingKey);
|
|
11028
11394
|
restored += 1;
|
|
11029
11395
|
LOG.info("CLI", `\u267B Restored hosted runtime: ${record.runtimeKey || record.runtimeId} (${record.displayName || record.workspace})`);
|
|
11030
11396
|
} catch (error) {
|
|
@@ -11211,16 +11577,17 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
11211
11577
|
var import_child_process6 = require("child_process");
|
|
11212
11578
|
var net = __toESM(require("net"));
|
|
11213
11579
|
var os12 = __toESM(require("os"));
|
|
11214
|
-
var
|
|
11580
|
+
var path13 = __toESM(require("path"));
|
|
11215
11581
|
|
|
11216
11582
|
// src/providers/provider-loader.ts
|
|
11217
11583
|
var fs6 = __toESM(require("fs"));
|
|
11218
|
-
var
|
|
11584
|
+
var path12 = __toESM(require("path"));
|
|
11219
11585
|
var os11 = __toESM(require("os"));
|
|
11220
11586
|
var chokidar = __toESM(require("chokidar"));
|
|
11221
11587
|
init_logger();
|
|
11222
11588
|
var ProviderLoader = class _ProviderLoader {
|
|
11223
11589
|
providers = /* @__PURE__ */ new Map();
|
|
11590
|
+
providerAvailability = /* @__PURE__ */ new Map();
|
|
11224
11591
|
userDir;
|
|
11225
11592
|
upstreamDir;
|
|
11226
11593
|
disableUpstream;
|
|
@@ -11236,12 +11603,12 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11236
11603
|
static META_FILE = ".meta.json";
|
|
11237
11604
|
constructor(options) {
|
|
11238
11605
|
this.logFn = options?.logFn || LOG.forComponent("Provider").asLogFn();
|
|
11239
|
-
const defaultProvidersDir =
|
|
11606
|
+
const defaultProvidersDir = path12.join(os11.homedir(), ".adhdev", "providers");
|
|
11240
11607
|
if (options?.userDir) {
|
|
11241
11608
|
this.userDir = options.userDir;
|
|
11242
11609
|
this.log(`Config 'providerDir' applied: ${this.userDir}`);
|
|
11243
11610
|
} else {
|
|
11244
|
-
const localRepoPath =
|
|
11611
|
+
const localRepoPath = path12.resolve(__dirname, "../../../../../adhdev-providers");
|
|
11245
11612
|
if (fs6.existsSync(localRepoPath)) {
|
|
11246
11613
|
this.userDir = localRepoPath;
|
|
11247
11614
|
this.log(`Auto-detected local public repository: ${this.userDir} (Dev workspace speedup)`);
|
|
@@ -11250,7 +11617,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11250
11617
|
this.log(`Using default user providers directory: ${this.userDir}`);
|
|
11251
11618
|
}
|
|
11252
11619
|
}
|
|
11253
|
-
this.upstreamDir =
|
|
11620
|
+
this.upstreamDir = path12.join(defaultProvidersDir, ".upstream");
|
|
11254
11621
|
this.disableUpstream = options?.disableUpstream ?? false;
|
|
11255
11622
|
}
|
|
11256
11623
|
log(msg) {
|
|
@@ -11280,7 +11647,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11280
11647
|
* Canonical provider directory shape for a given root.
|
|
11281
11648
|
*/
|
|
11282
11649
|
getProviderDir(root, category, type) {
|
|
11283
|
-
return
|
|
11650
|
+
return path12.join(root, category, type);
|
|
11284
11651
|
}
|
|
11285
11652
|
/**
|
|
11286
11653
|
* Canonical user override directory for a provider.
|
|
@@ -11307,7 +11674,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11307
11674
|
resolveProviderFile(type, ...segments) {
|
|
11308
11675
|
const dir = this.findProviderDirInternal(type);
|
|
11309
11676
|
if (!dir) return null;
|
|
11310
|
-
return
|
|
11677
|
+
return path12.join(dir, ...segments);
|
|
11311
11678
|
}
|
|
11312
11679
|
/**
|
|
11313
11680
|
* Load all providers (3-tier priority)
|
|
@@ -11318,6 +11685,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11318
11685
|
*/
|
|
11319
11686
|
loadAll() {
|
|
11320
11687
|
this.providers.clear();
|
|
11688
|
+
this.providerAvailability.clear();
|
|
11321
11689
|
let upstreamCount = 0;
|
|
11322
11690
|
if (!this.disableUpstream && fs6.existsSync(this.upstreamDir)) {
|
|
11323
11691
|
upstreamCount = this.loadDir(this.upstreamDir);
|
|
@@ -11345,7 +11713,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11345
11713
|
if (!fs6.existsSync(this.upstreamDir)) return false;
|
|
11346
11714
|
try {
|
|
11347
11715
|
return fs6.readdirSync(this.upstreamDir).some(
|
|
11348
|
-
(d) => fs6.statSync(
|
|
11716
|
+
(d) => fs6.statSync(path12.join(this.upstreamDir, d)).isDirectory()
|
|
11349
11717
|
);
|
|
11350
11718
|
} catch {
|
|
11351
11719
|
return false;
|
|
@@ -11388,11 +11756,12 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11388
11756
|
if ((p.category === "cli" || p.category === "acp") && p.spawn?.command) {
|
|
11389
11757
|
const verCmdConfig = p.versionCommand;
|
|
11390
11758
|
const versionCommand = typeof verCmdConfig === "object" && verCmdConfig !== null ? verCmdConfig[process.platform] : verCmdConfig;
|
|
11759
|
+
const command = this.getSpawnCommand(p.type, p.spawn.command);
|
|
11391
11760
|
result.push({
|
|
11392
11761
|
id: p.type,
|
|
11393
11762
|
displayName: p.displayName || p.name,
|
|
11394
11763
|
icon: p.icon || "\u{1F527}",
|
|
11395
|
-
command
|
|
11764
|
+
command,
|
|
11396
11765
|
category: p.category,
|
|
11397
11766
|
...typeof versionCommand === "string" && versionCommand.trim() ? { versionCommand: versionCommand.trim() } : {}
|
|
11398
11767
|
});
|
|
@@ -11521,6 +11890,71 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11521
11890
|
getAvailableIdeTypes() {
|
|
11522
11891
|
return [...this.providers.values()].filter((p) => p.category === "ide" && p.cdpPorts).map((p) => p.type);
|
|
11523
11892
|
}
|
|
11893
|
+
getSpawnCommand(type, fallback) {
|
|
11894
|
+
const override = this.getOptionalStringSetting(type, "executablePath");
|
|
11895
|
+
if (override) return override;
|
|
11896
|
+
return fallback || this.providers.get(type)?.spawn?.command || type;
|
|
11897
|
+
}
|
|
11898
|
+
getIdeCliCommand(type, fallback) {
|
|
11899
|
+
const override = this.getOptionalStringSetting(type, "cliPathOverride");
|
|
11900
|
+
if (override) return override;
|
|
11901
|
+
return fallback || this.providers.get(type)?.cli || null;
|
|
11902
|
+
}
|
|
11903
|
+
getIdePathCandidates(type, fallback) {
|
|
11904
|
+
const override = this.getOptionalStringSetting(type, "appPathOverride");
|
|
11905
|
+
if (override) return [override];
|
|
11906
|
+
if (fallback && fallback.length > 0) return fallback;
|
|
11907
|
+
const osPaths = this.providers.get(type)?.paths?.[process.platform];
|
|
11908
|
+
return Array.isArray(osPaths) ? [...osPaths] : [];
|
|
11909
|
+
}
|
|
11910
|
+
setProviderAvailability(type, state) {
|
|
11911
|
+
this.providerAvailability.set(type, {
|
|
11912
|
+
installed: !!state.installed,
|
|
11913
|
+
detectedPath: state.detectedPath ?? null
|
|
11914
|
+
});
|
|
11915
|
+
}
|
|
11916
|
+
setCliDetectionResults(results, replace = true) {
|
|
11917
|
+
if (replace) {
|
|
11918
|
+
for (const provider of this.providers.values()) {
|
|
11919
|
+
if (provider.category === "cli" || provider.category === "acp") {
|
|
11920
|
+
this.providerAvailability.set(provider.type, { installed: false, detectedPath: null });
|
|
11921
|
+
}
|
|
11922
|
+
}
|
|
11923
|
+
}
|
|
11924
|
+
for (const result of results) {
|
|
11925
|
+
this.setProviderAvailability(result.id, {
|
|
11926
|
+
installed: !!result.installed,
|
|
11927
|
+
detectedPath: result.path || null
|
|
11928
|
+
});
|
|
11929
|
+
}
|
|
11930
|
+
}
|
|
11931
|
+
setIdeDetectionResults(results, replace = true) {
|
|
11932
|
+
if (replace) {
|
|
11933
|
+
for (const provider of this.providers.values()) {
|
|
11934
|
+
if (provider.category === "ide") {
|
|
11935
|
+
this.providerAvailability.set(provider.type, { installed: false, detectedPath: null });
|
|
11936
|
+
}
|
|
11937
|
+
}
|
|
11938
|
+
}
|
|
11939
|
+
for (const result of results) {
|
|
11940
|
+
this.setProviderAvailability(result.id, {
|
|
11941
|
+
installed: !!result.installed,
|
|
11942
|
+
detectedPath: result.cliCommand || result.path || null
|
|
11943
|
+
});
|
|
11944
|
+
}
|
|
11945
|
+
}
|
|
11946
|
+
getAvailableProviderInfos() {
|
|
11947
|
+
return this.getAll().map((provider) => {
|
|
11948
|
+
const availability = this.providerAvailability.get(provider.type);
|
|
11949
|
+
return {
|
|
11950
|
+
...provider,
|
|
11951
|
+
...availability ? {
|
|
11952
|
+
installed: availability.installed,
|
|
11953
|
+
detectedPath: availability.detectedPath
|
|
11954
|
+
} : {}
|
|
11955
|
+
};
|
|
11956
|
+
});
|
|
11957
|
+
}
|
|
11524
11958
|
/**
|
|
11525
11959
|
* Register IDE providers to core/detector registry
|
|
11526
11960
|
* → Enables detectIDEs() to detect provider.js-based IDEs
|
|
@@ -11595,8 +12029,8 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11595
12029
|
resolved._resolvedScriptDir = entry.scriptDir;
|
|
11596
12030
|
resolved._resolvedScriptsSource = `compatibility:${entry.ideVersion}`;
|
|
11597
12031
|
if (providerDir) {
|
|
11598
|
-
const fullDir =
|
|
11599
|
-
resolved._resolvedScriptsPath = fs6.existsSync(
|
|
12032
|
+
const fullDir = path12.join(providerDir, entry.scriptDir);
|
|
12033
|
+
resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
|
|
11600
12034
|
}
|
|
11601
12035
|
matched = true;
|
|
11602
12036
|
}
|
|
@@ -11611,8 +12045,8 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11611
12045
|
resolved._resolvedScriptDir = base.defaultScriptDir;
|
|
11612
12046
|
resolved._resolvedScriptsSource = "defaultScriptDir:version_miss";
|
|
11613
12047
|
if (providerDir) {
|
|
11614
|
-
const fullDir =
|
|
11615
|
-
resolved._resolvedScriptsPath = fs6.existsSync(
|
|
12048
|
+
const fullDir = path12.join(providerDir, base.defaultScriptDir);
|
|
12049
|
+
resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
|
|
11616
12050
|
}
|
|
11617
12051
|
}
|
|
11618
12052
|
resolved._versionWarning = `Version ${currentVersion} not in compatibility matrix. Using default scripts.`;
|
|
@@ -11629,8 +12063,8 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11629
12063
|
resolved._resolvedScriptDir = dirOverride;
|
|
11630
12064
|
resolved._resolvedScriptsSource = `versions:${range}`;
|
|
11631
12065
|
if (providerDir) {
|
|
11632
|
-
const fullDir =
|
|
11633
|
-
resolved._resolvedScriptsPath = fs6.existsSync(
|
|
12066
|
+
const fullDir = path12.join(providerDir, dirOverride);
|
|
12067
|
+
resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
|
|
11634
12068
|
}
|
|
11635
12069
|
}
|
|
11636
12070
|
} else if (override.scripts) {
|
|
@@ -11646,8 +12080,8 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11646
12080
|
resolved._resolvedScriptDir = base.defaultScriptDir;
|
|
11647
12081
|
resolved._resolvedScriptsSource = "defaultScriptDir:no_version";
|
|
11648
12082
|
if (providerDir) {
|
|
11649
|
-
const fullDir =
|
|
11650
|
-
resolved._resolvedScriptsPath = fs6.existsSync(
|
|
12083
|
+
const fullDir = path12.join(providerDir, base.defaultScriptDir);
|
|
12084
|
+
resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
|
|
11651
12085
|
}
|
|
11652
12086
|
}
|
|
11653
12087
|
}
|
|
@@ -11672,14 +12106,14 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11672
12106
|
this.log(` [loadScriptsFromDir] ${type}: providerDir not found`);
|
|
11673
12107
|
return null;
|
|
11674
12108
|
}
|
|
11675
|
-
const dir =
|
|
12109
|
+
const dir = path12.join(providerDir, scriptDir);
|
|
11676
12110
|
if (!fs6.existsSync(dir)) {
|
|
11677
12111
|
this.log(` [loadScriptsFromDir] ${type}: dir not found: ${dir}`);
|
|
11678
12112
|
return null;
|
|
11679
12113
|
}
|
|
11680
12114
|
const cached = this.scriptsCache.get(dir);
|
|
11681
12115
|
if (cached) return cached;
|
|
11682
|
-
const scriptsJs =
|
|
12116
|
+
const scriptsJs = path12.join(dir, "scripts.js");
|
|
11683
12117
|
if (fs6.existsSync(scriptsJs)) {
|
|
11684
12118
|
try {
|
|
11685
12119
|
delete require.cache[require.resolve(scriptsJs)];
|
|
@@ -11721,7 +12155,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11721
12155
|
return;
|
|
11722
12156
|
}
|
|
11723
12157
|
if (filePath.endsWith(".js") || filePath.endsWith(".json")) {
|
|
11724
|
-
this.log(`File changed: ${
|
|
12158
|
+
this.log(`File changed: ${path12.basename(filePath)}, reloading...`);
|
|
11725
12159
|
this.reload();
|
|
11726
12160
|
}
|
|
11727
12161
|
};
|
|
@@ -11776,7 +12210,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11776
12210
|
}
|
|
11777
12211
|
const https = require("https");
|
|
11778
12212
|
const { execSync: execSync7 } = require("child_process");
|
|
11779
|
-
const metaPath =
|
|
12213
|
+
const metaPath = path12.join(this.upstreamDir, _ProviderLoader.META_FILE);
|
|
11780
12214
|
let prevEtag = "";
|
|
11781
12215
|
let prevTimestamp = 0;
|
|
11782
12216
|
try {
|
|
@@ -11793,7 +12227,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11793
12227
|
return { updated: false };
|
|
11794
12228
|
}
|
|
11795
12229
|
try {
|
|
11796
|
-
const etag = await new Promise((
|
|
12230
|
+
const etag = await new Promise((resolve12, reject) => {
|
|
11797
12231
|
const options = {
|
|
11798
12232
|
method: "HEAD",
|
|
11799
12233
|
hostname: "github.com",
|
|
@@ -11811,7 +12245,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11811
12245
|
headers: { "User-Agent": "adhdev-launcher" },
|
|
11812
12246
|
timeout: 1e4
|
|
11813
12247
|
}, (res2) => {
|
|
11814
|
-
|
|
12248
|
+
resolve12(res2.headers.etag || res2.headers["last-modified"] || "");
|
|
11815
12249
|
});
|
|
11816
12250
|
req2.on("error", reject);
|
|
11817
12251
|
req2.on("timeout", () => {
|
|
@@ -11820,7 +12254,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11820
12254
|
});
|
|
11821
12255
|
req2.end();
|
|
11822
12256
|
} else {
|
|
11823
|
-
|
|
12257
|
+
resolve12(res.headers.etag || res.headers["last-modified"] || "");
|
|
11824
12258
|
}
|
|
11825
12259
|
});
|
|
11826
12260
|
req.on("error", reject);
|
|
@@ -11836,17 +12270,17 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11836
12270
|
return { updated: false };
|
|
11837
12271
|
}
|
|
11838
12272
|
this.log("Downloading latest providers from GitHub...");
|
|
11839
|
-
const tmpTar =
|
|
11840
|
-
const tmpExtract =
|
|
12273
|
+
const tmpTar = path12.join(os11.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
|
|
12274
|
+
const tmpExtract = path12.join(os11.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
|
|
11841
12275
|
await this.downloadFile(_ProviderLoader.GITHUB_TARBALL_URL, tmpTar);
|
|
11842
12276
|
fs6.mkdirSync(tmpExtract, { recursive: true });
|
|
11843
12277
|
execSync7(`tar -xzf "${tmpTar}" -C "${tmpExtract}"`, { timeout: 3e4 });
|
|
11844
12278
|
const extracted = fs6.readdirSync(tmpExtract);
|
|
11845
12279
|
const rootDir = extracted.find(
|
|
11846
|
-
(d) => fs6.statSync(
|
|
12280
|
+
(d) => fs6.statSync(path12.join(tmpExtract, d)).isDirectory() && d.startsWith("adhdev-providers")
|
|
11847
12281
|
);
|
|
11848
12282
|
if (!rootDir) throw new Error("Unexpected tarball structure");
|
|
11849
|
-
const sourceDir =
|
|
12283
|
+
const sourceDir = path12.join(tmpExtract, rootDir);
|
|
11850
12284
|
const backupDir = this.upstreamDir + ".bak";
|
|
11851
12285
|
if (fs6.existsSync(this.upstreamDir)) {
|
|
11852
12286
|
if (fs6.existsSync(backupDir)) fs6.rmSync(backupDir, { recursive: true, force: true });
|
|
@@ -11884,7 +12318,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11884
12318
|
downloadFile(url, destPath) {
|
|
11885
12319
|
const https = require("https");
|
|
11886
12320
|
const http3 = require("http");
|
|
11887
|
-
return new Promise((
|
|
12321
|
+
return new Promise((resolve12, reject) => {
|
|
11888
12322
|
const doRequest = (reqUrl, redirectCount = 0) => {
|
|
11889
12323
|
if (redirectCount > 5) {
|
|
11890
12324
|
reject(new Error("Too many redirects"));
|
|
@@ -11904,7 +12338,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11904
12338
|
res.pipe(ws);
|
|
11905
12339
|
ws.on("finish", () => {
|
|
11906
12340
|
ws.close();
|
|
11907
|
-
|
|
12341
|
+
resolve12();
|
|
11908
12342
|
});
|
|
11909
12343
|
ws.on("error", reject);
|
|
11910
12344
|
});
|
|
@@ -11921,8 +12355,8 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11921
12355
|
copyDirRecursive(src, dest) {
|
|
11922
12356
|
fs6.mkdirSync(dest, { recursive: true });
|
|
11923
12357
|
for (const entry of fs6.readdirSync(src, { withFileTypes: true })) {
|
|
11924
|
-
const srcPath =
|
|
11925
|
-
const destPath =
|
|
12358
|
+
const srcPath = path12.join(src, entry.name);
|
|
12359
|
+
const destPath = path12.join(dest, entry.name);
|
|
11926
12360
|
if (entry.isDirectory()) {
|
|
11927
12361
|
this.copyDirRecursive(srcPath, destPath);
|
|
11928
12362
|
} else {
|
|
@@ -11933,7 +12367,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11933
12367
|
/** .meta.json save */
|
|
11934
12368
|
writeMeta(metaPath, etag, timestamp) {
|
|
11935
12369
|
try {
|
|
11936
|
-
fs6.mkdirSync(
|
|
12370
|
+
fs6.mkdirSync(path12.dirname(metaPath), { recursive: true });
|
|
11937
12371
|
fs6.writeFileSync(metaPath, JSON.stringify({
|
|
11938
12372
|
etag,
|
|
11939
12373
|
timestamp,
|
|
@@ -11950,7 +12384,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11950
12384
|
const scan = (d) => {
|
|
11951
12385
|
try {
|
|
11952
12386
|
for (const entry of fs6.readdirSync(d, { withFileTypes: true })) {
|
|
11953
|
-
if (entry.isDirectory()) scan(
|
|
12387
|
+
if (entry.isDirectory()) scan(path12.join(d, entry.name));
|
|
11954
12388
|
else if (entry.name === "provider.json") count++;
|
|
11955
12389
|
}
|
|
11956
12390
|
} catch {
|
|
@@ -11964,9 +12398,8 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11964
12398
|
* Get public settings schema for a provider (for dashboard UI rendering)
|
|
11965
12399
|
*/
|
|
11966
12400
|
getPublicSettings(type) {
|
|
11967
|
-
const
|
|
11968
|
-
|
|
11969
|
-
return Object.entries(provider.settings).filter(([, def]) => def.public === true).map(([key, def]) => ({ key, ...def }));
|
|
12401
|
+
const settings = this.getSettingsSchema(type);
|
|
12402
|
+
return Object.entries(settings).filter(([, def]) => def.public === true).map(([key, def]) => ({ key, ...def }));
|
|
11970
12403
|
}
|
|
11971
12404
|
/**
|
|
11972
12405
|
* Get public settings schema for all providers
|
|
@@ -11983,8 +12416,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11983
12416
|
* Resolved setting value for a provider (default + user override)
|
|
11984
12417
|
*/
|
|
11985
12418
|
getSettingValue(type, key) {
|
|
11986
|
-
const
|
|
11987
|
-
const schemaDef = provider?.settings?.[key];
|
|
12419
|
+
const schemaDef = this.getSettingsSchema(type)[key];
|
|
11988
12420
|
const defaultVal = schemaDef ? schemaDef.default : void 0;
|
|
11989
12421
|
try {
|
|
11990
12422
|
const { loadConfig: loadConfig2 } = (init_config(), __toCommonJS(config_exports));
|
|
@@ -11999,10 +12431,9 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11999
12431
|
* All resolved settings for a provider (default + user override)
|
|
12000
12432
|
*/
|
|
12001
12433
|
getSettings(type) {
|
|
12002
|
-
const
|
|
12003
|
-
if (!provider?.settings) return {};
|
|
12434
|
+
const settings = this.getSettingsSchema(type);
|
|
12004
12435
|
const result = {};
|
|
12005
|
-
for (const [key
|
|
12436
|
+
for (const [key] of Object.entries(settings)) {
|
|
12006
12437
|
result[key] = this.getSettingValue(type, key);
|
|
12007
12438
|
}
|
|
12008
12439
|
return result;
|
|
@@ -12011,11 +12442,11 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
12011
12442
|
* Save provider setting value (writes to config.json)
|
|
12012
12443
|
*/
|
|
12013
12444
|
setSetting(type, key, value) {
|
|
12014
|
-
const
|
|
12015
|
-
const schemaDef = provider?.settings?.[key];
|
|
12445
|
+
const schemaDef = this.getSettingsSchema(type)[key];
|
|
12016
12446
|
if (!schemaDef) return false;
|
|
12017
12447
|
if (!schemaDef.public) return false;
|
|
12018
12448
|
if (schemaDef.type === "boolean" && typeof value !== "boolean") return false;
|
|
12449
|
+
if (schemaDef.type === "string" && typeof value !== "string") return false;
|
|
12019
12450
|
if (schemaDef.type === "number") {
|
|
12020
12451
|
if (typeof value !== "number") return false;
|
|
12021
12452
|
if (schemaDef.min !== void 0 && value < schemaDef.min) return false;
|
|
@@ -12036,6 +12467,53 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
12036
12467
|
return false;
|
|
12037
12468
|
}
|
|
12038
12469
|
}
|
|
12470
|
+
getOptionalStringSetting(type, key) {
|
|
12471
|
+
const value = this.getSettingValue(type, key);
|
|
12472
|
+
if (typeof value !== "string") return null;
|
|
12473
|
+
const trimmed = value.trim();
|
|
12474
|
+
return trimmed ? trimmed : null;
|
|
12475
|
+
}
|
|
12476
|
+
getSettingsSchema(type) {
|
|
12477
|
+
const provider = this.providers.get(type);
|
|
12478
|
+
if (!provider) return {};
|
|
12479
|
+
return {
|
|
12480
|
+
...this.getSyntheticSettings(type, provider),
|
|
12481
|
+
...provider.settings || {}
|
|
12482
|
+
};
|
|
12483
|
+
}
|
|
12484
|
+
getSyntheticSettings(type, provider) {
|
|
12485
|
+
const result = {};
|
|
12486
|
+
if ((provider.category === "cli" || provider.category === "acp") && provider.spawn?.command && !provider.settings?.executablePath) {
|
|
12487
|
+
result.executablePath = {
|
|
12488
|
+
type: "string",
|
|
12489
|
+
default: "",
|
|
12490
|
+
public: true,
|
|
12491
|
+
label: "Executable path",
|
|
12492
|
+
description: "Optional absolute path for this provider binary. Leave blank to use the default PATH lookup."
|
|
12493
|
+
};
|
|
12494
|
+
}
|
|
12495
|
+
if (provider.category === "ide") {
|
|
12496
|
+
if (provider.cli && !provider.settings?.cliPathOverride) {
|
|
12497
|
+
result.cliPathOverride = {
|
|
12498
|
+
type: "string",
|
|
12499
|
+
default: "",
|
|
12500
|
+
public: true,
|
|
12501
|
+
label: "CLI path override",
|
|
12502
|
+
description: "Optional absolute path for the IDE CLI launcher. Leave blank to use the detected default."
|
|
12503
|
+
};
|
|
12504
|
+
}
|
|
12505
|
+
if (provider.paths && !provider.settings?.appPathOverride) {
|
|
12506
|
+
result.appPathOverride = {
|
|
12507
|
+
type: "string",
|
|
12508
|
+
default: "",
|
|
12509
|
+
public: true,
|
|
12510
|
+
label: "App path override",
|
|
12511
|
+
description: "Optional absolute path for the IDE app bundle or executable. Leave blank to use the default install locations."
|
|
12512
|
+
};
|
|
12513
|
+
}
|
|
12514
|
+
}
|
|
12515
|
+
return result;
|
|
12516
|
+
}
|
|
12039
12517
|
// ─── Private ───────────────────────────────────
|
|
12040
12518
|
/**
|
|
12041
12519
|
* Find the on-disk directory for a provider by type.
|
|
@@ -12049,17 +12527,17 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
12049
12527
|
for (const root of searchRoots) {
|
|
12050
12528
|
if (!fs6.existsSync(root)) continue;
|
|
12051
12529
|
const candidate = this.getProviderDir(root, cat, type);
|
|
12052
|
-
if (fs6.existsSync(
|
|
12053
|
-
const catDir =
|
|
12530
|
+
if (fs6.existsSync(path12.join(candidate, "provider.json"))) return candidate;
|
|
12531
|
+
const catDir = path12.join(root, cat);
|
|
12054
12532
|
if (fs6.existsSync(catDir)) {
|
|
12055
12533
|
try {
|
|
12056
12534
|
for (const entry of fs6.readdirSync(catDir, { withFileTypes: true })) {
|
|
12057
12535
|
if (!entry.isDirectory()) continue;
|
|
12058
|
-
const jsonPath =
|
|
12536
|
+
const jsonPath = path12.join(catDir, entry.name, "provider.json");
|
|
12059
12537
|
if (fs6.existsSync(jsonPath)) {
|
|
12060
12538
|
try {
|
|
12061
12539
|
const data = JSON.parse(fs6.readFileSync(jsonPath, "utf-8"));
|
|
12062
|
-
if (data.type === type) return
|
|
12540
|
+
if (data.type === type) return path12.join(catDir, entry.name);
|
|
12063
12541
|
} catch {
|
|
12064
12542
|
}
|
|
12065
12543
|
}
|
|
@@ -12076,7 +12554,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
12076
12554
|
* (template substitution is NOT applied here — scripts.js handles that)
|
|
12077
12555
|
*/
|
|
12078
12556
|
buildScriptWrappersFromDir(dir) {
|
|
12079
|
-
const scriptsJs =
|
|
12557
|
+
const scriptsJs = path12.join(dir, "scripts.js");
|
|
12080
12558
|
if (fs6.existsSync(scriptsJs)) {
|
|
12081
12559
|
try {
|
|
12082
12560
|
delete require.cache[require.resolve(scriptsJs)];
|
|
@@ -12090,7 +12568,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
12090
12568
|
for (const file of fs6.readdirSync(dir)) {
|
|
12091
12569
|
if (!file.endsWith(".js")) continue;
|
|
12092
12570
|
const scriptName = toCamel(file.replace(".js", ""));
|
|
12093
|
-
const filePath =
|
|
12571
|
+
const filePath = path12.join(dir, file);
|
|
12094
12572
|
result[scriptName] = (...args) => {
|
|
12095
12573
|
try {
|
|
12096
12574
|
let content = fs6.readFileSync(filePath, "utf-8");
|
|
@@ -12150,7 +12628,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
12150
12628
|
}
|
|
12151
12629
|
const hasJson = entries.some((e) => e.name === "provider.json");
|
|
12152
12630
|
if (hasJson) {
|
|
12153
|
-
const jsonPath =
|
|
12631
|
+
const jsonPath = path12.join(d, "provider.json");
|
|
12154
12632
|
try {
|
|
12155
12633
|
const raw = fs6.readFileSync(jsonPath, "utf-8");
|
|
12156
12634
|
const mod = JSON.parse(raw);
|
|
@@ -12163,7 +12641,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
12163
12641
|
delete mod.extensionIdPattern_flags;
|
|
12164
12642
|
}
|
|
12165
12643
|
const hasCompatibility = Array.isArray(mod.compatibility);
|
|
12166
|
-
const scriptsPath =
|
|
12644
|
+
const scriptsPath = path12.join(d, "scripts.js");
|
|
12167
12645
|
if (!hasCompatibility && fs6.existsSync(scriptsPath)) {
|
|
12168
12646
|
try {
|
|
12169
12647
|
delete require.cache[require.resolve(scriptsPath)];
|
|
@@ -12189,7 +12667,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
12189
12667
|
if (!entry.isDirectory()) continue;
|
|
12190
12668
|
if (entry.name.startsWith("_") || entry.name.startsWith(".")) continue;
|
|
12191
12669
|
if (excludeDirs && d === dir && excludeDirs.includes(entry.name)) continue;
|
|
12192
|
-
scan(
|
|
12670
|
+
scan(path12.join(d, entry.name));
|
|
12193
12671
|
}
|
|
12194
12672
|
}
|
|
12195
12673
|
};
|
|
@@ -12285,17 +12763,17 @@ async function findFreePort(ports) {
|
|
|
12285
12763
|
throw new Error("No free port found");
|
|
12286
12764
|
}
|
|
12287
12765
|
function checkPortFree(port) {
|
|
12288
|
-
return new Promise((
|
|
12766
|
+
return new Promise((resolve12) => {
|
|
12289
12767
|
const server = net.createServer();
|
|
12290
12768
|
server.unref();
|
|
12291
|
-
server.on("error", () =>
|
|
12769
|
+
server.on("error", () => resolve12(false));
|
|
12292
12770
|
server.listen(port, "127.0.0.1", () => {
|
|
12293
|
-
server.close(() =>
|
|
12771
|
+
server.close(() => resolve12(true));
|
|
12294
12772
|
});
|
|
12295
12773
|
});
|
|
12296
12774
|
}
|
|
12297
12775
|
async function isCdpActive(port) {
|
|
12298
|
-
return new Promise((
|
|
12776
|
+
return new Promise((resolve12) => {
|
|
12299
12777
|
const req = require("http").get(`http://127.0.0.1:${port}/json/version`, {
|
|
12300
12778
|
timeout: 2e3
|
|
12301
12779
|
}, (res) => {
|
|
@@ -12304,16 +12782,16 @@ async function isCdpActive(port) {
|
|
|
12304
12782
|
res.on("end", () => {
|
|
12305
12783
|
try {
|
|
12306
12784
|
const info = JSON.parse(data);
|
|
12307
|
-
|
|
12785
|
+
resolve12(!!info["WebKit-Version"] || !!info["Browser"]);
|
|
12308
12786
|
} catch {
|
|
12309
|
-
|
|
12787
|
+
resolve12(false);
|
|
12310
12788
|
}
|
|
12311
12789
|
});
|
|
12312
12790
|
});
|
|
12313
|
-
req.on("error", () =>
|
|
12791
|
+
req.on("error", () => resolve12(false));
|
|
12314
12792
|
req.on("timeout", () => {
|
|
12315
12793
|
req.destroy();
|
|
12316
|
-
|
|
12794
|
+
resolve12(false);
|
|
12317
12795
|
});
|
|
12318
12796
|
});
|
|
12319
12797
|
}
|
|
@@ -12447,8 +12925,8 @@ function detectCurrentWorkspace(ideId) {
|
|
|
12447
12925
|
const appNameMap = getMacAppIdentifiers();
|
|
12448
12926
|
const appName = appNameMap[ideId];
|
|
12449
12927
|
if (appName) {
|
|
12450
|
-
const storagePath =
|
|
12451
|
-
process.env.APPDATA ||
|
|
12928
|
+
const storagePath = path13.join(
|
|
12929
|
+
process.env.APPDATA || path13.join(os12.homedir(), "AppData", "Roaming"),
|
|
12452
12930
|
appName,
|
|
12453
12931
|
"storage.json"
|
|
12454
12932
|
);
|
|
@@ -12472,7 +12950,7 @@ function detectCurrentWorkspace(ideId) {
|
|
|
12472
12950
|
async function launchWithCdp(options = {}) {
|
|
12473
12951
|
const platform9 = os12.platform();
|
|
12474
12952
|
let targetIde;
|
|
12475
|
-
const ides = await detectIDEs();
|
|
12953
|
+
const ides = await detectIDEs(getProviderLoader());
|
|
12476
12954
|
if (options.ideId) {
|
|
12477
12955
|
targetIde = ides.find((i) => i.id === options.ideId && i.installed);
|
|
12478
12956
|
if (!targetIde) {
|
|
@@ -12626,9 +13104,9 @@ init_logger();
|
|
|
12626
13104
|
|
|
12627
13105
|
// src/logging/command-log.ts
|
|
12628
13106
|
var fs7 = __toESM(require("fs"));
|
|
12629
|
-
var
|
|
13107
|
+
var path14 = __toESM(require("path"));
|
|
12630
13108
|
var os13 = __toESM(require("os"));
|
|
12631
|
-
var LOG_DIR2 = process.platform === "win32" ?
|
|
13109
|
+
var LOG_DIR2 = process.platform === "win32" ? path14.join(process.env.LOCALAPPDATA || process.env.APPDATA || path14.join(os13.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path14.join(os13.homedir(), "Library", "Logs", "adhdev") : path14.join(os13.homedir(), ".local", "share", "adhdev", "logs");
|
|
12632
13110
|
var MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
12633
13111
|
var MAX_DAYS = 7;
|
|
12634
13112
|
try {
|
|
@@ -12666,13 +13144,13 @@ function getDateStr2() {
|
|
|
12666
13144
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
12667
13145
|
}
|
|
12668
13146
|
var currentDate2 = getDateStr2();
|
|
12669
|
-
var currentFile =
|
|
13147
|
+
var currentFile = path14.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
|
|
12670
13148
|
var writeCount2 = 0;
|
|
12671
13149
|
function checkRotation() {
|
|
12672
13150
|
const today = getDateStr2();
|
|
12673
13151
|
if (today !== currentDate2) {
|
|
12674
13152
|
currentDate2 = today;
|
|
12675
|
-
currentFile =
|
|
13153
|
+
currentFile = path14.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
|
|
12676
13154
|
cleanOldFiles();
|
|
12677
13155
|
}
|
|
12678
13156
|
}
|
|
@@ -12686,7 +13164,7 @@ function cleanOldFiles() {
|
|
|
12686
13164
|
const dateMatch = file.match(/commands-(\d{4}-\d{2}-\d{2})/);
|
|
12687
13165
|
if (dateMatch && dateMatch[1] < cutoffStr) {
|
|
12688
13166
|
try {
|
|
12689
|
-
fs7.unlinkSync(
|
|
13167
|
+
fs7.unlinkSync(path14.join(LOG_DIR2, file));
|
|
12690
13168
|
} catch {
|
|
12691
13169
|
}
|
|
12692
13170
|
}
|
|
@@ -12778,12 +13256,15 @@ function buildDetectedIdeInfos(detectedIdes, cdpManagers) {
|
|
|
12778
13256
|
}));
|
|
12779
13257
|
}
|
|
12780
13258
|
function buildAvailableProviders(providerLoader) {
|
|
12781
|
-
|
|
13259
|
+
const providers = providerLoader.getAvailableProviderInfos?.() || providerLoader.getAll();
|
|
13260
|
+
return providers.map((provider) => ({
|
|
12782
13261
|
type: provider.type,
|
|
12783
13262
|
name: provider.displayName || provider.type,
|
|
12784
13263
|
displayName: provider.displayName || provider.type,
|
|
12785
13264
|
icon: provider.icon || "\u{1F4BB}",
|
|
12786
|
-
category: provider.category
|
|
13265
|
+
category: provider.category,
|
|
13266
|
+
...provider.installed !== void 0 ? { installed: provider.installed } : {},
|
|
13267
|
+
...provider.detectedPath !== void 0 ? { detectedPath: provider.detectedPath } : {}
|
|
12787
13268
|
}));
|
|
12788
13269
|
}
|
|
12789
13270
|
function parseMessageTime(value) {
|
|
@@ -12911,13 +13392,13 @@ var import_child_process7 = require("child_process");
|
|
|
12911
13392
|
var import_child_process8 = require("child_process");
|
|
12912
13393
|
var fs8 = __toESM(require("fs"));
|
|
12913
13394
|
var os15 = __toESM(require("os"));
|
|
12914
|
-
var
|
|
13395
|
+
var path15 = __toESM(require("path"));
|
|
12915
13396
|
var UPGRADE_HELPER_ENV = "ADHDEV_DAEMON_UPGRADE_HELPER";
|
|
12916
13397
|
function getUpgradeLogPath() {
|
|
12917
13398
|
const home = os15.homedir();
|
|
12918
|
-
const dir =
|
|
13399
|
+
const dir = path15.join(home, ".adhdev");
|
|
12919
13400
|
fs8.mkdirSync(dir, { recursive: true });
|
|
12920
|
-
return
|
|
13401
|
+
return path15.join(dir, "daemon-upgrade.log");
|
|
12921
13402
|
}
|
|
12922
13403
|
function appendUpgradeLog(message) {
|
|
12923
13404
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
|
|
@@ -12950,14 +13431,14 @@ async function waitForPidExit(pid, timeoutMs) {
|
|
|
12950
13431
|
while (Date.now() - start < timeoutMs) {
|
|
12951
13432
|
try {
|
|
12952
13433
|
process.kill(pid, 0);
|
|
12953
|
-
await new Promise((
|
|
13434
|
+
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
12954
13435
|
} catch {
|
|
12955
13436
|
return;
|
|
12956
13437
|
}
|
|
12957
13438
|
}
|
|
12958
13439
|
}
|
|
12959
13440
|
function stopSessionHostProcesses(appName) {
|
|
12960
|
-
const pidFile =
|
|
13441
|
+
const pidFile = path15.join(os15.homedir(), ".adhdev", `${appName}-session-host.pid`);
|
|
12961
13442
|
try {
|
|
12962
13443
|
if (fs8.existsSync(pidFile)) {
|
|
12963
13444
|
const pid = Number.parseInt(fs8.readFileSync(pidFile, "utf8").trim(), 10);
|
|
@@ -12986,7 +13467,7 @@ function stopSessionHostProcesses(appName) {
|
|
|
12986
13467
|
}
|
|
12987
13468
|
}
|
|
12988
13469
|
function removeDaemonPidFile() {
|
|
12989
|
-
const pidFile =
|
|
13470
|
+
const pidFile = path15.join(os15.homedir(), ".adhdev", "daemon.pid");
|
|
12990
13471
|
try {
|
|
12991
13472
|
fs8.unlinkSync(pidFile);
|
|
12992
13473
|
} catch {
|
|
@@ -12997,7 +13478,7 @@ function cleanupStaleGlobalInstallDirs(pkgName) {
|
|
|
12997
13478
|
const npmRoot = (0, import_child_process7.execFileSync)(getNpmExecutable(), ["root", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
|
|
12998
13479
|
if (!npmRoot) return;
|
|
12999
13480
|
const npmPrefix = (0, import_child_process7.execFileSync)(getNpmExecutable(), ["prefix", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
|
|
13000
|
-
const binDir = process.platform === "win32" ? npmPrefix :
|
|
13481
|
+
const binDir = process.platform === "win32" ? npmPrefix : path15.join(npmPrefix, "bin");
|
|
13001
13482
|
const packageBaseName = pkgName.startsWith("@") ? pkgName.split("/")[1] : pkgName;
|
|
13002
13483
|
const binNames = /* @__PURE__ */ new Set([packageBaseName]);
|
|
13003
13484
|
if (pkgName === "@adhdev/daemon-standalone") {
|
|
@@ -13005,25 +13486,25 @@ function cleanupStaleGlobalInstallDirs(pkgName) {
|
|
|
13005
13486
|
}
|
|
13006
13487
|
if (pkgName.startsWith("@")) {
|
|
13007
13488
|
const [scope, name] = pkgName.split("/");
|
|
13008
|
-
const scopeDir =
|
|
13489
|
+
const scopeDir = path15.join(npmRoot, scope);
|
|
13009
13490
|
if (!fs8.existsSync(scopeDir)) return;
|
|
13010
13491
|
for (const entry of fs8.readdirSync(scopeDir)) {
|
|
13011
13492
|
if (!entry.startsWith(`.${name}-`)) continue;
|
|
13012
|
-
fs8.rmSync(
|
|
13013
|
-
appendUpgradeLog(`Removed stale scoped staging dir: ${
|
|
13493
|
+
fs8.rmSync(path15.join(scopeDir, entry), { recursive: true, force: true });
|
|
13494
|
+
appendUpgradeLog(`Removed stale scoped staging dir: ${path15.join(scopeDir, entry)}`);
|
|
13014
13495
|
}
|
|
13015
13496
|
} else {
|
|
13016
13497
|
for (const entry of fs8.readdirSync(npmRoot)) {
|
|
13017
13498
|
if (!entry.startsWith(`.${pkgName}-`)) continue;
|
|
13018
|
-
fs8.rmSync(
|
|
13019
|
-
appendUpgradeLog(`Removed stale staging dir: ${
|
|
13499
|
+
fs8.rmSync(path15.join(npmRoot, entry), { recursive: true, force: true });
|
|
13500
|
+
appendUpgradeLog(`Removed stale staging dir: ${path15.join(npmRoot, entry)}`);
|
|
13020
13501
|
}
|
|
13021
13502
|
}
|
|
13022
13503
|
if (fs8.existsSync(binDir)) {
|
|
13023
13504
|
for (const entry of fs8.readdirSync(binDir)) {
|
|
13024
13505
|
if (![...binNames].some((name) => entry.startsWith(`.${name}-`))) continue;
|
|
13025
|
-
fs8.rmSync(
|
|
13026
|
-
appendUpgradeLog(`Removed stale bin staging entry: ${
|
|
13506
|
+
fs8.rmSync(path15.join(binDir, entry), { recursive: true, force: true });
|
|
13507
|
+
appendUpgradeLog(`Removed stale bin staging entry: ${path15.join(binDir, entry)}`);
|
|
13027
13508
|
}
|
|
13028
13509
|
}
|
|
13029
13510
|
}
|
|
@@ -13065,7 +13546,7 @@ async function runDaemonUpgradeHelper(payload) {
|
|
|
13065
13546
|
appendUpgradeLog(installOutput.trim());
|
|
13066
13547
|
}
|
|
13067
13548
|
if (process.platform === "win32") {
|
|
13068
|
-
await new Promise((
|
|
13549
|
+
await new Promise((resolve12) => setTimeout(resolve12, 500));
|
|
13069
13550
|
cleanupStaleGlobalInstallDirs(payload.packageName);
|
|
13070
13551
|
appendUpgradeLog("Post-install staging cleanup complete");
|
|
13071
13552
|
}
|
|
@@ -13261,6 +13742,15 @@ var DaemonCommandRouter = class {
|
|
|
13261
13742
|
const record = await this.deps.sessionHostControl.forceDetachClient(sessionId, clientId);
|
|
13262
13743
|
return { success: true, record };
|
|
13263
13744
|
}
|
|
13745
|
+
case "session_host_prune_duplicate_sessions": {
|
|
13746
|
+
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
13747
|
+
const result = await this.deps.sessionHostControl.pruneDuplicateSessions({
|
|
13748
|
+
providerType: typeof args?.providerType === "string" ? args.providerType : void 0,
|
|
13749
|
+
workspace: typeof args?.workspace === "string" ? args.workspace : void 0,
|
|
13750
|
+
dryRun: args?.dryRun === true
|
|
13751
|
+
});
|
|
13752
|
+
return { success: true, result };
|
|
13753
|
+
}
|
|
13264
13754
|
case "session_host_acquire_write": {
|
|
13265
13755
|
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
13266
13756
|
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
@@ -13343,6 +13833,12 @@ var DaemonCommandRouter = class {
|
|
|
13343
13833
|
if (!ideType) throw new Error("ideType required");
|
|
13344
13834
|
const killProcess = args?.killProcess !== false;
|
|
13345
13835
|
await this.stopIde(ideType, killProcess);
|
|
13836
|
+
try {
|
|
13837
|
+
const results = await detectIDEs(this.deps.providerLoader);
|
|
13838
|
+
this.deps.detectedIdes.value = results;
|
|
13839
|
+
this.deps.providerLoader.setIdeDetectionResults(results, true);
|
|
13840
|
+
} catch {
|
|
13841
|
+
}
|
|
13346
13842
|
return { success: true, ideType, stopped: true, processKilled: killProcess };
|
|
13347
13843
|
}
|
|
13348
13844
|
// ─── IDE restart ───
|
|
@@ -13385,6 +13881,12 @@ var DaemonCommandRouter = class {
|
|
|
13385
13881
|
}
|
|
13386
13882
|
}
|
|
13387
13883
|
this.deps.onIdeConnected?.();
|
|
13884
|
+
try {
|
|
13885
|
+
const results = await detectIDEs(this.deps.providerLoader);
|
|
13886
|
+
this.deps.detectedIdes.value = results;
|
|
13887
|
+
this.deps.providerLoader.setIdeDetectionResults(results, true);
|
|
13888
|
+
} catch {
|
|
13889
|
+
}
|
|
13388
13890
|
if (result.success && resolvedWorkspace) {
|
|
13389
13891
|
try {
|
|
13390
13892
|
const next = appendRecentActivity(loadState(), {
|
|
@@ -13412,8 +13914,9 @@ var DaemonCommandRouter = class {
|
|
|
13412
13914
|
}
|
|
13413
13915
|
// ─── Detect IDEs ───
|
|
13414
13916
|
case "detect_ides": {
|
|
13415
|
-
const results = await detectIDEs();
|
|
13917
|
+
const results = await detectIDEs(this.deps.providerLoader);
|
|
13416
13918
|
this.deps.detectedIdes.value = results;
|
|
13919
|
+
this.deps.providerLoader.setIdeDetectionResults(results, true);
|
|
13417
13920
|
return { success: true, detectedInfo: results };
|
|
13418
13921
|
}
|
|
13419
13922
|
// ─── Set User Name ───
|
|
@@ -13833,6 +14336,51 @@ var ProviderStreamAdapter = class {
|
|
|
13833
14336
|
isTransportError(reason) {
|
|
13834
14337
|
return /Session with given id not found/i.test(reason) || /CDP not connected/i.test(reason) || /Target closed/i.test(reason) || /WebSocket not open/i.test(reason) || /not connected/i.test(reason) || /execution context/i.test(reason) || /Cannot find context with specified id/i.test(reason);
|
|
13835
14338
|
}
|
|
14339
|
+
titlesMatch(actual, expected) {
|
|
14340
|
+
const lhs = actual.trim().toLowerCase();
|
|
14341
|
+
const rhs = expected.trim().toLowerCase();
|
|
14342
|
+
if (!lhs || !rhs) return false;
|
|
14343
|
+
return lhs === rhs || lhs.includes(rhs) || rhs.includes(lhs);
|
|
14344
|
+
}
|
|
14345
|
+
messageCount(state) {
|
|
14346
|
+
return Array.isArray(state?.messages) ? state.messages.length : 0;
|
|
14347
|
+
}
|
|
14348
|
+
lastMessageSignature(state) {
|
|
14349
|
+
const messages = Array.isArray(state?.messages) ? state.messages : [];
|
|
14350
|
+
const last = messages[messages.length - 1];
|
|
14351
|
+
if (!last) return "";
|
|
14352
|
+
return `${last.role || ""}:${String(last.content || "").replace(/\s+/g, " ").trim()}`;
|
|
14353
|
+
}
|
|
14354
|
+
async verifySendOutcome(evaluate, before) {
|
|
14355
|
+
const beforeCount = this.messageCount(before);
|
|
14356
|
+
const beforeSignature = this.lastMessageSignature(before);
|
|
14357
|
+
for (let attempt = 0; attempt < 12; attempt += 1) {
|
|
14358
|
+
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
14359
|
+
let state;
|
|
14360
|
+
try {
|
|
14361
|
+
state = await this.readChat(evaluate);
|
|
14362
|
+
} catch {
|
|
14363
|
+
continue;
|
|
14364
|
+
}
|
|
14365
|
+
if (state.status === "waiting_approval") {
|
|
14366
|
+
return true;
|
|
14367
|
+
}
|
|
14368
|
+
const afterCount = this.messageCount(state);
|
|
14369
|
+
const afterSignature = this.lastMessageSignature(state);
|
|
14370
|
+
if (afterCount > beforeCount) return true;
|
|
14371
|
+
if (afterSignature && afterSignature !== beforeSignature) return true;
|
|
14372
|
+
}
|
|
14373
|
+
return false;
|
|
14374
|
+
}
|
|
14375
|
+
async readStableBaselineState(evaluate) {
|
|
14376
|
+
const first = await this.readChat(evaluate);
|
|
14377
|
+
if (this.messageCount(first) > 0 || this.lastMessageSignature(first)) {
|
|
14378
|
+
return first;
|
|
14379
|
+
}
|
|
14380
|
+
await new Promise((resolve12) => setTimeout(resolve12, 150));
|
|
14381
|
+
const second = await this.readChat(evaluate);
|
|
14382
|
+
return this.messageCount(second) >= this.messageCount(first) ? second : first;
|
|
14383
|
+
}
|
|
13836
14384
|
async readChat(evaluate) {
|
|
13837
14385
|
const script = this.callScript("readChat");
|
|
13838
14386
|
if (!script) return this.errorState("readChat script not available");
|
|
@@ -13858,6 +14406,9 @@ var ProviderStreamAdapter = class {
|
|
|
13858
14406
|
mode: data.mode,
|
|
13859
14407
|
activeModal: data.activeModal
|
|
13860
14408
|
};
|
|
14409
|
+
if (typeof data.title === "string" && data.title.trim()) {
|
|
14410
|
+
state.title = data.title.trim();
|
|
14411
|
+
}
|
|
13861
14412
|
const controlValues = extractProviderControlValues(this.provider.controls, data);
|
|
13862
14413
|
if (controlValues) state.controlValues = controlValues;
|
|
13863
14414
|
const effects = normalizeProviderEffects(data);
|
|
@@ -13881,6 +14432,12 @@ var ProviderStreamAdapter = class {
|
|
|
13881
14432
|
}
|
|
13882
14433
|
}
|
|
13883
14434
|
async sendMessage(evaluate, text) {
|
|
14435
|
+
let beforeState = null;
|
|
14436
|
+
try {
|
|
14437
|
+
beforeState = await this.readStableBaselineState(evaluate);
|
|
14438
|
+
} catch {
|
|
14439
|
+
beforeState = null;
|
|
14440
|
+
}
|
|
13884
14441
|
const params = { message: text };
|
|
13885
14442
|
const script = this.callScript("sendMessage", params) || this.callScript("sendMessage", text);
|
|
13886
14443
|
if (!script) throw new Error(`[${this.agentName}] sendMessage script not available`);
|
|
@@ -13898,7 +14455,9 @@ var ProviderStreamAdapter = class {
|
|
|
13898
14455
|
}
|
|
13899
14456
|
if (parsed && typeof parsed === "object") {
|
|
13900
14457
|
if (parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true) {
|
|
13901
|
-
|
|
14458
|
+
const verified = await this.verifySendOutcome(evaluate, beforeState);
|
|
14459
|
+
if (verified) return;
|
|
14460
|
+
throw new Error(`[${this.agentName}] sendMessage was not observed in chat state`);
|
|
13902
14461
|
}
|
|
13903
14462
|
if (typeof parsed.error === "string" && parsed.error.trim()) {
|
|
13904
14463
|
throw new Error(`[${this.agentName}] sendMessage failed: ${parsed.error}`);
|
|
@@ -13909,7 +14468,15 @@ var ProviderStreamAdapter = class {
|
|
|
13909
14468
|
async resolveAction(evaluate, action, button) {
|
|
13910
14469
|
const script = this.callScript("resolveAction", { action, button });
|
|
13911
14470
|
if (!script) return false;
|
|
13912
|
-
|
|
14471
|
+
const result = await evaluate(script);
|
|
14472
|
+
const parsed = this.parseMaybeJson(result);
|
|
14473
|
+
if (parsed === true) return true;
|
|
14474
|
+
if (typeof parsed === "string") {
|
|
14475
|
+
const normalized = parsed.trim().toLowerCase();
|
|
14476
|
+
return normalized === "ok" || normalized === "success" || normalized === "true" || normalized === "resolved" || normalized === "approved" || normalized === "rejected";
|
|
14477
|
+
}
|
|
14478
|
+
if (!parsed || typeof parsed !== "object") return false;
|
|
14479
|
+
return parsed.resolved === true || parsed.success === true || parsed.ok === true || parsed.found === true;
|
|
13913
14480
|
}
|
|
13914
14481
|
async newSession(evaluate) {
|
|
13915
14482
|
const script = this.callScript("newSession");
|
|
@@ -13927,7 +14494,10 @@ var ProviderStreamAdapter = class {
|
|
|
13927
14494
|
const raw = await evaluate(script, 1e4);
|
|
13928
14495
|
const data = typeof raw === "string" ? JSON.parse(raw) : raw;
|
|
13929
14496
|
if (data?.error) return [];
|
|
13930
|
-
|
|
14497
|
+
if (Array.isArray(data)) return data;
|
|
14498
|
+
if (Array.isArray(data?.sessions)) return data.sessions;
|
|
14499
|
+
if (Array.isArray(data?.chats)) return data.chats;
|
|
14500
|
+
return [];
|
|
13931
14501
|
} catch {
|
|
13932
14502
|
return [];
|
|
13933
14503
|
}
|
|
@@ -13935,7 +14505,24 @@ var ProviderStreamAdapter = class {
|
|
|
13935
14505
|
async switchSession(evaluate, sessionId) {
|
|
13936
14506
|
const script = this.callScript("switchSession", sessionId);
|
|
13937
14507
|
if (!script) return false;
|
|
13938
|
-
|
|
14508
|
+
const raw = await evaluate(script, 1e4);
|
|
14509
|
+
const data = this.parseMaybeJson(raw);
|
|
14510
|
+
if (data === true) return true;
|
|
14511
|
+
if (typeof data === "string") {
|
|
14512
|
+
const normalized = data.trim().toLowerCase();
|
|
14513
|
+
return normalized === "true" || normalized === "ok" || normalized === "switched" || normalized === "success";
|
|
14514
|
+
}
|
|
14515
|
+
if (data && typeof data === "object") {
|
|
14516
|
+
if (data.switched === true || data.success === true || data.ok === true) return true;
|
|
14517
|
+
if (typeof data.error === "string" && data.error.trim()) return false;
|
|
14518
|
+
}
|
|
14519
|
+
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
14520
|
+
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
14521
|
+
const state = await this.readChat(evaluate);
|
|
14522
|
+
const title = typeof state.title === "string" ? state.title : "";
|
|
14523
|
+
if (this.titlesMatch(title, sessionId)) return true;
|
|
14524
|
+
}
|
|
14525
|
+
return false;
|
|
13939
14526
|
}
|
|
13940
14527
|
async focusEditor(evaluate) {
|
|
13941
14528
|
const script = this.callScript("focusEditor");
|
|
@@ -14141,7 +14728,7 @@ var DaemonAgentStreamManager = class {
|
|
|
14141
14728
|
return false;
|
|
14142
14729
|
}
|
|
14143
14730
|
}
|
|
14144
|
-
async resolveSessionAction(cdp, sessionId, action) {
|
|
14731
|
+
async resolveSessionAction(cdp, sessionId, action, button) {
|
|
14145
14732
|
await this.ensureSessionPanelOpen(sessionId);
|
|
14146
14733
|
const target = this.getSessionTarget(sessionId);
|
|
14147
14734
|
if (!target?.parentSessionId) return false;
|
|
@@ -14151,7 +14738,7 @@ var DaemonAgentStreamManager = class {
|
|
|
14151
14738
|
if (!agent) return false;
|
|
14152
14739
|
try {
|
|
14153
14740
|
const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
|
|
14154
|
-
return await agent.adapter.resolveAction(evaluate, action);
|
|
14741
|
+
return await agent.adapter.resolveAction(evaluate, action, button);
|
|
14155
14742
|
} catch (e) {
|
|
14156
14743
|
this.logFn(`[AgentStream] resolveAction(${sessionId}) error: ${e.message}`);
|
|
14157
14744
|
return false;
|
|
@@ -14625,11 +15212,11 @@ var ProviderInstanceManager = class {
|
|
|
14625
15212
|
|
|
14626
15213
|
// src/providers/version-archive.ts
|
|
14627
15214
|
var fs10 = __toESM(require("fs"));
|
|
14628
|
-
var
|
|
15215
|
+
var path16 = __toESM(require("path"));
|
|
14629
15216
|
var os16 = __toESM(require("os"));
|
|
14630
15217
|
var import_child_process9 = require("child_process");
|
|
14631
15218
|
var import_os3 = require("os");
|
|
14632
|
-
var ARCHIVE_PATH =
|
|
15219
|
+
var ARCHIVE_PATH = path16.join(os16.homedir(), ".adhdev", "version-history.json");
|
|
14633
15220
|
var MAX_ENTRIES_PER_PROVIDER = 20;
|
|
14634
15221
|
var VersionArchive = class {
|
|
14635
15222
|
history = {};
|
|
@@ -14676,7 +15263,7 @@ var VersionArchive = class {
|
|
|
14676
15263
|
}
|
|
14677
15264
|
save() {
|
|
14678
15265
|
try {
|
|
14679
|
-
fs10.mkdirSync(
|
|
15266
|
+
fs10.mkdirSync(path16.dirname(ARCHIVE_PATH), { recursive: true });
|
|
14680
15267
|
fs10.writeFileSync(ARCHIVE_PATH, JSON.stringify(this.history, null, 2));
|
|
14681
15268
|
} catch {
|
|
14682
15269
|
}
|
|
@@ -14717,7 +15304,7 @@ function checkPathExists2(paths) {
|
|
|
14717
15304
|
for (const p of paths) {
|
|
14718
15305
|
if (p.includes("*")) {
|
|
14719
15306
|
const home = os16.homedir();
|
|
14720
|
-
const resolved = p.replace(/\*/g, home.split(
|
|
15307
|
+
const resolved = p.replace(/\*/g, home.split(path16.sep).pop() || "");
|
|
14721
15308
|
if (fs10.existsSync(resolved)) return resolved;
|
|
14722
15309
|
} else {
|
|
14723
15310
|
if (fs10.existsSync(p)) return p;
|
|
@@ -14727,7 +15314,7 @@ function checkPathExists2(paths) {
|
|
|
14727
15314
|
}
|
|
14728
15315
|
function getMacAppVersion(appPath) {
|
|
14729
15316
|
if ((0, import_os3.platform)() !== "darwin" || !appPath.endsWith(".app")) return null;
|
|
14730
|
-
const plistPath =
|
|
15317
|
+
const plistPath = path16.join(appPath, "Contents", "Info.plist");
|
|
14731
15318
|
if (!fs10.existsSync(plistPath)) return null;
|
|
14732
15319
|
const raw = runCommand(`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${plistPath}"`);
|
|
14733
15320
|
return raw || null;
|
|
@@ -14754,7 +15341,7 @@ async function detectAllVersions(loader, archive) {
|
|
|
14754
15341
|
const cliBin = provider.cli ? findBinary2(provider.cli) : null;
|
|
14755
15342
|
let resolvedBin = cliBin;
|
|
14756
15343
|
if (!resolvedBin && appPath && currentOs === "darwin") {
|
|
14757
|
-
const bundled =
|
|
15344
|
+
const bundled = path16.join(appPath, "Contents", "Resources", "app", "bin", provider.cli || "");
|
|
14758
15345
|
if (provider.cli && fs10.existsSync(bundled)) resolvedBin = bundled;
|
|
14759
15346
|
}
|
|
14760
15347
|
info.installed = !!(appPath || resolvedBin);
|
|
@@ -14795,7 +15382,7 @@ async function detectAllVersions(loader, archive) {
|
|
|
14795
15382
|
// src/daemon/dev-server.ts
|
|
14796
15383
|
var http2 = __toESM(require("http"));
|
|
14797
15384
|
var fs14 = __toESM(require("fs"));
|
|
14798
|
-
var
|
|
15385
|
+
var path20 = __toESM(require("path"));
|
|
14799
15386
|
|
|
14800
15387
|
// src/daemon/scaffold-template.ts
|
|
14801
15388
|
function generateFiles(type, name, category, opts = {}) {
|
|
@@ -15131,7 +15718,7 @@ init_logger();
|
|
|
15131
15718
|
|
|
15132
15719
|
// src/daemon/dev-cdp-handlers.ts
|
|
15133
15720
|
var fs11 = __toESM(require("fs"));
|
|
15134
|
-
var
|
|
15721
|
+
var path17 = __toESM(require("path"));
|
|
15135
15722
|
init_logger();
|
|
15136
15723
|
async function handleCdpEvaluate(ctx, req, res) {
|
|
15137
15724
|
const body = await ctx.readBody(req);
|
|
@@ -15310,17 +15897,17 @@ async function handleScriptHints(ctx, type, _req, res) {
|
|
|
15310
15897
|
return;
|
|
15311
15898
|
}
|
|
15312
15899
|
let scriptsPath = "";
|
|
15313
|
-
const directScripts =
|
|
15900
|
+
const directScripts = path17.join(dir, "scripts.js");
|
|
15314
15901
|
if (fs11.existsSync(directScripts)) {
|
|
15315
15902
|
scriptsPath = directScripts;
|
|
15316
15903
|
} else {
|
|
15317
|
-
const scriptsDir =
|
|
15904
|
+
const scriptsDir = path17.join(dir, "scripts");
|
|
15318
15905
|
if (fs11.existsSync(scriptsDir)) {
|
|
15319
15906
|
const versions = fs11.readdirSync(scriptsDir).filter((d) => {
|
|
15320
|
-
return fs11.statSync(
|
|
15907
|
+
return fs11.statSync(path17.join(scriptsDir, d)).isDirectory();
|
|
15321
15908
|
}).sort().reverse();
|
|
15322
15909
|
for (const ver of versions) {
|
|
15323
|
-
const p =
|
|
15910
|
+
const p = path17.join(scriptsDir, ver, "scripts.js");
|
|
15324
15911
|
if (fs11.existsSync(p)) {
|
|
15325
15912
|
scriptsPath = p;
|
|
15326
15913
|
break;
|
|
@@ -16139,7 +16726,7 @@ async function handleDomContext(ctx, type, req, res) {
|
|
|
16139
16726
|
|
|
16140
16727
|
// src/daemon/dev-cli-debug.ts
|
|
16141
16728
|
var fs12 = __toESM(require("fs"));
|
|
16142
|
-
var
|
|
16729
|
+
var path18 = __toESM(require("path"));
|
|
16143
16730
|
function slugifyFixtureName(value) {
|
|
16144
16731
|
const normalized = String(value || "").trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
16145
16732
|
return normalized || `fixture-${Date.now()}`;
|
|
@@ -16149,11 +16736,11 @@ function getCliFixtureDir(ctx, type) {
|
|
|
16149
16736
|
if (!providerDir) {
|
|
16150
16737
|
throw new Error(`Provider directory not found for '${type}'`);
|
|
16151
16738
|
}
|
|
16152
|
-
return
|
|
16739
|
+
return path18.join(providerDir, "fixtures");
|
|
16153
16740
|
}
|
|
16154
16741
|
function readCliFixture(ctx, type, name) {
|
|
16155
16742
|
const fixtureDir = getCliFixtureDir(ctx, type);
|
|
16156
|
-
const filePath =
|
|
16743
|
+
const filePath = path18.join(fixtureDir, `${name}.json`);
|
|
16157
16744
|
if (!fs12.existsSync(filePath)) {
|
|
16158
16745
|
throw new Error(`Fixture not found: ${filePath}`);
|
|
16159
16746
|
}
|
|
@@ -16313,7 +16900,7 @@ function getCliTargetBundle(ctx, type, instanceId) {
|
|
|
16313
16900
|
return { target, instance, adapter };
|
|
16314
16901
|
}
|
|
16315
16902
|
function sleep(ms) {
|
|
16316
|
-
return new Promise((
|
|
16903
|
+
return new Promise((resolve12) => setTimeout(resolve12, ms));
|
|
16317
16904
|
}
|
|
16318
16905
|
async function waitForCliReady(ctx, type, instanceId, timeoutMs) {
|
|
16319
16906
|
const startedAt = Date.now();
|
|
@@ -16912,7 +17499,7 @@ async function handleCliFixtureCapture(ctx, req, res) {
|
|
|
16912
17499
|
},
|
|
16913
17500
|
notes: typeof body?.notes === "string" ? body.notes : void 0
|
|
16914
17501
|
};
|
|
16915
|
-
const filePath =
|
|
17502
|
+
const filePath = path18.join(fixtureDir, `${name}.json`);
|
|
16916
17503
|
fs12.writeFileSync(filePath, JSON.stringify(fixture, null, 2));
|
|
16917
17504
|
ctx.json(res, 200, {
|
|
16918
17505
|
saved: true,
|
|
@@ -16936,7 +17523,7 @@ async function handleCliFixtureList(ctx, type, _req, res) {
|
|
|
16936
17523
|
return;
|
|
16937
17524
|
}
|
|
16938
17525
|
const fixtures = fs12.readdirSync(fixtureDir).filter((file) => file.endsWith(".json")).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" })).map((file) => {
|
|
16939
|
-
const fullPath =
|
|
17526
|
+
const fullPath = path18.join(fixtureDir, file);
|
|
16940
17527
|
try {
|
|
16941
17528
|
const raw = JSON.parse(fs12.readFileSync(fullPath, "utf-8"));
|
|
16942
17529
|
return {
|
|
@@ -17072,7 +17659,7 @@ async function handleCliRaw(ctx, req, res) {
|
|
|
17072
17659
|
|
|
17073
17660
|
// src/daemon/dev-auto-implement.ts
|
|
17074
17661
|
var fs13 = __toESM(require("fs"));
|
|
17075
|
-
var
|
|
17662
|
+
var path19 = __toESM(require("path"));
|
|
17076
17663
|
var os17 = __toESM(require("os"));
|
|
17077
17664
|
function getAutoImplPid(ctx) {
|
|
17078
17665
|
const proc = ctx.autoImplProcess;
|
|
@@ -17112,22 +17699,22 @@ function getLatestScriptVersionDir(scriptsDir) {
|
|
|
17112
17699
|
if (!fs13.existsSync(scriptsDir)) return null;
|
|
17113
17700
|
const versions = fs13.readdirSync(scriptsDir).filter((d) => {
|
|
17114
17701
|
try {
|
|
17115
|
-
return fs13.statSync(
|
|
17702
|
+
return fs13.statSync(path19.join(scriptsDir, d)).isDirectory();
|
|
17116
17703
|
} catch {
|
|
17117
17704
|
return false;
|
|
17118
17705
|
}
|
|
17119
17706
|
}).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" }));
|
|
17120
17707
|
if (versions.length === 0) return null;
|
|
17121
|
-
return
|
|
17708
|
+
return path19.join(scriptsDir, versions[0]);
|
|
17122
17709
|
}
|
|
17123
17710
|
function resolveAutoImplWritableProviderDir(ctx, category, type, requestedDir) {
|
|
17124
|
-
const canonicalUserDir =
|
|
17125
|
-
const desiredDir = requestedDir ?
|
|
17126
|
-
const upstreamRoot =
|
|
17127
|
-
if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${
|
|
17711
|
+
const canonicalUserDir = path19.resolve(ctx.providerLoader.getUserProviderDir(category, type));
|
|
17712
|
+
const desiredDir = requestedDir ? path19.resolve(requestedDir) : canonicalUserDir;
|
|
17713
|
+
const upstreamRoot = path19.resolve(ctx.providerLoader.getUpstreamDir());
|
|
17714
|
+
if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path19.sep}`)) {
|
|
17128
17715
|
return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
|
|
17129
17716
|
}
|
|
17130
|
-
if (
|
|
17717
|
+
if (path19.basename(desiredDir) !== type) {
|
|
17131
17718
|
return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
|
|
17132
17719
|
}
|
|
17133
17720
|
const sourceDir = ctx.findProviderDir(type);
|
|
@@ -17135,11 +17722,11 @@ function resolveAutoImplWritableProviderDir(ctx, category, type, requestedDir) {
|
|
|
17135
17722
|
return { dir: null, reason: `Provider source directory not found for '${type}'` };
|
|
17136
17723
|
}
|
|
17137
17724
|
if (!fs13.existsSync(desiredDir)) {
|
|
17138
|
-
fs13.mkdirSync(
|
|
17725
|
+
fs13.mkdirSync(path19.dirname(desiredDir), { recursive: true });
|
|
17139
17726
|
fs13.cpSync(sourceDir, desiredDir, { recursive: true });
|
|
17140
17727
|
ctx.log(`Auto-implement writable copy created: ${desiredDir}`);
|
|
17141
17728
|
}
|
|
17142
|
-
const providerJson =
|
|
17729
|
+
const providerJson = path19.join(desiredDir, "provider.json");
|
|
17143
17730
|
if (!fs13.existsSync(providerJson)) {
|
|
17144
17731
|
return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
|
|
17145
17732
|
}
|
|
@@ -17162,13 +17749,13 @@ function loadAutoImplReferenceScripts(ctx, referenceType) {
|
|
|
17162
17749
|
const refDir = ctx.findProviderDir(referenceType);
|
|
17163
17750
|
if (!refDir || !fs13.existsSync(refDir)) return {};
|
|
17164
17751
|
const referenceScripts = {};
|
|
17165
|
-
const scriptsDir =
|
|
17752
|
+
const scriptsDir = path19.join(refDir, "scripts");
|
|
17166
17753
|
const latestDir = getLatestScriptVersionDir(scriptsDir);
|
|
17167
17754
|
if (!latestDir) return referenceScripts;
|
|
17168
17755
|
for (const file of fs13.readdirSync(latestDir)) {
|
|
17169
17756
|
if (!file.endsWith(".js")) continue;
|
|
17170
17757
|
try {
|
|
17171
|
-
referenceScripts[file] = fs13.readFileSync(
|
|
17758
|
+
referenceScripts[file] = fs13.readFileSync(path19.join(latestDir, file), "utf-8");
|
|
17172
17759
|
} catch {
|
|
17173
17760
|
}
|
|
17174
17761
|
}
|
|
@@ -17276,9 +17863,9 @@ async function handleAutoImplement(ctx, type, req, res) {
|
|
|
17276
17863
|
});
|
|
17277
17864
|
const referenceScripts = loadAutoImplReferenceScripts(ctx, resolvedReference);
|
|
17278
17865
|
const prompt = buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domContext, referenceScripts, comment, resolvedReference, verification);
|
|
17279
|
-
const tmpDir =
|
|
17866
|
+
const tmpDir = path19.join(os17.tmpdir(), "adhdev-autoimpl");
|
|
17280
17867
|
if (!fs13.existsSync(tmpDir)) fs13.mkdirSync(tmpDir, { recursive: true });
|
|
17281
|
-
const promptFile =
|
|
17868
|
+
const promptFile = path19.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
|
|
17282
17869
|
fs13.writeFileSync(promptFile, prompt, "utf-8");
|
|
17283
17870
|
ctx.log(`Auto-implement prompt written to ${promptFile} (${prompt.length} chars)`);
|
|
17284
17871
|
const agentProvider = ctx.providerLoader.resolve(agent) || ctx.providerLoader.getMeta(agent);
|
|
@@ -17705,7 +18292,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
|
|
|
17705
18292
|
setMode: "set_mode.js"
|
|
17706
18293
|
};
|
|
17707
18294
|
const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
|
|
17708
|
-
const scriptsDir =
|
|
18295
|
+
const scriptsDir = path19.join(providerDir, "scripts");
|
|
17709
18296
|
const latestScriptsDir = getLatestScriptVersionDir(scriptsDir);
|
|
17710
18297
|
if (latestScriptsDir) {
|
|
17711
18298
|
lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
|
|
@@ -17716,7 +18303,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
|
|
|
17716
18303
|
for (const file of fs13.readdirSync(latestScriptsDir)) {
|
|
17717
18304
|
if (file.endsWith(".js") && targetFileNames.has(file)) {
|
|
17718
18305
|
try {
|
|
17719
|
-
const content = fs13.readFileSync(
|
|
18306
|
+
const content = fs13.readFileSync(path19.join(latestScriptsDir, file), "utf-8");
|
|
17720
18307
|
lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
|
|
17721
18308
|
lines.push("```javascript");
|
|
17722
18309
|
lines.push(content);
|
|
@@ -17733,7 +18320,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
|
|
|
17733
18320
|
lines.push("");
|
|
17734
18321
|
for (const file of refFiles) {
|
|
17735
18322
|
try {
|
|
17736
|
-
const content = fs13.readFileSync(
|
|
18323
|
+
const content = fs13.readFileSync(path19.join(latestScriptsDir, file), "utf-8");
|
|
17737
18324
|
lines.push(`### \`${file}\` \u{1F512}`);
|
|
17738
18325
|
lines.push("```javascript");
|
|
17739
18326
|
lines.push(content);
|
|
@@ -17774,10 +18361,10 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
|
|
|
17774
18361
|
lines.push("");
|
|
17775
18362
|
}
|
|
17776
18363
|
}
|
|
17777
|
-
const docsDir =
|
|
18364
|
+
const docsDir = path19.join(providerDir, "../../docs");
|
|
17778
18365
|
const loadGuide = (name) => {
|
|
17779
18366
|
try {
|
|
17780
|
-
const p =
|
|
18367
|
+
const p = path19.join(docsDir, name);
|
|
17781
18368
|
if (fs13.existsSync(p)) return fs13.readFileSync(p, "utf-8");
|
|
17782
18369
|
} catch {
|
|
17783
18370
|
}
|
|
@@ -18012,7 +18599,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
|
|
|
18012
18599
|
parseApproval: "parse_approval.js"
|
|
18013
18600
|
};
|
|
18014
18601
|
const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
|
|
18015
|
-
const scriptsDir =
|
|
18602
|
+
const scriptsDir = path19.join(providerDir, "scripts");
|
|
18016
18603
|
const latestScriptsDir = getLatestScriptVersionDir(scriptsDir);
|
|
18017
18604
|
if (latestScriptsDir) {
|
|
18018
18605
|
lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
|
|
@@ -18024,7 +18611,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
|
|
|
18024
18611
|
if (!file.endsWith(".js")) continue;
|
|
18025
18612
|
if (!targetFileNames.has(file)) continue;
|
|
18026
18613
|
try {
|
|
18027
|
-
const content = fs13.readFileSync(
|
|
18614
|
+
const content = fs13.readFileSync(path19.join(latestScriptsDir, file), "utf-8");
|
|
18028
18615
|
lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
|
|
18029
18616
|
lines.push("```javascript");
|
|
18030
18617
|
lines.push(content);
|
|
@@ -18040,7 +18627,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
|
|
|
18040
18627
|
lines.push("");
|
|
18041
18628
|
for (const file of refFiles) {
|
|
18042
18629
|
try {
|
|
18043
|
-
const content = fs13.readFileSync(
|
|
18630
|
+
const content = fs13.readFileSync(path19.join(latestScriptsDir, file), "utf-8");
|
|
18044
18631
|
lines.push(`### \`${file}\` \u{1F512}`);
|
|
18045
18632
|
lines.push("```javascript");
|
|
18046
18633
|
lines.push(content);
|
|
@@ -18073,10 +18660,10 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
|
|
|
18073
18660
|
lines.push("");
|
|
18074
18661
|
}
|
|
18075
18662
|
}
|
|
18076
|
-
const docsDir =
|
|
18663
|
+
const docsDir = path19.join(providerDir, "../../docs");
|
|
18077
18664
|
const loadGuide = (name) => {
|
|
18078
18665
|
try {
|
|
18079
|
-
const p =
|
|
18666
|
+
const p = path19.join(docsDir, name);
|
|
18080
18667
|
if (fs13.existsSync(p)) return fs13.readFileSync(p, "utf-8");
|
|
18081
18668
|
} catch {
|
|
18082
18669
|
}
|
|
@@ -18487,8 +19074,8 @@ var DevServer = class _DevServer {
|
|
|
18487
19074
|
}
|
|
18488
19075
|
getEndpointList() {
|
|
18489
19076
|
return this.routes.map((r) => {
|
|
18490
|
-
const
|
|
18491
|
-
return `${r.method.padEnd(5)} ${
|
|
19077
|
+
const path21 = typeof r.pattern === "string" ? r.pattern : r.pattern.source.replace(/\\\//g, "/").replace(/\(\[.*?\]\+\)/g, ":type").replace(/[\^$]/g, "");
|
|
19078
|
+
return `${r.method.padEnd(5)} ${path21}`;
|
|
18492
19079
|
});
|
|
18493
19080
|
}
|
|
18494
19081
|
async start(port = DEV_SERVER_PORT) {
|
|
@@ -18519,15 +19106,15 @@ var DevServer = class _DevServer {
|
|
|
18519
19106
|
this.json(res, 500, { error: e.message });
|
|
18520
19107
|
}
|
|
18521
19108
|
});
|
|
18522
|
-
return new Promise((
|
|
19109
|
+
return new Promise((resolve12, reject) => {
|
|
18523
19110
|
this.server.listen(port, "127.0.0.1", () => {
|
|
18524
19111
|
this.log(`Dev server listening on http://127.0.0.1:${port}`);
|
|
18525
|
-
|
|
19112
|
+
resolve12();
|
|
18526
19113
|
});
|
|
18527
19114
|
this.server.on("error", (e) => {
|
|
18528
19115
|
if (e.code === "EADDRINUSE") {
|
|
18529
19116
|
this.log(`Port ${port} in use, skipping dev server`);
|
|
18530
|
-
|
|
19117
|
+
resolve12();
|
|
18531
19118
|
} else {
|
|
18532
19119
|
reject(e);
|
|
18533
19120
|
}
|
|
@@ -18610,20 +19197,20 @@ var DevServer = class _DevServer {
|
|
|
18610
19197
|
child.stderr?.on("data", (d) => {
|
|
18611
19198
|
stderr += d.toString().slice(0, 2e3);
|
|
18612
19199
|
});
|
|
18613
|
-
await new Promise((
|
|
19200
|
+
await new Promise((resolve12) => {
|
|
18614
19201
|
const timer = setTimeout(() => {
|
|
18615
19202
|
child.kill();
|
|
18616
|
-
|
|
19203
|
+
resolve12();
|
|
18617
19204
|
}, 3e3);
|
|
18618
19205
|
child.on("exit", () => {
|
|
18619
19206
|
clearTimeout(timer);
|
|
18620
|
-
|
|
19207
|
+
resolve12();
|
|
18621
19208
|
});
|
|
18622
19209
|
child.stdout?.once("data", () => {
|
|
18623
19210
|
setTimeout(() => {
|
|
18624
19211
|
child.kill();
|
|
18625
19212
|
clearTimeout(timer);
|
|
18626
|
-
|
|
19213
|
+
resolve12();
|
|
18627
19214
|
}, 500);
|
|
18628
19215
|
});
|
|
18629
19216
|
});
|
|
@@ -18770,12 +19357,12 @@ var DevServer = class _DevServer {
|
|
|
18770
19357
|
// ─── DevConsole SPA ───
|
|
18771
19358
|
getConsoleDistDir() {
|
|
18772
19359
|
const candidates = [
|
|
18773
|
-
|
|
18774
|
-
|
|
18775
|
-
|
|
19360
|
+
path20.resolve(__dirname, "../../web-devconsole/dist"),
|
|
19361
|
+
path20.resolve(__dirname, "../../../web-devconsole/dist"),
|
|
19362
|
+
path20.join(process.cwd(), "packages/web-devconsole/dist")
|
|
18776
19363
|
];
|
|
18777
19364
|
for (const dir of candidates) {
|
|
18778
|
-
if (fs14.existsSync(
|
|
19365
|
+
if (fs14.existsSync(path20.join(dir, "index.html"))) return dir;
|
|
18779
19366
|
}
|
|
18780
19367
|
return null;
|
|
18781
19368
|
}
|
|
@@ -18785,7 +19372,7 @@ var DevServer = class _DevServer {
|
|
|
18785
19372
|
this.json(res, 500, { error: "DevConsole not found. Run: npm run build -w packages/web-devconsole" });
|
|
18786
19373
|
return;
|
|
18787
19374
|
}
|
|
18788
|
-
const htmlPath =
|
|
19375
|
+
const htmlPath = path20.join(distDir, "index.html");
|
|
18789
19376
|
try {
|
|
18790
19377
|
const html = fs14.readFileSync(htmlPath, "utf-8");
|
|
18791
19378
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
@@ -18810,15 +19397,15 @@ var DevServer = class _DevServer {
|
|
|
18810
19397
|
this.json(res, 404, { error: "Not found" });
|
|
18811
19398
|
return;
|
|
18812
19399
|
}
|
|
18813
|
-
const safePath =
|
|
18814
|
-
const filePath =
|
|
19400
|
+
const safePath = path20.normalize(pathname).replace(/^\.\.\//, "");
|
|
19401
|
+
const filePath = path20.join(distDir, safePath);
|
|
18815
19402
|
if (!filePath.startsWith(distDir)) {
|
|
18816
19403
|
this.json(res, 403, { error: "Forbidden" });
|
|
18817
19404
|
return;
|
|
18818
19405
|
}
|
|
18819
19406
|
try {
|
|
18820
19407
|
const content = fs14.readFileSync(filePath);
|
|
18821
|
-
const ext =
|
|
19408
|
+
const ext = path20.extname(filePath);
|
|
18822
19409
|
const contentType = _DevServer.MIME_MAP[ext] || "application/octet-stream";
|
|
18823
19410
|
res.writeHead(200, { "Content-Type": contentType, "Cache-Control": "public, max-age=31536000, immutable" });
|
|
18824
19411
|
res.end(content);
|
|
@@ -18931,9 +19518,9 @@ var DevServer = class _DevServer {
|
|
|
18931
19518
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
18932
19519
|
if (entry.isDirectory()) {
|
|
18933
19520
|
files.push({ path: rel, size: 0, type: "dir" });
|
|
18934
|
-
scan(
|
|
19521
|
+
scan(path20.join(d, entry.name), rel);
|
|
18935
19522
|
} else {
|
|
18936
|
-
const stat = fs14.statSync(
|
|
19523
|
+
const stat = fs14.statSync(path20.join(d, entry.name));
|
|
18937
19524
|
files.push({ path: rel, size: stat.size, type: "file" });
|
|
18938
19525
|
}
|
|
18939
19526
|
}
|
|
@@ -18956,7 +19543,7 @@ var DevServer = class _DevServer {
|
|
|
18956
19543
|
this.json(res, 404, { error: `Provider directory not found: ${type}` });
|
|
18957
19544
|
return;
|
|
18958
19545
|
}
|
|
18959
|
-
const fullPath =
|
|
19546
|
+
const fullPath = path20.resolve(dir, path20.normalize(filePath));
|
|
18960
19547
|
if (!fullPath.startsWith(dir)) {
|
|
18961
19548
|
this.json(res, 403, { error: "Forbidden" });
|
|
18962
19549
|
return;
|
|
@@ -18981,14 +19568,14 @@ var DevServer = class _DevServer {
|
|
|
18981
19568
|
this.json(res, 404, { error: `Provider directory not found: ${type}` });
|
|
18982
19569
|
return;
|
|
18983
19570
|
}
|
|
18984
|
-
const fullPath =
|
|
19571
|
+
const fullPath = path20.resolve(dir, path20.normalize(filePath));
|
|
18985
19572
|
if (!fullPath.startsWith(dir)) {
|
|
18986
19573
|
this.json(res, 403, { error: "Forbidden" });
|
|
18987
19574
|
return;
|
|
18988
19575
|
}
|
|
18989
19576
|
try {
|
|
18990
19577
|
if (fs14.existsSync(fullPath)) fs14.copyFileSync(fullPath, fullPath + ".bak");
|
|
18991
|
-
fs14.mkdirSync(
|
|
19578
|
+
fs14.mkdirSync(path20.dirname(fullPath), { recursive: true });
|
|
18992
19579
|
fs14.writeFileSync(fullPath, content, "utf-8");
|
|
18993
19580
|
this.log(`File saved: ${fullPath} (${content.length} chars)`);
|
|
18994
19581
|
this.providerLoader.reload();
|
|
@@ -19005,7 +19592,7 @@ var DevServer = class _DevServer {
|
|
|
19005
19592
|
return;
|
|
19006
19593
|
}
|
|
19007
19594
|
for (const name of ["scripts.js", "provider.json"]) {
|
|
19008
|
-
const p =
|
|
19595
|
+
const p = path20.join(dir, name);
|
|
19009
19596
|
if (fs14.existsSync(p)) {
|
|
19010
19597
|
const source = fs14.readFileSync(p, "utf-8");
|
|
19011
19598
|
this.json(res, 200, { type, path: p, source, lines: source.split("\n").length });
|
|
@@ -19026,8 +19613,8 @@ var DevServer = class _DevServer {
|
|
|
19026
19613
|
this.json(res, 404, { error: `Provider not found: ${type}` });
|
|
19027
19614
|
return;
|
|
19028
19615
|
}
|
|
19029
|
-
const target = fs14.existsSync(
|
|
19030
|
-
const targetPath =
|
|
19616
|
+
const target = fs14.existsSync(path20.join(dir, "scripts.js")) ? "scripts.js" : "provider.json";
|
|
19617
|
+
const targetPath = path20.join(dir, target);
|
|
19031
19618
|
try {
|
|
19032
19619
|
if (fs14.existsSync(targetPath)) fs14.copyFileSync(targetPath, targetPath + ".bak");
|
|
19033
19620
|
fs14.writeFileSync(targetPath, source, "utf-8");
|
|
@@ -19132,14 +19719,14 @@ var DevServer = class _DevServer {
|
|
|
19132
19719
|
child.stderr?.on("data", (d) => {
|
|
19133
19720
|
stderr += d.toString();
|
|
19134
19721
|
});
|
|
19135
|
-
await new Promise((
|
|
19722
|
+
await new Promise((resolve12) => {
|
|
19136
19723
|
const timer = setTimeout(() => {
|
|
19137
19724
|
child.kill();
|
|
19138
|
-
|
|
19725
|
+
resolve12();
|
|
19139
19726
|
}, timeout);
|
|
19140
19727
|
child.on("exit", () => {
|
|
19141
19728
|
clearTimeout(timer);
|
|
19142
|
-
|
|
19729
|
+
resolve12();
|
|
19143
19730
|
});
|
|
19144
19731
|
});
|
|
19145
19732
|
const elapsed = Date.now() - start;
|
|
@@ -19187,7 +19774,7 @@ var DevServer = class _DevServer {
|
|
|
19187
19774
|
}
|
|
19188
19775
|
let targetDir;
|
|
19189
19776
|
targetDir = this.providerLoader.getUserProviderDir(category, type);
|
|
19190
|
-
const jsonPath =
|
|
19777
|
+
const jsonPath = path20.join(targetDir, "provider.json");
|
|
19191
19778
|
if (fs14.existsSync(jsonPath)) {
|
|
19192
19779
|
this.json(res, 409, { error: `Provider already exists at ${targetDir}`, path: targetDir });
|
|
19193
19780
|
return;
|
|
@@ -19199,8 +19786,8 @@ var DevServer = class _DevServer {
|
|
|
19199
19786
|
const createdFiles = ["provider.json"];
|
|
19200
19787
|
if (result.files) {
|
|
19201
19788
|
for (const [relPath, content] of Object.entries(result.files)) {
|
|
19202
|
-
const fullPath =
|
|
19203
|
-
fs14.mkdirSync(
|
|
19789
|
+
const fullPath = path20.join(targetDir, relPath);
|
|
19790
|
+
fs14.mkdirSync(path20.dirname(fullPath), { recursive: true });
|
|
19204
19791
|
fs14.writeFileSync(fullPath, content, "utf-8");
|
|
19205
19792
|
createdFiles.push(relPath);
|
|
19206
19793
|
}
|
|
@@ -19253,22 +19840,22 @@ var DevServer = class _DevServer {
|
|
|
19253
19840
|
if (!fs14.existsSync(scriptsDir)) return null;
|
|
19254
19841
|
const versions = fs14.readdirSync(scriptsDir).filter((d) => {
|
|
19255
19842
|
try {
|
|
19256
|
-
return fs14.statSync(
|
|
19843
|
+
return fs14.statSync(path20.join(scriptsDir, d)).isDirectory();
|
|
19257
19844
|
} catch {
|
|
19258
19845
|
return false;
|
|
19259
19846
|
}
|
|
19260
19847
|
}).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" }));
|
|
19261
19848
|
if (versions.length === 0) return null;
|
|
19262
|
-
return
|
|
19849
|
+
return path20.join(scriptsDir, versions[0]);
|
|
19263
19850
|
}
|
|
19264
19851
|
resolveAutoImplWritableProviderDir(category, type, requestedDir) {
|
|
19265
|
-
const canonicalUserDir =
|
|
19266
|
-
const desiredDir = requestedDir ?
|
|
19267
|
-
const upstreamRoot =
|
|
19268
|
-
if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${
|
|
19852
|
+
const canonicalUserDir = path20.resolve(this.providerLoader.getUserProviderDir(category, type));
|
|
19853
|
+
const desiredDir = requestedDir ? path20.resolve(requestedDir) : canonicalUserDir;
|
|
19854
|
+
const upstreamRoot = path20.resolve(this.providerLoader.getUpstreamDir());
|
|
19855
|
+
if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path20.sep}`)) {
|
|
19269
19856
|
return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
|
|
19270
19857
|
}
|
|
19271
|
-
if (
|
|
19858
|
+
if (path20.basename(desiredDir) !== type) {
|
|
19272
19859
|
return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
|
|
19273
19860
|
}
|
|
19274
19861
|
const sourceDir = this.findProviderDir(type);
|
|
@@ -19276,11 +19863,11 @@ var DevServer = class _DevServer {
|
|
|
19276
19863
|
return { dir: null, reason: `Provider source directory not found for '${type}'` };
|
|
19277
19864
|
}
|
|
19278
19865
|
if (!fs14.existsSync(desiredDir)) {
|
|
19279
|
-
fs14.mkdirSync(
|
|
19866
|
+
fs14.mkdirSync(path20.dirname(desiredDir), { recursive: true });
|
|
19280
19867
|
fs14.cpSync(sourceDir, desiredDir, { recursive: true });
|
|
19281
19868
|
this.log(`Auto-implement writable copy created: ${desiredDir}`);
|
|
19282
19869
|
}
|
|
19283
|
-
const providerJson =
|
|
19870
|
+
const providerJson = path20.join(desiredDir, "provider.json");
|
|
19284
19871
|
if (!fs14.existsSync(providerJson)) {
|
|
19285
19872
|
return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
|
|
19286
19873
|
}
|
|
@@ -19328,7 +19915,7 @@ var DevServer = class _DevServer {
|
|
|
19328
19915
|
setMode: "set_mode.js"
|
|
19329
19916
|
};
|
|
19330
19917
|
const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
|
|
19331
|
-
const scriptsDir =
|
|
19918
|
+
const scriptsDir = path20.join(providerDir, "scripts");
|
|
19332
19919
|
const latestScriptsDir = this.getLatestScriptVersionDir(scriptsDir);
|
|
19333
19920
|
if (latestScriptsDir) {
|
|
19334
19921
|
lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
|
|
@@ -19339,7 +19926,7 @@ var DevServer = class _DevServer {
|
|
|
19339
19926
|
for (const file of fs14.readdirSync(latestScriptsDir)) {
|
|
19340
19927
|
if (file.endsWith(".js") && targetFileNames.has(file)) {
|
|
19341
19928
|
try {
|
|
19342
|
-
const content = fs14.readFileSync(
|
|
19929
|
+
const content = fs14.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
|
|
19343
19930
|
lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
|
|
19344
19931
|
lines.push("```javascript");
|
|
19345
19932
|
lines.push(content);
|
|
@@ -19356,7 +19943,7 @@ var DevServer = class _DevServer {
|
|
|
19356
19943
|
lines.push("");
|
|
19357
19944
|
for (const file of refFiles) {
|
|
19358
19945
|
try {
|
|
19359
|
-
const content = fs14.readFileSync(
|
|
19946
|
+
const content = fs14.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
|
|
19360
19947
|
lines.push(`### \`${file}\` \u{1F512}`);
|
|
19361
19948
|
lines.push("```javascript");
|
|
19362
19949
|
lines.push(content);
|
|
@@ -19397,10 +19984,10 @@ var DevServer = class _DevServer {
|
|
|
19397
19984
|
lines.push("");
|
|
19398
19985
|
}
|
|
19399
19986
|
}
|
|
19400
|
-
const docsDir =
|
|
19987
|
+
const docsDir = path20.join(providerDir, "../../docs");
|
|
19401
19988
|
const loadGuide = (name) => {
|
|
19402
19989
|
try {
|
|
19403
|
-
const p =
|
|
19990
|
+
const p = path20.join(docsDir, name);
|
|
19404
19991
|
if (fs14.existsSync(p)) return fs14.readFileSync(p, "utf-8");
|
|
19405
19992
|
} catch {
|
|
19406
19993
|
}
|
|
@@ -19574,7 +20161,7 @@ var DevServer = class _DevServer {
|
|
|
19574
20161
|
parseApproval: "parse_approval.js"
|
|
19575
20162
|
};
|
|
19576
20163
|
const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
|
|
19577
|
-
const scriptsDir =
|
|
20164
|
+
const scriptsDir = path20.join(providerDir, "scripts");
|
|
19578
20165
|
const latestScriptsDir = this.getLatestScriptVersionDir(scriptsDir);
|
|
19579
20166
|
if (latestScriptsDir) {
|
|
19580
20167
|
lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
|
|
@@ -19586,7 +20173,7 @@ var DevServer = class _DevServer {
|
|
|
19586
20173
|
if (!file.endsWith(".js")) continue;
|
|
19587
20174
|
if (!targetFileNames.has(file)) continue;
|
|
19588
20175
|
try {
|
|
19589
|
-
const content = fs14.readFileSync(
|
|
20176
|
+
const content = fs14.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
|
|
19590
20177
|
lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
|
|
19591
20178
|
lines.push("```javascript");
|
|
19592
20179
|
lines.push(content);
|
|
@@ -19602,7 +20189,7 @@ var DevServer = class _DevServer {
|
|
|
19602
20189
|
lines.push("");
|
|
19603
20190
|
for (const file of refFiles) {
|
|
19604
20191
|
try {
|
|
19605
|
-
const content = fs14.readFileSync(
|
|
20192
|
+
const content = fs14.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
|
|
19606
20193
|
lines.push(`### \`${file}\` \u{1F512}`);
|
|
19607
20194
|
lines.push("```javascript");
|
|
19608
20195
|
lines.push(content);
|
|
@@ -19635,10 +20222,10 @@ var DevServer = class _DevServer {
|
|
|
19635
20222
|
lines.push("");
|
|
19636
20223
|
}
|
|
19637
20224
|
}
|
|
19638
|
-
const docsDir =
|
|
20225
|
+
const docsDir = path20.join(providerDir, "../../docs");
|
|
19639
20226
|
const loadGuide = (name) => {
|
|
19640
20227
|
try {
|
|
19641
|
-
const p =
|
|
20228
|
+
const p = path20.join(docsDir, name);
|
|
19642
20229
|
if (fs14.existsSync(p)) return fs14.readFileSync(p, "utf-8");
|
|
19643
20230
|
} catch {
|
|
19644
20231
|
}
|
|
@@ -19814,14 +20401,14 @@ data: ${JSON.stringify(msg.data)}
|
|
|
19814
20401
|
res.end(JSON.stringify(data, null, 2));
|
|
19815
20402
|
}
|
|
19816
20403
|
async readBody(req) {
|
|
19817
|
-
return new Promise((
|
|
20404
|
+
return new Promise((resolve12) => {
|
|
19818
20405
|
let body = "";
|
|
19819
20406
|
req.on("data", (chunk) => body += chunk);
|
|
19820
20407
|
req.on("end", () => {
|
|
19821
20408
|
try {
|
|
19822
|
-
|
|
20409
|
+
resolve12(JSON.parse(body));
|
|
19823
20410
|
} catch {
|
|
19824
|
-
|
|
20411
|
+
resolve12({});
|
|
19825
20412
|
}
|
|
19826
20413
|
});
|
|
19827
20414
|
});
|
|
@@ -20289,7 +20876,7 @@ async function waitForReady(endpoint, timeoutMs = STARTUP_TIMEOUT_MS) {
|
|
|
20289
20876
|
const deadline = Date.now() + timeoutMs;
|
|
20290
20877
|
while (Date.now() < deadline) {
|
|
20291
20878
|
if (await canConnect(endpoint)) return;
|
|
20292
|
-
await new Promise((
|
|
20879
|
+
await new Promise((resolve12) => setTimeout(resolve12, STARTUP_POLL_MS));
|
|
20293
20880
|
}
|
|
20294
20881
|
throw new Error(`Session host did not become ready within ${timeoutMs}ms`);
|
|
20295
20882
|
}
|
|
@@ -20445,10 +21032,10 @@ async function installExtension(ide, extension) {
|
|
|
20445
21032
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
20446
21033
|
const fs15 = await import("fs");
|
|
20447
21034
|
fs15.writeFileSync(vsixPath, buffer);
|
|
20448
|
-
return new Promise((
|
|
21035
|
+
return new Promise((resolve12) => {
|
|
20449
21036
|
const cmd = `"${ide.cliCommand}" --install-extension "${vsixPath}" --force`;
|
|
20450
21037
|
(0, import_child_process10.exec)(cmd, { timeout: 6e4 }, (error, _stdout, stderr) => {
|
|
20451
|
-
|
|
21038
|
+
resolve12({
|
|
20452
21039
|
extensionId: extension.id,
|
|
20453
21040
|
marketplaceId: extension.marketplaceId,
|
|
20454
21041
|
success: !error,
|
|
@@ -20461,11 +21048,11 @@ async function installExtension(ide, extension) {
|
|
|
20461
21048
|
} catch (e) {
|
|
20462
21049
|
}
|
|
20463
21050
|
}
|
|
20464
|
-
return new Promise((
|
|
21051
|
+
return new Promise((resolve12) => {
|
|
20465
21052
|
const cmd = `"${ide.cliCommand}" --install-extension ${extension.marketplaceId} --force`;
|
|
20466
21053
|
(0, import_child_process10.exec)(cmd, { timeout: 6e4 }, (error, stdout, stderr) => {
|
|
20467
21054
|
if (error) {
|
|
20468
|
-
|
|
21055
|
+
resolve12({
|
|
20469
21056
|
extensionId: extension.id,
|
|
20470
21057
|
marketplaceId: extension.marketplaceId,
|
|
20471
21058
|
success: false,
|
|
@@ -20473,7 +21060,7 @@ async function installExtension(ide, extension) {
|
|
|
20473
21060
|
error: stderr || error.message
|
|
20474
21061
|
});
|
|
20475
21062
|
} else {
|
|
20476
|
-
|
|
21063
|
+
resolve12({
|
|
20477
21064
|
extensionId: extension.id,
|
|
20478
21065
|
marketplaceId: extension.marketplaceId,
|
|
20479
21066
|
success: true,
|
|
@@ -20609,13 +21196,32 @@ async function initDaemonComponents(config) {
|
|
|
20609
21196
|
const detectedIdesRef = { value: [] };
|
|
20610
21197
|
let agentStreamManager = null;
|
|
20611
21198
|
let poller = null;
|
|
21199
|
+
const refreshProviderAvailability = async (providerType) => {
|
|
21200
|
+
const targetProvider = providerType ? providerLoader.getMeta(providerLoader.resolveAlias(providerType)) : null;
|
|
21201
|
+
const targetCategory = targetProvider?.category;
|
|
21202
|
+
if (!providerType || targetCategory === "cli" || targetCategory === "acp") {
|
|
21203
|
+
if (providerType && targetProvider) {
|
|
21204
|
+
const detected = await detectCLI(targetProvider.type, providerLoader, { includeVersion: false });
|
|
21205
|
+
providerLoader.setProviderAvailability(targetProvider.type, {
|
|
21206
|
+
installed: !!detected,
|
|
21207
|
+
detectedPath: detected?.path || null
|
|
21208
|
+
});
|
|
21209
|
+
} else {
|
|
21210
|
+
providerLoader.setCliDetectionResults(await detectCLIs(providerLoader, { includeVersion: false }), true);
|
|
21211
|
+
}
|
|
21212
|
+
}
|
|
21213
|
+
if (!providerType || targetCategory === "ide") {
|
|
21214
|
+
detectedIdesRef.value = await detectIDEs(providerLoader);
|
|
21215
|
+
providerLoader.setIdeDetectionResults(detectedIdesRef.value, true);
|
|
21216
|
+
}
|
|
21217
|
+
};
|
|
20612
21218
|
const cliManager = new DaemonCliManager({
|
|
20613
21219
|
...config.cliManagerDeps,
|
|
20614
21220
|
getInstanceManager: () => instanceManager,
|
|
20615
21221
|
getSessionRegistry: () => sessionRegistry
|
|
20616
21222
|
}, providerLoader);
|
|
20617
21223
|
LOG.info("Init", "Detecting IDEs...");
|
|
20618
|
-
|
|
21224
|
+
await refreshProviderAvailability();
|
|
20619
21225
|
const installed = detectedIdesRef.value.filter((i) => i.installed);
|
|
20620
21226
|
LOG.info("Init", `Found ${installed.length} IDE(s): ${installed.map((i) => i.id).join(", ") || "none"}`);
|
|
20621
21227
|
const cdpSetupContext = {
|
|
@@ -20655,7 +21261,11 @@ async function initDaemonComponents(config) {
|
|
|
20655
21261
|
adapters: cliManager.adapters,
|
|
20656
21262
|
providerLoader,
|
|
20657
21263
|
instanceManager,
|
|
20658
|
-
sessionRegistry
|
|
21264
|
+
sessionRegistry,
|
|
21265
|
+
onProviderSettingChanged: async (providerType) => {
|
|
21266
|
+
await refreshProviderAvailability(providerType);
|
|
21267
|
+
config.onStatusChange?.();
|
|
21268
|
+
}
|
|
20659
21269
|
});
|
|
20660
21270
|
agentStreamManager = new DaemonAgentStreamManager(
|
|
20661
21271
|
LOG.forComponent("AgentStream").asLogFn(),
|