@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.mjs
CHANGED
|
@@ -233,7 +233,7 @@ var init_config = __esm({
|
|
|
233
233
|
|
|
234
234
|
// src/logging/logger.ts
|
|
235
235
|
import * as fs2 from "fs";
|
|
236
|
-
import * as
|
|
236
|
+
import * as path6 from "path";
|
|
237
237
|
import * as os4 from "os";
|
|
238
238
|
function setLogLevel(level) {
|
|
239
239
|
currentLevel = level;
|
|
@@ -249,13 +249,13 @@ function getDaemonLogDir() {
|
|
|
249
249
|
return LOG_DIR;
|
|
250
250
|
}
|
|
251
251
|
function getCurrentDaemonLogPath(date = /* @__PURE__ */ new Date()) {
|
|
252
|
-
return
|
|
252
|
+
return path6.join(LOG_DIR, `daemon-${date.toISOString().slice(0, 10)}.log`);
|
|
253
253
|
}
|
|
254
254
|
function checkDateRotation() {
|
|
255
255
|
const today = getDateStr();
|
|
256
256
|
if (today !== currentDate) {
|
|
257
257
|
currentDate = today;
|
|
258
|
-
currentLogFile =
|
|
258
|
+
currentLogFile = path6.join(LOG_DIR, `daemon-${currentDate}.log`);
|
|
259
259
|
cleanOldLogs();
|
|
260
260
|
}
|
|
261
261
|
}
|
|
@@ -269,7 +269,7 @@ function cleanOldLogs() {
|
|
|
269
269
|
const dateMatch = file.match(/daemon-(\d{4}-\d{2}-\d{2})/);
|
|
270
270
|
if (dateMatch && dateMatch[1] < cutoffStr) {
|
|
271
271
|
try {
|
|
272
|
-
fs2.unlinkSync(
|
|
272
|
+
fs2.unlinkSync(path6.join(LOG_DIR, file));
|
|
273
273
|
} catch {
|
|
274
274
|
}
|
|
275
275
|
}
|
|
@@ -393,7 +393,7 @@ var init_logger = __esm({
|
|
|
393
393
|
LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
394
394
|
LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
|
|
395
395
|
currentLevel = "info";
|
|
396
|
-
LOG_DIR = process.platform === "win32" ?
|
|
396
|
+
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");
|
|
397
397
|
MAX_LOG_SIZE = 5 * 1024 * 1024;
|
|
398
398
|
MAX_LOG_DAYS = 7;
|
|
399
399
|
try {
|
|
@@ -401,16 +401,16 @@ var init_logger = __esm({
|
|
|
401
401
|
} catch {
|
|
402
402
|
}
|
|
403
403
|
currentDate = getDateStr();
|
|
404
|
-
currentLogFile =
|
|
404
|
+
currentLogFile = path6.join(LOG_DIR, `daemon-${currentDate}.log`);
|
|
405
405
|
cleanOldLogs();
|
|
406
406
|
try {
|
|
407
|
-
const oldLog =
|
|
407
|
+
const oldLog = path6.join(LOG_DIR, "daemon.log");
|
|
408
408
|
if (fs2.existsSync(oldLog)) {
|
|
409
409
|
const stat = fs2.statSync(oldLog);
|
|
410
410
|
const oldDate = stat.mtime.toISOString().slice(0, 10);
|
|
411
|
-
fs2.renameSync(oldLog,
|
|
411
|
+
fs2.renameSync(oldLog, path6.join(LOG_DIR, `daemon-${oldDate}.log`));
|
|
412
412
|
}
|
|
413
|
-
const oldLogBackup =
|
|
413
|
+
const oldLogBackup = path6.join(LOG_DIR, "daemon.log.old");
|
|
414
414
|
if (fs2.existsSync(oldLogBackup)) {
|
|
415
415
|
fs2.unlinkSync(oldLogBackup);
|
|
416
416
|
}
|
|
@@ -442,7 +442,7 @@ var init_logger = __esm({
|
|
|
442
442
|
}
|
|
443
443
|
};
|
|
444
444
|
interceptorInstalled = false;
|
|
445
|
-
LOG_PATH =
|
|
445
|
+
LOG_PATH = path6.join(LOG_DIR, `daemon-${getDateStr()}.log`);
|
|
446
446
|
}
|
|
447
447
|
});
|
|
448
448
|
|
|
@@ -802,7 +802,7 @@ __export(provider_cli_adapter_exports, {
|
|
|
802
802
|
normalizeCliProviderForRuntime: () => normalizeCliProviderForRuntime
|
|
803
803
|
});
|
|
804
804
|
import * as os8 from "os";
|
|
805
|
-
import * as
|
|
805
|
+
import * as path9 from "path";
|
|
806
806
|
import { execSync as execSync3 } from "child_process";
|
|
807
807
|
function stripAnsi(str) {
|
|
808
808
|
return str.replace(/\x1B\][^\x07]*\x07/g, "").replace(/\x1B\][\s\S]*?\x1B\\/g, "").replace(/\x1B[P^_X][\s\S]*?(?:\x07|\x1B\\)/g, "").replace(/\x1B\[\d*[A-HJKSTfG]/g, " ").replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "").replace(/ +/g, " ");
|
|
@@ -873,16 +873,22 @@ function computeTerminalQueryTail(buffer) {
|
|
|
873
873
|
return "";
|
|
874
874
|
}
|
|
875
875
|
function findBinary(name) {
|
|
876
|
+
const trimmed = String(name || "").trim();
|
|
877
|
+
if (!trimmed) return trimmed;
|
|
878
|
+
const expanded = trimmed.startsWith("~") ? path9.join(os8.homedir(), trimmed.slice(1)) : trimmed;
|
|
879
|
+
if (path9.isAbsolute(expanded) || expanded.includes("/") || expanded.includes("\\")) {
|
|
880
|
+
return path9.isAbsolute(expanded) ? expanded : path9.resolve(expanded);
|
|
881
|
+
}
|
|
876
882
|
const isWin = os8.platform() === "win32";
|
|
877
883
|
try {
|
|
878
|
-
const cmd = isWin ? `where ${
|
|
884
|
+
const cmd = isWin ? `where ${trimmed}` : `which ${trimmed}`;
|
|
879
885
|
return execSync3(cmd, { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }).trim().split("\n")[0].trim();
|
|
880
886
|
} catch {
|
|
881
|
-
return isWin ? `${
|
|
887
|
+
return isWin ? `${trimmed}.cmd` : trimmed;
|
|
882
888
|
}
|
|
883
889
|
}
|
|
884
890
|
function isScriptBinary(binaryPath) {
|
|
885
|
-
if (!
|
|
891
|
+
if (!path9.isAbsolute(binaryPath)) return false;
|
|
886
892
|
try {
|
|
887
893
|
const fs15 = __require("fs");
|
|
888
894
|
const resolved = fs15.realpathSync(binaryPath);
|
|
@@ -898,7 +904,7 @@ function isScriptBinary(binaryPath) {
|
|
|
898
904
|
}
|
|
899
905
|
}
|
|
900
906
|
function looksLikeMachOOrElf(filePath) {
|
|
901
|
-
if (!
|
|
907
|
+
if (!path9.isAbsolute(filePath)) return false;
|
|
902
908
|
try {
|
|
903
909
|
const fs15 = __require("fs");
|
|
904
910
|
const resolved = fs15.realpathSync(filePath);
|
|
@@ -1379,16 +1385,17 @@ var init_provider_cli_adapter = __esm({
|
|
|
1379
1385
|
async spawn() {
|
|
1380
1386
|
if (this.ptyProcess) return;
|
|
1381
1387
|
const { spawn: spawnConfig } = this.provider;
|
|
1382
|
-
const
|
|
1388
|
+
const configuredCommand = typeof this.runtimeSettings.executablePath === "string" && this.runtimeSettings.executablePath.trim() ? this.runtimeSettings.executablePath.trim() : spawnConfig.command;
|
|
1389
|
+
const binaryPath = findBinary(configuredCommand);
|
|
1383
1390
|
const isWin = os8.platform() === "win32";
|
|
1384
1391
|
const allArgs = [...spawnConfig.args, ...this.extraArgs];
|
|
1385
1392
|
LOG.info("CLI", `[${this.cliType}] Spawning in ${this.workingDir}`);
|
|
1386
1393
|
this.resetTraceSession();
|
|
1387
1394
|
let shellCmd;
|
|
1388
1395
|
let shellArgs;
|
|
1389
|
-
const useShellUnix = !isWin && (!!spawnConfig.shell || !
|
|
1396
|
+
const useShellUnix = !isWin && (!!spawnConfig.shell || !path9.isAbsolute(binaryPath) || isScriptBinary(binaryPath) || !looksLikeMachOOrElf(binaryPath));
|
|
1390
1397
|
const isCmdShim = isWin && /\.(cmd|bat)$/i.test(binaryPath);
|
|
1391
|
-
const useShellWin = !!spawnConfig.shell || isCmdShim || !
|
|
1398
|
+
const useShellWin = !!spawnConfig.shell || isCmdShim || !path9.isAbsolute(binaryPath) || isScriptBinary(binaryPath);
|
|
1392
1399
|
const useShell = isWin ? useShellWin : useShellUnix;
|
|
1393
1400
|
if (useShell) {
|
|
1394
1401
|
if (!spawnConfig.shell && !isWin) {
|
|
@@ -1633,6 +1640,9 @@ var init_provider_cli_adapter = __esm({
|
|
|
1633
1640
|
looksLikeVisibleIdlePrompt(screenText) {
|
|
1634
1641
|
const text = String(screenText || "");
|
|
1635
1642
|
if (!text.trim()) return false;
|
|
1643
|
+
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)) {
|
|
1644
|
+
return true;
|
|
1645
|
+
}
|
|
1636
1646
|
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);
|
|
1637
1647
|
}
|
|
1638
1648
|
findLastMatchingLineIndex(lines, predicate) {
|
|
@@ -1762,7 +1772,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
1762
1772
|
`[${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)}`
|
|
1763
1773
|
);
|
|
1764
1774
|
}
|
|
1765
|
-
await new Promise((
|
|
1775
|
+
await new Promise((resolve12) => setTimeout(resolve12, 50));
|
|
1766
1776
|
}
|
|
1767
1777
|
const finalScreenText = this.terminalScreen.getText() || "";
|
|
1768
1778
|
LOG.warn(
|
|
@@ -2273,7 +2283,7 @@ ${data.message || ""}`.trim();
|
|
|
2273
2283
|
const deadline = Date.now() + 1e4;
|
|
2274
2284
|
while (this.startupParseGate && Date.now() < deadline) {
|
|
2275
2285
|
this.resolveStartupState("send_wait");
|
|
2276
|
-
await new Promise((
|
|
2286
|
+
await new Promise((resolve12) => setTimeout(resolve12, 50));
|
|
2277
2287
|
}
|
|
2278
2288
|
}
|
|
2279
2289
|
await this.waitForInteractivePrompt();
|
|
@@ -2498,17 +2508,17 @@ ${data.message || ""}`.trim();
|
|
|
2498
2508
|
}
|
|
2499
2509
|
}
|
|
2500
2510
|
waitForStopped(timeoutMs) {
|
|
2501
|
-
return new Promise((
|
|
2511
|
+
return new Promise((resolve12) => {
|
|
2502
2512
|
const startedAt = Date.now();
|
|
2503
2513
|
const timer = setInterval(() => {
|
|
2504
2514
|
if (!this.ptyProcess || this.currentStatus === "stopped") {
|
|
2505
2515
|
clearInterval(timer);
|
|
2506
|
-
|
|
2516
|
+
resolve12(true);
|
|
2507
2517
|
return;
|
|
2508
2518
|
}
|
|
2509
2519
|
if (Date.now() - startedAt >= timeoutMs) {
|
|
2510
2520
|
clearInterval(timer);
|
|
2511
|
-
|
|
2521
|
+
resolve12(false);
|
|
2512
2522
|
}
|
|
2513
2523
|
}, 100);
|
|
2514
2524
|
});
|
|
@@ -3124,6 +3134,7 @@ function resetState() {
|
|
|
3124
3134
|
import { execSync } from "child_process";
|
|
3125
3135
|
import { existsSync as existsSync4 } from "fs";
|
|
3126
3136
|
import { platform, homedir as homedir3 } from "os";
|
|
3137
|
+
import * as path4 from "path";
|
|
3127
3138
|
var BUILTIN_IDE_DEFINITIONS = [];
|
|
3128
3139
|
var registeredIDEs = /* @__PURE__ */ new Map();
|
|
3129
3140
|
function registerIDEDefinition(def) {
|
|
@@ -3140,9 +3151,16 @@ function getMergedDefinitions() {
|
|
|
3140
3151
|
return [...merged.values()];
|
|
3141
3152
|
}
|
|
3142
3153
|
function findCliCommand(command) {
|
|
3154
|
+
const trimmed = String(command || "").trim();
|
|
3155
|
+
if (!trimmed) return null;
|
|
3156
|
+
if (path4.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
|
|
3157
|
+
const candidate = trimmed.startsWith("~") ? path4.join(homedir3(), trimmed.slice(1)) : trimmed;
|
|
3158
|
+
const resolved = path4.isAbsolute(candidate) ? candidate : path4.resolve(candidate);
|
|
3159
|
+
return existsSync4(resolved) ? resolved : null;
|
|
3160
|
+
}
|
|
3143
3161
|
try {
|
|
3144
3162
|
const result = execSync(
|
|
3145
|
-
platform() === "win32" ? `where ${
|
|
3163
|
+
platform() === "win32" ? `where ${trimmed}` : `which ${trimmed}`,
|
|
3146
3164
|
{ encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }
|
|
3147
3165
|
).trim();
|
|
3148
3166
|
return result.split("\n")[0] || null;
|
|
@@ -3165,23 +3183,23 @@ function getIdeVersion(cliCommand) {
|
|
|
3165
3183
|
function checkPathExists(paths) {
|
|
3166
3184
|
const home = homedir3();
|
|
3167
3185
|
for (const p of paths) {
|
|
3168
|
-
|
|
3186
|
+
const normalized = p.startsWith("~") ? path4.join(home, p.slice(1)) : p;
|
|
3187
|
+
if (normalized.includes("*")) {
|
|
3169
3188
|
const username = home.split(/[\\/]/).pop() || "";
|
|
3170
|
-
const resolved =
|
|
3189
|
+
const resolved = normalized.replace("*", username);
|
|
3171
3190
|
if (existsSync4(resolved)) return resolved;
|
|
3172
3191
|
} else {
|
|
3173
|
-
if (existsSync4(
|
|
3192
|
+
if (existsSync4(normalized)) return normalized;
|
|
3174
3193
|
}
|
|
3175
3194
|
}
|
|
3176
3195
|
return null;
|
|
3177
3196
|
}
|
|
3178
|
-
async function detectIDEs() {
|
|
3197
|
+
async function detectIDEs(providerLoader) {
|
|
3179
3198
|
const os18 = platform();
|
|
3180
3199
|
const results = [];
|
|
3181
3200
|
for (const def of getMergedDefinitions()) {
|
|
3182
|
-
const cliPath = findCliCommand(def.cli);
|
|
3183
|
-
const appPath = checkPathExists(def.paths[os18] || []);
|
|
3184
|
-
const installed = !!(cliPath || appPath);
|
|
3201
|
+
const cliPath = findCliCommand(providerLoader?.getIdeCliCommand(def.id, def.cli) || def.cli);
|
|
3202
|
+
const appPath = checkPathExists(providerLoader?.getIdePathCandidates(def.id, def.paths[os18] || []) || []);
|
|
3185
3203
|
let resolvedCli = cliPath;
|
|
3186
3204
|
if (!resolvedCli && appPath && os18 === "darwin") {
|
|
3187
3205
|
const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
|
|
@@ -3204,6 +3222,7 @@ async function detectIDEs() {
|
|
|
3204
3222
|
}
|
|
3205
3223
|
}
|
|
3206
3224
|
}
|
|
3225
|
+
const installed = os18 === "darwin" ? !!(resolvedCli || appPath) : !!resolvedCli;
|
|
3207
3226
|
const version = resolvedCli ? getIdeVersion(resolvedCli) : null;
|
|
3208
3227
|
results.push({
|
|
3209
3228
|
id: def.id,
|
|
@@ -3222,48 +3241,77 @@ async function detectIDEs() {
|
|
|
3222
3241
|
// src/detection/cli-detector.ts
|
|
3223
3242
|
import { exec } from "child_process";
|
|
3224
3243
|
import * as os2 from "os";
|
|
3244
|
+
import * as path5 from "path";
|
|
3245
|
+
import { existsSync as existsSync5 } from "fs";
|
|
3225
3246
|
function parseVersion(raw) {
|
|
3226
3247
|
const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
|
|
3227
3248
|
return match ? match[1] : raw.split("\n")[0].slice(0, 100);
|
|
3228
3249
|
}
|
|
3250
|
+
function shellQuote(value) {
|
|
3251
|
+
if (/^[a-zA-Z0-9_./:@%+=,-]+$/.test(value)) return value;
|
|
3252
|
+
return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
3253
|
+
}
|
|
3254
|
+
function expandHome(value) {
|
|
3255
|
+
const trimmed = value.trim();
|
|
3256
|
+
if (!trimmed.startsWith("~")) return trimmed;
|
|
3257
|
+
return path5.join(os2.homedir(), trimmed.slice(1));
|
|
3258
|
+
}
|
|
3259
|
+
function isExplicitCommandPath(command) {
|
|
3260
|
+
const trimmed = command.trim();
|
|
3261
|
+
return path5.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
|
|
3262
|
+
}
|
|
3263
|
+
function resolveCommandPath(command) {
|
|
3264
|
+
const trimmed = command.trim();
|
|
3265
|
+
if (!trimmed) return null;
|
|
3266
|
+
if (isExplicitCommandPath(trimmed)) {
|
|
3267
|
+
const expanded = expandHome(trimmed);
|
|
3268
|
+
const candidate = path5.isAbsolute(expanded) ? expanded : path5.resolve(expanded);
|
|
3269
|
+
return existsSync5(candidate) ? candidate : null;
|
|
3270
|
+
}
|
|
3271
|
+
return null;
|
|
3272
|
+
}
|
|
3229
3273
|
function execAsync(cmd, timeoutMs = 5e3) {
|
|
3230
|
-
return new Promise((
|
|
3274
|
+
return new Promise((resolve12) => {
|
|
3231
3275
|
const child = exec(cmd, { encoding: "utf-8", timeout: timeoutMs }, (err, stdout) => {
|
|
3232
3276
|
if (err || !stdout?.trim()) {
|
|
3233
|
-
|
|
3277
|
+
resolve12(null);
|
|
3234
3278
|
} else {
|
|
3235
|
-
|
|
3279
|
+
resolve12(stdout.trim());
|
|
3236
3280
|
}
|
|
3237
3281
|
});
|
|
3238
|
-
child.on("error", () =>
|
|
3282
|
+
child.on("error", () => resolve12(null));
|
|
3239
3283
|
});
|
|
3240
3284
|
}
|
|
3241
|
-
async function detectCLIs(providerLoader) {
|
|
3285
|
+
async function detectCLIs(providerLoader, options) {
|
|
3242
3286
|
const platform9 = os2.platform();
|
|
3243
3287
|
const whichCmd = platform9 === "win32" ? "where" : "which";
|
|
3288
|
+
const includeVersion = options?.includeVersion !== false;
|
|
3244
3289
|
const cliList = providerLoader ? providerLoader.getCliDetectionList() : [];
|
|
3245
3290
|
const results = await Promise.all(
|
|
3246
3291
|
cliList.map(async (cli) => {
|
|
3247
3292
|
try {
|
|
3248
|
-
const
|
|
3293
|
+
const explicitPath = resolveCommandPath(cli.command);
|
|
3294
|
+
const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(cli.command)}`);
|
|
3249
3295
|
if (!pathResult) return { ...cli, installed: false };
|
|
3250
|
-
const firstPath = pathResult.split("\n")[0];
|
|
3296
|
+
const firstPath = explicitPath || pathResult.split("\n")[0];
|
|
3251
3297
|
let version;
|
|
3252
|
-
|
|
3298
|
+
if (includeVersion) {
|
|
3253
3299
|
const versionCommands = [
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3300
|
+
`"${firstPath}" --version`,
|
|
3301
|
+
`"${firstPath}" -V`,
|
|
3302
|
+
`"${firstPath}" -v`,
|
|
3303
|
+
cli.versionCommand
|
|
3258
3304
|
].filter((v) => !!v);
|
|
3259
|
-
|
|
3260
|
-
const
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3305
|
+
try {
|
|
3306
|
+
for (const versionCommand of versionCommands) {
|
|
3307
|
+
const versionResult = await execAsync(versionCommand, 3e3);
|
|
3308
|
+
if (versionResult) {
|
|
3309
|
+
version = parseVersion(versionResult);
|
|
3310
|
+
break;
|
|
3311
|
+
}
|
|
3264
3312
|
}
|
|
3313
|
+
} catch {
|
|
3265
3314
|
}
|
|
3266
|
-
} catch {
|
|
3267
3315
|
}
|
|
3268
3316
|
return { ...cli, installed: true, version, path: firstPath };
|
|
3269
3317
|
} catch {
|
|
@@ -3273,7 +3321,7 @@ async function detectCLIs(providerLoader) {
|
|
|
3273
3321
|
);
|
|
3274
3322
|
return results;
|
|
3275
3323
|
}
|
|
3276
|
-
async function detectCLI(cliId, providerLoader) {
|
|
3324
|
+
async function detectCLI(cliId, providerLoader, options) {
|
|
3277
3325
|
const resolvedId = providerLoader ? providerLoader.resolveAlias(cliId) : cliId;
|
|
3278
3326
|
if (providerLoader) {
|
|
3279
3327
|
const cliList = providerLoader.getCliDetectionList();
|
|
@@ -3282,25 +3330,28 @@ async function detectCLI(cliId, providerLoader) {
|
|
|
3282
3330
|
const platform9 = os2.platform();
|
|
3283
3331
|
const whichCmd = platform9 === "win32" ? "where" : "which";
|
|
3284
3332
|
try {
|
|
3285
|
-
const
|
|
3333
|
+
const explicitPath = resolveCommandPath(target.command);
|
|
3334
|
+
const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(target.command)}`);
|
|
3286
3335
|
if (!pathResult) return null;
|
|
3287
|
-
const firstPath = pathResult.split("\n")[0];
|
|
3336
|
+
const firstPath = explicitPath || pathResult.split("\n")[0];
|
|
3288
3337
|
let version;
|
|
3289
|
-
|
|
3338
|
+
if (options?.includeVersion !== false) {
|
|
3290
3339
|
const versionCommands = [
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3340
|
+
`"${firstPath}" --version`,
|
|
3341
|
+
`"${firstPath}" -V`,
|
|
3342
|
+
`"${firstPath}" -v`,
|
|
3343
|
+
target.versionCommand
|
|
3295
3344
|
].filter((v) => !!v);
|
|
3296
|
-
|
|
3297
|
-
const
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3345
|
+
try {
|
|
3346
|
+
for (const versionCommand of versionCommands) {
|
|
3347
|
+
const versionResult = await execAsync(versionCommand, 3e3);
|
|
3348
|
+
if (versionResult) {
|
|
3349
|
+
version = parseVersion(versionResult);
|
|
3350
|
+
break;
|
|
3351
|
+
}
|
|
3301
3352
|
}
|
|
3353
|
+
} catch {
|
|
3302
3354
|
}
|
|
3303
|
-
} catch {
|
|
3304
3355
|
}
|
|
3305
3356
|
return { ...target, installed: true, version, path: firstPath };
|
|
3306
3357
|
} catch {
|
|
@@ -3308,7 +3359,7 @@ async function detectCLI(cliId, providerLoader) {
|
|
|
3308
3359
|
}
|
|
3309
3360
|
}
|
|
3310
3361
|
}
|
|
3311
|
-
const all = await detectCLIs(providerLoader);
|
|
3362
|
+
const all = await detectCLIs(providerLoader, options);
|
|
3312
3363
|
return all.find((c) => c.id === resolvedId && c.installed) || null;
|
|
3313
3364
|
}
|
|
3314
3365
|
|
|
@@ -3435,7 +3486,7 @@ var DaemonCdpManager = class {
|
|
|
3435
3486
|
* Returns multiple entries if multiple IDE windows are open on same port
|
|
3436
3487
|
*/
|
|
3437
3488
|
static listAllTargets(port) {
|
|
3438
|
-
return new Promise((
|
|
3489
|
+
return new Promise((resolve12) => {
|
|
3439
3490
|
const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
|
|
3440
3491
|
let data = "";
|
|
3441
3492
|
res.on("data", (chunk) => data += chunk.toString());
|
|
@@ -3451,16 +3502,16 @@ var DaemonCdpManager = class {
|
|
|
3451
3502
|
(t) => !isNonMain(t.title || "") && t.url?.includes("workbench.html") && !t.url?.includes("agent")
|
|
3452
3503
|
);
|
|
3453
3504
|
const fallbackPages = pages.filter((t) => !isNonMain(t.title || ""));
|
|
3454
|
-
|
|
3505
|
+
resolve12(mainPages.length > 0 ? mainPages : fallbackPages);
|
|
3455
3506
|
} catch {
|
|
3456
|
-
|
|
3507
|
+
resolve12([]);
|
|
3457
3508
|
}
|
|
3458
3509
|
});
|
|
3459
3510
|
});
|
|
3460
|
-
req.on("error", () =>
|
|
3511
|
+
req.on("error", () => resolve12([]));
|
|
3461
3512
|
req.setTimeout(2e3, () => {
|
|
3462
3513
|
req.destroy();
|
|
3463
|
-
|
|
3514
|
+
resolve12([]);
|
|
3464
3515
|
});
|
|
3465
3516
|
});
|
|
3466
3517
|
}
|
|
@@ -3500,7 +3551,7 @@ var DaemonCdpManager = class {
|
|
|
3500
3551
|
}
|
|
3501
3552
|
}
|
|
3502
3553
|
findTargetOnPort(port) {
|
|
3503
|
-
return new Promise((
|
|
3554
|
+
return new Promise((resolve12) => {
|
|
3504
3555
|
const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
|
|
3505
3556
|
let data = "";
|
|
3506
3557
|
res.on("data", (chunk) => data += chunk.toString());
|
|
@@ -3511,7 +3562,7 @@ var DaemonCdpManager = class {
|
|
|
3511
3562
|
(t) => (t.type === "page" || t.type === "browser" || t.type === "Page") && t.webSocketDebuggerUrl
|
|
3512
3563
|
);
|
|
3513
3564
|
if (pages.length === 0) {
|
|
3514
|
-
|
|
3565
|
+
resolve12(targets.find((t) => t.webSocketDebuggerUrl) || null);
|
|
3515
3566
|
return;
|
|
3516
3567
|
}
|
|
3517
3568
|
const mainPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
|
|
@@ -3521,24 +3572,24 @@ var DaemonCdpManager = class {
|
|
|
3521
3572
|
const specific = list.find((t) => t.id === this._targetId);
|
|
3522
3573
|
if (specific) {
|
|
3523
3574
|
this._pageTitle = specific.title || "";
|
|
3524
|
-
|
|
3575
|
+
resolve12(specific);
|
|
3525
3576
|
} else {
|
|
3526
3577
|
this.log(`[CDP] Target ${this._targetId} not found in page list`);
|
|
3527
|
-
|
|
3578
|
+
resolve12(null);
|
|
3528
3579
|
}
|
|
3529
3580
|
return;
|
|
3530
3581
|
}
|
|
3531
3582
|
this._pageTitle = list[0]?.title || "";
|
|
3532
|
-
|
|
3583
|
+
resolve12(list[0]);
|
|
3533
3584
|
} catch {
|
|
3534
|
-
|
|
3585
|
+
resolve12(null);
|
|
3535
3586
|
}
|
|
3536
3587
|
});
|
|
3537
3588
|
});
|
|
3538
|
-
req.on("error", () =>
|
|
3589
|
+
req.on("error", () => resolve12(null));
|
|
3539
3590
|
req.setTimeout(2e3, () => {
|
|
3540
3591
|
req.destroy();
|
|
3541
|
-
|
|
3592
|
+
resolve12(null);
|
|
3542
3593
|
});
|
|
3543
3594
|
});
|
|
3544
3595
|
}
|
|
@@ -3549,7 +3600,7 @@ var DaemonCdpManager = class {
|
|
|
3549
3600
|
this.extensionProviders = providers;
|
|
3550
3601
|
}
|
|
3551
3602
|
connectToTarget(wsUrl) {
|
|
3552
|
-
return new Promise((
|
|
3603
|
+
return new Promise((resolve12) => {
|
|
3553
3604
|
this.ws = new WebSocket(wsUrl);
|
|
3554
3605
|
this.ws.on("open", async () => {
|
|
3555
3606
|
this._connected = true;
|
|
@@ -3559,17 +3610,17 @@ var DaemonCdpManager = class {
|
|
|
3559
3610
|
}
|
|
3560
3611
|
this.connectBrowserWs().catch(() => {
|
|
3561
3612
|
});
|
|
3562
|
-
|
|
3613
|
+
resolve12(true);
|
|
3563
3614
|
});
|
|
3564
3615
|
this.ws.on("message", (data) => {
|
|
3565
3616
|
try {
|
|
3566
3617
|
const msg = JSON.parse(data.toString());
|
|
3567
3618
|
if (msg.id && this.pending.has(msg.id)) {
|
|
3568
|
-
const { resolve:
|
|
3619
|
+
const { resolve: resolve13, reject } = this.pending.get(msg.id);
|
|
3569
3620
|
this.pending.delete(msg.id);
|
|
3570
3621
|
this.failureCount = 0;
|
|
3571
3622
|
if (msg.error) reject(new Error(msg.error.message));
|
|
3572
|
-
else
|
|
3623
|
+
else resolve13(msg.result);
|
|
3573
3624
|
} else if (msg.method === "Runtime.executionContextCreated") {
|
|
3574
3625
|
this.contexts.add(msg.params.context.id);
|
|
3575
3626
|
} else if (msg.method === "Runtime.executionContextDestroyed") {
|
|
@@ -3592,7 +3643,7 @@ var DaemonCdpManager = class {
|
|
|
3592
3643
|
this.ws.on("error", (err) => {
|
|
3593
3644
|
this.log(`[CDP] WebSocket error: ${err.message}`);
|
|
3594
3645
|
this._connected = false;
|
|
3595
|
-
|
|
3646
|
+
resolve12(false);
|
|
3596
3647
|
});
|
|
3597
3648
|
});
|
|
3598
3649
|
}
|
|
@@ -3606,7 +3657,7 @@ var DaemonCdpManager = class {
|
|
|
3606
3657
|
return;
|
|
3607
3658
|
}
|
|
3608
3659
|
this.log(`[CDP] Connecting browser WS for target discovery...`);
|
|
3609
|
-
await new Promise((
|
|
3660
|
+
await new Promise((resolve12, reject) => {
|
|
3610
3661
|
this.browserWs = new WebSocket(browserWsUrl);
|
|
3611
3662
|
this.browserWs.on("open", async () => {
|
|
3612
3663
|
this._browserConnected = true;
|
|
@@ -3616,16 +3667,16 @@ var DaemonCdpManager = class {
|
|
|
3616
3667
|
} catch (e) {
|
|
3617
3668
|
this.log(`[CDP] setDiscoverTargets failed: ${e.message}`);
|
|
3618
3669
|
}
|
|
3619
|
-
|
|
3670
|
+
resolve12();
|
|
3620
3671
|
});
|
|
3621
3672
|
this.browserWs.on("message", (data) => {
|
|
3622
3673
|
try {
|
|
3623
3674
|
const msg = JSON.parse(data.toString());
|
|
3624
3675
|
if (msg.id && this.browserPending.has(msg.id)) {
|
|
3625
|
-
const { resolve:
|
|
3676
|
+
const { resolve: resolve13, reject: reject2 } = this.browserPending.get(msg.id);
|
|
3626
3677
|
this.browserPending.delete(msg.id);
|
|
3627
3678
|
if (msg.error) reject2(new Error(msg.error.message));
|
|
3628
|
-
else
|
|
3679
|
+
else resolve13(msg.result);
|
|
3629
3680
|
}
|
|
3630
3681
|
} catch {
|
|
3631
3682
|
}
|
|
@@ -3645,31 +3696,31 @@ var DaemonCdpManager = class {
|
|
|
3645
3696
|
}
|
|
3646
3697
|
}
|
|
3647
3698
|
getBrowserWsUrl() {
|
|
3648
|
-
return new Promise((
|
|
3699
|
+
return new Promise((resolve12) => {
|
|
3649
3700
|
const req = http.get(`http://127.0.0.1:${this.port}/json/version`, (res) => {
|
|
3650
3701
|
let data = "";
|
|
3651
3702
|
res.on("data", (chunk) => data += chunk.toString());
|
|
3652
3703
|
res.on("end", () => {
|
|
3653
3704
|
try {
|
|
3654
3705
|
const info = JSON.parse(data);
|
|
3655
|
-
|
|
3706
|
+
resolve12(info.webSocketDebuggerUrl || null);
|
|
3656
3707
|
} catch {
|
|
3657
|
-
|
|
3708
|
+
resolve12(null);
|
|
3658
3709
|
}
|
|
3659
3710
|
});
|
|
3660
3711
|
});
|
|
3661
|
-
req.on("error", () =>
|
|
3712
|
+
req.on("error", () => resolve12(null));
|
|
3662
3713
|
req.setTimeout(3e3, () => {
|
|
3663
3714
|
req.destroy();
|
|
3664
|
-
|
|
3715
|
+
resolve12(null);
|
|
3665
3716
|
});
|
|
3666
3717
|
});
|
|
3667
3718
|
}
|
|
3668
3719
|
sendBrowser(method, params = {}, timeoutMs = 15e3) {
|
|
3669
|
-
return new Promise((
|
|
3720
|
+
return new Promise((resolve12, reject) => {
|
|
3670
3721
|
if (!this.browserWs || !this._browserConnected) return reject(new Error("Browser WS not connected"));
|
|
3671
3722
|
const id = this.browserMsgId++;
|
|
3672
|
-
this.browserPending.set(id, { resolve:
|
|
3723
|
+
this.browserPending.set(id, { resolve: resolve12, reject });
|
|
3673
3724
|
this.browserWs.send(JSON.stringify({ id, method, params }));
|
|
3674
3725
|
setTimeout(() => {
|
|
3675
3726
|
if (this.browserPending.has(id)) {
|
|
@@ -3709,11 +3760,11 @@ var DaemonCdpManager = class {
|
|
|
3709
3760
|
}
|
|
3710
3761
|
// ─── CDP Protocol ────────────────────────────────────────
|
|
3711
3762
|
sendInternal(method, params = {}, timeoutMs = 15e3) {
|
|
3712
|
-
return new Promise((
|
|
3763
|
+
return new Promise((resolve12, reject) => {
|
|
3713
3764
|
if (!this.ws || !this._connected) return reject(new Error("CDP not connected"));
|
|
3714
3765
|
if (this.ws.readyState !== WebSocket.OPEN) return reject(new Error("WebSocket not open"));
|
|
3715
3766
|
const id = this.msgId++;
|
|
3716
|
-
this.pending.set(id, { resolve:
|
|
3767
|
+
this.pending.set(id, { resolve: resolve12, reject });
|
|
3717
3768
|
this.ws.send(JSON.stringify({ id, method, params }));
|
|
3718
3769
|
setTimeout(() => {
|
|
3719
3770
|
if (this.pending.has(id)) {
|
|
@@ -3962,7 +4013,7 @@ var DaemonCdpManager = class {
|
|
|
3962
4013
|
const browserWs = this.browserWs;
|
|
3963
4014
|
let msgId = this.browserMsgId;
|
|
3964
4015
|
const sendWs = (method, params = {}, sessionId) => {
|
|
3965
|
-
return new Promise((
|
|
4016
|
+
return new Promise((resolve12, reject) => {
|
|
3966
4017
|
const mid = msgId++;
|
|
3967
4018
|
this.browserMsgId = msgId;
|
|
3968
4019
|
const handler = (raw) => {
|
|
@@ -3971,7 +4022,7 @@ var DaemonCdpManager = class {
|
|
|
3971
4022
|
if (msg.id === mid) {
|
|
3972
4023
|
browserWs.removeListener("message", handler);
|
|
3973
4024
|
if (msg.error) reject(new Error(msg.error.message || JSON.stringify(msg.error)));
|
|
3974
|
-
else
|
|
4025
|
+
else resolve12(msg.result);
|
|
3975
4026
|
}
|
|
3976
4027
|
} catch {
|
|
3977
4028
|
}
|
|
@@ -4162,14 +4213,14 @@ var DaemonCdpManager = class {
|
|
|
4162
4213
|
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
|
4163
4214
|
throw new Error("CDP not connected");
|
|
4164
4215
|
}
|
|
4165
|
-
return new Promise((
|
|
4216
|
+
return new Promise((resolve12, reject) => {
|
|
4166
4217
|
const id = getNextId();
|
|
4167
4218
|
pendingMap.set(id, {
|
|
4168
4219
|
resolve: (result) => {
|
|
4169
4220
|
if (result?.result?.subtype === "error") {
|
|
4170
4221
|
reject(new Error(result.result.description));
|
|
4171
4222
|
} else {
|
|
4172
|
-
|
|
4223
|
+
resolve12(result?.result?.value);
|
|
4173
4224
|
}
|
|
4174
4225
|
},
|
|
4175
4226
|
reject
|
|
@@ -4201,10 +4252,10 @@ var DaemonCdpManager = class {
|
|
|
4201
4252
|
throw new Error("CDP not connected");
|
|
4202
4253
|
}
|
|
4203
4254
|
const sendViaSession = (method, params = {}) => {
|
|
4204
|
-
return new Promise((
|
|
4255
|
+
return new Promise((resolve12, reject) => {
|
|
4205
4256
|
const pendingMap = this._browserConnected ? this.browserPending : this.pending;
|
|
4206
4257
|
const id = this._browserConnected ? this.browserMsgId++ : this.msgId++;
|
|
4207
|
-
pendingMap.set(id, { resolve:
|
|
4258
|
+
pendingMap.set(id, { resolve: resolve12, reject });
|
|
4208
4259
|
ws.send(JSON.stringify({ id, sessionId, method, params }));
|
|
4209
4260
|
setTimeout(() => {
|
|
4210
4261
|
if (pendingMap.has(id)) {
|
|
@@ -4832,15 +4883,76 @@ function normalizeControlValue(value) {
|
|
|
4832
4883
|
|
|
4833
4884
|
// src/config/chat-history.ts
|
|
4834
4885
|
import * as fs3 from "fs";
|
|
4835
|
-
import * as
|
|
4886
|
+
import * as path7 from "path";
|
|
4836
4887
|
import * as os5 from "os";
|
|
4837
|
-
var HISTORY_DIR =
|
|
4888
|
+
var HISTORY_DIR = path7.join(os5.homedir(), ".adhdev", "history");
|
|
4838
4889
|
var RETAIN_DAYS = 30;
|
|
4890
|
+
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;
|
|
4891
|
+
function normalizeHistoryComparable(text) {
|
|
4892
|
+
return String(text || "").replace(/\s+/g, " ").trim();
|
|
4893
|
+
}
|
|
4894
|
+
function cleanupHistoryContent(agentType, role, content) {
|
|
4895
|
+
let value = String(content || "").replace(/\r\n/g, "\n").trim();
|
|
4896
|
+
if (!value) return "";
|
|
4897
|
+
if (agentType === "codex-cli" && role === "assistant") {
|
|
4898
|
+
const filtered = value.split("\n").filter((line) => !CODEX_STARTER_PROMPT_RE.test(line.trim())).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
4899
|
+
value = filtered;
|
|
4900
|
+
}
|
|
4901
|
+
return value;
|
|
4902
|
+
}
|
|
4903
|
+
function buildHistoryMessageHash(agentType, message) {
|
|
4904
|
+
if (message.historyDedupKey) return message.historyDedupKey;
|
|
4905
|
+
const cleaned = cleanupHistoryContent(agentType, message.role, message.content);
|
|
4906
|
+
return `${message.kind || "standard"}:${message.role}:${message.receivedAt || 0}:${normalizeHistoryComparable(cleaned)}`;
|
|
4907
|
+
}
|
|
4908
|
+
function buildHistoryMessageSignature(agentType, message) {
|
|
4909
|
+
const cleaned = cleanupHistoryContent(agentType, message.role, message.content);
|
|
4910
|
+
return `${message.kind || "standard"}:${message.role}:${normalizeHistoryComparable(cleaned)}`;
|
|
4911
|
+
}
|
|
4912
|
+
function isAdjacentHistoryDuplicate(agentType, previous, next) {
|
|
4913
|
+
if (!previous || !next) return false;
|
|
4914
|
+
return buildHistoryMessageSignature(agentType, previous) === buildHistoryMessageSignature(agentType, next);
|
|
4915
|
+
}
|
|
4916
|
+
function collapseReplayAssistantTurns(agentType, messages) {
|
|
4917
|
+
if (agentType !== "codex-cli") return messages;
|
|
4918
|
+
const collapsed = [];
|
|
4919
|
+
let sawAssistantSinceLastUser = false;
|
|
4920
|
+
for (const message of messages) {
|
|
4921
|
+
if (message.role === "user") {
|
|
4922
|
+
sawAssistantSinceLastUser = false;
|
|
4923
|
+
collapsed.push(message);
|
|
4924
|
+
continue;
|
|
4925
|
+
}
|
|
4926
|
+
if (message.role === "assistant") {
|
|
4927
|
+
if (sawAssistantSinceLastUser) continue;
|
|
4928
|
+
sawAssistantSinceLastUser = true;
|
|
4929
|
+
collapsed.push(message);
|
|
4930
|
+
continue;
|
|
4931
|
+
}
|
|
4932
|
+
collapsed.push(message);
|
|
4933
|
+
}
|
|
4934
|
+
return collapsed;
|
|
4935
|
+
}
|
|
4936
|
+
function sanitizeHistoryMessage(agentType, message) {
|
|
4937
|
+
if (!message || message.role !== "user" && message.role !== "assistant" && message.role !== "system") {
|
|
4938
|
+
return null;
|
|
4939
|
+
}
|
|
4940
|
+
const content = cleanupHistoryContent(agentType, message.role, message.content);
|
|
4941
|
+
if (!content) return null;
|
|
4942
|
+
return {
|
|
4943
|
+
...message,
|
|
4944
|
+
content
|
|
4945
|
+
};
|
|
4946
|
+
}
|
|
4839
4947
|
var ChatHistoryWriter = class {
|
|
4840
4948
|
/** Last seen message count per agent (deduplication) */
|
|
4841
4949
|
lastSeenCounts = /* @__PURE__ */ new Map();
|
|
4842
4950
|
/** Last seen message hash per agent (deduplication) */
|
|
4843
4951
|
lastSeenHashes = /* @__PURE__ */ new Map();
|
|
4952
|
+
/** Last appended normalized message signature per agent/session */
|
|
4953
|
+
lastSeenSignatures = /* @__PURE__ */ new Map();
|
|
4954
|
+
/** Last appended normalized non-system turn signature per agent/session */
|
|
4955
|
+
lastSeenTurnSignatures = /* @__PURE__ */ new Map();
|
|
4844
4956
|
rotated = false;
|
|
4845
4957
|
/**
|
|
4846
4958
|
* Append new messages to history
|
|
@@ -4862,14 +4974,36 @@ var ChatHistoryWriter = class {
|
|
|
4862
4974
|
}
|
|
4863
4975
|
const newMessages = [];
|
|
4864
4976
|
for (const msg of messages) {
|
|
4865
|
-
const
|
|
4977
|
+
const role = msg.role;
|
|
4978
|
+
if (role !== "user" && role !== "assistant" && role !== "system") continue;
|
|
4979
|
+
const content = cleanupHistoryContent(agentType, role, msg.content || "");
|
|
4980
|
+
if (!content) continue;
|
|
4981
|
+
const receivedAt = msg.receivedAt || Date.now();
|
|
4982
|
+
const hash = buildHistoryMessageHash(agentType, {
|
|
4983
|
+
role,
|
|
4984
|
+
content,
|
|
4985
|
+
receivedAt,
|
|
4986
|
+
kind: typeof msg.kind === "string" ? msg.kind : void 0,
|
|
4987
|
+
historyDedupKey: msg.historyDedupKey
|
|
4988
|
+
});
|
|
4989
|
+
const signature = buildHistoryMessageSignature(agentType, {
|
|
4990
|
+
role,
|
|
4991
|
+
content,
|
|
4992
|
+
kind: typeof msg.kind === "string" ? msg.kind : void 0
|
|
4993
|
+
});
|
|
4866
4994
|
if (seenHashes.has(hash)) continue;
|
|
4995
|
+
if (this.lastSeenSignatures.get(dedupKey) === signature) continue;
|
|
4996
|
+
if (role !== "system" && this.lastSeenTurnSignatures.get(dedupKey) === signature) continue;
|
|
4867
4997
|
seenHashes.add(hash);
|
|
4998
|
+
this.lastSeenSignatures.set(dedupKey, signature);
|
|
4999
|
+
if (role !== "system") {
|
|
5000
|
+
this.lastSeenTurnSignatures.set(dedupKey, signature);
|
|
5001
|
+
}
|
|
4868
5002
|
newMessages.push({
|
|
4869
|
-
ts: new Date(
|
|
4870
|
-
receivedAt
|
|
4871
|
-
role
|
|
4872
|
-
content
|
|
5003
|
+
ts: new Date(receivedAt).toISOString(),
|
|
5004
|
+
receivedAt,
|
|
5005
|
+
role,
|
|
5006
|
+
content,
|
|
4873
5007
|
kind: typeof msg.kind === "string" ? msg.kind : void 0,
|
|
4874
5008
|
senderName: typeof msg.senderName === "string" ? msg.senderName : void 0,
|
|
4875
5009
|
agent: agentType,
|
|
@@ -4879,16 +5013,18 @@ var ChatHistoryWriter = class {
|
|
|
4879
5013
|
});
|
|
4880
5014
|
}
|
|
4881
5015
|
if (newMessages.length === 0) return;
|
|
4882
|
-
const dir =
|
|
5016
|
+
const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
|
|
4883
5017
|
fs3.mkdirSync(dir, { recursive: true });
|
|
4884
5018
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4885
5019
|
const filePrefix = effectiveHistoryKey ? `${this.sanitize(effectiveHistoryKey)}_` : "";
|
|
4886
|
-
const filePath =
|
|
5020
|
+
const filePath = path7.join(dir, `${filePrefix}${date}.jsonl`);
|
|
4887
5021
|
const lines = newMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
4888
5022
|
fs3.appendFileSync(filePath, lines, "utf-8");
|
|
4889
5023
|
const prevCount = this.lastSeenCounts.get(dedupKey) || 0;
|
|
4890
5024
|
if (messages.length < prevCount * 0.5 && prevCount > 3) {
|
|
4891
5025
|
seenHashes.clear();
|
|
5026
|
+
this.lastSeenSignatures.delete(dedupKey);
|
|
5027
|
+
this.lastSeenTurnSignatures.delete(dedupKey);
|
|
4892
5028
|
for (const msg of messages) {
|
|
4893
5029
|
seenHashes.add(msg.historyDedupKey || `${msg.kind || "standard"}:${msg.role}:${(msg.content || "").slice(0, 50)}`);
|
|
4894
5030
|
}
|
|
@@ -4902,6 +5038,54 @@ var ChatHistoryWriter = class {
|
|
|
4902
5038
|
} catch {
|
|
4903
5039
|
}
|
|
4904
5040
|
}
|
|
5041
|
+
seedSessionHistory(agentType, messages = [], historySessionId, instanceId) {
|
|
5042
|
+
const effectiveHistoryKey = historySessionId || instanceId;
|
|
5043
|
+
const dedupKey = effectiveHistoryKey ? `${agentType}:${effectiveHistoryKey}` : agentType;
|
|
5044
|
+
const seenHashes = /* @__PURE__ */ new Set();
|
|
5045
|
+
for (const raw of messages) {
|
|
5046
|
+
const role = raw?.role;
|
|
5047
|
+
if (role !== "user" && role !== "assistant" && role !== "system") continue;
|
|
5048
|
+
const content = cleanupHistoryContent(agentType, role, raw?.content || "");
|
|
5049
|
+
if (!content) continue;
|
|
5050
|
+
seenHashes.add(buildHistoryMessageHash(agentType, {
|
|
5051
|
+
role,
|
|
5052
|
+
content,
|
|
5053
|
+
receivedAt: raw?.receivedAt || 0,
|
|
5054
|
+
kind: typeof raw?.kind === "string" ? raw.kind : void 0,
|
|
5055
|
+
historyDedupKey: raw?.historyDedupKey
|
|
5056
|
+
}));
|
|
5057
|
+
}
|
|
5058
|
+
this.lastSeenHashes.set(dedupKey, seenHashes);
|
|
5059
|
+
this.lastSeenCounts.set(dedupKey, messages.length);
|
|
5060
|
+
const lastMessage = [...messages].reverse().find((raw) => {
|
|
5061
|
+
const role = raw?.role;
|
|
5062
|
+
if (role !== "user" && role !== "assistant" && role !== "system") return false;
|
|
5063
|
+
return !!cleanupHistoryContent(agentType, role, raw?.content || "");
|
|
5064
|
+
});
|
|
5065
|
+
const lastTurnMessage = [...messages].reverse().find((raw) => {
|
|
5066
|
+
const role = raw?.role;
|
|
5067
|
+
if (role !== "user" && role !== "assistant") return false;
|
|
5068
|
+
return !!cleanupHistoryContent(agentType, role, raw?.content || "");
|
|
5069
|
+
});
|
|
5070
|
+
if (lastMessage) {
|
|
5071
|
+
this.lastSeenSignatures.set(dedupKey, buildHistoryMessageSignature(agentType, {
|
|
5072
|
+
role: lastMessage.role,
|
|
5073
|
+
content: lastMessage.content,
|
|
5074
|
+
kind: typeof lastMessage.kind === "string" ? lastMessage.kind : void 0
|
|
5075
|
+
}));
|
|
5076
|
+
} else {
|
|
5077
|
+
this.lastSeenSignatures.delete(dedupKey);
|
|
5078
|
+
}
|
|
5079
|
+
if (lastTurnMessage) {
|
|
5080
|
+
this.lastSeenTurnSignatures.set(dedupKey, buildHistoryMessageSignature(agentType, {
|
|
5081
|
+
role: lastTurnMessage.role,
|
|
5082
|
+
content: lastTurnMessage.content,
|
|
5083
|
+
kind: typeof lastTurnMessage.kind === "string" ? lastTurnMessage.kind : void 0
|
|
5084
|
+
}));
|
|
5085
|
+
} else {
|
|
5086
|
+
this.lastSeenTurnSignatures.delete(dedupKey);
|
|
5087
|
+
}
|
|
5088
|
+
}
|
|
4905
5089
|
appendSystemMarker(agentType, content, options = {}) {
|
|
4906
5090
|
this.appendNewMessages(
|
|
4907
5091
|
agentType,
|
|
@@ -4932,19 +5116,29 @@ var ChatHistoryWriter = class {
|
|
|
4932
5116
|
this.lastSeenHashes.set(toDedupKey, nextHashes);
|
|
4933
5117
|
this.lastSeenHashes.delete(fromDedupKey);
|
|
4934
5118
|
}
|
|
5119
|
+
const fromSignature = this.lastSeenSignatures.get(fromDedupKey);
|
|
5120
|
+
if (fromSignature) {
|
|
5121
|
+
this.lastSeenSignatures.set(toDedupKey, fromSignature);
|
|
5122
|
+
this.lastSeenSignatures.delete(fromDedupKey);
|
|
5123
|
+
}
|
|
5124
|
+
const fromTurnSignature = this.lastSeenTurnSignatures.get(fromDedupKey);
|
|
5125
|
+
if (fromTurnSignature) {
|
|
5126
|
+
this.lastSeenTurnSignatures.set(toDedupKey, fromTurnSignature);
|
|
5127
|
+
this.lastSeenTurnSignatures.delete(fromDedupKey);
|
|
5128
|
+
}
|
|
4935
5129
|
const fromCount = this.lastSeenCounts.get(fromDedupKey);
|
|
4936
5130
|
if (typeof fromCount === "number") {
|
|
4937
5131
|
this.lastSeenCounts.set(toDedupKey, Math.max(fromCount, this.lastSeenCounts.get(toDedupKey) || 0));
|
|
4938
5132
|
this.lastSeenCounts.delete(fromDedupKey);
|
|
4939
5133
|
}
|
|
4940
|
-
const dir =
|
|
5134
|
+
const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
|
|
4941
5135
|
if (!fs3.existsSync(dir)) return;
|
|
4942
5136
|
const fromPrefix = `${this.sanitize(fromId)}_`;
|
|
4943
5137
|
const toPrefix = `${this.sanitize(toId)}_`;
|
|
4944
5138
|
const files = fs3.readdirSync(dir).filter((file) => file.startsWith(fromPrefix) && file.endsWith(".jsonl"));
|
|
4945
5139
|
for (const file of files) {
|
|
4946
|
-
const sourcePath =
|
|
4947
|
-
const targetPath =
|
|
5140
|
+
const sourcePath = path7.join(dir, file);
|
|
5141
|
+
const targetPath = path7.join(dir, `${toPrefix}${file.slice(fromPrefix.length)}`);
|
|
4948
5142
|
const sourceLines = fs3.readFileSync(sourcePath, "utf-8").split("\n").filter(Boolean);
|
|
4949
5143
|
const rewritten = sourceLines.map((line) => {
|
|
4950
5144
|
try {
|
|
@@ -4973,10 +5167,61 @@ var ChatHistoryWriter = class {
|
|
|
4973
5167
|
} catch {
|
|
4974
5168
|
}
|
|
4975
5169
|
}
|
|
5170
|
+
compactHistorySession(agentType, historySessionId) {
|
|
5171
|
+
const sessionId = String(historySessionId || "").trim();
|
|
5172
|
+
if (!sessionId) return;
|
|
5173
|
+
try {
|
|
5174
|
+
const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
|
|
5175
|
+
if (!fs3.existsSync(dir)) return;
|
|
5176
|
+
const prefix = `${this.sanitize(sessionId)}_`;
|
|
5177
|
+
const files = fs3.readdirSync(dir).filter((file) => file.startsWith(prefix) && file.endsWith(".jsonl")).sort();
|
|
5178
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5179
|
+
for (const file of files) {
|
|
5180
|
+
const filePath = path7.join(dir, file);
|
|
5181
|
+
const lines = fs3.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
5182
|
+
const next = [];
|
|
5183
|
+
for (const line of lines) {
|
|
5184
|
+
let parsed = null;
|
|
5185
|
+
try {
|
|
5186
|
+
parsed = JSON.parse(line);
|
|
5187
|
+
} catch {
|
|
5188
|
+
parsed = null;
|
|
5189
|
+
}
|
|
5190
|
+
if (!parsed || parsed.historySessionId !== sessionId) continue;
|
|
5191
|
+
const sanitized = sanitizeHistoryMessage(agentType, parsed);
|
|
5192
|
+
if (!sanitized) continue;
|
|
5193
|
+
const hash = buildHistoryMessageHash(agentType, sanitized);
|
|
5194
|
+
if (seen.has(hash)) continue;
|
|
5195
|
+
seen.add(hash);
|
|
5196
|
+
next.push(sanitized);
|
|
5197
|
+
}
|
|
5198
|
+
next.sort((a, b) => a.receivedAt - b.receivedAt);
|
|
5199
|
+
const dedupedAdjacent = [];
|
|
5200
|
+
let lastTurn = null;
|
|
5201
|
+
for (const entry of next) {
|
|
5202
|
+
const previous = dedupedAdjacent[dedupedAdjacent.length - 1];
|
|
5203
|
+
if (isAdjacentHistoryDuplicate(agentType, previous, entry)) continue;
|
|
5204
|
+
if (entry.role !== "system" && isAdjacentHistoryDuplicate(agentType, lastTurn, entry)) continue;
|
|
5205
|
+
dedupedAdjacent.push(entry);
|
|
5206
|
+
if (entry.role !== "system") lastTurn = entry;
|
|
5207
|
+
}
|
|
5208
|
+
const collapsed = collapseReplayAssistantTurns(agentType, dedupedAdjacent);
|
|
5209
|
+
if (collapsed.length === 0) {
|
|
5210
|
+
fs3.unlinkSync(filePath);
|
|
5211
|
+
continue;
|
|
5212
|
+
}
|
|
5213
|
+
fs3.writeFileSync(filePath, `${collapsed.map((entry) => JSON.stringify(entry)).join("\n")}
|
|
5214
|
+
`, "utf-8");
|
|
5215
|
+
}
|
|
5216
|
+
} catch {
|
|
5217
|
+
}
|
|
5218
|
+
}
|
|
4976
5219
|
/** Called when agent session is explicitly changed */
|
|
4977
5220
|
onSessionChange(agentType) {
|
|
4978
5221
|
this.lastSeenHashes.delete(agentType);
|
|
4979
5222
|
this.lastSeenCounts.delete(agentType);
|
|
5223
|
+
this.lastSeenSignatures.delete(agentType);
|
|
5224
|
+
this.lastSeenTurnSignatures.delete(agentType);
|
|
4980
5225
|
}
|
|
4981
5226
|
/** Delete history files older than 30 days */
|
|
4982
5227
|
async rotateOldFiles() {
|
|
@@ -4985,10 +5230,10 @@ var ChatHistoryWriter = class {
|
|
|
4985
5230
|
const cutoff = Date.now() - RETAIN_DAYS * 24 * 60 * 60 * 1e3;
|
|
4986
5231
|
const agentDirs = fs3.readdirSync(HISTORY_DIR, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
4987
5232
|
for (const dir of agentDirs) {
|
|
4988
|
-
const dirPath =
|
|
5233
|
+
const dirPath = path7.join(HISTORY_DIR, dir.name);
|
|
4989
5234
|
const files = fs3.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl") || f.endsWith(".terminal.log"));
|
|
4990
5235
|
for (const file of files) {
|
|
4991
|
-
const filePath =
|
|
5236
|
+
const filePath = path7.join(dirPath, file);
|
|
4992
5237
|
const stat = fs3.statSync(filePath);
|
|
4993
5238
|
if (stat.mtimeMs < cutoff) {
|
|
4994
5239
|
fs3.unlinkSync(filePath);
|
|
@@ -5006,7 +5251,7 @@ var ChatHistoryWriter = class {
|
|
|
5006
5251
|
function readChatHistory(agentType, offset = 0, limit = 30, historySessionId) {
|
|
5007
5252
|
try {
|
|
5008
5253
|
const sanitized = agentType.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
5009
|
-
const dir =
|
|
5254
|
+
const dir = path7.join(HISTORY_DIR, sanitized);
|
|
5010
5255
|
if (!fs3.existsSync(dir)) return { messages: [], hasMore: false };
|
|
5011
5256
|
const sanitizedInstance = historySessionId?.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
5012
5257
|
const files = fs3.readdirSync(dir).filter((f) => {
|
|
@@ -5017,23 +5262,37 @@ function readChatHistory(agentType, offset = 0, limit = 30, historySessionId) {
|
|
|
5017
5262
|
return true;
|
|
5018
5263
|
}).sort().reverse();
|
|
5019
5264
|
const allMessages = [];
|
|
5020
|
-
const
|
|
5265
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5021
5266
|
for (const file of files) {
|
|
5022
|
-
|
|
5023
|
-
const filePath = path5.join(dir, file);
|
|
5267
|
+
const filePath = path7.join(dir, file);
|
|
5024
5268
|
const content = fs3.readFileSync(filePath, "utf-8");
|
|
5025
5269
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
5026
|
-
for (let i =
|
|
5027
|
-
if (allMessages.length >= needed) break;
|
|
5270
|
+
for (let i = 0; i < lines.length; i++) {
|
|
5028
5271
|
try {
|
|
5029
|
-
|
|
5272
|
+
const parsed = JSON.parse(lines[i]);
|
|
5273
|
+
const sanitizedMessage = sanitizeHistoryMessage(agentType, parsed);
|
|
5274
|
+
if (!sanitizedMessage) continue;
|
|
5275
|
+
const hash = buildHistoryMessageHash(agentType, sanitizedMessage);
|
|
5276
|
+
if (seen.has(hash)) continue;
|
|
5277
|
+
seen.add(hash);
|
|
5278
|
+
allMessages.push(sanitizedMessage);
|
|
5030
5279
|
} catch {
|
|
5031
5280
|
}
|
|
5032
5281
|
}
|
|
5033
5282
|
}
|
|
5034
|
-
|
|
5035
|
-
const
|
|
5036
|
-
|
|
5283
|
+
allMessages.sort((a, b) => a.receivedAt - b.receivedAt);
|
|
5284
|
+
const chronological = [];
|
|
5285
|
+
let lastTurn = null;
|
|
5286
|
+
for (const message of allMessages) {
|
|
5287
|
+
const previous = chronological[chronological.length - 1];
|
|
5288
|
+
if (isAdjacentHistoryDuplicate(agentType, previous, message)) continue;
|
|
5289
|
+
if (message.role !== "system" && isAdjacentHistoryDuplicate(agentType, lastTurn, message)) continue;
|
|
5290
|
+
chronological.push(message);
|
|
5291
|
+
if (message.role !== "system") lastTurn = message;
|
|
5292
|
+
}
|
|
5293
|
+
const collapsed = collapseReplayAssistantTurns(agentType, chronological);
|
|
5294
|
+
const sliced = collapsed.slice(offset, offset + limit);
|
|
5295
|
+
const hasMore = collapsed.length > offset + limit;
|
|
5037
5296
|
return { messages: sliced, hasMore };
|
|
5038
5297
|
} catch {
|
|
5039
5298
|
return { messages: [], hasMore: false };
|
|
@@ -5042,7 +5301,7 @@ function readChatHistory(agentType, offset = 0, limit = 30, historySessionId) {
|
|
|
5042
5301
|
function listSavedHistorySessions(agentType, options = {}) {
|
|
5043
5302
|
try {
|
|
5044
5303
|
const sanitized = agentType.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
5045
|
-
const dir =
|
|
5304
|
+
const dir = path7.join(HISTORY_DIR, sanitized);
|
|
5046
5305
|
if (!fs3.existsSync(dir)) return { sessions: [], hasMore: false };
|
|
5047
5306
|
const groupedFiles = /* @__PURE__ */ new Map();
|
|
5048
5307
|
const filePattern = /^([A-Za-z0-9_-]+)_\d{4}-\d{2}-\d{2}\.jsonl$/;
|
|
@@ -5063,7 +5322,7 @@ function listSavedHistorySessions(agentType, options = {}) {
|
|
|
5063
5322
|
let sessionTitle = "";
|
|
5064
5323
|
let preview = "";
|
|
5065
5324
|
for (const file of files.sort()) {
|
|
5066
|
-
const filePath =
|
|
5325
|
+
const filePath = path7.join(dir, file);
|
|
5067
5326
|
const content = fs3.readFileSync(filePath, "utf-8");
|
|
5068
5327
|
const lines = content.split("\n").filter(Boolean);
|
|
5069
5328
|
for (const line of lines) {
|
|
@@ -6887,6 +7146,46 @@ function didProviderConfirmSend(result) {
|
|
|
6887
7146
|
if (!parsed || typeof parsed !== "object") return false;
|
|
6888
7147
|
return parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true;
|
|
6889
7148
|
}
|
|
7149
|
+
async function readExtensionChatState(h) {
|
|
7150
|
+
try {
|
|
7151
|
+
const evalResult = await h.evaluateProviderScript("readChat", void 0, 5e4);
|
|
7152
|
+
if (!evalResult?.result) return null;
|
|
7153
|
+
const parsed = parseMaybeJson(evalResult.result);
|
|
7154
|
+
return parsed && typeof parsed === "object" ? parsed : null;
|
|
7155
|
+
} catch {
|
|
7156
|
+
return null;
|
|
7157
|
+
}
|
|
7158
|
+
}
|
|
7159
|
+
function getStateMessageCount(state) {
|
|
7160
|
+
return Array.isArray(state?.messages) ? state.messages.length : 0;
|
|
7161
|
+
}
|
|
7162
|
+
function getStateLastSignature(state) {
|
|
7163
|
+
const messages = Array.isArray(state?.messages) ? state.messages : [];
|
|
7164
|
+
const last = messages[messages.length - 1];
|
|
7165
|
+
if (!last) return "";
|
|
7166
|
+
return `${last.role || ""}:${String(last.content || "").replace(/\s+/g, " ").trim()}`;
|
|
7167
|
+
}
|
|
7168
|
+
async function getStableExtensionBaseline(h) {
|
|
7169
|
+
const first = await readExtensionChatState(h);
|
|
7170
|
+
if (getStateMessageCount(first) > 0 || getStateLastSignature(first)) return first;
|
|
7171
|
+
await new Promise((resolve12) => setTimeout(resolve12, 150));
|
|
7172
|
+
const second = await readExtensionChatState(h);
|
|
7173
|
+
return getStateMessageCount(second) >= getStateMessageCount(first) ? second : first;
|
|
7174
|
+
}
|
|
7175
|
+
async function verifyExtensionSendObserved(h, before) {
|
|
7176
|
+
const beforeCount = getStateMessageCount(before);
|
|
7177
|
+
const beforeSignature = getStateLastSignature(before);
|
|
7178
|
+
for (let attempt = 0; attempt < 12; attempt += 1) {
|
|
7179
|
+
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
7180
|
+
const state = await readExtensionChatState(h);
|
|
7181
|
+
if (state?.status === "waiting_approval") return true;
|
|
7182
|
+
const afterCount = getStateMessageCount(state);
|
|
7183
|
+
const afterSignature = getStateLastSignature(state);
|
|
7184
|
+
if (afterCount > beforeCount) return true;
|
|
7185
|
+
if (afterSignature && afterSignature !== beforeSignature) return true;
|
|
7186
|
+
}
|
|
7187
|
+
return false;
|
|
7188
|
+
}
|
|
6890
7189
|
async function handleChatHistory(h, args) {
|
|
6891
7190
|
const { agentType, offset, limit } = args;
|
|
6892
7191
|
const historySessionId = getHistorySessionId(h, args);
|
|
@@ -7067,12 +7366,17 @@ async function handleSendChat(h, args) {
|
|
|
7067
7366
|
if (isExtensionTransport(transport)) {
|
|
7068
7367
|
_log(`Extension: ${provider?.type || "unknown_extension"}`);
|
|
7069
7368
|
try {
|
|
7369
|
+
const beforeState = await getStableExtensionBaseline(h);
|
|
7070
7370
|
const evalResult = await h.evaluateProviderScript("sendMessage", { message: text }, 3e4);
|
|
7071
7371
|
if (evalResult?.result) {
|
|
7072
7372
|
const parsed = parseMaybeJson(evalResult.result);
|
|
7073
7373
|
if (didProviderConfirmSend(parsed)) {
|
|
7074
|
-
|
|
7075
|
-
|
|
7374
|
+
const observed = await verifyExtensionSendObserved(h, beforeState);
|
|
7375
|
+
if (observed) {
|
|
7376
|
+
_log(`Extension script sent OK`);
|
|
7377
|
+
return _logSendSuccess("extension-script");
|
|
7378
|
+
}
|
|
7379
|
+
_log(`Extension script reported send but no chat-state change was observed`);
|
|
7076
7380
|
}
|
|
7077
7381
|
if (parsed?.needsTypeAndSend) {
|
|
7078
7382
|
_log(`Extension needsTypeAndSend \u2192 AgentStreamManager`);
|
|
@@ -7238,6 +7542,14 @@ async function handleListChats(h, args) {
|
|
|
7238
7542
|
} catch {
|
|
7239
7543
|
}
|
|
7240
7544
|
}
|
|
7545
|
+
if (parsed?.sessions && Array.isArray(parsed.sessions)) {
|
|
7546
|
+
LOG.info("Command", `[list_chats] OK: ${parsed.sessions.length} chats`);
|
|
7547
|
+
return { success: true, chats: parsed.sessions };
|
|
7548
|
+
}
|
|
7549
|
+
if (parsed?.chats && Array.isArray(parsed.chats)) {
|
|
7550
|
+
LOG.info("Command", `[list_chats] OK: ${parsed.chats.length} chats`);
|
|
7551
|
+
return { success: true, chats: parsed.chats };
|
|
7552
|
+
}
|
|
7241
7553
|
if (Array.isArray(parsed)) {
|
|
7242
7554
|
LOG.info("Command", `[list_chats] OK: ${parsed.length} chats`);
|
|
7243
7555
|
return { success: true, chats: parsed };
|
|
@@ -7307,7 +7619,13 @@ async function handleSwitchChat(h, args) {
|
|
|
7307
7619
|
} catch (e) {
|
|
7308
7620
|
return { success: false, error: `webviewSwitchSession failed: ${e.message}` };
|
|
7309
7621
|
}
|
|
7310
|
-
const
|
|
7622
|
+
const switchParams = {
|
|
7623
|
+
sessionId,
|
|
7624
|
+
title: sessionId,
|
|
7625
|
+
id: sessionId,
|
|
7626
|
+
SESSION_ID: JSON.stringify(sessionId)
|
|
7627
|
+
};
|
|
7628
|
+
const script = h.getProviderScript("switchSession", switchParams) || h.getProviderScript("switch_session", switchParams);
|
|
7311
7629
|
if (!script) return { success: false, error: "switch_session script not available" };
|
|
7312
7630
|
try {
|
|
7313
7631
|
const raw = await cdp.evaluate(script, 15e3);
|
|
@@ -7386,8 +7704,8 @@ async function handleSetMode(h, args) {
|
|
|
7386
7704
|
const adapter = getTargetedCliAdapter(h, args, provider?.type);
|
|
7387
7705
|
if (adapter) {
|
|
7388
7706
|
const acpInstance = adapter._acpInstance;
|
|
7389
|
-
if (acpInstance && typeof acpInstance.
|
|
7390
|
-
acpInstance.
|
|
7707
|
+
if (acpInstance && typeof acpInstance.setMode === "function") {
|
|
7708
|
+
await acpInstance.setMode(mode);
|
|
7391
7709
|
return { success: true, mode };
|
|
7392
7710
|
}
|
|
7393
7711
|
}
|
|
@@ -7444,9 +7762,9 @@ async function handleChangeModel(h, args) {
|
|
|
7444
7762
|
LOG.info("Command", `[change_model] ACP adapter found: ${!!adapter}, type=${adapter?.cliType}, hasAcpInstance=${!!adapter?._acpInstance}`);
|
|
7445
7763
|
if (adapter) {
|
|
7446
7764
|
const acpInstance = adapter._acpInstance;
|
|
7447
|
-
if (acpInstance && typeof acpInstance.
|
|
7448
|
-
acpInstance.
|
|
7449
|
-
LOG.info("Command", `[change_model]
|
|
7765
|
+
if (acpInstance && typeof acpInstance.setConfigOption === "function") {
|
|
7766
|
+
await acpInstance.setConfigOption("model", model);
|
|
7767
|
+
LOG.info("Command", `[change_model] Updated ACP model to ${model}`);
|
|
7450
7768
|
return { success: true, model };
|
|
7451
7769
|
}
|
|
7452
7770
|
}
|
|
@@ -7563,9 +7881,21 @@ async function handleResolveAction(h, args) {
|
|
|
7563
7881
|
return { success: true, buttonIndex, button: buttons[buttonIndex] ?? button };
|
|
7564
7882
|
}
|
|
7565
7883
|
if (isExtensionTransport(transport) && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
|
|
7566
|
-
const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action);
|
|
7884
|
+
const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action, button);
|
|
7567
7885
|
return { success: ok };
|
|
7568
7886
|
}
|
|
7887
|
+
if (transport === "acp") {
|
|
7888
|
+
const adapter = getTargetedCliAdapter(h, args, provider?.type);
|
|
7889
|
+
const acpInstance = adapter?._acpInstance;
|
|
7890
|
+
if (!acpInstance) return { success: false, error: "ACP instance not found" };
|
|
7891
|
+
try {
|
|
7892
|
+
await acpInstance.resolvePermission(action === "approve" || action === "accept" || action === "always");
|
|
7893
|
+
LOG.info("Command", `[resolveAction] ACP \u2192 ${action}`);
|
|
7894
|
+
return { success: true, action };
|
|
7895
|
+
} catch (e) {
|
|
7896
|
+
return { success: false, error: e?.message || "ACP resolve action failed" };
|
|
7897
|
+
}
|
|
7898
|
+
}
|
|
7569
7899
|
if (provider?.scripts?.webviewResolveAction || provider?.scripts?.webview_resolve_action) {
|
|
7570
7900
|
const script = h.getProviderScript("webviewResolveAction", { action, button, buttonText: button }) || h.getProviderScript("webview_resolve_action", { action, button, buttonText: button });
|
|
7571
7901
|
if (script) {
|
|
@@ -7644,7 +7974,7 @@ async function handleResolveAction(h, args) {
|
|
|
7644
7974
|
|
|
7645
7975
|
// src/commands/cdp-commands.ts
|
|
7646
7976
|
import * as fs4 from "fs";
|
|
7647
|
-
import * as
|
|
7977
|
+
import * as path8 from "path";
|
|
7648
7978
|
import * as os6 from "os";
|
|
7649
7979
|
var KEY_TO_VK = {
|
|
7650
7980
|
Backspace: 8,
|
|
@@ -7896,25 +8226,25 @@ function resolveSafePath(requestedPath) {
|
|
|
7896
8226
|
const inputPath = rawPath || ".";
|
|
7897
8227
|
const home = os6.homedir();
|
|
7898
8228
|
if (inputPath.startsWith("~")) {
|
|
7899
|
-
return
|
|
8229
|
+
return path8.resolve(path8.join(home, inputPath.slice(1)));
|
|
7900
8230
|
}
|
|
7901
8231
|
if (process.platform === "win32") {
|
|
7902
8232
|
const normalized = normalizeWindowsRequestedPath(inputPath);
|
|
7903
|
-
if (
|
|
7904
|
-
return
|
|
8233
|
+
if (path8.win32.isAbsolute(normalized)) {
|
|
8234
|
+
return path8.win32.normalize(normalized);
|
|
7905
8235
|
}
|
|
7906
|
-
return
|
|
8236
|
+
return path8.win32.resolve(normalized);
|
|
7907
8237
|
}
|
|
7908
|
-
if (
|
|
7909
|
-
return
|
|
8238
|
+
if (path8.isAbsolute(inputPath)) {
|
|
8239
|
+
return path8.normalize(inputPath);
|
|
7910
8240
|
}
|
|
7911
|
-
return
|
|
8241
|
+
return path8.resolve(inputPath);
|
|
7912
8242
|
}
|
|
7913
8243
|
function listDirectoryEntriesSafe(dirPath) {
|
|
7914
8244
|
const entries = fs4.readdirSync(dirPath, { withFileTypes: true });
|
|
7915
8245
|
const files = [];
|
|
7916
8246
|
for (const entry of entries) {
|
|
7917
|
-
const entryPath =
|
|
8247
|
+
const entryPath = path8.join(dirPath, entry.name);
|
|
7918
8248
|
try {
|
|
7919
8249
|
if (entry.isDirectory()) {
|
|
7920
8250
|
files.push({ name: entry.name, type: "directory" });
|
|
@@ -7953,7 +8283,7 @@ async function handleFileRead(h, args) {
|
|
|
7953
8283
|
async function handleFileWrite(h, args) {
|
|
7954
8284
|
try {
|
|
7955
8285
|
const filePath = resolveSafePath(args?.path);
|
|
7956
|
-
fs4.mkdirSync(
|
|
8286
|
+
fs4.mkdirSync(path8.dirname(filePath), { recursive: true });
|
|
7957
8287
|
fs4.writeFileSync(filePath, args?.content || "", "utf-8");
|
|
7958
8288
|
return { success: true, path: filePath };
|
|
7959
8289
|
} catch (e) {
|
|
@@ -8041,7 +8371,7 @@ function handleGetProviderSettings(h, args) {
|
|
|
8041
8371
|
}
|
|
8042
8372
|
return { success: true, settings: allSettings, values: allValues };
|
|
8043
8373
|
}
|
|
8044
|
-
function handleSetProviderSetting(h, args) {
|
|
8374
|
+
async function handleSetProviderSetting(h, args) {
|
|
8045
8375
|
const loader = h.ctx.providerLoader;
|
|
8046
8376
|
const { providerType, key, value } = args || {};
|
|
8047
8377
|
if (!providerType || !key || value === void 0) {
|
|
@@ -8054,6 +8384,7 @@ function handleSetProviderSetting(h, args) {
|
|
|
8054
8384
|
const updated = h.ctx.instanceManager.updateInstanceSettings(providerType, allSettings);
|
|
8055
8385
|
LOG.info("Command", `[set_provider_setting] ${providerType}.${key}=${JSON.stringify(value)} \u2192 ${updated} instance(s) updated`);
|
|
8056
8386
|
}
|
|
8387
|
+
await h.ctx.onProviderSettingChanged?.(providerType, key, value);
|
|
8057
8388
|
return { success: true, providerType, key, value };
|
|
8058
8389
|
}
|
|
8059
8390
|
return { success: false, error: `Failed to set ${providerType}.${key} \u2014 invalid key, value, or not a public setting` };
|
|
@@ -8091,10 +8422,10 @@ function getCliScriptCommand(payload) {
|
|
|
8091
8422
|
}
|
|
8092
8423
|
const command = payload.command;
|
|
8093
8424
|
if (!command || typeof command !== "object") return null;
|
|
8094
|
-
if (command.type !== "send_message") return null;
|
|
8425
|
+
if (command.type !== "send_message" && command.type !== "pty_write") return null;
|
|
8095
8426
|
const text = typeof command.text === "string" ? command.text.trim() : typeof command.message === "string" ? command.message.trim() : "";
|
|
8096
8427
|
if (!text) return null;
|
|
8097
|
-
return { type:
|
|
8428
|
+
return { type: command.type, text };
|
|
8098
8429
|
}
|
|
8099
8430
|
function applyProviderPatch(h, args, payload) {
|
|
8100
8431
|
if (!payload || typeof payload !== "object") return;
|
|
@@ -8135,6 +8466,8 @@ async function executeProviderScript(h, args, scriptName) {
|
|
|
8135
8466
|
const cliCommand = getCliScriptCommand(parsed.payload);
|
|
8136
8467
|
if (cliCommand?.type === "send_message" && cliCommand.text) {
|
|
8137
8468
|
await adapter.sendMessage(cliCommand.text);
|
|
8469
|
+
} else if (cliCommand?.type === "pty_write" && cliCommand.text && adapter.writeRaw) {
|
|
8470
|
+
adapter.writeRaw(cliCommand.text + "\r");
|
|
8138
8471
|
}
|
|
8139
8472
|
applyProviderPatch(h, args, parsed.payload);
|
|
8140
8473
|
return { success: true, ...parsed.payload && typeof parsed.payload === "object" ? parsed.payload : { result: parsed.payload } };
|
|
@@ -8813,7 +9146,7 @@ var DaemonCommandHandler = class {
|
|
|
8813
9146
|
try {
|
|
8814
9147
|
const http3 = await import("http");
|
|
8815
9148
|
const postData = JSON.stringify(body);
|
|
8816
|
-
const result = await new Promise((
|
|
9149
|
+
const result = await new Promise((resolve12, reject) => {
|
|
8817
9150
|
const req = http3.request({
|
|
8818
9151
|
hostname: "127.0.0.1",
|
|
8819
9152
|
port: 19280,
|
|
@@ -8825,9 +9158,9 @@ var DaemonCommandHandler = class {
|
|
|
8825
9158
|
res.on("data", (chunk) => data += chunk);
|
|
8826
9159
|
res.on("end", () => {
|
|
8827
9160
|
try {
|
|
8828
|
-
|
|
9161
|
+
resolve12(JSON.parse(data));
|
|
8829
9162
|
} catch {
|
|
8830
|
-
|
|
9163
|
+
resolve12({ raw: data });
|
|
8831
9164
|
}
|
|
8832
9165
|
});
|
|
8833
9166
|
});
|
|
@@ -8845,15 +9178,15 @@ var DaemonCommandHandler = class {
|
|
|
8845
9178
|
if (!providerType) return { success: false, error: "providerType required" };
|
|
8846
9179
|
try {
|
|
8847
9180
|
const http3 = await import("http");
|
|
8848
|
-
const result = await new Promise((
|
|
9181
|
+
const result = await new Promise((resolve12, reject) => {
|
|
8849
9182
|
http3.get(`http://127.0.0.1:19280/api/providers/${providerType}/${endpoint}`, (res) => {
|
|
8850
9183
|
let data = "";
|
|
8851
9184
|
res.on("data", (chunk) => data += chunk);
|
|
8852
9185
|
res.on("end", () => {
|
|
8853
9186
|
try {
|
|
8854
|
-
|
|
9187
|
+
resolve12(JSON.parse(data));
|
|
8855
9188
|
} catch {
|
|
8856
|
-
|
|
9189
|
+
resolve12({ raw: data });
|
|
8857
9190
|
}
|
|
8858
9191
|
});
|
|
8859
9192
|
}).on("error", reject);
|
|
@@ -8867,7 +9200,7 @@ var DaemonCommandHandler = class {
|
|
|
8867
9200
|
try {
|
|
8868
9201
|
const http3 = await import("http");
|
|
8869
9202
|
const postData = JSON.stringify(args || {});
|
|
8870
|
-
const result = await new Promise((
|
|
9203
|
+
const result = await new Promise((resolve12, reject) => {
|
|
8871
9204
|
const req = http3.request({
|
|
8872
9205
|
hostname: "127.0.0.1",
|
|
8873
9206
|
port: 19280,
|
|
@@ -8879,9 +9212,9 @@ var DaemonCommandHandler = class {
|
|
|
8879
9212
|
res.on("data", (chunk) => data += chunk);
|
|
8880
9213
|
res.on("end", () => {
|
|
8881
9214
|
try {
|
|
8882
|
-
|
|
9215
|
+
resolve12(JSON.parse(data));
|
|
8883
9216
|
} catch {
|
|
8884
|
-
|
|
9217
|
+
resolve12({ raw: data });
|
|
8885
9218
|
}
|
|
8886
9219
|
});
|
|
8887
9220
|
});
|
|
@@ -8899,7 +9232,7 @@ var DaemonCommandHandler = class {
|
|
|
8899
9232
|
// src/commands/cli-manager.ts
|
|
8900
9233
|
init_provider_cli_adapter();
|
|
8901
9234
|
import * as os10 from "os";
|
|
8902
|
-
import * as
|
|
9235
|
+
import * as path11 from "path";
|
|
8903
9236
|
import * as crypto4 from "crypto";
|
|
8904
9237
|
import chalk from "chalk";
|
|
8905
9238
|
init_config();
|
|
@@ -8907,7 +9240,7 @@ init_config();
|
|
|
8907
9240
|
// src/providers/cli-provider-instance.ts
|
|
8908
9241
|
init_provider_cli_adapter();
|
|
8909
9242
|
import * as os9 from "os";
|
|
8910
|
-
import * as
|
|
9243
|
+
import * as path10 from "path";
|
|
8911
9244
|
import * as crypto3 from "crypto";
|
|
8912
9245
|
import * as fs5 from "fs";
|
|
8913
9246
|
import { createRequire } from "module";
|
|
@@ -8915,7 +9248,7 @@ init_logger();
|
|
|
8915
9248
|
var CachedDatabaseSync = null;
|
|
8916
9249
|
function getDatabaseSync() {
|
|
8917
9250
|
if (CachedDatabaseSync) return CachedDatabaseSync;
|
|
8918
|
-
const requireFn = typeof __require === "function" ? __require : createRequire(
|
|
9251
|
+
const requireFn = typeof __require === "function" ? __require : createRequire(path10.join(process.cwd(), "__adhdev_sqlite_loader__.js"));
|
|
8919
9252
|
const sqliteModule = requireFn(`node:${"sqlite"}`);
|
|
8920
9253
|
CachedDatabaseSync = sqliteModule.DatabaseSync;
|
|
8921
9254
|
if (!CachedDatabaseSync) {
|
|
@@ -8955,6 +9288,7 @@ var CliProviderInstance = class {
|
|
|
8955
9288
|
historyWriter;
|
|
8956
9289
|
runtimeMessages = [];
|
|
8957
9290
|
instanceId;
|
|
9291
|
+
suppressIdleHistoryReplay = false;
|
|
8958
9292
|
presentationMode;
|
|
8959
9293
|
providerSessionId;
|
|
8960
9294
|
launchMode;
|
|
@@ -8982,7 +9316,15 @@ var CliProviderInstance = class {
|
|
|
8982
9316
|
await this.adapter.spawn();
|
|
8983
9317
|
this.maybeAppendRuntimeRecoveryMessage(this.adapter.getRuntimeMetadata());
|
|
8984
9318
|
if (this.providerSessionId) {
|
|
9319
|
+
this.historyWriter.compactHistorySession(this.type, this.providerSessionId);
|
|
8985
9320
|
const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
|
|
9321
|
+
this.historyWriter.seedSessionHistory(
|
|
9322
|
+
this.type,
|
|
9323
|
+
restoredHistory.messages,
|
|
9324
|
+
this.providerSessionId,
|
|
9325
|
+
this.instanceId
|
|
9326
|
+
);
|
|
9327
|
+
this.suppressIdleHistoryReplay = restoredHistory.messages.length > 0;
|
|
8986
9328
|
if (restoredHistory.messages.length > 0) {
|
|
8987
9329
|
this.adapter.seedCommittedMessages(
|
|
8988
9330
|
restoredHistory.messages.map((message) => ({
|
|
@@ -9026,7 +9368,7 @@ var CliProviderInstance = class {
|
|
|
9026
9368
|
} else if (this.type === "codex-cli") {
|
|
9027
9369
|
probedSessionId = this.probeSessionIdFromConfig({
|
|
9028
9370
|
dbPath: "~/.codex/state_5.sqlite",
|
|
9029
|
-
query: "select id from threads where cwd in ({dirs}) and
|
|
9371
|
+
query: "select id from threads where cwd in ({dirs}) and updated_at >= ? and archived = 0 order by updated_at desc limit 1",
|
|
9030
9372
|
timestampFormat: "unix_s"
|
|
9031
9373
|
});
|
|
9032
9374
|
} else if (this.type === "goose-cli") {
|
|
@@ -9086,6 +9428,7 @@ var CliProviderInstance = class {
|
|
|
9086
9428
|
const mergedMessages = this.mergeConversationMessages(parsedMessages);
|
|
9087
9429
|
const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
9088
9430
|
if (parsedMessages.length > 0) {
|
|
9431
|
+
const shouldSkipReplayPersist = this.suppressIdleHistoryReplay && adapterStatus.status === "idle" && parsedStatus?.status === "idle";
|
|
9089
9432
|
let messagesToSave = parsedMessages;
|
|
9090
9433
|
if (parsedStatus?.status === "generating" || parsedStatus?.status === "long_generating") {
|
|
9091
9434
|
const lastIdx = messagesToSave.length - 1;
|
|
@@ -9093,7 +9436,7 @@ var CliProviderInstance = class {
|
|
|
9093
9436
|
messagesToSave = messagesToSave.slice(0, lastIdx);
|
|
9094
9437
|
}
|
|
9095
9438
|
}
|
|
9096
|
-
if (messagesToSave.length > 0) {
|
|
9439
|
+
if (!shouldSkipReplayPersist && messagesToSave.length > 0) {
|
|
9097
9440
|
this.historyWriter.appendNewMessages(
|
|
9098
9441
|
this.type,
|
|
9099
9442
|
messagesToSave,
|
|
@@ -9188,6 +9531,7 @@ var CliProviderInstance = class {
|
|
|
9188
9531
|
if (newStatus !== this.lastStatus) {
|
|
9189
9532
|
LOG.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
|
|
9190
9533
|
if (this.lastStatus === "idle" && newStatus === "generating") {
|
|
9534
|
+
this.suppressIdleHistoryReplay = false;
|
|
9191
9535
|
if (this.completedDebouncePending) {
|
|
9192
9536
|
LOG.info("CLI", `[${this.type}] cancelled pending completed (resumed generating)`);
|
|
9193
9537
|
if (this.completedDebounceTimer) {
|
|
@@ -9207,6 +9551,7 @@ var CliProviderInstance = class {
|
|
|
9207
9551
|
this.generatingDebounceTimer = null;
|
|
9208
9552
|
}, 1e3);
|
|
9209
9553
|
} else if (newStatus === "waiting_approval") {
|
|
9554
|
+
this.suppressIdleHistoryReplay = false;
|
|
9210
9555
|
if (this.generatingDebouncePending) {
|
|
9211
9556
|
if (this.generatingDebounceTimer) {
|
|
9212
9557
|
clearTimeout(this.generatingDebounceTimer);
|
|
@@ -9749,8 +10094,9 @@ var AcpProviderInstance = class {
|
|
|
9749
10094
|
async setConfigOption(category, value) {
|
|
9750
10095
|
const opt = this.configOptions.find((c) => c.category === category);
|
|
9751
10096
|
if (!opt) {
|
|
9752
|
-
|
|
9753
|
-
|
|
10097
|
+
const message = `[${this.type}] No config option for category: ${category}`;
|
|
10098
|
+
this.log.warn(message);
|
|
10099
|
+
throw new Error(message);
|
|
9754
10100
|
}
|
|
9755
10101
|
if (this.useStaticConfig) {
|
|
9756
10102
|
opt.currentValue = value;
|
|
@@ -9762,8 +10108,9 @@ var AcpProviderInstance = class {
|
|
|
9762
10108
|
return;
|
|
9763
10109
|
}
|
|
9764
10110
|
if (!this.connection || !this.sessionId) {
|
|
9765
|
-
|
|
9766
|
-
|
|
10111
|
+
const message = `[${this.type}] Cannot set config: no active connection/session`;
|
|
10112
|
+
this.log.warn(message);
|
|
10113
|
+
throw new Error(message);
|
|
9767
10114
|
}
|
|
9768
10115
|
try {
|
|
9769
10116
|
this.log.info(`[${this.type}] Sending session/set_config_option: configId=${opt.configId} value=${value} sessionId=${this.sessionId}`);
|
|
@@ -9777,7 +10124,9 @@ var AcpProviderInstance = class {
|
|
|
9777
10124
|
if (result?.configOptions) this.parseConfigOptions(result.configOptions);
|
|
9778
10125
|
this.log.info(`[${this.type}] Config ${category} set to: ${value} | response: ${JSON.stringify(result)?.slice(0, 300)}`);
|
|
9779
10126
|
} catch (e) {
|
|
9780
|
-
|
|
10127
|
+
const message = e?.message || "Unknown ACP config error";
|
|
10128
|
+
this.log.warn(`[${this.type}] set_config_option failed: ${message}`);
|
|
10129
|
+
throw new Error(message);
|
|
9781
10130
|
}
|
|
9782
10131
|
}
|
|
9783
10132
|
async setMode(modeId) {
|
|
@@ -9793,8 +10142,9 @@ var AcpProviderInstance = class {
|
|
|
9793
10142
|
return;
|
|
9794
10143
|
}
|
|
9795
10144
|
if (!this.connection || !this.sessionId) {
|
|
9796
|
-
|
|
9797
|
-
|
|
10145
|
+
const message = `[${this.type}] Cannot set mode: no active connection/session`;
|
|
10146
|
+
this.log.warn(message);
|
|
10147
|
+
throw new Error(message);
|
|
9798
10148
|
}
|
|
9799
10149
|
try {
|
|
9800
10150
|
await this.connection.setSessionMode({
|
|
@@ -9804,7 +10154,9 @@ var AcpProviderInstance = class {
|
|
|
9804
10154
|
this.currentMode = modeId;
|
|
9805
10155
|
this.log.info(`[${this.type}] Mode set to: ${modeId}`);
|
|
9806
10156
|
} catch (e) {
|
|
9807
|
-
|
|
10157
|
+
const message = e?.message || "Unknown ACP mode error";
|
|
10158
|
+
this.log.warn(`[${this.type}] set_mode failed: ${message}`);
|
|
10159
|
+
throw new Error(message);
|
|
9808
10160
|
}
|
|
9809
10161
|
}
|
|
9810
10162
|
/** Static config: kill process and restart with new args */
|
|
@@ -9852,7 +10204,7 @@ var AcpProviderInstance = class {
|
|
|
9852
10204
|
if (!spawnConfig) {
|
|
9853
10205
|
throw new Error(`[ACP:${this.type}] No spawn config defined`);
|
|
9854
10206
|
}
|
|
9855
|
-
const command = spawnConfig.command;
|
|
10207
|
+
const command = typeof this.settings.executablePath === "string" && this.settings.executablePath.trim() ? this.settings.executablePath.trim() : spawnConfig.command;
|
|
9856
10208
|
let baseArgs = spawnConfig.args || [];
|
|
9857
10209
|
if (this.provider.spawnArgBuilder && Object.keys(this.selectedConfig).length > 0) {
|
|
9858
10210
|
baseArgs = this.provider.spawnArgBuilder(this.selectedConfig);
|
|
@@ -9968,13 +10320,13 @@ var AcpProviderInstance = class {
|
|
|
9968
10320
|
}
|
|
9969
10321
|
this.currentStatus = "waiting_approval";
|
|
9970
10322
|
this.detectStatusTransition();
|
|
9971
|
-
const approved = await new Promise((
|
|
9972
|
-
this.permissionResolvers.push(
|
|
10323
|
+
const approved = await new Promise((resolve12) => {
|
|
10324
|
+
this.permissionResolvers.push(resolve12);
|
|
9973
10325
|
setTimeout(() => {
|
|
9974
|
-
const idx = this.permissionResolvers.indexOf(
|
|
10326
|
+
const idx = this.permissionResolvers.indexOf(resolve12);
|
|
9975
10327
|
if (idx >= 0) {
|
|
9976
10328
|
this.permissionResolvers.splice(idx, 1);
|
|
9977
|
-
|
|
10329
|
+
resolve12(false);
|
|
9978
10330
|
}
|
|
9979
10331
|
}, 3e5);
|
|
9980
10332
|
});
|
|
@@ -10681,7 +11033,7 @@ var DaemonCliManager = class {
|
|
|
10681
11033
|
async startSession(cliType, workingDir, cliArgs, initialModel, options) {
|
|
10682
11034
|
const trimmed = (workingDir || "").trim();
|
|
10683
11035
|
if (!trimmed) throw new Error("working directory required");
|
|
10684
|
-
const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os10.homedir()) :
|
|
11036
|
+
const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os10.homedir()) : path11.resolve(trimmed);
|
|
10685
11037
|
const normalizedType = this.providerLoader.resolveAlias(cliType);
|
|
10686
11038
|
const provider = this.providerLoader.getByAlias(cliType);
|
|
10687
11039
|
const key = crypto4.randomUUID();
|
|
@@ -10768,10 +11120,10 @@ ${installInfo}`
|
|
|
10768
11120
|
if (!cliInfo) {
|
|
10769
11121
|
const installHint = provider?.install || "";
|
|
10770
11122
|
const displayName = provider?.displayName || provider?.name || cliType;
|
|
10771
|
-
const spawnCmd = provider?.spawn?.command || cliType;
|
|
11123
|
+
const spawnCmd = this.providerLoader.getSpawnCommand(normalizedType, provider?.spawn?.command || cliType);
|
|
10772
11124
|
throw new Error(
|
|
10773
11125
|
`${displayName} is not installed.
|
|
10774
|
-
Command '${spawnCmd}' not
|
|
11126
|
+
Command '${spawnCmd}' is not available.
|
|
10775
11127
|
` + (installHint ? `
|
|
10776
11128
|
${installHint}
|
|
10777
11129
|
` : "") + `
|
|
@@ -10917,6 +11269,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
10917
11269
|
if (!instanceManager) return 0;
|
|
10918
11270
|
const sessions = records || await this.deps.listHostedCliRuntimes?.() || [];
|
|
10919
11271
|
let restored = 0;
|
|
11272
|
+
const restoredBindings = /* @__PURE__ */ new Set();
|
|
10920
11273
|
for (const record of sessions) {
|
|
10921
11274
|
if (!record?.runtimeId || !record?.cliType || !record?.workspace) continue;
|
|
10922
11275
|
if (this.adapters.has(record.runtimeId) || instanceManager.getInstance(record.runtimeId)) continue;
|
|
@@ -10930,6 +11283,18 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
10930
11283
|
record.cliArgs,
|
|
10931
11284
|
record.providerSessionId
|
|
10932
11285
|
);
|
|
11286
|
+
const bindingKey = [
|
|
11287
|
+
normalizedType,
|
|
11288
|
+
record.workspace,
|
|
11289
|
+
sessionBinding.providerSessionId || record.runtimeId
|
|
11290
|
+
].join("::");
|
|
11291
|
+
if (restoredBindings.has(bindingKey)) {
|
|
11292
|
+
LOG.info(
|
|
11293
|
+
"CLI",
|
|
11294
|
+
`\u21B7 Skipping duplicate hosted runtime restore: ${record.runtimeKey || record.runtimeId} (${normalizedType} @ ${record.workspace}) binding=${sessionBinding.providerSessionId || "runtime"}`
|
|
11295
|
+
);
|
|
11296
|
+
continue;
|
|
11297
|
+
}
|
|
10933
11298
|
try {
|
|
10934
11299
|
await this.registerCliInstance(
|
|
10935
11300
|
record.runtimeId,
|
|
@@ -10945,6 +11310,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
10945
11310
|
launchMode: "manual"
|
|
10946
11311
|
}
|
|
10947
11312
|
);
|
|
11313
|
+
restoredBindings.add(bindingKey);
|
|
10948
11314
|
restored += 1;
|
|
10949
11315
|
LOG.info("CLI", `\u267B Restored hosted runtime: ${record.runtimeKey || record.runtimeId} (${record.displayName || record.workspace})`);
|
|
10950
11316
|
} catch (error) {
|
|
@@ -11131,16 +11497,17 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
11131
11497
|
import { execSync as execSync4, spawn as spawn2 } from "child_process";
|
|
11132
11498
|
import * as net from "net";
|
|
11133
11499
|
import * as os12 from "os";
|
|
11134
|
-
import * as
|
|
11500
|
+
import * as path13 from "path";
|
|
11135
11501
|
|
|
11136
11502
|
// src/providers/provider-loader.ts
|
|
11137
11503
|
import * as fs6 from "fs";
|
|
11138
|
-
import * as
|
|
11504
|
+
import * as path12 from "path";
|
|
11139
11505
|
import * as os11 from "os";
|
|
11140
11506
|
import * as chokidar from "chokidar";
|
|
11141
11507
|
init_logger();
|
|
11142
11508
|
var ProviderLoader = class _ProviderLoader {
|
|
11143
11509
|
providers = /* @__PURE__ */ new Map();
|
|
11510
|
+
providerAvailability = /* @__PURE__ */ new Map();
|
|
11144
11511
|
userDir;
|
|
11145
11512
|
upstreamDir;
|
|
11146
11513
|
disableUpstream;
|
|
@@ -11156,12 +11523,12 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11156
11523
|
static META_FILE = ".meta.json";
|
|
11157
11524
|
constructor(options) {
|
|
11158
11525
|
this.logFn = options?.logFn || LOG.forComponent("Provider").asLogFn();
|
|
11159
|
-
const defaultProvidersDir =
|
|
11526
|
+
const defaultProvidersDir = path12.join(os11.homedir(), ".adhdev", "providers");
|
|
11160
11527
|
if (options?.userDir) {
|
|
11161
11528
|
this.userDir = options.userDir;
|
|
11162
11529
|
this.log(`Config 'providerDir' applied: ${this.userDir}`);
|
|
11163
11530
|
} else {
|
|
11164
|
-
const localRepoPath =
|
|
11531
|
+
const localRepoPath = path12.resolve(__dirname, "../../../../../adhdev-providers");
|
|
11165
11532
|
if (fs6.existsSync(localRepoPath)) {
|
|
11166
11533
|
this.userDir = localRepoPath;
|
|
11167
11534
|
this.log(`Auto-detected local public repository: ${this.userDir} (Dev workspace speedup)`);
|
|
@@ -11170,7 +11537,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11170
11537
|
this.log(`Using default user providers directory: ${this.userDir}`);
|
|
11171
11538
|
}
|
|
11172
11539
|
}
|
|
11173
|
-
this.upstreamDir =
|
|
11540
|
+
this.upstreamDir = path12.join(defaultProvidersDir, ".upstream");
|
|
11174
11541
|
this.disableUpstream = options?.disableUpstream ?? false;
|
|
11175
11542
|
}
|
|
11176
11543
|
log(msg) {
|
|
@@ -11200,7 +11567,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11200
11567
|
* Canonical provider directory shape for a given root.
|
|
11201
11568
|
*/
|
|
11202
11569
|
getProviderDir(root, category, type) {
|
|
11203
|
-
return
|
|
11570
|
+
return path12.join(root, category, type);
|
|
11204
11571
|
}
|
|
11205
11572
|
/**
|
|
11206
11573
|
* Canonical user override directory for a provider.
|
|
@@ -11227,7 +11594,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11227
11594
|
resolveProviderFile(type, ...segments) {
|
|
11228
11595
|
const dir = this.findProviderDirInternal(type);
|
|
11229
11596
|
if (!dir) return null;
|
|
11230
|
-
return
|
|
11597
|
+
return path12.join(dir, ...segments);
|
|
11231
11598
|
}
|
|
11232
11599
|
/**
|
|
11233
11600
|
* Load all providers (3-tier priority)
|
|
@@ -11238,6 +11605,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11238
11605
|
*/
|
|
11239
11606
|
loadAll() {
|
|
11240
11607
|
this.providers.clear();
|
|
11608
|
+
this.providerAvailability.clear();
|
|
11241
11609
|
let upstreamCount = 0;
|
|
11242
11610
|
if (!this.disableUpstream && fs6.existsSync(this.upstreamDir)) {
|
|
11243
11611
|
upstreamCount = this.loadDir(this.upstreamDir);
|
|
@@ -11265,7 +11633,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11265
11633
|
if (!fs6.existsSync(this.upstreamDir)) return false;
|
|
11266
11634
|
try {
|
|
11267
11635
|
return fs6.readdirSync(this.upstreamDir).some(
|
|
11268
|
-
(d) => fs6.statSync(
|
|
11636
|
+
(d) => fs6.statSync(path12.join(this.upstreamDir, d)).isDirectory()
|
|
11269
11637
|
);
|
|
11270
11638
|
} catch {
|
|
11271
11639
|
return false;
|
|
@@ -11308,11 +11676,12 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11308
11676
|
if ((p.category === "cli" || p.category === "acp") && p.spawn?.command) {
|
|
11309
11677
|
const verCmdConfig = p.versionCommand;
|
|
11310
11678
|
const versionCommand = typeof verCmdConfig === "object" && verCmdConfig !== null ? verCmdConfig[process.platform] : verCmdConfig;
|
|
11679
|
+
const command = this.getSpawnCommand(p.type, p.spawn.command);
|
|
11311
11680
|
result.push({
|
|
11312
11681
|
id: p.type,
|
|
11313
11682
|
displayName: p.displayName || p.name,
|
|
11314
11683
|
icon: p.icon || "\u{1F527}",
|
|
11315
|
-
command
|
|
11684
|
+
command,
|
|
11316
11685
|
category: p.category,
|
|
11317
11686
|
...typeof versionCommand === "string" && versionCommand.trim() ? { versionCommand: versionCommand.trim() } : {}
|
|
11318
11687
|
});
|
|
@@ -11441,6 +11810,71 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11441
11810
|
getAvailableIdeTypes() {
|
|
11442
11811
|
return [...this.providers.values()].filter((p) => p.category === "ide" && p.cdpPorts).map((p) => p.type);
|
|
11443
11812
|
}
|
|
11813
|
+
getSpawnCommand(type, fallback) {
|
|
11814
|
+
const override = this.getOptionalStringSetting(type, "executablePath");
|
|
11815
|
+
if (override) return override;
|
|
11816
|
+
return fallback || this.providers.get(type)?.spawn?.command || type;
|
|
11817
|
+
}
|
|
11818
|
+
getIdeCliCommand(type, fallback) {
|
|
11819
|
+
const override = this.getOptionalStringSetting(type, "cliPathOverride");
|
|
11820
|
+
if (override) return override;
|
|
11821
|
+
return fallback || this.providers.get(type)?.cli || null;
|
|
11822
|
+
}
|
|
11823
|
+
getIdePathCandidates(type, fallback) {
|
|
11824
|
+
const override = this.getOptionalStringSetting(type, "appPathOverride");
|
|
11825
|
+
if (override) return [override];
|
|
11826
|
+
if (fallback && fallback.length > 0) return fallback;
|
|
11827
|
+
const osPaths = this.providers.get(type)?.paths?.[process.platform];
|
|
11828
|
+
return Array.isArray(osPaths) ? [...osPaths] : [];
|
|
11829
|
+
}
|
|
11830
|
+
setProviderAvailability(type, state) {
|
|
11831
|
+
this.providerAvailability.set(type, {
|
|
11832
|
+
installed: !!state.installed,
|
|
11833
|
+
detectedPath: state.detectedPath ?? null
|
|
11834
|
+
});
|
|
11835
|
+
}
|
|
11836
|
+
setCliDetectionResults(results, replace = true) {
|
|
11837
|
+
if (replace) {
|
|
11838
|
+
for (const provider of this.providers.values()) {
|
|
11839
|
+
if (provider.category === "cli" || provider.category === "acp") {
|
|
11840
|
+
this.providerAvailability.set(provider.type, { installed: false, detectedPath: null });
|
|
11841
|
+
}
|
|
11842
|
+
}
|
|
11843
|
+
}
|
|
11844
|
+
for (const result of results) {
|
|
11845
|
+
this.setProviderAvailability(result.id, {
|
|
11846
|
+
installed: !!result.installed,
|
|
11847
|
+
detectedPath: result.path || null
|
|
11848
|
+
});
|
|
11849
|
+
}
|
|
11850
|
+
}
|
|
11851
|
+
setIdeDetectionResults(results, replace = true) {
|
|
11852
|
+
if (replace) {
|
|
11853
|
+
for (const provider of this.providers.values()) {
|
|
11854
|
+
if (provider.category === "ide") {
|
|
11855
|
+
this.providerAvailability.set(provider.type, { installed: false, detectedPath: null });
|
|
11856
|
+
}
|
|
11857
|
+
}
|
|
11858
|
+
}
|
|
11859
|
+
for (const result of results) {
|
|
11860
|
+
this.setProviderAvailability(result.id, {
|
|
11861
|
+
installed: !!result.installed,
|
|
11862
|
+
detectedPath: result.cliCommand || result.path || null
|
|
11863
|
+
});
|
|
11864
|
+
}
|
|
11865
|
+
}
|
|
11866
|
+
getAvailableProviderInfos() {
|
|
11867
|
+
return this.getAll().map((provider) => {
|
|
11868
|
+
const availability = this.providerAvailability.get(provider.type);
|
|
11869
|
+
return {
|
|
11870
|
+
...provider,
|
|
11871
|
+
...availability ? {
|
|
11872
|
+
installed: availability.installed,
|
|
11873
|
+
detectedPath: availability.detectedPath
|
|
11874
|
+
} : {}
|
|
11875
|
+
};
|
|
11876
|
+
});
|
|
11877
|
+
}
|
|
11444
11878
|
/**
|
|
11445
11879
|
* Register IDE providers to core/detector registry
|
|
11446
11880
|
* → Enables detectIDEs() to detect provider.js-based IDEs
|
|
@@ -11515,8 +11949,8 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11515
11949
|
resolved._resolvedScriptDir = entry.scriptDir;
|
|
11516
11950
|
resolved._resolvedScriptsSource = `compatibility:${entry.ideVersion}`;
|
|
11517
11951
|
if (providerDir) {
|
|
11518
|
-
const fullDir =
|
|
11519
|
-
resolved._resolvedScriptsPath = fs6.existsSync(
|
|
11952
|
+
const fullDir = path12.join(providerDir, entry.scriptDir);
|
|
11953
|
+
resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
|
|
11520
11954
|
}
|
|
11521
11955
|
matched = true;
|
|
11522
11956
|
}
|
|
@@ -11531,8 +11965,8 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11531
11965
|
resolved._resolvedScriptDir = base.defaultScriptDir;
|
|
11532
11966
|
resolved._resolvedScriptsSource = "defaultScriptDir:version_miss";
|
|
11533
11967
|
if (providerDir) {
|
|
11534
|
-
const fullDir =
|
|
11535
|
-
resolved._resolvedScriptsPath = fs6.existsSync(
|
|
11968
|
+
const fullDir = path12.join(providerDir, base.defaultScriptDir);
|
|
11969
|
+
resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
|
|
11536
11970
|
}
|
|
11537
11971
|
}
|
|
11538
11972
|
resolved._versionWarning = `Version ${currentVersion} not in compatibility matrix. Using default scripts.`;
|
|
@@ -11549,8 +11983,8 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11549
11983
|
resolved._resolvedScriptDir = dirOverride;
|
|
11550
11984
|
resolved._resolvedScriptsSource = `versions:${range}`;
|
|
11551
11985
|
if (providerDir) {
|
|
11552
|
-
const fullDir =
|
|
11553
|
-
resolved._resolvedScriptsPath = fs6.existsSync(
|
|
11986
|
+
const fullDir = path12.join(providerDir, dirOverride);
|
|
11987
|
+
resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
|
|
11554
11988
|
}
|
|
11555
11989
|
}
|
|
11556
11990
|
} else if (override.scripts) {
|
|
@@ -11566,8 +12000,8 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11566
12000
|
resolved._resolvedScriptDir = base.defaultScriptDir;
|
|
11567
12001
|
resolved._resolvedScriptsSource = "defaultScriptDir:no_version";
|
|
11568
12002
|
if (providerDir) {
|
|
11569
|
-
const fullDir =
|
|
11570
|
-
resolved._resolvedScriptsPath = fs6.existsSync(
|
|
12003
|
+
const fullDir = path12.join(providerDir, base.defaultScriptDir);
|
|
12004
|
+
resolved._resolvedScriptsPath = fs6.existsSync(path12.join(fullDir, "scripts.js")) ? path12.join(fullDir, "scripts.js") : fullDir;
|
|
11571
12005
|
}
|
|
11572
12006
|
}
|
|
11573
12007
|
}
|
|
@@ -11592,14 +12026,14 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11592
12026
|
this.log(` [loadScriptsFromDir] ${type}: providerDir not found`);
|
|
11593
12027
|
return null;
|
|
11594
12028
|
}
|
|
11595
|
-
const dir =
|
|
12029
|
+
const dir = path12.join(providerDir, scriptDir);
|
|
11596
12030
|
if (!fs6.existsSync(dir)) {
|
|
11597
12031
|
this.log(` [loadScriptsFromDir] ${type}: dir not found: ${dir}`);
|
|
11598
12032
|
return null;
|
|
11599
12033
|
}
|
|
11600
12034
|
const cached = this.scriptsCache.get(dir);
|
|
11601
12035
|
if (cached) return cached;
|
|
11602
|
-
const scriptsJs =
|
|
12036
|
+
const scriptsJs = path12.join(dir, "scripts.js");
|
|
11603
12037
|
if (fs6.existsSync(scriptsJs)) {
|
|
11604
12038
|
try {
|
|
11605
12039
|
delete __require.cache[__require.resolve(scriptsJs)];
|
|
@@ -11641,7 +12075,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11641
12075
|
return;
|
|
11642
12076
|
}
|
|
11643
12077
|
if (filePath.endsWith(".js") || filePath.endsWith(".json")) {
|
|
11644
|
-
this.log(`File changed: ${
|
|
12078
|
+
this.log(`File changed: ${path12.basename(filePath)}, reloading...`);
|
|
11645
12079
|
this.reload();
|
|
11646
12080
|
}
|
|
11647
12081
|
};
|
|
@@ -11696,7 +12130,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11696
12130
|
}
|
|
11697
12131
|
const https = __require("https");
|
|
11698
12132
|
const { execSync: execSync7 } = __require("child_process");
|
|
11699
|
-
const metaPath =
|
|
12133
|
+
const metaPath = path12.join(this.upstreamDir, _ProviderLoader.META_FILE);
|
|
11700
12134
|
let prevEtag = "";
|
|
11701
12135
|
let prevTimestamp = 0;
|
|
11702
12136
|
try {
|
|
@@ -11713,7 +12147,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11713
12147
|
return { updated: false };
|
|
11714
12148
|
}
|
|
11715
12149
|
try {
|
|
11716
|
-
const etag = await new Promise((
|
|
12150
|
+
const etag = await new Promise((resolve12, reject) => {
|
|
11717
12151
|
const options = {
|
|
11718
12152
|
method: "HEAD",
|
|
11719
12153
|
hostname: "github.com",
|
|
@@ -11731,7 +12165,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11731
12165
|
headers: { "User-Agent": "adhdev-launcher" },
|
|
11732
12166
|
timeout: 1e4
|
|
11733
12167
|
}, (res2) => {
|
|
11734
|
-
|
|
12168
|
+
resolve12(res2.headers.etag || res2.headers["last-modified"] || "");
|
|
11735
12169
|
});
|
|
11736
12170
|
req2.on("error", reject);
|
|
11737
12171
|
req2.on("timeout", () => {
|
|
@@ -11740,7 +12174,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11740
12174
|
});
|
|
11741
12175
|
req2.end();
|
|
11742
12176
|
} else {
|
|
11743
|
-
|
|
12177
|
+
resolve12(res.headers.etag || res.headers["last-modified"] || "");
|
|
11744
12178
|
}
|
|
11745
12179
|
});
|
|
11746
12180
|
req.on("error", reject);
|
|
@@ -11756,17 +12190,17 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11756
12190
|
return { updated: false };
|
|
11757
12191
|
}
|
|
11758
12192
|
this.log("Downloading latest providers from GitHub...");
|
|
11759
|
-
const tmpTar =
|
|
11760
|
-
const tmpExtract =
|
|
12193
|
+
const tmpTar = path12.join(os11.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
|
|
12194
|
+
const tmpExtract = path12.join(os11.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
|
|
11761
12195
|
await this.downloadFile(_ProviderLoader.GITHUB_TARBALL_URL, tmpTar);
|
|
11762
12196
|
fs6.mkdirSync(tmpExtract, { recursive: true });
|
|
11763
12197
|
execSync7(`tar -xzf "${tmpTar}" -C "${tmpExtract}"`, { timeout: 3e4 });
|
|
11764
12198
|
const extracted = fs6.readdirSync(tmpExtract);
|
|
11765
12199
|
const rootDir = extracted.find(
|
|
11766
|
-
(d) => fs6.statSync(
|
|
12200
|
+
(d) => fs6.statSync(path12.join(tmpExtract, d)).isDirectory() && d.startsWith("adhdev-providers")
|
|
11767
12201
|
);
|
|
11768
12202
|
if (!rootDir) throw new Error("Unexpected tarball structure");
|
|
11769
|
-
const sourceDir =
|
|
12203
|
+
const sourceDir = path12.join(tmpExtract, rootDir);
|
|
11770
12204
|
const backupDir = this.upstreamDir + ".bak";
|
|
11771
12205
|
if (fs6.existsSync(this.upstreamDir)) {
|
|
11772
12206
|
if (fs6.existsSync(backupDir)) fs6.rmSync(backupDir, { recursive: true, force: true });
|
|
@@ -11804,7 +12238,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11804
12238
|
downloadFile(url, destPath) {
|
|
11805
12239
|
const https = __require("https");
|
|
11806
12240
|
const http3 = __require("http");
|
|
11807
|
-
return new Promise((
|
|
12241
|
+
return new Promise((resolve12, reject) => {
|
|
11808
12242
|
const doRequest = (reqUrl, redirectCount = 0) => {
|
|
11809
12243
|
if (redirectCount > 5) {
|
|
11810
12244
|
reject(new Error("Too many redirects"));
|
|
@@ -11824,7 +12258,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11824
12258
|
res.pipe(ws);
|
|
11825
12259
|
ws.on("finish", () => {
|
|
11826
12260
|
ws.close();
|
|
11827
|
-
|
|
12261
|
+
resolve12();
|
|
11828
12262
|
});
|
|
11829
12263
|
ws.on("error", reject);
|
|
11830
12264
|
});
|
|
@@ -11841,8 +12275,8 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11841
12275
|
copyDirRecursive(src, dest) {
|
|
11842
12276
|
fs6.mkdirSync(dest, { recursive: true });
|
|
11843
12277
|
for (const entry of fs6.readdirSync(src, { withFileTypes: true })) {
|
|
11844
|
-
const srcPath =
|
|
11845
|
-
const destPath =
|
|
12278
|
+
const srcPath = path12.join(src, entry.name);
|
|
12279
|
+
const destPath = path12.join(dest, entry.name);
|
|
11846
12280
|
if (entry.isDirectory()) {
|
|
11847
12281
|
this.copyDirRecursive(srcPath, destPath);
|
|
11848
12282
|
} else {
|
|
@@ -11853,7 +12287,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11853
12287
|
/** .meta.json save */
|
|
11854
12288
|
writeMeta(metaPath, etag, timestamp) {
|
|
11855
12289
|
try {
|
|
11856
|
-
fs6.mkdirSync(
|
|
12290
|
+
fs6.mkdirSync(path12.dirname(metaPath), { recursive: true });
|
|
11857
12291
|
fs6.writeFileSync(metaPath, JSON.stringify({
|
|
11858
12292
|
etag,
|
|
11859
12293
|
timestamp,
|
|
@@ -11870,7 +12304,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11870
12304
|
const scan = (d) => {
|
|
11871
12305
|
try {
|
|
11872
12306
|
for (const entry of fs6.readdirSync(d, { withFileTypes: true })) {
|
|
11873
|
-
if (entry.isDirectory()) scan(
|
|
12307
|
+
if (entry.isDirectory()) scan(path12.join(d, entry.name));
|
|
11874
12308
|
else if (entry.name === "provider.json") count++;
|
|
11875
12309
|
}
|
|
11876
12310
|
} catch {
|
|
@@ -11884,9 +12318,8 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11884
12318
|
* Get public settings schema for a provider (for dashboard UI rendering)
|
|
11885
12319
|
*/
|
|
11886
12320
|
getPublicSettings(type) {
|
|
11887
|
-
const
|
|
11888
|
-
|
|
11889
|
-
return Object.entries(provider.settings).filter(([, def]) => def.public === true).map(([key, def]) => ({ key, ...def }));
|
|
12321
|
+
const settings = this.getSettingsSchema(type);
|
|
12322
|
+
return Object.entries(settings).filter(([, def]) => def.public === true).map(([key, def]) => ({ key, ...def }));
|
|
11890
12323
|
}
|
|
11891
12324
|
/**
|
|
11892
12325
|
* Get public settings schema for all providers
|
|
@@ -11903,8 +12336,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11903
12336
|
* Resolved setting value for a provider (default + user override)
|
|
11904
12337
|
*/
|
|
11905
12338
|
getSettingValue(type, key) {
|
|
11906
|
-
const
|
|
11907
|
-
const schemaDef = provider?.settings?.[key];
|
|
12339
|
+
const schemaDef = this.getSettingsSchema(type)[key];
|
|
11908
12340
|
const defaultVal = schemaDef ? schemaDef.default : void 0;
|
|
11909
12341
|
try {
|
|
11910
12342
|
const { loadConfig: loadConfig2 } = (init_config(), __toCommonJS(config_exports));
|
|
@@ -11919,10 +12351,9 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11919
12351
|
* All resolved settings for a provider (default + user override)
|
|
11920
12352
|
*/
|
|
11921
12353
|
getSettings(type) {
|
|
11922
|
-
const
|
|
11923
|
-
if (!provider?.settings) return {};
|
|
12354
|
+
const settings = this.getSettingsSchema(type);
|
|
11924
12355
|
const result = {};
|
|
11925
|
-
for (const [key
|
|
12356
|
+
for (const [key] of Object.entries(settings)) {
|
|
11926
12357
|
result[key] = this.getSettingValue(type, key);
|
|
11927
12358
|
}
|
|
11928
12359
|
return result;
|
|
@@ -11931,11 +12362,11 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11931
12362
|
* Save provider setting value (writes to config.json)
|
|
11932
12363
|
*/
|
|
11933
12364
|
setSetting(type, key, value) {
|
|
11934
|
-
const
|
|
11935
|
-
const schemaDef = provider?.settings?.[key];
|
|
12365
|
+
const schemaDef = this.getSettingsSchema(type)[key];
|
|
11936
12366
|
if (!schemaDef) return false;
|
|
11937
12367
|
if (!schemaDef.public) return false;
|
|
11938
12368
|
if (schemaDef.type === "boolean" && typeof value !== "boolean") return false;
|
|
12369
|
+
if (schemaDef.type === "string" && typeof value !== "string") return false;
|
|
11939
12370
|
if (schemaDef.type === "number") {
|
|
11940
12371
|
if (typeof value !== "number") return false;
|
|
11941
12372
|
if (schemaDef.min !== void 0 && value < schemaDef.min) return false;
|
|
@@ -11956,6 +12387,53 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11956
12387
|
return false;
|
|
11957
12388
|
}
|
|
11958
12389
|
}
|
|
12390
|
+
getOptionalStringSetting(type, key) {
|
|
12391
|
+
const value = this.getSettingValue(type, key);
|
|
12392
|
+
if (typeof value !== "string") return null;
|
|
12393
|
+
const trimmed = value.trim();
|
|
12394
|
+
return trimmed ? trimmed : null;
|
|
12395
|
+
}
|
|
12396
|
+
getSettingsSchema(type) {
|
|
12397
|
+
const provider = this.providers.get(type);
|
|
12398
|
+
if (!provider) return {};
|
|
12399
|
+
return {
|
|
12400
|
+
...this.getSyntheticSettings(type, provider),
|
|
12401
|
+
...provider.settings || {}
|
|
12402
|
+
};
|
|
12403
|
+
}
|
|
12404
|
+
getSyntheticSettings(type, provider) {
|
|
12405
|
+
const result = {};
|
|
12406
|
+
if ((provider.category === "cli" || provider.category === "acp") && provider.spawn?.command && !provider.settings?.executablePath) {
|
|
12407
|
+
result.executablePath = {
|
|
12408
|
+
type: "string",
|
|
12409
|
+
default: "",
|
|
12410
|
+
public: true,
|
|
12411
|
+
label: "Executable path",
|
|
12412
|
+
description: "Optional absolute path for this provider binary. Leave blank to use the default PATH lookup."
|
|
12413
|
+
};
|
|
12414
|
+
}
|
|
12415
|
+
if (provider.category === "ide") {
|
|
12416
|
+
if (provider.cli && !provider.settings?.cliPathOverride) {
|
|
12417
|
+
result.cliPathOverride = {
|
|
12418
|
+
type: "string",
|
|
12419
|
+
default: "",
|
|
12420
|
+
public: true,
|
|
12421
|
+
label: "CLI path override",
|
|
12422
|
+
description: "Optional absolute path for the IDE CLI launcher. Leave blank to use the detected default."
|
|
12423
|
+
};
|
|
12424
|
+
}
|
|
12425
|
+
if (provider.paths && !provider.settings?.appPathOverride) {
|
|
12426
|
+
result.appPathOverride = {
|
|
12427
|
+
type: "string",
|
|
12428
|
+
default: "",
|
|
12429
|
+
public: true,
|
|
12430
|
+
label: "App path override",
|
|
12431
|
+
description: "Optional absolute path for the IDE app bundle or executable. Leave blank to use the default install locations."
|
|
12432
|
+
};
|
|
12433
|
+
}
|
|
12434
|
+
}
|
|
12435
|
+
return result;
|
|
12436
|
+
}
|
|
11959
12437
|
// ─── Private ───────────────────────────────────
|
|
11960
12438
|
/**
|
|
11961
12439
|
* Find the on-disk directory for a provider by type.
|
|
@@ -11969,17 +12447,17 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11969
12447
|
for (const root of searchRoots) {
|
|
11970
12448
|
if (!fs6.existsSync(root)) continue;
|
|
11971
12449
|
const candidate = this.getProviderDir(root, cat, type);
|
|
11972
|
-
if (fs6.existsSync(
|
|
11973
|
-
const catDir =
|
|
12450
|
+
if (fs6.existsSync(path12.join(candidate, "provider.json"))) return candidate;
|
|
12451
|
+
const catDir = path12.join(root, cat);
|
|
11974
12452
|
if (fs6.existsSync(catDir)) {
|
|
11975
12453
|
try {
|
|
11976
12454
|
for (const entry of fs6.readdirSync(catDir, { withFileTypes: true })) {
|
|
11977
12455
|
if (!entry.isDirectory()) continue;
|
|
11978
|
-
const jsonPath =
|
|
12456
|
+
const jsonPath = path12.join(catDir, entry.name, "provider.json");
|
|
11979
12457
|
if (fs6.existsSync(jsonPath)) {
|
|
11980
12458
|
try {
|
|
11981
12459
|
const data = JSON.parse(fs6.readFileSync(jsonPath, "utf-8"));
|
|
11982
|
-
if (data.type === type) return
|
|
12460
|
+
if (data.type === type) return path12.join(catDir, entry.name);
|
|
11983
12461
|
} catch {
|
|
11984
12462
|
}
|
|
11985
12463
|
}
|
|
@@ -11996,7 +12474,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
11996
12474
|
* (template substitution is NOT applied here — scripts.js handles that)
|
|
11997
12475
|
*/
|
|
11998
12476
|
buildScriptWrappersFromDir(dir) {
|
|
11999
|
-
const scriptsJs =
|
|
12477
|
+
const scriptsJs = path12.join(dir, "scripts.js");
|
|
12000
12478
|
if (fs6.existsSync(scriptsJs)) {
|
|
12001
12479
|
try {
|
|
12002
12480
|
delete __require.cache[__require.resolve(scriptsJs)];
|
|
@@ -12010,7 +12488,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
12010
12488
|
for (const file of fs6.readdirSync(dir)) {
|
|
12011
12489
|
if (!file.endsWith(".js")) continue;
|
|
12012
12490
|
const scriptName = toCamel(file.replace(".js", ""));
|
|
12013
|
-
const filePath =
|
|
12491
|
+
const filePath = path12.join(dir, file);
|
|
12014
12492
|
result[scriptName] = (...args) => {
|
|
12015
12493
|
try {
|
|
12016
12494
|
let content = fs6.readFileSync(filePath, "utf-8");
|
|
@@ -12070,7 +12548,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
12070
12548
|
}
|
|
12071
12549
|
const hasJson = entries.some((e) => e.name === "provider.json");
|
|
12072
12550
|
if (hasJson) {
|
|
12073
|
-
const jsonPath =
|
|
12551
|
+
const jsonPath = path12.join(d, "provider.json");
|
|
12074
12552
|
try {
|
|
12075
12553
|
const raw = fs6.readFileSync(jsonPath, "utf-8");
|
|
12076
12554
|
const mod = JSON.parse(raw);
|
|
@@ -12083,7 +12561,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
12083
12561
|
delete mod.extensionIdPattern_flags;
|
|
12084
12562
|
}
|
|
12085
12563
|
const hasCompatibility = Array.isArray(mod.compatibility);
|
|
12086
|
-
const scriptsPath =
|
|
12564
|
+
const scriptsPath = path12.join(d, "scripts.js");
|
|
12087
12565
|
if (!hasCompatibility && fs6.existsSync(scriptsPath)) {
|
|
12088
12566
|
try {
|
|
12089
12567
|
delete __require.cache[__require.resolve(scriptsPath)];
|
|
@@ -12109,7 +12587,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
12109
12587
|
if (!entry.isDirectory()) continue;
|
|
12110
12588
|
if (entry.name.startsWith("_") || entry.name.startsWith(".")) continue;
|
|
12111
12589
|
if (excludeDirs && d === dir && excludeDirs.includes(entry.name)) continue;
|
|
12112
|
-
scan(
|
|
12590
|
+
scan(path12.join(d, entry.name));
|
|
12113
12591
|
}
|
|
12114
12592
|
}
|
|
12115
12593
|
};
|
|
@@ -12205,17 +12683,17 @@ async function findFreePort(ports) {
|
|
|
12205
12683
|
throw new Error("No free port found");
|
|
12206
12684
|
}
|
|
12207
12685
|
function checkPortFree(port) {
|
|
12208
|
-
return new Promise((
|
|
12686
|
+
return new Promise((resolve12) => {
|
|
12209
12687
|
const server = net.createServer();
|
|
12210
12688
|
server.unref();
|
|
12211
|
-
server.on("error", () =>
|
|
12689
|
+
server.on("error", () => resolve12(false));
|
|
12212
12690
|
server.listen(port, "127.0.0.1", () => {
|
|
12213
|
-
server.close(() =>
|
|
12691
|
+
server.close(() => resolve12(true));
|
|
12214
12692
|
});
|
|
12215
12693
|
});
|
|
12216
12694
|
}
|
|
12217
12695
|
async function isCdpActive(port) {
|
|
12218
|
-
return new Promise((
|
|
12696
|
+
return new Promise((resolve12) => {
|
|
12219
12697
|
const req = __require("http").get(`http://127.0.0.1:${port}/json/version`, {
|
|
12220
12698
|
timeout: 2e3
|
|
12221
12699
|
}, (res) => {
|
|
@@ -12224,16 +12702,16 @@ async function isCdpActive(port) {
|
|
|
12224
12702
|
res.on("end", () => {
|
|
12225
12703
|
try {
|
|
12226
12704
|
const info = JSON.parse(data);
|
|
12227
|
-
|
|
12705
|
+
resolve12(!!info["WebKit-Version"] || !!info["Browser"]);
|
|
12228
12706
|
} catch {
|
|
12229
|
-
|
|
12707
|
+
resolve12(false);
|
|
12230
12708
|
}
|
|
12231
12709
|
});
|
|
12232
12710
|
});
|
|
12233
|
-
req.on("error", () =>
|
|
12711
|
+
req.on("error", () => resolve12(false));
|
|
12234
12712
|
req.on("timeout", () => {
|
|
12235
12713
|
req.destroy();
|
|
12236
|
-
|
|
12714
|
+
resolve12(false);
|
|
12237
12715
|
});
|
|
12238
12716
|
});
|
|
12239
12717
|
}
|
|
@@ -12367,8 +12845,8 @@ function detectCurrentWorkspace(ideId) {
|
|
|
12367
12845
|
const appNameMap = getMacAppIdentifiers();
|
|
12368
12846
|
const appName = appNameMap[ideId];
|
|
12369
12847
|
if (appName) {
|
|
12370
|
-
const storagePath =
|
|
12371
|
-
process.env.APPDATA ||
|
|
12848
|
+
const storagePath = path13.join(
|
|
12849
|
+
process.env.APPDATA || path13.join(os12.homedir(), "AppData", "Roaming"),
|
|
12372
12850
|
appName,
|
|
12373
12851
|
"storage.json"
|
|
12374
12852
|
);
|
|
@@ -12392,7 +12870,7 @@ function detectCurrentWorkspace(ideId) {
|
|
|
12392
12870
|
async function launchWithCdp(options = {}) {
|
|
12393
12871
|
const platform9 = os12.platform();
|
|
12394
12872
|
let targetIde;
|
|
12395
|
-
const ides = await detectIDEs();
|
|
12873
|
+
const ides = await detectIDEs(getProviderLoader());
|
|
12396
12874
|
if (options.ideId) {
|
|
12397
12875
|
targetIde = ides.find((i) => i.id === options.ideId && i.installed);
|
|
12398
12876
|
if (!targetIde) {
|
|
@@ -12546,9 +13024,9 @@ init_logger();
|
|
|
12546
13024
|
|
|
12547
13025
|
// src/logging/command-log.ts
|
|
12548
13026
|
import * as fs7 from "fs";
|
|
12549
|
-
import * as
|
|
13027
|
+
import * as path14 from "path";
|
|
12550
13028
|
import * as os13 from "os";
|
|
12551
|
-
var LOG_DIR2 = process.platform === "win32" ?
|
|
13029
|
+
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");
|
|
12552
13030
|
var MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
12553
13031
|
var MAX_DAYS = 7;
|
|
12554
13032
|
try {
|
|
@@ -12586,13 +13064,13 @@ function getDateStr2() {
|
|
|
12586
13064
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
12587
13065
|
}
|
|
12588
13066
|
var currentDate2 = getDateStr2();
|
|
12589
|
-
var currentFile =
|
|
13067
|
+
var currentFile = path14.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
|
|
12590
13068
|
var writeCount2 = 0;
|
|
12591
13069
|
function checkRotation() {
|
|
12592
13070
|
const today = getDateStr2();
|
|
12593
13071
|
if (today !== currentDate2) {
|
|
12594
13072
|
currentDate2 = today;
|
|
12595
|
-
currentFile =
|
|
13073
|
+
currentFile = path14.join(LOG_DIR2, `commands-${currentDate2}.jsonl`);
|
|
12596
13074
|
cleanOldFiles();
|
|
12597
13075
|
}
|
|
12598
13076
|
}
|
|
@@ -12606,7 +13084,7 @@ function cleanOldFiles() {
|
|
|
12606
13084
|
const dateMatch = file.match(/commands-(\d{4}-\d{2}-\d{2})/);
|
|
12607
13085
|
if (dateMatch && dateMatch[1] < cutoffStr) {
|
|
12608
13086
|
try {
|
|
12609
|
-
fs7.unlinkSync(
|
|
13087
|
+
fs7.unlinkSync(path14.join(LOG_DIR2, file));
|
|
12610
13088
|
} catch {
|
|
12611
13089
|
}
|
|
12612
13090
|
}
|
|
@@ -12698,12 +13176,15 @@ function buildDetectedIdeInfos(detectedIdes, cdpManagers) {
|
|
|
12698
13176
|
}));
|
|
12699
13177
|
}
|
|
12700
13178
|
function buildAvailableProviders(providerLoader) {
|
|
12701
|
-
|
|
13179
|
+
const providers = providerLoader.getAvailableProviderInfos?.() || providerLoader.getAll();
|
|
13180
|
+
return providers.map((provider) => ({
|
|
12702
13181
|
type: provider.type,
|
|
12703
13182
|
name: provider.displayName || provider.type,
|
|
12704
13183
|
displayName: provider.displayName || provider.type,
|
|
12705
13184
|
icon: provider.icon || "\u{1F4BB}",
|
|
12706
|
-
category: provider.category
|
|
13185
|
+
category: provider.category,
|
|
13186
|
+
...provider.installed !== void 0 ? { installed: provider.installed } : {},
|
|
13187
|
+
...provider.detectedPath !== void 0 ? { detectedPath: provider.detectedPath } : {}
|
|
12707
13188
|
}));
|
|
12708
13189
|
}
|
|
12709
13190
|
function parseMessageTime(value) {
|
|
@@ -12831,13 +13312,13 @@ import { execFileSync } from "child_process";
|
|
|
12831
13312
|
import { spawn as spawn3 } from "child_process";
|
|
12832
13313
|
import * as fs8 from "fs";
|
|
12833
13314
|
import * as os15 from "os";
|
|
12834
|
-
import * as
|
|
13315
|
+
import * as path15 from "path";
|
|
12835
13316
|
var UPGRADE_HELPER_ENV = "ADHDEV_DAEMON_UPGRADE_HELPER";
|
|
12836
13317
|
function getUpgradeLogPath() {
|
|
12837
13318
|
const home = os15.homedir();
|
|
12838
|
-
const dir =
|
|
13319
|
+
const dir = path15.join(home, ".adhdev");
|
|
12839
13320
|
fs8.mkdirSync(dir, { recursive: true });
|
|
12840
|
-
return
|
|
13321
|
+
return path15.join(dir, "daemon-upgrade.log");
|
|
12841
13322
|
}
|
|
12842
13323
|
function appendUpgradeLog(message) {
|
|
12843
13324
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
|
|
@@ -12870,14 +13351,14 @@ async function waitForPidExit(pid, timeoutMs) {
|
|
|
12870
13351
|
while (Date.now() - start < timeoutMs) {
|
|
12871
13352
|
try {
|
|
12872
13353
|
process.kill(pid, 0);
|
|
12873
|
-
await new Promise((
|
|
13354
|
+
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
12874
13355
|
} catch {
|
|
12875
13356
|
return;
|
|
12876
13357
|
}
|
|
12877
13358
|
}
|
|
12878
13359
|
}
|
|
12879
13360
|
function stopSessionHostProcesses(appName) {
|
|
12880
|
-
const pidFile =
|
|
13361
|
+
const pidFile = path15.join(os15.homedir(), ".adhdev", `${appName}-session-host.pid`);
|
|
12881
13362
|
try {
|
|
12882
13363
|
if (fs8.existsSync(pidFile)) {
|
|
12883
13364
|
const pid = Number.parseInt(fs8.readFileSync(pidFile, "utf8").trim(), 10);
|
|
@@ -12906,7 +13387,7 @@ function stopSessionHostProcesses(appName) {
|
|
|
12906
13387
|
}
|
|
12907
13388
|
}
|
|
12908
13389
|
function removeDaemonPidFile() {
|
|
12909
|
-
const pidFile =
|
|
13390
|
+
const pidFile = path15.join(os15.homedir(), ".adhdev", "daemon.pid");
|
|
12910
13391
|
try {
|
|
12911
13392
|
fs8.unlinkSync(pidFile);
|
|
12912
13393
|
} catch {
|
|
@@ -12917,7 +13398,7 @@ function cleanupStaleGlobalInstallDirs(pkgName) {
|
|
|
12917
13398
|
const npmRoot = execFileSync(getNpmExecutable(), ["root", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
|
|
12918
13399
|
if (!npmRoot) return;
|
|
12919
13400
|
const npmPrefix = execFileSync(getNpmExecutable(), ["prefix", "-g"], { encoding: "utf8", ...npmExecOpts }).trim();
|
|
12920
|
-
const binDir = process.platform === "win32" ? npmPrefix :
|
|
13401
|
+
const binDir = process.platform === "win32" ? npmPrefix : path15.join(npmPrefix, "bin");
|
|
12921
13402
|
const packageBaseName = pkgName.startsWith("@") ? pkgName.split("/")[1] : pkgName;
|
|
12922
13403
|
const binNames = /* @__PURE__ */ new Set([packageBaseName]);
|
|
12923
13404
|
if (pkgName === "@adhdev/daemon-standalone") {
|
|
@@ -12925,25 +13406,25 @@ function cleanupStaleGlobalInstallDirs(pkgName) {
|
|
|
12925
13406
|
}
|
|
12926
13407
|
if (pkgName.startsWith("@")) {
|
|
12927
13408
|
const [scope, name] = pkgName.split("/");
|
|
12928
|
-
const scopeDir =
|
|
13409
|
+
const scopeDir = path15.join(npmRoot, scope);
|
|
12929
13410
|
if (!fs8.existsSync(scopeDir)) return;
|
|
12930
13411
|
for (const entry of fs8.readdirSync(scopeDir)) {
|
|
12931
13412
|
if (!entry.startsWith(`.${name}-`)) continue;
|
|
12932
|
-
fs8.rmSync(
|
|
12933
|
-
appendUpgradeLog(`Removed stale scoped staging dir: ${
|
|
13413
|
+
fs8.rmSync(path15.join(scopeDir, entry), { recursive: true, force: true });
|
|
13414
|
+
appendUpgradeLog(`Removed stale scoped staging dir: ${path15.join(scopeDir, entry)}`);
|
|
12934
13415
|
}
|
|
12935
13416
|
} else {
|
|
12936
13417
|
for (const entry of fs8.readdirSync(npmRoot)) {
|
|
12937
13418
|
if (!entry.startsWith(`.${pkgName}-`)) continue;
|
|
12938
|
-
fs8.rmSync(
|
|
12939
|
-
appendUpgradeLog(`Removed stale staging dir: ${
|
|
13419
|
+
fs8.rmSync(path15.join(npmRoot, entry), { recursive: true, force: true });
|
|
13420
|
+
appendUpgradeLog(`Removed stale staging dir: ${path15.join(npmRoot, entry)}`);
|
|
12940
13421
|
}
|
|
12941
13422
|
}
|
|
12942
13423
|
if (fs8.existsSync(binDir)) {
|
|
12943
13424
|
for (const entry of fs8.readdirSync(binDir)) {
|
|
12944
13425
|
if (![...binNames].some((name) => entry.startsWith(`.${name}-`))) continue;
|
|
12945
|
-
fs8.rmSync(
|
|
12946
|
-
appendUpgradeLog(`Removed stale bin staging entry: ${
|
|
13426
|
+
fs8.rmSync(path15.join(binDir, entry), { recursive: true, force: true });
|
|
13427
|
+
appendUpgradeLog(`Removed stale bin staging entry: ${path15.join(binDir, entry)}`);
|
|
12947
13428
|
}
|
|
12948
13429
|
}
|
|
12949
13430
|
}
|
|
@@ -12985,7 +13466,7 @@ async function runDaemonUpgradeHelper(payload) {
|
|
|
12985
13466
|
appendUpgradeLog(installOutput.trim());
|
|
12986
13467
|
}
|
|
12987
13468
|
if (process.platform === "win32") {
|
|
12988
|
-
await new Promise((
|
|
13469
|
+
await new Promise((resolve12) => setTimeout(resolve12, 500));
|
|
12989
13470
|
cleanupStaleGlobalInstallDirs(payload.packageName);
|
|
12990
13471
|
appendUpgradeLog("Post-install staging cleanup complete");
|
|
12991
13472
|
}
|
|
@@ -13181,6 +13662,15 @@ var DaemonCommandRouter = class {
|
|
|
13181
13662
|
const record = await this.deps.sessionHostControl.forceDetachClient(sessionId, clientId);
|
|
13182
13663
|
return { success: true, record };
|
|
13183
13664
|
}
|
|
13665
|
+
case "session_host_prune_duplicate_sessions": {
|
|
13666
|
+
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
13667
|
+
const result = await this.deps.sessionHostControl.pruneDuplicateSessions({
|
|
13668
|
+
providerType: typeof args?.providerType === "string" ? args.providerType : void 0,
|
|
13669
|
+
workspace: typeof args?.workspace === "string" ? args.workspace : void 0,
|
|
13670
|
+
dryRun: args?.dryRun === true
|
|
13671
|
+
});
|
|
13672
|
+
return { success: true, result };
|
|
13673
|
+
}
|
|
13184
13674
|
case "session_host_acquire_write": {
|
|
13185
13675
|
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
13186
13676
|
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
@@ -13263,6 +13753,12 @@ var DaemonCommandRouter = class {
|
|
|
13263
13753
|
if (!ideType) throw new Error("ideType required");
|
|
13264
13754
|
const killProcess = args?.killProcess !== false;
|
|
13265
13755
|
await this.stopIde(ideType, killProcess);
|
|
13756
|
+
try {
|
|
13757
|
+
const results = await detectIDEs(this.deps.providerLoader);
|
|
13758
|
+
this.deps.detectedIdes.value = results;
|
|
13759
|
+
this.deps.providerLoader.setIdeDetectionResults(results, true);
|
|
13760
|
+
} catch {
|
|
13761
|
+
}
|
|
13266
13762
|
return { success: true, ideType, stopped: true, processKilled: killProcess };
|
|
13267
13763
|
}
|
|
13268
13764
|
// ─── IDE restart ───
|
|
@@ -13305,6 +13801,12 @@ var DaemonCommandRouter = class {
|
|
|
13305
13801
|
}
|
|
13306
13802
|
}
|
|
13307
13803
|
this.deps.onIdeConnected?.();
|
|
13804
|
+
try {
|
|
13805
|
+
const results = await detectIDEs(this.deps.providerLoader);
|
|
13806
|
+
this.deps.detectedIdes.value = results;
|
|
13807
|
+
this.deps.providerLoader.setIdeDetectionResults(results, true);
|
|
13808
|
+
} catch {
|
|
13809
|
+
}
|
|
13308
13810
|
if (result.success && resolvedWorkspace) {
|
|
13309
13811
|
try {
|
|
13310
13812
|
const next = appendRecentActivity(loadState(), {
|
|
@@ -13332,8 +13834,9 @@ var DaemonCommandRouter = class {
|
|
|
13332
13834
|
}
|
|
13333
13835
|
// ─── Detect IDEs ───
|
|
13334
13836
|
case "detect_ides": {
|
|
13335
|
-
const results = await detectIDEs();
|
|
13837
|
+
const results = await detectIDEs(this.deps.providerLoader);
|
|
13336
13838
|
this.deps.detectedIdes.value = results;
|
|
13839
|
+
this.deps.providerLoader.setIdeDetectionResults(results, true);
|
|
13337
13840
|
return { success: true, detectedInfo: results };
|
|
13338
13841
|
}
|
|
13339
13842
|
// ─── Set User Name ───
|
|
@@ -13753,6 +14256,51 @@ var ProviderStreamAdapter = class {
|
|
|
13753
14256
|
isTransportError(reason) {
|
|
13754
14257
|
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);
|
|
13755
14258
|
}
|
|
14259
|
+
titlesMatch(actual, expected) {
|
|
14260
|
+
const lhs = actual.trim().toLowerCase();
|
|
14261
|
+
const rhs = expected.trim().toLowerCase();
|
|
14262
|
+
if (!lhs || !rhs) return false;
|
|
14263
|
+
return lhs === rhs || lhs.includes(rhs) || rhs.includes(lhs);
|
|
14264
|
+
}
|
|
14265
|
+
messageCount(state) {
|
|
14266
|
+
return Array.isArray(state?.messages) ? state.messages.length : 0;
|
|
14267
|
+
}
|
|
14268
|
+
lastMessageSignature(state) {
|
|
14269
|
+
const messages = Array.isArray(state?.messages) ? state.messages : [];
|
|
14270
|
+
const last = messages[messages.length - 1];
|
|
14271
|
+
if (!last) return "";
|
|
14272
|
+
return `${last.role || ""}:${String(last.content || "").replace(/\s+/g, " ").trim()}`;
|
|
14273
|
+
}
|
|
14274
|
+
async verifySendOutcome(evaluate, before) {
|
|
14275
|
+
const beforeCount = this.messageCount(before);
|
|
14276
|
+
const beforeSignature = this.lastMessageSignature(before);
|
|
14277
|
+
for (let attempt = 0; attempt < 12; attempt += 1) {
|
|
14278
|
+
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
14279
|
+
let state;
|
|
14280
|
+
try {
|
|
14281
|
+
state = await this.readChat(evaluate);
|
|
14282
|
+
} catch {
|
|
14283
|
+
continue;
|
|
14284
|
+
}
|
|
14285
|
+
if (state.status === "waiting_approval") {
|
|
14286
|
+
return true;
|
|
14287
|
+
}
|
|
14288
|
+
const afterCount = this.messageCount(state);
|
|
14289
|
+
const afterSignature = this.lastMessageSignature(state);
|
|
14290
|
+
if (afterCount > beforeCount) return true;
|
|
14291
|
+
if (afterSignature && afterSignature !== beforeSignature) return true;
|
|
14292
|
+
}
|
|
14293
|
+
return false;
|
|
14294
|
+
}
|
|
14295
|
+
async readStableBaselineState(evaluate) {
|
|
14296
|
+
const first = await this.readChat(evaluate);
|
|
14297
|
+
if (this.messageCount(first) > 0 || this.lastMessageSignature(first)) {
|
|
14298
|
+
return first;
|
|
14299
|
+
}
|
|
14300
|
+
await new Promise((resolve12) => setTimeout(resolve12, 150));
|
|
14301
|
+
const second = await this.readChat(evaluate);
|
|
14302
|
+
return this.messageCount(second) >= this.messageCount(first) ? second : first;
|
|
14303
|
+
}
|
|
13756
14304
|
async readChat(evaluate) {
|
|
13757
14305
|
const script = this.callScript("readChat");
|
|
13758
14306
|
if (!script) return this.errorState("readChat script not available");
|
|
@@ -13778,6 +14326,9 @@ var ProviderStreamAdapter = class {
|
|
|
13778
14326
|
mode: data.mode,
|
|
13779
14327
|
activeModal: data.activeModal
|
|
13780
14328
|
};
|
|
14329
|
+
if (typeof data.title === "string" && data.title.trim()) {
|
|
14330
|
+
state.title = data.title.trim();
|
|
14331
|
+
}
|
|
13781
14332
|
const controlValues = extractProviderControlValues(this.provider.controls, data);
|
|
13782
14333
|
if (controlValues) state.controlValues = controlValues;
|
|
13783
14334
|
const effects = normalizeProviderEffects(data);
|
|
@@ -13801,6 +14352,12 @@ var ProviderStreamAdapter = class {
|
|
|
13801
14352
|
}
|
|
13802
14353
|
}
|
|
13803
14354
|
async sendMessage(evaluate, text) {
|
|
14355
|
+
let beforeState = null;
|
|
14356
|
+
try {
|
|
14357
|
+
beforeState = await this.readStableBaselineState(evaluate);
|
|
14358
|
+
} catch {
|
|
14359
|
+
beforeState = null;
|
|
14360
|
+
}
|
|
13804
14361
|
const params = { message: text };
|
|
13805
14362
|
const script = this.callScript("sendMessage", params) || this.callScript("sendMessage", text);
|
|
13806
14363
|
if (!script) throw new Error(`[${this.agentName}] sendMessage script not available`);
|
|
@@ -13818,7 +14375,9 @@ var ProviderStreamAdapter = class {
|
|
|
13818
14375
|
}
|
|
13819
14376
|
if (parsed && typeof parsed === "object") {
|
|
13820
14377
|
if (parsed.sent === true || parsed.success === true || parsed.ok === true || parsed.submitted === true || parsed.dispatched === true) {
|
|
13821
|
-
|
|
14378
|
+
const verified = await this.verifySendOutcome(evaluate, beforeState);
|
|
14379
|
+
if (verified) return;
|
|
14380
|
+
throw new Error(`[${this.agentName}] sendMessage was not observed in chat state`);
|
|
13822
14381
|
}
|
|
13823
14382
|
if (typeof parsed.error === "string" && parsed.error.trim()) {
|
|
13824
14383
|
throw new Error(`[${this.agentName}] sendMessage failed: ${parsed.error}`);
|
|
@@ -13829,7 +14388,15 @@ var ProviderStreamAdapter = class {
|
|
|
13829
14388
|
async resolveAction(evaluate, action, button) {
|
|
13830
14389
|
const script = this.callScript("resolveAction", { action, button });
|
|
13831
14390
|
if (!script) return false;
|
|
13832
|
-
|
|
14391
|
+
const result = await evaluate(script);
|
|
14392
|
+
const parsed = this.parseMaybeJson(result);
|
|
14393
|
+
if (parsed === true) return true;
|
|
14394
|
+
if (typeof parsed === "string") {
|
|
14395
|
+
const normalized = parsed.trim().toLowerCase();
|
|
14396
|
+
return normalized === "ok" || normalized === "success" || normalized === "true" || normalized === "resolved" || normalized === "approved" || normalized === "rejected";
|
|
14397
|
+
}
|
|
14398
|
+
if (!parsed || typeof parsed !== "object") return false;
|
|
14399
|
+
return parsed.resolved === true || parsed.success === true || parsed.ok === true || parsed.found === true;
|
|
13833
14400
|
}
|
|
13834
14401
|
async newSession(evaluate) {
|
|
13835
14402
|
const script = this.callScript("newSession");
|
|
@@ -13847,7 +14414,10 @@ var ProviderStreamAdapter = class {
|
|
|
13847
14414
|
const raw = await evaluate(script, 1e4);
|
|
13848
14415
|
const data = typeof raw === "string" ? JSON.parse(raw) : raw;
|
|
13849
14416
|
if (data?.error) return [];
|
|
13850
|
-
|
|
14417
|
+
if (Array.isArray(data)) return data;
|
|
14418
|
+
if (Array.isArray(data?.sessions)) return data.sessions;
|
|
14419
|
+
if (Array.isArray(data?.chats)) return data.chats;
|
|
14420
|
+
return [];
|
|
13851
14421
|
} catch {
|
|
13852
14422
|
return [];
|
|
13853
14423
|
}
|
|
@@ -13855,7 +14425,24 @@ var ProviderStreamAdapter = class {
|
|
|
13855
14425
|
async switchSession(evaluate, sessionId) {
|
|
13856
14426
|
const script = this.callScript("switchSession", sessionId);
|
|
13857
14427
|
if (!script) return false;
|
|
13858
|
-
|
|
14428
|
+
const raw = await evaluate(script, 1e4);
|
|
14429
|
+
const data = this.parseMaybeJson(raw);
|
|
14430
|
+
if (data === true) return true;
|
|
14431
|
+
if (typeof data === "string") {
|
|
14432
|
+
const normalized = data.trim().toLowerCase();
|
|
14433
|
+
return normalized === "true" || normalized === "ok" || normalized === "switched" || normalized === "success";
|
|
14434
|
+
}
|
|
14435
|
+
if (data && typeof data === "object") {
|
|
14436
|
+
if (data.switched === true || data.success === true || data.ok === true) return true;
|
|
14437
|
+
if (typeof data.error === "string" && data.error.trim()) return false;
|
|
14438
|
+
}
|
|
14439
|
+
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
14440
|
+
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
14441
|
+
const state = await this.readChat(evaluate);
|
|
14442
|
+
const title = typeof state.title === "string" ? state.title : "";
|
|
14443
|
+
if (this.titlesMatch(title, sessionId)) return true;
|
|
14444
|
+
}
|
|
14445
|
+
return false;
|
|
13859
14446
|
}
|
|
13860
14447
|
async focusEditor(evaluate) {
|
|
13861
14448
|
const script = this.callScript("focusEditor");
|
|
@@ -14061,7 +14648,7 @@ var DaemonAgentStreamManager = class {
|
|
|
14061
14648
|
return false;
|
|
14062
14649
|
}
|
|
14063
14650
|
}
|
|
14064
|
-
async resolveSessionAction(cdp, sessionId, action) {
|
|
14651
|
+
async resolveSessionAction(cdp, sessionId, action, button) {
|
|
14065
14652
|
await this.ensureSessionPanelOpen(sessionId);
|
|
14066
14653
|
const target = this.getSessionTarget(sessionId);
|
|
14067
14654
|
if (!target?.parentSessionId) return false;
|
|
@@ -14071,7 +14658,7 @@ var DaemonAgentStreamManager = class {
|
|
|
14071
14658
|
if (!agent) return false;
|
|
14072
14659
|
try {
|
|
14073
14660
|
const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
|
|
14074
|
-
return await agent.adapter.resolveAction(evaluate, action);
|
|
14661
|
+
return await agent.adapter.resolveAction(evaluate, action, button);
|
|
14075
14662
|
} catch (e) {
|
|
14076
14663
|
this.logFn(`[AgentStream] resolveAction(${sessionId}) error: ${e.message}`);
|
|
14077
14664
|
return false;
|
|
@@ -14545,11 +15132,11 @@ var ProviderInstanceManager = class {
|
|
|
14545
15132
|
|
|
14546
15133
|
// src/providers/version-archive.ts
|
|
14547
15134
|
import * as fs10 from "fs";
|
|
14548
|
-
import * as
|
|
15135
|
+
import * as path16 from "path";
|
|
14549
15136
|
import * as os16 from "os";
|
|
14550
15137
|
import { execSync as execSync5 } from "child_process";
|
|
14551
15138
|
import { platform as platform7 } from "os";
|
|
14552
|
-
var ARCHIVE_PATH =
|
|
15139
|
+
var ARCHIVE_PATH = path16.join(os16.homedir(), ".adhdev", "version-history.json");
|
|
14553
15140
|
var MAX_ENTRIES_PER_PROVIDER = 20;
|
|
14554
15141
|
var VersionArchive = class {
|
|
14555
15142
|
history = {};
|
|
@@ -14596,7 +15183,7 @@ var VersionArchive = class {
|
|
|
14596
15183
|
}
|
|
14597
15184
|
save() {
|
|
14598
15185
|
try {
|
|
14599
|
-
fs10.mkdirSync(
|
|
15186
|
+
fs10.mkdirSync(path16.dirname(ARCHIVE_PATH), { recursive: true });
|
|
14600
15187
|
fs10.writeFileSync(ARCHIVE_PATH, JSON.stringify(this.history, null, 2));
|
|
14601
15188
|
} catch {
|
|
14602
15189
|
}
|
|
@@ -14637,7 +15224,7 @@ function checkPathExists2(paths) {
|
|
|
14637
15224
|
for (const p of paths) {
|
|
14638
15225
|
if (p.includes("*")) {
|
|
14639
15226
|
const home = os16.homedir();
|
|
14640
|
-
const resolved = p.replace(/\*/g, home.split(
|
|
15227
|
+
const resolved = p.replace(/\*/g, home.split(path16.sep).pop() || "");
|
|
14641
15228
|
if (fs10.existsSync(resolved)) return resolved;
|
|
14642
15229
|
} else {
|
|
14643
15230
|
if (fs10.existsSync(p)) return p;
|
|
@@ -14647,7 +15234,7 @@ function checkPathExists2(paths) {
|
|
|
14647
15234
|
}
|
|
14648
15235
|
function getMacAppVersion(appPath) {
|
|
14649
15236
|
if (platform7() !== "darwin" || !appPath.endsWith(".app")) return null;
|
|
14650
|
-
const plistPath =
|
|
15237
|
+
const plistPath = path16.join(appPath, "Contents", "Info.plist");
|
|
14651
15238
|
if (!fs10.existsSync(plistPath)) return null;
|
|
14652
15239
|
const raw = runCommand(`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${plistPath}"`);
|
|
14653
15240
|
return raw || null;
|
|
@@ -14674,7 +15261,7 @@ async function detectAllVersions(loader, archive) {
|
|
|
14674
15261
|
const cliBin = provider.cli ? findBinary2(provider.cli) : null;
|
|
14675
15262
|
let resolvedBin = cliBin;
|
|
14676
15263
|
if (!resolvedBin && appPath && currentOs === "darwin") {
|
|
14677
|
-
const bundled =
|
|
15264
|
+
const bundled = path16.join(appPath, "Contents", "Resources", "app", "bin", provider.cli || "");
|
|
14678
15265
|
if (provider.cli && fs10.existsSync(bundled)) resolvedBin = bundled;
|
|
14679
15266
|
}
|
|
14680
15267
|
info.installed = !!(appPath || resolvedBin);
|
|
@@ -14715,7 +15302,7 @@ async function detectAllVersions(loader, archive) {
|
|
|
14715
15302
|
// src/daemon/dev-server.ts
|
|
14716
15303
|
import * as http2 from "http";
|
|
14717
15304
|
import * as fs14 from "fs";
|
|
14718
|
-
import * as
|
|
15305
|
+
import * as path20 from "path";
|
|
14719
15306
|
|
|
14720
15307
|
// src/daemon/scaffold-template.ts
|
|
14721
15308
|
function generateFiles(type, name, category, opts = {}) {
|
|
@@ -15052,7 +15639,7 @@ init_logger();
|
|
|
15052
15639
|
// src/daemon/dev-cdp-handlers.ts
|
|
15053
15640
|
init_logger();
|
|
15054
15641
|
import * as fs11 from "fs";
|
|
15055
|
-
import * as
|
|
15642
|
+
import * as path17 from "path";
|
|
15056
15643
|
async function handleCdpEvaluate(ctx, req, res) {
|
|
15057
15644
|
const body = await ctx.readBody(req);
|
|
15058
15645
|
const { expression, timeout, ideType } = body;
|
|
@@ -15230,17 +15817,17 @@ async function handleScriptHints(ctx, type, _req, res) {
|
|
|
15230
15817
|
return;
|
|
15231
15818
|
}
|
|
15232
15819
|
let scriptsPath = "";
|
|
15233
|
-
const directScripts =
|
|
15820
|
+
const directScripts = path17.join(dir, "scripts.js");
|
|
15234
15821
|
if (fs11.existsSync(directScripts)) {
|
|
15235
15822
|
scriptsPath = directScripts;
|
|
15236
15823
|
} else {
|
|
15237
|
-
const scriptsDir =
|
|
15824
|
+
const scriptsDir = path17.join(dir, "scripts");
|
|
15238
15825
|
if (fs11.existsSync(scriptsDir)) {
|
|
15239
15826
|
const versions = fs11.readdirSync(scriptsDir).filter((d) => {
|
|
15240
|
-
return fs11.statSync(
|
|
15827
|
+
return fs11.statSync(path17.join(scriptsDir, d)).isDirectory();
|
|
15241
15828
|
}).sort().reverse();
|
|
15242
15829
|
for (const ver of versions) {
|
|
15243
|
-
const p =
|
|
15830
|
+
const p = path17.join(scriptsDir, ver, "scripts.js");
|
|
15244
15831
|
if (fs11.existsSync(p)) {
|
|
15245
15832
|
scriptsPath = p;
|
|
15246
15833
|
break;
|
|
@@ -16059,7 +16646,7 @@ async function handleDomContext(ctx, type, req, res) {
|
|
|
16059
16646
|
|
|
16060
16647
|
// src/daemon/dev-cli-debug.ts
|
|
16061
16648
|
import * as fs12 from "fs";
|
|
16062
|
-
import * as
|
|
16649
|
+
import * as path18 from "path";
|
|
16063
16650
|
function slugifyFixtureName(value) {
|
|
16064
16651
|
const normalized = String(value || "").trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
16065
16652
|
return normalized || `fixture-${Date.now()}`;
|
|
@@ -16069,11 +16656,11 @@ function getCliFixtureDir(ctx, type) {
|
|
|
16069
16656
|
if (!providerDir) {
|
|
16070
16657
|
throw new Error(`Provider directory not found for '${type}'`);
|
|
16071
16658
|
}
|
|
16072
|
-
return
|
|
16659
|
+
return path18.join(providerDir, "fixtures");
|
|
16073
16660
|
}
|
|
16074
16661
|
function readCliFixture(ctx, type, name) {
|
|
16075
16662
|
const fixtureDir = getCliFixtureDir(ctx, type);
|
|
16076
|
-
const filePath =
|
|
16663
|
+
const filePath = path18.join(fixtureDir, `${name}.json`);
|
|
16077
16664
|
if (!fs12.existsSync(filePath)) {
|
|
16078
16665
|
throw new Error(`Fixture not found: ${filePath}`);
|
|
16079
16666
|
}
|
|
@@ -16233,7 +16820,7 @@ function getCliTargetBundle(ctx, type, instanceId) {
|
|
|
16233
16820
|
return { target, instance, adapter };
|
|
16234
16821
|
}
|
|
16235
16822
|
function sleep(ms) {
|
|
16236
|
-
return new Promise((
|
|
16823
|
+
return new Promise((resolve12) => setTimeout(resolve12, ms));
|
|
16237
16824
|
}
|
|
16238
16825
|
async function waitForCliReady(ctx, type, instanceId, timeoutMs) {
|
|
16239
16826
|
const startedAt = Date.now();
|
|
@@ -16832,7 +17419,7 @@ async function handleCliFixtureCapture(ctx, req, res) {
|
|
|
16832
17419
|
},
|
|
16833
17420
|
notes: typeof body?.notes === "string" ? body.notes : void 0
|
|
16834
17421
|
};
|
|
16835
|
-
const filePath =
|
|
17422
|
+
const filePath = path18.join(fixtureDir, `${name}.json`);
|
|
16836
17423
|
fs12.writeFileSync(filePath, JSON.stringify(fixture, null, 2));
|
|
16837
17424
|
ctx.json(res, 200, {
|
|
16838
17425
|
saved: true,
|
|
@@ -16856,7 +17443,7 @@ async function handleCliFixtureList(ctx, type, _req, res) {
|
|
|
16856
17443
|
return;
|
|
16857
17444
|
}
|
|
16858
17445
|
const fixtures = fs12.readdirSync(fixtureDir).filter((file) => file.endsWith(".json")).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" })).map((file) => {
|
|
16859
|
-
const fullPath =
|
|
17446
|
+
const fullPath = path18.join(fixtureDir, file);
|
|
16860
17447
|
try {
|
|
16861
17448
|
const raw = JSON.parse(fs12.readFileSync(fullPath, "utf-8"));
|
|
16862
17449
|
return {
|
|
@@ -16992,7 +17579,7 @@ async function handleCliRaw(ctx, req, res) {
|
|
|
16992
17579
|
|
|
16993
17580
|
// src/daemon/dev-auto-implement.ts
|
|
16994
17581
|
import * as fs13 from "fs";
|
|
16995
|
-
import * as
|
|
17582
|
+
import * as path19 from "path";
|
|
16996
17583
|
import * as os17 from "os";
|
|
16997
17584
|
function getAutoImplPid(ctx) {
|
|
16998
17585
|
const proc = ctx.autoImplProcess;
|
|
@@ -17032,22 +17619,22 @@ function getLatestScriptVersionDir(scriptsDir) {
|
|
|
17032
17619
|
if (!fs13.existsSync(scriptsDir)) return null;
|
|
17033
17620
|
const versions = fs13.readdirSync(scriptsDir).filter((d) => {
|
|
17034
17621
|
try {
|
|
17035
|
-
return fs13.statSync(
|
|
17622
|
+
return fs13.statSync(path19.join(scriptsDir, d)).isDirectory();
|
|
17036
17623
|
} catch {
|
|
17037
17624
|
return false;
|
|
17038
17625
|
}
|
|
17039
17626
|
}).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" }));
|
|
17040
17627
|
if (versions.length === 0) return null;
|
|
17041
|
-
return
|
|
17628
|
+
return path19.join(scriptsDir, versions[0]);
|
|
17042
17629
|
}
|
|
17043
17630
|
function resolveAutoImplWritableProviderDir(ctx, category, type, requestedDir) {
|
|
17044
|
-
const canonicalUserDir =
|
|
17045
|
-
const desiredDir = requestedDir ?
|
|
17046
|
-
const upstreamRoot =
|
|
17047
|
-
if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${
|
|
17631
|
+
const canonicalUserDir = path19.resolve(ctx.providerLoader.getUserProviderDir(category, type));
|
|
17632
|
+
const desiredDir = requestedDir ? path19.resolve(requestedDir) : canonicalUserDir;
|
|
17633
|
+
const upstreamRoot = path19.resolve(ctx.providerLoader.getUpstreamDir());
|
|
17634
|
+
if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path19.sep}`)) {
|
|
17048
17635
|
return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
|
|
17049
17636
|
}
|
|
17050
|
-
if (
|
|
17637
|
+
if (path19.basename(desiredDir) !== type) {
|
|
17051
17638
|
return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
|
|
17052
17639
|
}
|
|
17053
17640
|
const sourceDir = ctx.findProviderDir(type);
|
|
@@ -17055,11 +17642,11 @@ function resolveAutoImplWritableProviderDir(ctx, category, type, requestedDir) {
|
|
|
17055
17642
|
return { dir: null, reason: `Provider source directory not found for '${type}'` };
|
|
17056
17643
|
}
|
|
17057
17644
|
if (!fs13.existsSync(desiredDir)) {
|
|
17058
|
-
fs13.mkdirSync(
|
|
17645
|
+
fs13.mkdirSync(path19.dirname(desiredDir), { recursive: true });
|
|
17059
17646
|
fs13.cpSync(sourceDir, desiredDir, { recursive: true });
|
|
17060
17647
|
ctx.log(`Auto-implement writable copy created: ${desiredDir}`);
|
|
17061
17648
|
}
|
|
17062
|
-
const providerJson =
|
|
17649
|
+
const providerJson = path19.join(desiredDir, "provider.json");
|
|
17063
17650
|
if (!fs13.existsSync(providerJson)) {
|
|
17064
17651
|
return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
|
|
17065
17652
|
}
|
|
@@ -17082,13 +17669,13 @@ function loadAutoImplReferenceScripts(ctx, referenceType) {
|
|
|
17082
17669
|
const refDir = ctx.findProviderDir(referenceType);
|
|
17083
17670
|
if (!refDir || !fs13.existsSync(refDir)) return {};
|
|
17084
17671
|
const referenceScripts = {};
|
|
17085
|
-
const scriptsDir =
|
|
17672
|
+
const scriptsDir = path19.join(refDir, "scripts");
|
|
17086
17673
|
const latestDir = getLatestScriptVersionDir(scriptsDir);
|
|
17087
17674
|
if (!latestDir) return referenceScripts;
|
|
17088
17675
|
for (const file of fs13.readdirSync(latestDir)) {
|
|
17089
17676
|
if (!file.endsWith(".js")) continue;
|
|
17090
17677
|
try {
|
|
17091
|
-
referenceScripts[file] = fs13.readFileSync(
|
|
17678
|
+
referenceScripts[file] = fs13.readFileSync(path19.join(latestDir, file), "utf-8");
|
|
17092
17679
|
} catch {
|
|
17093
17680
|
}
|
|
17094
17681
|
}
|
|
@@ -17196,9 +17783,9 @@ async function handleAutoImplement(ctx, type, req, res) {
|
|
|
17196
17783
|
});
|
|
17197
17784
|
const referenceScripts = loadAutoImplReferenceScripts(ctx, resolvedReference);
|
|
17198
17785
|
const prompt = buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domContext, referenceScripts, comment, resolvedReference, verification);
|
|
17199
|
-
const tmpDir =
|
|
17786
|
+
const tmpDir = path19.join(os17.tmpdir(), "adhdev-autoimpl");
|
|
17200
17787
|
if (!fs13.existsSync(tmpDir)) fs13.mkdirSync(tmpDir, { recursive: true });
|
|
17201
|
-
const promptFile =
|
|
17788
|
+
const promptFile = path19.join(tmpDir, `prompt-${type}-${Date.now()}.md`);
|
|
17202
17789
|
fs13.writeFileSync(promptFile, prompt, "utf-8");
|
|
17203
17790
|
ctx.log(`Auto-implement prompt written to ${promptFile} (${prompt.length} chars)`);
|
|
17204
17791
|
const agentProvider = ctx.providerLoader.resolve(agent) || ctx.providerLoader.getMeta(agent);
|
|
@@ -17625,7 +18212,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
|
|
|
17625
18212
|
setMode: "set_mode.js"
|
|
17626
18213
|
};
|
|
17627
18214
|
const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
|
|
17628
|
-
const scriptsDir =
|
|
18215
|
+
const scriptsDir = path19.join(providerDir, "scripts");
|
|
17629
18216
|
const latestScriptsDir = getLatestScriptVersionDir(scriptsDir);
|
|
17630
18217
|
if (latestScriptsDir) {
|
|
17631
18218
|
lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
|
|
@@ -17636,7 +18223,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
|
|
|
17636
18223
|
for (const file of fs13.readdirSync(latestScriptsDir)) {
|
|
17637
18224
|
if (file.endsWith(".js") && targetFileNames.has(file)) {
|
|
17638
18225
|
try {
|
|
17639
|
-
const content = fs13.readFileSync(
|
|
18226
|
+
const content = fs13.readFileSync(path19.join(latestScriptsDir, file), "utf-8");
|
|
17640
18227
|
lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
|
|
17641
18228
|
lines.push("```javascript");
|
|
17642
18229
|
lines.push(content);
|
|
@@ -17653,7 +18240,7 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
|
|
|
17653
18240
|
lines.push("");
|
|
17654
18241
|
for (const file of refFiles) {
|
|
17655
18242
|
try {
|
|
17656
|
-
const content = fs13.readFileSync(
|
|
18243
|
+
const content = fs13.readFileSync(path19.join(latestScriptsDir, file), "utf-8");
|
|
17657
18244
|
lines.push(`### \`${file}\` \u{1F512}`);
|
|
17658
18245
|
lines.push("```javascript");
|
|
17659
18246
|
lines.push(content);
|
|
@@ -17694,10 +18281,10 @@ function buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domCon
|
|
|
17694
18281
|
lines.push("");
|
|
17695
18282
|
}
|
|
17696
18283
|
}
|
|
17697
|
-
const docsDir =
|
|
18284
|
+
const docsDir = path19.join(providerDir, "../../docs");
|
|
17698
18285
|
const loadGuide = (name) => {
|
|
17699
18286
|
try {
|
|
17700
|
-
const p =
|
|
18287
|
+
const p = path19.join(docsDir, name);
|
|
17701
18288
|
if (fs13.existsSync(p)) return fs13.readFileSync(p, "utf-8");
|
|
17702
18289
|
} catch {
|
|
17703
18290
|
}
|
|
@@ -17932,7 +18519,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
|
|
|
17932
18519
|
parseApproval: "parse_approval.js"
|
|
17933
18520
|
};
|
|
17934
18521
|
const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
|
|
17935
|
-
const scriptsDir =
|
|
18522
|
+
const scriptsDir = path19.join(providerDir, "scripts");
|
|
17936
18523
|
const latestScriptsDir = getLatestScriptVersionDir(scriptsDir);
|
|
17937
18524
|
if (latestScriptsDir) {
|
|
17938
18525
|
lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
|
|
@@ -17944,7 +18531,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
|
|
|
17944
18531
|
if (!file.endsWith(".js")) continue;
|
|
17945
18532
|
if (!targetFileNames.has(file)) continue;
|
|
17946
18533
|
try {
|
|
17947
|
-
const content = fs13.readFileSync(
|
|
18534
|
+
const content = fs13.readFileSync(path19.join(latestScriptsDir, file), "utf-8");
|
|
17948
18535
|
lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
|
|
17949
18536
|
lines.push("```javascript");
|
|
17950
18537
|
lines.push(content);
|
|
@@ -17960,7 +18547,7 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
|
|
|
17960
18547
|
lines.push("");
|
|
17961
18548
|
for (const file of refFiles) {
|
|
17962
18549
|
try {
|
|
17963
|
-
const content = fs13.readFileSync(
|
|
18550
|
+
const content = fs13.readFileSync(path19.join(latestScriptsDir, file), "utf-8");
|
|
17964
18551
|
lines.push(`### \`${file}\` \u{1F512}`);
|
|
17965
18552
|
lines.push("```javascript");
|
|
17966
18553
|
lines.push(content);
|
|
@@ -17993,10 +18580,10 @@ function buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, ref
|
|
|
17993
18580
|
lines.push("");
|
|
17994
18581
|
}
|
|
17995
18582
|
}
|
|
17996
|
-
const docsDir =
|
|
18583
|
+
const docsDir = path19.join(providerDir, "../../docs");
|
|
17997
18584
|
const loadGuide = (name) => {
|
|
17998
18585
|
try {
|
|
17999
|
-
const p =
|
|
18586
|
+
const p = path19.join(docsDir, name);
|
|
18000
18587
|
if (fs13.existsSync(p)) return fs13.readFileSync(p, "utf-8");
|
|
18001
18588
|
} catch {
|
|
18002
18589
|
}
|
|
@@ -18407,8 +18994,8 @@ var DevServer = class _DevServer {
|
|
|
18407
18994
|
}
|
|
18408
18995
|
getEndpointList() {
|
|
18409
18996
|
return this.routes.map((r) => {
|
|
18410
|
-
const
|
|
18411
|
-
return `${r.method.padEnd(5)} ${
|
|
18997
|
+
const path21 = typeof r.pattern === "string" ? r.pattern : r.pattern.source.replace(/\\\//g, "/").replace(/\(\[.*?\]\+\)/g, ":type").replace(/[\^$]/g, "");
|
|
18998
|
+
return `${r.method.padEnd(5)} ${path21}`;
|
|
18412
18999
|
});
|
|
18413
19000
|
}
|
|
18414
19001
|
async start(port = DEV_SERVER_PORT) {
|
|
@@ -18439,15 +19026,15 @@ var DevServer = class _DevServer {
|
|
|
18439
19026
|
this.json(res, 500, { error: e.message });
|
|
18440
19027
|
}
|
|
18441
19028
|
});
|
|
18442
|
-
return new Promise((
|
|
19029
|
+
return new Promise((resolve12, reject) => {
|
|
18443
19030
|
this.server.listen(port, "127.0.0.1", () => {
|
|
18444
19031
|
this.log(`Dev server listening on http://127.0.0.1:${port}`);
|
|
18445
|
-
|
|
19032
|
+
resolve12();
|
|
18446
19033
|
});
|
|
18447
19034
|
this.server.on("error", (e) => {
|
|
18448
19035
|
if (e.code === "EADDRINUSE") {
|
|
18449
19036
|
this.log(`Port ${port} in use, skipping dev server`);
|
|
18450
|
-
|
|
19037
|
+
resolve12();
|
|
18451
19038
|
} else {
|
|
18452
19039
|
reject(e);
|
|
18453
19040
|
}
|
|
@@ -18530,20 +19117,20 @@ var DevServer = class _DevServer {
|
|
|
18530
19117
|
child.stderr?.on("data", (d) => {
|
|
18531
19118
|
stderr += d.toString().slice(0, 2e3);
|
|
18532
19119
|
});
|
|
18533
|
-
await new Promise((
|
|
19120
|
+
await new Promise((resolve12) => {
|
|
18534
19121
|
const timer = setTimeout(() => {
|
|
18535
19122
|
child.kill();
|
|
18536
|
-
|
|
19123
|
+
resolve12();
|
|
18537
19124
|
}, 3e3);
|
|
18538
19125
|
child.on("exit", () => {
|
|
18539
19126
|
clearTimeout(timer);
|
|
18540
|
-
|
|
19127
|
+
resolve12();
|
|
18541
19128
|
});
|
|
18542
19129
|
child.stdout?.once("data", () => {
|
|
18543
19130
|
setTimeout(() => {
|
|
18544
19131
|
child.kill();
|
|
18545
19132
|
clearTimeout(timer);
|
|
18546
|
-
|
|
19133
|
+
resolve12();
|
|
18547
19134
|
}, 500);
|
|
18548
19135
|
});
|
|
18549
19136
|
});
|
|
@@ -18690,12 +19277,12 @@ var DevServer = class _DevServer {
|
|
|
18690
19277
|
// ─── DevConsole SPA ───
|
|
18691
19278
|
getConsoleDistDir() {
|
|
18692
19279
|
const candidates = [
|
|
18693
|
-
|
|
18694
|
-
|
|
18695
|
-
|
|
19280
|
+
path20.resolve(__dirname, "../../web-devconsole/dist"),
|
|
19281
|
+
path20.resolve(__dirname, "../../../web-devconsole/dist"),
|
|
19282
|
+
path20.join(process.cwd(), "packages/web-devconsole/dist")
|
|
18696
19283
|
];
|
|
18697
19284
|
for (const dir of candidates) {
|
|
18698
|
-
if (fs14.existsSync(
|
|
19285
|
+
if (fs14.existsSync(path20.join(dir, "index.html"))) return dir;
|
|
18699
19286
|
}
|
|
18700
19287
|
return null;
|
|
18701
19288
|
}
|
|
@@ -18705,7 +19292,7 @@ var DevServer = class _DevServer {
|
|
|
18705
19292
|
this.json(res, 500, { error: "DevConsole not found. Run: npm run build -w packages/web-devconsole" });
|
|
18706
19293
|
return;
|
|
18707
19294
|
}
|
|
18708
|
-
const htmlPath =
|
|
19295
|
+
const htmlPath = path20.join(distDir, "index.html");
|
|
18709
19296
|
try {
|
|
18710
19297
|
const html = fs14.readFileSync(htmlPath, "utf-8");
|
|
18711
19298
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
@@ -18730,15 +19317,15 @@ var DevServer = class _DevServer {
|
|
|
18730
19317
|
this.json(res, 404, { error: "Not found" });
|
|
18731
19318
|
return;
|
|
18732
19319
|
}
|
|
18733
|
-
const safePath =
|
|
18734
|
-
const filePath =
|
|
19320
|
+
const safePath = path20.normalize(pathname).replace(/^\.\.\//, "");
|
|
19321
|
+
const filePath = path20.join(distDir, safePath);
|
|
18735
19322
|
if (!filePath.startsWith(distDir)) {
|
|
18736
19323
|
this.json(res, 403, { error: "Forbidden" });
|
|
18737
19324
|
return;
|
|
18738
19325
|
}
|
|
18739
19326
|
try {
|
|
18740
19327
|
const content = fs14.readFileSync(filePath);
|
|
18741
|
-
const ext =
|
|
19328
|
+
const ext = path20.extname(filePath);
|
|
18742
19329
|
const contentType = _DevServer.MIME_MAP[ext] || "application/octet-stream";
|
|
18743
19330
|
res.writeHead(200, { "Content-Type": contentType, "Cache-Control": "public, max-age=31536000, immutable" });
|
|
18744
19331
|
res.end(content);
|
|
@@ -18851,9 +19438,9 @@ var DevServer = class _DevServer {
|
|
|
18851
19438
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
18852
19439
|
if (entry.isDirectory()) {
|
|
18853
19440
|
files.push({ path: rel, size: 0, type: "dir" });
|
|
18854
|
-
scan(
|
|
19441
|
+
scan(path20.join(d, entry.name), rel);
|
|
18855
19442
|
} else {
|
|
18856
|
-
const stat = fs14.statSync(
|
|
19443
|
+
const stat = fs14.statSync(path20.join(d, entry.name));
|
|
18857
19444
|
files.push({ path: rel, size: stat.size, type: "file" });
|
|
18858
19445
|
}
|
|
18859
19446
|
}
|
|
@@ -18876,7 +19463,7 @@ var DevServer = class _DevServer {
|
|
|
18876
19463
|
this.json(res, 404, { error: `Provider directory not found: ${type}` });
|
|
18877
19464
|
return;
|
|
18878
19465
|
}
|
|
18879
|
-
const fullPath =
|
|
19466
|
+
const fullPath = path20.resolve(dir, path20.normalize(filePath));
|
|
18880
19467
|
if (!fullPath.startsWith(dir)) {
|
|
18881
19468
|
this.json(res, 403, { error: "Forbidden" });
|
|
18882
19469
|
return;
|
|
@@ -18901,14 +19488,14 @@ var DevServer = class _DevServer {
|
|
|
18901
19488
|
this.json(res, 404, { error: `Provider directory not found: ${type}` });
|
|
18902
19489
|
return;
|
|
18903
19490
|
}
|
|
18904
|
-
const fullPath =
|
|
19491
|
+
const fullPath = path20.resolve(dir, path20.normalize(filePath));
|
|
18905
19492
|
if (!fullPath.startsWith(dir)) {
|
|
18906
19493
|
this.json(res, 403, { error: "Forbidden" });
|
|
18907
19494
|
return;
|
|
18908
19495
|
}
|
|
18909
19496
|
try {
|
|
18910
19497
|
if (fs14.existsSync(fullPath)) fs14.copyFileSync(fullPath, fullPath + ".bak");
|
|
18911
|
-
fs14.mkdirSync(
|
|
19498
|
+
fs14.mkdirSync(path20.dirname(fullPath), { recursive: true });
|
|
18912
19499
|
fs14.writeFileSync(fullPath, content, "utf-8");
|
|
18913
19500
|
this.log(`File saved: ${fullPath} (${content.length} chars)`);
|
|
18914
19501
|
this.providerLoader.reload();
|
|
@@ -18925,7 +19512,7 @@ var DevServer = class _DevServer {
|
|
|
18925
19512
|
return;
|
|
18926
19513
|
}
|
|
18927
19514
|
for (const name of ["scripts.js", "provider.json"]) {
|
|
18928
|
-
const p =
|
|
19515
|
+
const p = path20.join(dir, name);
|
|
18929
19516
|
if (fs14.existsSync(p)) {
|
|
18930
19517
|
const source = fs14.readFileSync(p, "utf-8");
|
|
18931
19518
|
this.json(res, 200, { type, path: p, source, lines: source.split("\n").length });
|
|
@@ -18946,8 +19533,8 @@ var DevServer = class _DevServer {
|
|
|
18946
19533
|
this.json(res, 404, { error: `Provider not found: ${type}` });
|
|
18947
19534
|
return;
|
|
18948
19535
|
}
|
|
18949
|
-
const target = fs14.existsSync(
|
|
18950
|
-
const targetPath =
|
|
19536
|
+
const target = fs14.existsSync(path20.join(dir, "scripts.js")) ? "scripts.js" : "provider.json";
|
|
19537
|
+
const targetPath = path20.join(dir, target);
|
|
18951
19538
|
try {
|
|
18952
19539
|
if (fs14.existsSync(targetPath)) fs14.copyFileSync(targetPath, targetPath + ".bak");
|
|
18953
19540
|
fs14.writeFileSync(targetPath, source, "utf-8");
|
|
@@ -19052,14 +19639,14 @@ var DevServer = class _DevServer {
|
|
|
19052
19639
|
child.stderr?.on("data", (d) => {
|
|
19053
19640
|
stderr += d.toString();
|
|
19054
19641
|
});
|
|
19055
|
-
await new Promise((
|
|
19642
|
+
await new Promise((resolve12) => {
|
|
19056
19643
|
const timer = setTimeout(() => {
|
|
19057
19644
|
child.kill();
|
|
19058
|
-
|
|
19645
|
+
resolve12();
|
|
19059
19646
|
}, timeout);
|
|
19060
19647
|
child.on("exit", () => {
|
|
19061
19648
|
clearTimeout(timer);
|
|
19062
|
-
|
|
19649
|
+
resolve12();
|
|
19063
19650
|
});
|
|
19064
19651
|
});
|
|
19065
19652
|
const elapsed = Date.now() - start;
|
|
@@ -19107,7 +19694,7 @@ var DevServer = class _DevServer {
|
|
|
19107
19694
|
}
|
|
19108
19695
|
let targetDir;
|
|
19109
19696
|
targetDir = this.providerLoader.getUserProviderDir(category, type);
|
|
19110
|
-
const jsonPath =
|
|
19697
|
+
const jsonPath = path20.join(targetDir, "provider.json");
|
|
19111
19698
|
if (fs14.existsSync(jsonPath)) {
|
|
19112
19699
|
this.json(res, 409, { error: `Provider already exists at ${targetDir}`, path: targetDir });
|
|
19113
19700
|
return;
|
|
@@ -19119,8 +19706,8 @@ var DevServer = class _DevServer {
|
|
|
19119
19706
|
const createdFiles = ["provider.json"];
|
|
19120
19707
|
if (result.files) {
|
|
19121
19708
|
for (const [relPath, content] of Object.entries(result.files)) {
|
|
19122
|
-
const fullPath =
|
|
19123
|
-
fs14.mkdirSync(
|
|
19709
|
+
const fullPath = path20.join(targetDir, relPath);
|
|
19710
|
+
fs14.mkdirSync(path20.dirname(fullPath), { recursive: true });
|
|
19124
19711
|
fs14.writeFileSync(fullPath, content, "utf-8");
|
|
19125
19712
|
createdFiles.push(relPath);
|
|
19126
19713
|
}
|
|
@@ -19173,22 +19760,22 @@ var DevServer = class _DevServer {
|
|
|
19173
19760
|
if (!fs14.existsSync(scriptsDir)) return null;
|
|
19174
19761
|
const versions = fs14.readdirSync(scriptsDir).filter((d) => {
|
|
19175
19762
|
try {
|
|
19176
|
-
return fs14.statSync(
|
|
19763
|
+
return fs14.statSync(path20.join(scriptsDir, d)).isDirectory();
|
|
19177
19764
|
} catch {
|
|
19178
19765
|
return false;
|
|
19179
19766
|
}
|
|
19180
19767
|
}).sort((a, b) => b.localeCompare(a, void 0, { numeric: true, sensitivity: "base" }));
|
|
19181
19768
|
if (versions.length === 0) return null;
|
|
19182
|
-
return
|
|
19769
|
+
return path20.join(scriptsDir, versions[0]);
|
|
19183
19770
|
}
|
|
19184
19771
|
resolveAutoImplWritableProviderDir(category, type, requestedDir) {
|
|
19185
|
-
const canonicalUserDir =
|
|
19186
|
-
const desiredDir = requestedDir ?
|
|
19187
|
-
const upstreamRoot =
|
|
19188
|
-
if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${
|
|
19772
|
+
const canonicalUserDir = path20.resolve(this.providerLoader.getUserProviderDir(category, type));
|
|
19773
|
+
const desiredDir = requestedDir ? path20.resolve(requestedDir) : canonicalUserDir;
|
|
19774
|
+
const upstreamRoot = path20.resolve(this.providerLoader.getUpstreamDir());
|
|
19775
|
+
if (desiredDir === upstreamRoot || desiredDir.startsWith(`${upstreamRoot}${path20.sep}`)) {
|
|
19189
19776
|
return { dir: null, reason: `Refusing to write into upstream provider directory: ${desiredDir}` };
|
|
19190
19777
|
}
|
|
19191
|
-
if (
|
|
19778
|
+
if (path20.basename(desiredDir) !== type) {
|
|
19192
19779
|
return { dir: null, reason: `Requested writable provider directory must end with '${type}': ${desiredDir}` };
|
|
19193
19780
|
}
|
|
19194
19781
|
const sourceDir = this.findProviderDir(type);
|
|
@@ -19196,11 +19783,11 @@ var DevServer = class _DevServer {
|
|
|
19196
19783
|
return { dir: null, reason: `Provider source directory not found for '${type}'` };
|
|
19197
19784
|
}
|
|
19198
19785
|
if (!fs14.existsSync(desiredDir)) {
|
|
19199
|
-
fs14.mkdirSync(
|
|
19786
|
+
fs14.mkdirSync(path20.dirname(desiredDir), { recursive: true });
|
|
19200
19787
|
fs14.cpSync(sourceDir, desiredDir, { recursive: true });
|
|
19201
19788
|
this.log(`Auto-implement writable copy created: ${desiredDir}`);
|
|
19202
19789
|
}
|
|
19203
|
-
const providerJson =
|
|
19790
|
+
const providerJson = path20.join(desiredDir, "provider.json");
|
|
19204
19791
|
if (!fs14.existsSync(providerJson)) {
|
|
19205
19792
|
return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
|
|
19206
19793
|
}
|
|
@@ -19248,7 +19835,7 @@ var DevServer = class _DevServer {
|
|
|
19248
19835
|
setMode: "set_mode.js"
|
|
19249
19836
|
};
|
|
19250
19837
|
const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
|
|
19251
|
-
const scriptsDir =
|
|
19838
|
+
const scriptsDir = path20.join(providerDir, "scripts");
|
|
19252
19839
|
const latestScriptsDir = this.getLatestScriptVersionDir(scriptsDir);
|
|
19253
19840
|
if (latestScriptsDir) {
|
|
19254
19841
|
lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
|
|
@@ -19259,7 +19846,7 @@ var DevServer = class _DevServer {
|
|
|
19259
19846
|
for (const file of fs14.readdirSync(latestScriptsDir)) {
|
|
19260
19847
|
if (file.endsWith(".js") && targetFileNames.has(file)) {
|
|
19261
19848
|
try {
|
|
19262
|
-
const content = fs14.readFileSync(
|
|
19849
|
+
const content = fs14.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
|
|
19263
19850
|
lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
|
|
19264
19851
|
lines.push("```javascript");
|
|
19265
19852
|
lines.push(content);
|
|
@@ -19276,7 +19863,7 @@ var DevServer = class _DevServer {
|
|
|
19276
19863
|
lines.push("");
|
|
19277
19864
|
for (const file of refFiles) {
|
|
19278
19865
|
try {
|
|
19279
|
-
const content = fs14.readFileSync(
|
|
19866
|
+
const content = fs14.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
|
|
19280
19867
|
lines.push(`### \`${file}\` \u{1F512}`);
|
|
19281
19868
|
lines.push("```javascript");
|
|
19282
19869
|
lines.push(content);
|
|
@@ -19317,10 +19904,10 @@ var DevServer = class _DevServer {
|
|
|
19317
19904
|
lines.push("");
|
|
19318
19905
|
}
|
|
19319
19906
|
}
|
|
19320
|
-
const docsDir =
|
|
19907
|
+
const docsDir = path20.join(providerDir, "../../docs");
|
|
19321
19908
|
const loadGuide = (name) => {
|
|
19322
19909
|
try {
|
|
19323
|
-
const p =
|
|
19910
|
+
const p = path20.join(docsDir, name);
|
|
19324
19911
|
if (fs14.existsSync(p)) return fs14.readFileSync(p, "utf-8");
|
|
19325
19912
|
} catch {
|
|
19326
19913
|
}
|
|
@@ -19494,7 +20081,7 @@ var DevServer = class _DevServer {
|
|
|
19494
20081
|
parseApproval: "parse_approval.js"
|
|
19495
20082
|
};
|
|
19496
20083
|
const targetFileNames = new Set(functions.map((fn) => funcToFile[fn]).filter(Boolean));
|
|
19497
|
-
const scriptsDir =
|
|
20084
|
+
const scriptsDir = path20.join(providerDir, "scripts");
|
|
19498
20085
|
const latestScriptsDir = this.getLatestScriptVersionDir(scriptsDir);
|
|
19499
20086
|
if (latestScriptsDir) {
|
|
19500
20087
|
lines.push(`Scripts version directory: \`${latestScriptsDir}\``);
|
|
@@ -19506,7 +20093,7 @@ var DevServer = class _DevServer {
|
|
|
19506
20093
|
if (!file.endsWith(".js")) continue;
|
|
19507
20094
|
if (!targetFileNames.has(file)) continue;
|
|
19508
20095
|
try {
|
|
19509
|
-
const content = fs14.readFileSync(
|
|
20096
|
+
const content = fs14.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
|
|
19510
20097
|
lines.push(`### \`${file}\` \u270F\uFE0F EDIT`);
|
|
19511
20098
|
lines.push("```javascript");
|
|
19512
20099
|
lines.push(content);
|
|
@@ -19522,7 +20109,7 @@ var DevServer = class _DevServer {
|
|
|
19522
20109
|
lines.push("");
|
|
19523
20110
|
for (const file of refFiles) {
|
|
19524
20111
|
try {
|
|
19525
|
-
const content = fs14.readFileSync(
|
|
20112
|
+
const content = fs14.readFileSync(path20.join(latestScriptsDir, file), "utf-8");
|
|
19526
20113
|
lines.push(`### \`${file}\` \u{1F512}`);
|
|
19527
20114
|
lines.push("```javascript");
|
|
19528
20115
|
lines.push(content);
|
|
@@ -19555,10 +20142,10 @@ var DevServer = class _DevServer {
|
|
|
19555
20142
|
lines.push("");
|
|
19556
20143
|
}
|
|
19557
20144
|
}
|
|
19558
|
-
const docsDir =
|
|
20145
|
+
const docsDir = path20.join(providerDir, "../../docs");
|
|
19559
20146
|
const loadGuide = (name) => {
|
|
19560
20147
|
try {
|
|
19561
|
-
const p =
|
|
20148
|
+
const p = path20.join(docsDir, name);
|
|
19562
20149
|
if (fs14.existsSync(p)) return fs14.readFileSync(p, "utf-8");
|
|
19563
20150
|
} catch {
|
|
19564
20151
|
}
|
|
@@ -19734,14 +20321,14 @@ data: ${JSON.stringify(msg.data)}
|
|
|
19734
20321
|
res.end(JSON.stringify(data, null, 2));
|
|
19735
20322
|
}
|
|
19736
20323
|
async readBody(req) {
|
|
19737
|
-
return new Promise((
|
|
20324
|
+
return new Promise((resolve12) => {
|
|
19738
20325
|
let body = "";
|
|
19739
20326
|
req.on("data", (chunk) => body += chunk);
|
|
19740
20327
|
req.on("end", () => {
|
|
19741
20328
|
try {
|
|
19742
|
-
|
|
20329
|
+
resolve12(JSON.parse(body));
|
|
19743
20330
|
} catch {
|
|
19744
|
-
|
|
20331
|
+
resolve12({});
|
|
19745
20332
|
}
|
|
19746
20333
|
});
|
|
19747
20334
|
});
|
|
@@ -20214,7 +20801,7 @@ async function waitForReady(endpoint, timeoutMs = STARTUP_TIMEOUT_MS) {
|
|
|
20214
20801
|
const deadline = Date.now() + timeoutMs;
|
|
20215
20802
|
while (Date.now() < deadline) {
|
|
20216
20803
|
if (await canConnect(endpoint)) return;
|
|
20217
|
-
await new Promise((
|
|
20804
|
+
await new Promise((resolve12) => setTimeout(resolve12, STARTUP_POLL_MS));
|
|
20218
20805
|
}
|
|
20219
20806
|
throw new Error(`Session host did not become ready within ${timeoutMs}ms`);
|
|
20220
20807
|
}
|
|
@@ -20370,10 +20957,10 @@ async function installExtension(ide, extension) {
|
|
|
20370
20957
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
20371
20958
|
const fs15 = await import("fs");
|
|
20372
20959
|
fs15.writeFileSync(vsixPath, buffer);
|
|
20373
|
-
return new Promise((
|
|
20960
|
+
return new Promise((resolve12) => {
|
|
20374
20961
|
const cmd = `"${ide.cliCommand}" --install-extension "${vsixPath}" --force`;
|
|
20375
20962
|
exec2(cmd, { timeout: 6e4 }, (error, _stdout, stderr) => {
|
|
20376
|
-
|
|
20963
|
+
resolve12({
|
|
20377
20964
|
extensionId: extension.id,
|
|
20378
20965
|
marketplaceId: extension.marketplaceId,
|
|
20379
20966
|
success: !error,
|
|
@@ -20386,11 +20973,11 @@ async function installExtension(ide, extension) {
|
|
|
20386
20973
|
} catch (e) {
|
|
20387
20974
|
}
|
|
20388
20975
|
}
|
|
20389
|
-
return new Promise((
|
|
20976
|
+
return new Promise((resolve12) => {
|
|
20390
20977
|
const cmd = `"${ide.cliCommand}" --install-extension ${extension.marketplaceId} --force`;
|
|
20391
20978
|
exec2(cmd, { timeout: 6e4 }, (error, stdout, stderr) => {
|
|
20392
20979
|
if (error) {
|
|
20393
|
-
|
|
20980
|
+
resolve12({
|
|
20394
20981
|
extensionId: extension.id,
|
|
20395
20982
|
marketplaceId: extension.marketplaceId,
|
|
20396
20983
|
success: false,
|
|
@@ -20398,7 +20985,7 @@ async function installExtension(ide, extension) {
|
|
|
20398
20985
|
error: stderr || error.message
|
|
20399
20986
|
});
|
|
20400
20987
|
} else {
|
|
20401
|
-
|
|
20988
|
+
resolve12({
|
|
20402
20989
|
extensionId: extension.id,
|
|
20403
20990
|
marketplaceId: extension.marketplaceId,
|
|
20404
20991
|
success: true,
|
|
@@ -20534,13 +21121,32 @@ async function initDaemonComponents(config) {
|
|
|
20534
21121
|
const detectedIdesRef = { value: [] };
|
|
20535
21122
|
let agentStreamManager = null;
|
|
20536
21123
|
let poller = null;
|
|
21124
|
+
const refreshProviderAvailability = async (providerType) => {
|
|
21125
|
+
const targetProvider = providerType ? providerLoader.getMeta(providerLoader.resolveAlias(providerType)) : null;
|
|
21126
|
+
const targetCategory = targetProvider?.category;
|
|
21127
|
+
if (!providerType || targetCategory === "cli" || targetCategory === "acp") {
|
|
21128
|
+
if (providerType && targetProvider) {
|
|
21129
|
+
const detected = await detectCLI(targetProvider.type, providerLoader, { includeVersion: false });
|
|
21130
|
+
providerLoader.setProviderAvailability(targetProvider.type, {
|
|
21131
|
+
installed: !!detected,
|
|
21132
|
+
detectedPath: detected?.path || null
|
|
21133
|
+
});
|
|
21134
|
+
} else {
|
|
21135
|
+
providerLoader.setCliDetectionResults(await detectCLIs(providerLoader, { includeVersion: false }), true);
|
|
21136
|
+
}
|
|
21137
|
+
}
|
|
21138
|
+
if (!providerType || targetCategory === "ide") {
|
|
21139
|
+
detectedIdesRef.value = await detectIDEs(providerLoader);
|
|
21140
|
+
providerLoader.setIdeDetectionResults(detectedIdesRef.value, true);
|
|
21141
|
+
}
|
|
21142
|
+
};
|
|
20537
21143
|
const cliManager = new DaemonCliManager({
|
|
20538
21144
|
...config.cliManagerDeps,
|
|
20539
21145
|
getInstanceManager: () => instanceManager,
|
|
20540
21146
|
getSessionRegistry: () => sessionRegistry
|
|
20541
21147
|
}, providerLoader);
|
|
20542
21148
|
LOG.info("Init", "Detecting IDEs...");
|
|
20543
|
-
|
|
21149
|
+
await refreshProviderAvailability();
|
|
20544
21150
|
const installed = detectedIdesRef.value.filter((i) => i.installed);
|
|
20545
21151
|
LOG.info("Init", `Found ${installed.length} IDE(s): ${installed.map((i) => i.id).join(", ") || "none"}`);
|
|
20546
21152
|
const cdpSetupContext = {
|
|
@@ -20580,7 +21186,11 @@ async function initDaemonComponents(config) {
|
|
|
20580
21186
|
adapters: cliManager.adapters,
|
|
20581
21187
|
providerLoader,
|
|
20582
21188
|
instanceManager,
|
|
20583
|
-
sessionRegistry
|
|
21189
|
+
sessionRegistry,
|
|
21190
|
+
onProviderSettingChanged: async (providerType) => {
|
|
21191
|
+
await refreshProviderAvailability(providerType);
|
|
21192
|
+
config.onStatusChange?.();
|
|
21193
|
+
}
|
|
20584
21194
|
});
|
|
20585
21195
|
agentStreamManager = new DaemonAgentStreamManager(
|
|
20586
21196
|
LOG.forComponent("AgentStream").asLogFn(),
|