@integrity-labs/agt-cli 0.27.29 → 0.27.31
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/bin/agt.js +19 -4
- package/dist/bin/agt.js.map +1 -1
- package/dist/{chunk-HX74HMG4.js → chunk-E6MGOZTL.js} +1 -1
- package/dist/lib/manager-worker.js +341 -115
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/mcp/slack-channel.js +76 -0
- package/package.json +1 -1
- /package/dist/{chunk-HX74HMG4.js.map → chunk-E6MGOZTL.js.map} +0 -0
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
provisionOrientHook,
|
|
16
16
|
provisionStopHook,
|
|
17
17
|
requireHost
|
|
18
|
-
} from "../chunk-
|
|
18
|
+
} from "../chunk-E6MGOZTL.js";
|
|
19
19
|
import {
|
|
20
20
|
getProjectDir as getProjectDir2,
|
|
21
21
|
getReadyTasks,
|
|
@@ -77,10 +77,10 @@ import {
|
|
|
77
77
|
|
|
78
78
|
// src/lib/manager-worker.ts
|
|
79
79
|
import { createHash as createHash3 } from "crypto";
|
|
80
|
-
import { readFileSync as
|
|
80
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, appendFileSync, mkdirSync as mkdirSync4, chmodSync, existsSync as existsSync5, rmSync as rmSync2, readdirSync as readdirSync3, statSync as statSync2, unlinkSync, copyFileSync } from "fs";
|
|
81
81
|
import https from "https";
|
|
82
82
|
import { execFileSync as syncExecFile } from "child_process";
|
|
83
|
-
import { join as join6, dirname as
|
|
83
|
+
import { join as join6, dirname as dirname3 } from "path";
|
|
84
84
|
import { homedir as homedir4 } from "os";
|
|
85
85
|
import { fileURLToPath } from "url";
|
|
86
86
|
|
|
@@ -988,6 +988,95 @@ function formatCoalesceDeferLogLine(opts) {
|
|
|
988
988
|
return `[self-update] coalescing \u2014 ${opts.installed} \u2192 ${opts.latest} (${opts.channelLabel}) available, deferring for ${remainingSec}s (last restart inside ${windowMin}min window). See ENG-5862. Override with AGT_SELF_UPDATE_COALESCE_MS=0 during incident response.`;
|
|
989
989
|
}
|
|
990
990
|
|
|
991
|
+
// src/lib/atomic-write.ts
|
|
992
|
+
import { closeSync, fsyncSync, openSync, writeSync, renameSync, mkdirSync as mkdirSync2 } from "fs";
|
|
993
|
+
import { dirname as dirname2 } from "path";
|
|
994
|
+
function atomicWriteFileSync(path, data) {
|
|
995
|
+
const dirPath = dirname2(path);
|
|
996
|
+
const tmpPath = `${path}.tmp.${process.pid}.${Math.random().toString(36).slice(2, 8)}`;
|
|
997
|
+
try {
|
|
998
|
+
mkdirSync2(dirPath, { recursive: true });
|
|
999
|
+
} catch {
|
|
1000
|
+
}
|
|
1001
|
+
const fd = openSync(tmpPath, "w", 420);
|
|
1002
|
+
try {
|
|
1003
|
+
writeSync(fd, data);
|
|
1004
|
+
try {
|
|
1005
|
+
fsyncSync(fd);
|
|
1006
|
+
} catch {
|
|
1007
|
+
}
|
|
1008
|
+
} finally {
|
|
1009
|
+
closeSync(fd);
|
|
1010
|
+
}
|
|
1011
|
+
renameSync(tmpPath, path);
|
|
1012
|
+
try {
|
|
1013
|
+
const dirFd = openSync(dirPath, "r");
|
|
1014
|
+
try {
|
|
1015
|
+
fsyncSync(dirFd);
|
|
1016
|
+
} finally {
|
|
1017
|
+
closeSync(dirFd);
|
|
1018
|
+
}
|
|
1019
|
+
} catch {
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
// src/lib/claude-pid-tracker.ts
|
|
1024
|
+
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
1025
|
+
function readPidFile(path) {
|
|
1026
|
+
if (!existsSync(path)) return { version: 1, spawns: [] };
|
|
1027
|
+
try {
|
|
1028
|
+
const raw = JSON.parse(readFileSync2(path, "utf-8"));
|
|
1029
|
+
if (raw.version !== 1 || !Array.isArray(raw.spawns)) return { version: 1, spawns: [] };
|
|
1030
|
+
const spawns = raw.spawns.filter(
|
|
1031
|
+
(s) => !!s && typeof s.pid === "number" && Number.isFinite(s.pid) && s.pid > 0
|
|
1032
|
+
);
|
|
1033
|
+
return { version: 1, spawns };
|
|
1034
|
+
} catch {
|
|
1035
|
+
return { version: 1, spawns: [] };
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
function writePidFile(path, file) {
|
|
1039
|
+
atomicWriteFileSync(path, JSON.stringify(file, null, 2));
|
|
1040
|
+
}
|
|
1041
|
+
function addSpawn(path, record) {
|
|
1042
|
+
const file = readPidFile(path);
|
|
1043
|
+
const filtered = file.spawns.filter((s) => s.pid !== record.pid);
|
|
1044
|
+
writePidFile(path, { version: 1, spawns: [...filtered, record] });
|
|
1045
|
+
}
|
|
1046
|
+
function removeSpawn(path, pid) {
|
|
1047
|
+
const file = readPidFile(path);
|
|
1048
|
+
const next = file.spawns.filter((s) => s.pid !== pid);
|
|
1049
|
+
if (next.length !== file.spawns.length) writePidFile(path, { version: 1, spawns: next });
|
|
1050
|
+
}
|
|
1051
|
+
function decideReaperActions(file, isAlive, looksLikeClaude = () => true) {
|
|
1052
|
+
const toKill = [];
|
|
1053
|
+
const alreadyDead = [];
|
|
1054
|
+
const pidReusedSkipped = [];
|
|
1055
|
+
for (const s of file.spawns) {
|
|
1056
|
+
if (!isAlive(s.pid)) {
|
|
1057
|
+
alreadyDead.push(s);
|
|
1058
|
+
continue;
|
|
1059
|
+
}
|
|
1060
|
+
if (!looksLikeClaude(s.pid)) {
|
|
1061
|
+
pidReusedSkipped.push(s);
|
|
1062
|
+
continue;
|
|
1063
|
+
}
|
|
1064
|
+
toKill.push(s);
|
|
1065
|
+
}
|
|
1066
|
+
const toResetKanban = toKill.filter(
|
|
1067
|
+
(s) => s.kind === "kanban-work" && typeof s.kanban_card_id === "string" && s.kanban_card_id.length > 0
|
|
1068
|
+
);
|
|
1069
|
+
return { toKill, alreadyDead, pidReusedSkipped, toResetKanban };
|
|
1070
|
+
}
|
|
1071
|
+
function formatDrainShutdownLine(opts) {
|
|
1072
|
+
const killed = opts.killedPids && opts.killedPids.length > 0 ? ` killed_pids=${opts.killedPids.join(",")}` : "";
|
|
1073
|
+
const note = opts.note ? ` note="${opts.note.replace(/"/g, '\\"')}"` : "";
|
|
1074
|
+
return `[drain] step=${opts.step} elapsed_ms=${opts.elapsedMs}${killed}${note}`;
|
|
1075
|
+
}
|
|
1076
|
+
function formatReaperBootLine(opts) {
|
|
1077
|
+
return `[drain] step=boot-reaper total=${opts.totalRecorded} killed=${opts.killed} already_dead=${opts.alreadyDead} pid_reused_skipped=${opts.pidReusedSkipped} kanban_reset=${opts.kanbanReset}`;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
991
1080
|
// src/lib/usage-banner-monitor.ts
|
|
992
1081
|
var MIN_CHECK_INTERVAL_MS = 6e4;
|
|
993
1082
|
var PANE_TAIL_LINES_FOR_BANNER = 200;
|
|
@@ -1044,7 +1133,7 @@ async function maybeReportUsageBanner(args) {
|
|
|
1044
1133
|
}
|
|
1045
1134
|
|
|
1046
1135
|
// src/lib/token-usage-monitor.ts
|
|
1047
|
-
import { readdirSync, readFileSync as
|
|
1136
|
+
import { readdirSync, readFileSync as readFileSync3, statSync } from "fs";
|
|
1048
1137
|
import { join } from "path";
|
|
1049
1138
|
var MIN_CHECK_INTERVAL_MS2 = 6e4;
|
|
1050
1139
|
var TRANSCRIPT_MTIME_WINDOW_MS = 2 * 24 * 60 * 60 * 1e3;
|
|
@@ -1090,7 +1179,7 @@ async function maybeReportTokenUsage(args) {
|
|
|
1090
1179
|
}
|
|
1091
1180
|
let content;
|
|
1092
1181
|
try {
|
|
1093
|
-
content =
|
|
1182
|
+
content = readFileSync3(path, "utf-8");
|
|
1094
1183
|
} catch (err) {
|
|
1095
1184
|
log2(`[token-usage] read failed for '${codeName}/${name}': ${err.message}`);
|
|
1096
1185
|
continue;
|
|
@@ -1171,7 +1260,7 @@ async function maybeReportTokenUsage(args) {
|
|
|
1171
1260
|
}
|
|
1172
1261
|
|
|
1173
1262
|
// src/lib/activity-cache-monitor.ts
|
|
1174
|
-
import { existsSync, readFileSync as
|
|
1263
|
+
import { existsSync as existsSync2, readFileSync as readFileSync4 } from "fs";
|
|
1175
1264
|
import { homedir } from "os";
|
|
1176
1265
|
import { join as join2 } from "path";
|
|
1177
1266
|
var MIN_CHECK_INTERVAL_MS3 = 6e4;
|
|
@@ -1218,12 +1307,12 @@ async function maybeReportActivityCache(args) {
|
|
|
1218
1307
|
const nowMs = now.getTime();
|
|
1219
1308
|
if (nowMs - state3.lastCheckedAt < MIN_CHECK_INTERVAL_MS3) return;
|
|
1220
1309
|
state3.lastCheckedAt = nowMs;
|
|
1221
|
-
if (!
|
|
1310
|
+
if (!existsSync2(STATS_CACHE_PATH)) {
|
|
1222
1311
|
return;
|
|
1223
1312
|
}
|
|
1224
1313
|
let raw;
|
|
1225
1314
|
try {
|
|
1226
|
-
raw =
|
|
1315
|
+
raw = readFileSync4(STATS_CACHE_PATH, "utf-8");
|
|
1227
1316
|
} catch (err) {
|
|
1228
1317
|
log2(`[activity-cache] readFileSync failed: ${err.message}`);
|
|
1229
1318
|
return;
|
|
@@ -1790,7 +1879,7 @@ function normalize(value) {
|
|
|
1790
1879
|
}
|
|
1791
1880
|
|
|
1792
1881
|
// src/lib/channel-hash-cache.ts
|
|
1793
|
-
import { existsSync as
|
|
1882
|
+
import { existsSync as existsSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
|
|
1794
1883
|
import { join as join4 } from "path";
|
|
1795
1884
|
var CACHE_FILENAME = "channel-hash-cache.json";
|
|
1796
1885
|
function getChannelHashCacheFile(configDir) {
|
|
@@ -1798,10 +1887,10 @@ function getChannelHashCacheFile(configDir) {
|
|
|
1798
1887
|
}
|
|
1799
1888
|
function loadChannelHashCache(target, configDir) {
|
|
1800
1889
|
const path = getChannelHashCacheFile(configDir);
|
|
1801
|
-
if (!
|
|
1890
|
+
if (!existsSync3(path)) return;
|
|
1802
1891
|
let parsed;
|
|
1803
1892
|
try {
|
|
1804
|
-
parsed = JSON.parse(
|
|
1893
|
+
parsed = JSON.parse(readFileSync5(path, "utf-8"));
|
|
1805
1894
|
} catch {
|
|
1806
1895
|
return;
|
|
1807
1896
|
}
|
|
@@ -2346,7 +2435,7 @@ function clearAgentState(agentId, codeName) {
|
|
|
2346
2435
|
}
|
|
2347
2436
|
|
|
2348
2437
|
// src/lib/restart-flags.ts
|
|
2349
|
-
import { existsSync as
|
|
2438
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync6, renameSync as renameSync2, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
2350
2439
|
import { homedir as homedir3 } from "os";
|
|
2351
2440
|
import { join as join5 } from "path";
|
|
2352
2441
|
import { randomUUID } from "crypto";
|
|
@@ -2358,12 +2447,12 @@ function flagPath(codeName) {
|
|
|
2358
2447
|
}
|
|
2359
2448
|
function readRestartFlags() {
|
|
2360
2449
|
const dir = restartFlagsDir();
|
|
2361
|
-
if (!
|
|
2450
|
+
if (!existsSync4(dir)) return [];
|
|
2362
2451
|
const out = [];
|
|
2363
2452
|
for (const entry of readdirSync2(dir)) {
|
|
2364
2453
|
if (!entry.endsWith(".flag")) continue;
|
|
2365
2454
|
try {
|
|
2366
|
-
const raw =
|
|
2455
|
+
const raw = readFileSync6(join5(dir, entry), "utf8");
|
|
2367
2456
|
const parsed = JSON.parse(raw);
|
|
2368
2457
|
if (typeof parsed.codeName !== "string" || parsed.codeName.length === 0) {
|
|
2369
2458
|
parsed.codeName = entry.replace(/\.flag$/, "");
|
|
@@ -2381,7 +2470,7 @@ function readRestartFlags() {
|
|
|
2381
2470
|
}
|
|
2382
2471
|
function deleteRestartFlag(codeName) {
|
|
2383
2472
|
const path = flagPath(codeName);
|
|
2384
|
-
if (
|
|
2473
|
+
if (existsSync4(path)) {
|
|
2385
2474
|
rmSync(path, { force: true });
|
|
2386
2475
|
}
|
|
2387
2476
|
}
|
|
@@ -3062,7 +3151,7 @@ var runningMcpHashes = /* @__PURE__ */ new Map();
|
|
|
3062
3151
|
var runningMcpServerKeys = /* @__PURE__ */ new Map();
|
|
3063
3152
|
function projectMcpHash(_codeName, projectDir) {
|
|
3064
3153
|
try {
|
|
3065
|
-
const raw =
|
|
3154
|
+
const raw = readFileSync7(join6(projectDir, ".mcp.json"), "utf-8");
|
|
3066
3155
|
return createHash3("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
|
|
3067
3156
|
} catch {
|
|
3068
3157
|
return null;
|
|
@@ -3070,7 +3159,7 @@ function projectMcpHash(_codeName, projectDir) {
|
|
|
3070
3159
|
}
|
|
3071
3160
|
function projectMcpKeys(_codeName, projectDir) {
|
|
3072
3161
|
try {
|
|
3073
|
-
const raw =
|
|
3162
|
+
const raw = readFileSync7(join6(projectDir, ".mcp.json"), "utf-8");
|
|
3074
3163
|
const parsed = JSON.parse(raw);
|
|
3075
3164
|
const servers = parsed.mcpServers;
|
|
3076
3165
|
if (!servers || typeof servers !== "object") return /* @__PURE__ */ new Set();
|
|
@@ -3081,7 +3170,7 @@ function projectMcpKeys(_codeName, projectDir) {
|
|
|
3081
3170
|
}
|
|
3082
3171
|
function readMcpHttpServerConfig(projectDir, serverKey) {
|
|
3083
3172
|
try {
|
|
3084
|
-
const raw =
|
|
3173
|
+
const raw = readFileSync7(join6(projectDir, ".mcp.json"), "utf-8");
|
|
3085
3174
|
const servers = JSON.parse(raw).mcpServers ?? {};
|
|
3086
3175
|
const entry = servers[serverKey];
|
|
3087
3176
|
if (entry && typeof entry.url === "string" && (entry.type === "http" || entry.type === void 0)) {
|
|
@@ -3255,7 +3344,7 @@ var cachedFrameworkVersion = null;
|
|
|
3255
3344
|
var lastVersionCheckAt = 0;
|
|
3256
3345
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
3257
3346
|
var lastResponsivenessProbeAt = 0;
|
|
3258
|
-
var agtCliVersion = true ? "0.27.
|
|
3347
|
+
var agtCliVersion = true ? "0.27.31" : "dev";
|
|
3259
3348
|
function resolveBrewPath(execFileSync4) {
|
|
3260
3349
|
try {
|
|
3261
3350
|
const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -3268,7 +3357,7 @@ function resolveBrewPath(execFileSync4) {
|
|
|
3268
3357
|
"/usr/local/bin/brew"
|
|
3269
3358
|
];
|
|
3270
3359
|
for (const path of fallbacks) {
|
|
3271
|
-
if (
|
|
3360
|
+
if (existsSync5(path)) return path;
|
|
3272
3361
|
}
|
|
3273
3362
|
return null;
|
|
3274
3363
|
}
|
|
@@ -3278,7 +3367,7 @@ function claudeBinaryInstalled(execFileSync4) {
|
|
|
3278
3367
|
"/opt/homebrew/bin/claude",
|
|
3279
3368
|
"/usr/local/bin/claude"
|
|
3280
3369
|
];
|
|
3281
|
-
if (canonical.some((path) =>
|
|
3370
|
+
if (canonical.some((path) => existsSync5(path))) return true;
|
|
3282
3371
|
try {
|
|
3283
3372
|
execFileSync4("which", ["claude"], { timeout: 5e3 });
|
|
3284
3373
|
return true;
|
|
@@ -3350,7 +3439,7 @@ async function ensureToolkitCli(toolkitSlug) {
|
|
|
3350
3439
|
toolkitCliEnsured.add(toolkitSlug);
|
|
3351
3440
|
return;
|
|
3352
3441
|
}
|
|
3353
|
-
brewBinDir =
|
|
3442
|
+
brewBinDir = dirname3(brewPath);
|
|
3354
3443
|
const isRoot = typeof process.getuid === "function" && process.getuid() === 0;
|
|
3355
3444
|
log(`[toolkit-install] ${toolkitSlug}: installing via brew (${pkg})\u2026`);
|
|
3356
3445
|
if (isRoot) {
|
|
@@ -3431,8 +3520,8 @@ function claudeManagedSettingsPath() {
|
|
|
3431
3520
|
function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
|
|
3432
3521
|
try {
|
|
3433
3522
|
let settings = {};
|
|
3434
|
-
if (
|
|
3435
|
-
const raw =
|
|
3523
|
+
if (existsSync5(path)) {
|
|
3524
|
+
const raw = readFileSync7(path, "utf-8").trim();
|
|
3436
3525
|
if (raw) {
|
|
3437
3526
|
let parsed;
|
|
3438
3527
|
try {
|
|
@@ -3448,7 +3537,7 @@ function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
|
|
|
3448
3537
|
}
|
|
3449
3538
|
if (settings.channelsEnabled === true) return;
|
|
3450
3539
|
settings.channelsEnabled = true;
|
|
3451
|
-
|
|
3540
|
+
mkdirSync4(dirname3(path), { recursive: true });
|
|
3452
3541
|
writeFileSync4(path, `${JSON.stringify(settings, null, 2)}
|
|
3453
3542
|
`);
|
|
3454
3543
|
log(`[managed-settings] set channelsEnabled:true in ${path} (ENG-5786 \u2014 unblocks Claude Code channels)`);
|
|
@@ -3488,11 +3577,11 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
3488
3577
|
log(`Claude Code install failed: ${err.message}`);
|
|
3489
3578
|
return;
|
|
3490
3579
|
}
|
|
3491
|
-
const brewBinDir =
|
|
3580
|
+
const brewBinDir = dirname3(brewPath);
|
|
3492
3581
|
if (!process.env.PATH?.split(":").includes(brewBinDir)) {
|
|
3493
3582
|
process.env.PATH = `${brewBinDir}:${process.env.PATH ?? ""}`;
|
|
3494
3583
|
}
|
|
3495
|
-
if (
|
|
3584
|
+
if (existsSync5("/home/linuxbrew/.linuxbrew/bin/claude")) {
|
|
3496
3585
|
log("Claude Code installed successfully");
|
|
3497
3586
|
} else {
|
|
3498
3587
|
log("Claude Code install completed but binary not found at expected path \u2014 check brew logs");
|
|
@@ -3514,7 +3603,7 @@ function stampClaudeCodeUpgradeMarker() {
|
|
|
3514
3603
|
}
|
|
3515
3604
|
function claudeCodeUpgradeThrottled() {
|
|
3516
3605
|
try {
|
|
3517
|
-
const lastCheck = parseInt(
|
|
3606
|
+
const lastCheck = parseInt(readFileSync7(claudeCodeUpgradeMarkerPath(), "utf-8").trim(), 10);
|
|
3518
3607
|
if (!Number.isFinite(lastCheck)) return false;
|
|
3519
3608
|
return Date.now() - lastCheck < CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS;
|
|
3520
3609
|
} catch {
|
|
@@ -3811,7 +3900,7 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
3811
3900
|
const claudeDir = join6(homedir4(), ".claude");
|
|
3812
3901
|
for (const filename of [".credentials.json", "credentials.json"]) {
|
|
3813
3902
|
const p = join6(claudeDir, filename);
|
|
3814
|
-
if (
|
|
3903
|
+
if (existsSync5(p)) {
|
|
3815
3904
|
try {
|
|
3816
3905
|
rmSync2(p, { force: true });
|
|
3817
3906
|
log(`[${label}] Removed ${p} (api_key mode \u2014 preventing OAuth fallback)`);
|
|
@@ -3825,13 +3914,13 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
3825
3914
|
}
|
|
3826
3915
|
function loadGatewayPorts() {
|
|
3827
3916
|
try {
|
|
3828
|
-
return JSON.parse(
|
|
3917
|
+
return JSON.parse(readFileSync7(GATEWAY_PORTS_FILE, "utf-8"));
|
|
3829
3918
|
} catch {
|
|
3830
3919
|
return {};
|
|
3831
3920
|
}
|
|
3832
3921
|
}
|
|
3833
3922
|
function saveGatewayPorts(ports) {
|
|
3834
|
-
|
|
3923
|
+
mkdirSync4(AUGMENTED_DIR, { recursive: true });
|
|
3835
3924
|
writeFileSync4(GATEWAY_PORTS_FILE, JSON.stringify(ports, null, 2));
|
|
3836
3925
|
}
|
|
3837
3926
|
function allocatePort(codeName) {
|
|
@@ -3869,7 +3958,7 @@ function saveChannelHashCache2() {
|
|
|
3869
3958
|
function send(msg) {
|
|
3870
3959
|
if (msg.type === "state-update") {
|
|
3871
3960
|
try {
|
|
3872
|
-
|
|
3961
|
+
atomicWriteFileSync(getStateFile(), JSON.stringify(msg.state, null, 2));
|
|
3873
3962
|
} catch {
|
|
3874
3963
|
}
|
|
3875
3964
|
}
|
|
@@ -3901,8 +3990,8 @@ function log(msg) {
|
|
|
3901
3990
|
if (!managerLogPath) {
|
|
3902
3991
|
try {
|
|
3903
3992
|
managerLogPath = join6(homedir4(), ".augmented", "manager.log");
|
|
3904
|
-
|
|
3905
|
-
if (
|
|
3993
|
+
mkdirSync4(dirname3(managerLogPath), { recursive: true });
|
|
3994
|
+
if (existsSync5(managerLogPath)) {
|
|
3906
3995
|
chmodSync(managerLogPath, 384);
|
|
3907
3996
|
}
|
|
3908
3997
|
} catch {
|
|
@@ -3930,7 +4019,7 @@ function sha256(content) {
|
|
|
3930
4019
|
}
|
|
3931
4020
|
function hashFile(filePath) {
|
|
3932
4021
|
try {
|
|
3933
|
-
const content =
|
|
4022
|
+
const content = readFileSync7(filePath, "utf-8");
|
|
3934
4023
|
return sha256(content);
|
|
3935
4024
|
} catch {
|
|
3936
4025
|
return null;
|
|
@@ -4011,7 +4100,7 @@ async function migrateToProfiles() {
|
|
|
4011
4100
|
const sharedConfigPath = join6(homeDir, ".openclaw", "openclaw.json");
|
|
4012
4101
|
let sharedConfig;
|
|
4013
4102
|
try {
|
|
4014
|
-
sharedConfig = JSON.parse(
|
|
4103
|
+
sharedConfig = JSON.parse(readFileSync7(sharedConfigPath, "utf-8"));
|
|
4015
4104
|
} catch {
|
|
4016
4105
|
return;
|
|
4017
4106
|
}
|
|
@@ -4025,7 +4114,7 @@ async function migrateToProfiles() {
|
|
|
4025
4114
|
if (!codeName) continue;
|
|
4026
4115
|
if (codeName === "main") continue;
|
|
4027
4116
|
const profileDir = join6(homeDir, `.openclaw-${codeName}`);
|
|
4028
|
-
if (
|
|
4117
|
+
if (existsSync5(join6(profileDir, "openclaw.json"))) continue;
|
|
4029
4118
|
log(`Migrating agent '${codeName}' to per-agent profile`);
|
|
4030
4119
|
if (adapter.seedProfileConfig) {
|
|
4031
4120
|
adapter.seedProfileConfig(codeName);
|
|
@@ -4033,9 +4122,9 @@ async function migrateToProfiles() {
|
|
|
4033
4122
|
const sharedAuthDir = join6(homeDir, ".openclaw", "agents", codeName, "agent");
|
|
4034
4123
|
const profileAuthDir = join6(profileDir, "agents", codeName, "agent");
|
|
4035
4124
|
const authFile = join6(sharedAuthDir, "auth-profiles.json");
|
|
4036
|
-
if (
|
|
4037
|
-
|
|
4038
|
-
const authContent =
|
|
4125
|
+
if (existsSync5(authFile)) {
|
|
4126
|
+
mkdirSync4(profileAuthDir, { recursive: true });
|
|
4127
|
+
const authContent = readFileSync7(authFile, "utf-8");
|
|
4039
4128
|
writeFileSync4(join6(profileAuthDir, "auth-profiles.json"), authContent);
|
|
4040
4129
|
}
|
|
4041
4130
|
allocatePort(codeName);
|
|
@@ -4074,7 +4163,7 @@ function readGatewayToken(codeName) {
|
|
|
4074
4163
|
}
|
|
4075
4164
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4076
4165
|
try {
|
|
4077
|
-
const cfg = JSON.parse(
|
|
4166
|
+
const cfg = JSON.parse(readFileSync7(join6(homeDir, `.openclaw-${codeName}`, "openclaw.json"), "utf-8"));
|
|
4078
4167
|
return cfg?.gateway?.auth?.token;
|
|
4079
4168
|
} catch {
|
|
4080
4169
|
return void 0;
|
|
@@ -4084,9 +4173,9 @@ var GATEWAY_HUNG_TIMEOUT_MS = 5 * 6e4;
|
|
|
4084
4173
|
function isGatewayHung(codeName) {
|
|
4085
4174
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4086
4175
|
const jobsPath = join6(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
4087
|
-
if (!
|
|
4176
|
+
if (!existsSync5(jobsPath)) return false;
|
|
4088
4177
|
try {
|
|
4089
|
-
const data = JSON.parse(
|
|
4178
|
+
const data = JSON.parse(readFileSync7(jobsPath, "utf-8"));
|
|
4090
4179
|
const jobs = data.jobs ?? data;
|
|
4091
4180
|
if (!Array.isArray(jobs)) return false;
|
|
4092
4181
|
const now = Date.now();
|
|
@@ -4126,8 +4215,8 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
4126
4215
|
try {
|
|
4127
4216
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4128
4217
|
const configPath = join6(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
4129
|
-
if (
|
|
4130
|
-
const cfg = JSON.parse(
|
|
4218
|
+
if (existsSync5(configPath)) {
|
|
4219
|
+
const cfg = JSON.parse(readFileSync7(configPath, "utf-8"));
|
|
4131
4220
|
if (cfg.gateway?.port !== status.port) {
|
|
4132
4221
|
if (!cfg.gateway) cfg.gateway = {};
|
|
4133
4222
|
cfg.gateway.port = status.port;
|
|
@@ -4152,8 +4241,8 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
4152
4241
|
try {
|
|
4153
4242
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4154
4243
|
const configPath = join6(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
4155
|
-
if (
|
|
4156
|
-
const cfg = JSON.parse(
|
|
4244
|
+
if (existsSync5(configPath)) {
|
|
4245
|
+
const cfg = JSON.parse(readFileSync7(configPath, "utf-8"));
|
|
4157
4246
|
if (!cfg.gateway) cfg.gateway = {};
|
|
4158
4247
|
cfg.gateway.port = port;
|
|
4159
4248
|
writeFileSync4(configPath, JSON.stringify(cfg, null, 2));
|
|
@@ -4489,7 +4578,7 @@ async function pollCycle() {
|
|
|
4489
4578
|
if (restartAckStateChanged) {
|
|
4490
4579
|
try {
|
|
4491
4580
|
const ackedState = { ...state4, agents: agentStates };
|
|
4492
|
-
|
|
4581
|
+
atomicWriteFileSync(getStateFile(), JSON.stringify(ackedState, null, 2));
|
|
4493
4582
|
} catch (err) {
|
|
4494
4583
|
log(`[restart] failed to persist ack immediately: ${err.message}`);
|
|
4495
4584
|
}
|
|
@@ -4709,7 +4798,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4709
4798
|
const residuals = {
|
|
4710
4799
|
gatewayRunning: gatewayLiveness.running,
|
|
4711
4800
|
portAllocated: Object.prototype.hasOwnProperty.call(ports, agent.code_name),
|
|
4712
|
-
provisionDirExists:
|
|
4801
|
+
provisionDirExists: existsSync5(agentDir)
|
|
4713
4802
|
};
|
|
4714
4803
|
if (!hasRevokedResiduals(residuals)) {
|
|
4715
4804
|
agentStates.push({
|
|
@@ -4868,7 +4957,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4868
4957
|
try {
|
|
4869
4958
|
const artifacts = generateArtifacts(agent, refreshData, frameworkAdapter);
|
|
4870
4959
|
const changedFiles = [];
|
|
4871
|
-
|
|
4960
|
+
mkdirSync4(agentDir, { recursive: true });
|
|
4872
4961
|
for (const artifact of artifacts) {
|
|
4873
4962
|
const filePath = join6(agentDir, artifact.relativePath);
|
|
4874
4963
|
let existingHash;
|
|
@@ -4890,7 +4979,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4890
4979
|
newHash = sha256(stripDynamicSections(artifact.content));
|
|
4891
4980
|
try {
|
|
4892
4981
|
const projectClaudeMd = join6(config.configDir, agent.code_name, "project", "CLAUDE.md");
|
|
4893
|
-
const existing =
|
|
4982
|
+
const existing = readFileSync7(projectClaudeMd, "utf-8");
|
|
4894
4983
|
existingHash = sha256(stripDynamicSections(existing));
|
|
4895
4984
|
} catch {
|
|
4896
4985
|
existingHash = null;
|
|
@@ -4908,7 +4997,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4908
4997
|
const generatorKeys = Object.keys(generatorServers);
|
|
4909
4998
|
let existingRaw = "";
|
|
4910
4999
|
try {
|
|
4911
|
-
existingRaw =
|
|
5000
|
+
existingRaw = readFileSync7(filePath, "utf-8");
|
|
4912
5001
|
} catch {
|
|
4913
5002
|
}
|
|
4914
5003
|
const existingServers = parseMcp(existingRaw);
|
|
@@ -4930,18 +5019,18 @@ async function processAgent(agent, agentStates) {
|
|
|
4930
5019
|
}
|
|
4931
5020
|
}
|
|
4932
5021
|
if (changedFiles.length > 0) {
|
|
4933
|
-
const isFirst = !
|
|
5022
|
+
const isFirst = !existsSync5(join6(agentDir, "CHARTER.md"));
|
|
4934
5023
|
const verb = isFirst ? "Provisioning" : "Updating";
|
|
4935
5024
|
const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
|
|
4936
5025
|
log(`${verb} '${agent.code_name}': ${fileNames}`);
|
|
4937
5026
|
for (const file of changedFiles) {
|
|
4938
5027
|
const filePath = join6(agentDir, file.relativePath);
|
|
4939
|
-
|
|
5028
|
+
mkdirSync4(dirname3(filePath), { recursive: true });
|
|
4940
5029
|
writeFileSync4(filePath, file.content);
|
|
4941
5030
|
}
|
|
4942
5031
|
try {
|
|
4943
5032
|
const provSkillsDir = join6(agentDir, ".claude", "skills");
|
|
4944
|
-
if (
|
|
5033
|
+
if (existsSync5(provSkillsDir)) {
|
|
4945
5034
|
for (const folder of readdirSync3(provSkillsDir)) {
|
|
4946
5035
|
if (folder.startsWith("knowledge-")) {
|
|
4947
5036
|
try {
|
|
@@ -5023,7 +5112,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5023
5112
|
}
|
|
5024
5113
|
let lastDriftCheckAt = now;
|
|
5025
5114
|
const written = agentState.writtenHashes.get(agent.agent_id);
|
|
5026
|
-
if (written &&
|
|
5115
|
+
if (written && existsSync5(agentDir)) {
|
|
5027
5116
|
const driftedFiles = [];
|
|
5028
5117
|
for (const [file, expectedHash] of written) {
|
|
5029
5118
|
const localHash = hashFile(join6(agentDir, file));
|
|
@@ -5240,13 +5329,13 @@ async function processAgent(agent, agentStates) {
|
|
|
5240
5329
|
try {
|
|
5241
5330
|
const agentProvisionDir = agentDir;
|
|
5242
5331
|
const projectDir = join6(homedir4(), ".augmented", agent.code_name, "project");
|
|
5243
|
-
|
|
5244
|
-
|
|
5332
|
+
mkdirSync4(agentProvisionDir, { recursive: true });
|
|
5333
|
+
mkdirSync4(projectDir, { recursive: true });
|
|
5245
5334
|
const provisionMcpPath = join6(agentProvisionDir, ".mcp.json");
|
|
5246
5335
|
const projectMcpPath = join6(projectDir, ".mcp.json");
|
|
5247
5336
|
let mcpConfig = { mcpServers: {} };
|
|
5248
5337
|
try {
|
|
5249
|
-
mcpConfig = JSON.parse(
|
|
5338
|
+
mcpConfig = JSON.parse(readFileSync7(provisionMcpPath, "utf-8"));
|
|
5250
5339
|
if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
|
|
5251
5340
|
} catch {
|
|
5252
5341
|
}
|
|
@@ -5256,7 +5345,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5256
5345
|
const tz = directChatTeamSettings?.["timezone"];
|
|
5257
5346
|
return typeof tz === "string" && tz.trim() !== "" ? tz.trim() : void 0;
|
|
5258
5347
|
})();
|
|
5259
|
-
if (
|
|
5348
|
+
if (existsSync5(localDirectChatChannel) && !mcpConfig.mcpServers["direct-chat"]) {
|
|
5260
5349
|
mcpConfig.mcpServers["direct-chat"] = {
|
|
5261
5350
|
command: "node",
|
|
5262
5351
|
args: [localDirectChatChannel],
|
|
@@ -5273,7 +5362,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5273
5362
|
log(`Channel credentials written for '${agent.code_name}/direct-chat'`);
|
|
5274
5363
|
}
|
|
5275
5364
|
const staleChannelsPath = join6(projectDir, ".mcp-channels.json");
|
|
5276
|
-
if (
|
|
5365
|
+
if (existsSync5(staleChannelsPath)) {
|
|
5277
5366
|
try {
|
|
5278
5367
|
rmSync2(staleChannelsPath, { force: true });
|
|
5279
5368
|
} catch {
|
|
@@ -5351,7 +5440,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5351
5440
|
const envIntPath = join6(projectDir, ".env.integrations");
|
|
5352
5441
|
let preWriteEnv;
|
|
5353
5442
|
try {
|
|
5354
|
-
preWriteEnv =
|
|
5443
|
+
preWriteEnv = readFileSync7(envIntPath, "utf-8");
|
|
5355
5444
|
} catch {
|
|
5356
5445
|
preWriteEnv = void 0;
|
|
5357
5446
|
}
|
|
@@ -5364,8 +5453,8 @@ async function processAgent(agent, agentStates) {
|
|
|
5364
5453
|
if (fw === "claude-code" && isSessionHealthy(agent.code_name)) {
|
|
5365
5454
|
try {
|
|
5366
5455
|
const projectMcpPath = join6(projectDir, ".mcp.json");
|
|
5367
|
-
const postWriteEnv =
|
|
5368
|
-
const mcpContent =
|
|
5456
|
+
const postWriteEnv = readFileSync7(envIntPath, "utf-8");
|
|
5457
|
+
const mcpContent = readFileSync7(projectMcpPath, "utf-8");
|
|
5369
5458
|
const changedVars = diffEnvIntegrations(preWriteEnv, postWriteEnv);
|
|
5370
5459
|
const mcpJsonForReap = JSON.parse(mcpContent);
|
|
5371
5460
|
const affectedServerKeys = findMcpServersUsingVars(mcpJsonForReap, changedVars);
|
|
@@ -5433,8 +5522,8 @@ async function processAgent(agent, agentStates) {
|
|
|
5433
5522
|
const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
|
|
5434
5523
|
if (mcpPath) {
|
|
5435
5524
|
try {
|
|
5436
|
-
const { readFileSync:
|
|
5437
|
-
const mcpConfig = JSON.parse(
|
|
5525
|
+
const { readFileSync: readFileSync8 } = await import("fs");
|
|
5526
|
+
const mcpConfig = JSON.parse(readFileSync8(mcpPath, "utf-8"));
|
|
5438
5527
|
if (mcpConfig.mcpServers) {
|
|
5439
5528
|
const managedPrefixes = [
|
|
5440
5529
|
"composio_",
|
|
@@ -5531,7 +5620,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5531
5620
|
if (frameworkAdapter.installPlugin) {
|
|
5532
5621
|
try {
|
|
5533
5622
|
const pluginPath = join6(process.cwd(), "packages", "openclaw-plugin-augmented", "src", "index.ts");
|
|
5534
|
-
if (
|
|
5623
|
+
if (existsSync5(pluginPath)) {
|
|
5535
5624
|
frameworkAdapter.installPlugin(agent.code_name, "augmented", pluginPath, {
|
|
5536
5625
|
agtHost: requireHost(),
|
|
5537
5626
|
agtApiKey: getApiKey() ?? void 0,
|
|
@@ -5610,7 +5699,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5610
5699
|
// install target but cheap to sweep.
|
|
5611
5700
|
join6(agentDir, ".claude", "skills")
|
|
5612
5701
|
];
|
|
5613
|
-
const existingDirs = candidateSkillDirs.filter((d) =>
|
|
5702
|
+
const existingDirs = candidateSkillDirs.filter((d) => existsSync5(d));
|
|
5614
5703
|
const discoveredEntries = /* @__PURE__ */ new Set();
|
|
5615
5704
|
for (const dir of existingDirs) {
|
|
5616
5705
|
try {
|
|
@@ -5625,7 +5714,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5625
5714
|
const removeSkillFolder = (entry, reason) => {
|
|
5626
5715
|
for (const dir of existingDirs) {
|
|
5627
5716
|
const p = join6(dir, entry);
|
|
5628
|
-
if (
|
|
5717
|
+
if (existsSync5(p)) {
|
|
5629
5718
|
rmSync3(p, { recursive: true, force: true });
|
|
5630
5719
|
}
|
|
5631
5720
|
}
|
|
@@ -5787,7 +5876,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5787
5876
|
let mcpJsonParsed = null;
|
|
5788
5877
|
try {
|
|
5789
5878
|
const mcpPath = join6(getProjectDir(agent.code_name), ".mcp.json");
|
|
5790
|
-
mcpJsonParsed = JSON.parse(
|
|
5879
|
+
mcpJsonParsed = JSON.parse(readFileSync7(mcpPath, "utf-8"));
|
|
5791
5880
|
} catch {
|
|
5792
5881
|
}
|
|
5793
5882
|
reapMissingMcpSessions({
|
|
@@ -5942,9 +6031,9 @@ async function processAgent(agent, agentStates) {
|
|
|
5942
6031
|
if (agentFw === "openclaw" && gatewayRunning && gatewayPort) {
|
|
5943
6032
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
5944
6033
|
const jobsPath = join6(homeDir, `.openclaw-${agent.code_name}`, "cron", "jobs.json");
|
|
5945
|
-
if (
|
|
6034
|
+
if (existsSync5(jobsPath)) {
|
|
5946
6035
|
try {
|
|
5947
|
-
const jobsData = JSON.parse(
|
|
6036
|
+
const jobsData = JSON.parse(readFileSync7(jobsPath, "utf-8"));
|
|
5948
6037
|
const kanbanJob = (jobsData.jobs ?? []).find(
|
|
5949
6038
|
(j) => typeof j.name === "string" && j.name.includes("kanban-work")
|
|
5950
6039
|
);
|
|
@@ -6078,7 +6167,7 @@ In progress for ${age} minutes \u2014 auto-failed`).catch(() => {
|
|
|
6078
6167
|
}
|
|
6079
6168
|
}
|
|
6080
6169
|
const trackedFiles = frameworkAdapter.driftTrackedFiles();
|
|
6081
|
-
if (trackedFiles.length > 0 &&
|
|
6170
|
+
if (trackedFiles.length > 0 && existsSync5(agentDir)) {
|
|
6082
6171
|
const hashes = /* @__PURE__ */ new Map();
|
|
6083
6172
|
for (const file of trackedFiles) {
|
|
6084
6173
|
const h = hashFile(join6(agentDir, file));
|
|
@@ -6125,9 +6214,9 @@ function cleanupStaleSessions(codeName) {
|
|
|
6125
6214
|
}
|
|
6126
6215
|
function cleanupCronSessions(sessionsDir, keepCount) {
|
|
6127
6216
|
const indexPath = join6(sessionsDir, "sessions.json");
|
|
6128
|
-
if (!
|
|
6217
|
+
if (!existsSync5(indexPath)) return;
|
|
6129
6218
|
try {
|
|
6130
|
-
const raw =
|
|
6219
|
+
const raw = readFileSync7(indexPath, "utf-8");
|
|
6131
6220
|
const index = JSON.parse(raw);
|
|
6132
6221
|
const cronRunKeys = Object.keys(index).filter((k) => k.includes(":cron:") && k.includes(":run:")).map((k) => ({
|
|
6133
6222
|
key: k,
|
|
@@ -6142,7 +6231,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
6142
6231
|
if (entry.sessionId) {
|
|
6143
6232
|
const sessionFile = join6(sessionsDir, `${entry.sessionId}.jsonl`);
|
|
6144
6233
|
try {
|
|
6145
|
-
if (
|
|
6234
|
+
if (existsSync5(sessionFile)) {
|
|
6146
6235
|
unlinkSync(sessionFile);
|
|
6147
6236
|
deletedFiles++;
|
|
6148
6237
|
}
|
|
@@ -6163,7 +6252,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
6163
6252
|
if (parentSessionId) {
|
|
6164
6253
|
try {
|
|
6165
6254
|
const f = join6(sessionsDir, `${parentSessionId}.jsonl`);
|
|
6166
|
-
if (
|
|
6255
|
+
if (existsSync5(f)) {
|
|
6167
6256
|
unlinkSync(f);
|
|
6168
6257
|
deletedFiles++;
|
|
6169
6258
|
}
|
|
@@ -6181,9 +6270,9 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
6181
6270
|
}
|
|
6182
6271
|
var STALE_RUN_TIMEOUT_MS = 5 * 6e4;
|
|
6183
6272
|
function clearStaleCronRunState(jobsPath) {
|
|
6184
|
-
if (!
|
|
6273
|
+
if (!existsSync5(jobsPath)) return;
|
|
6185
6274
|
try {
|
|
6186
|
-
const raw =
|
|
6275
|
+
const raw = readFileSync7(jobsPath, "utf-8");
|
|
6187
6276
|
const data = JSON.parse(raw);
|
|
6188
6277
|
const jobs = data.jobs ?? data;
|
|
6189
6278
|
if (!Array.isArray(jobs)) return;
|
|
@@ -6214,7 +6303,7 @@ function clearStaleCronRunState(jobsPath) {
|
|
|
6214
6303
|
}
|
|
6215
6304
|
}
|
|
6216
6305
|
function cleanupOldFiles(dir, maxAgeDays, ext) {
|
|
6217
|
-
if (!
|
|
6306
|
+
if (!existsSync5(dir)) return;
|
|
6218
6307
|
const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1e3;
|
|
6219
6308
|
let removed = 0;
|
|
6220
6309
|
try {
|
|
@@ -6239,6 +6328,25 @@ function cleanupOldFiles(dir, maxAgeDays, ext) {
|
|
|
6239
6328
|
var inFlightClaudeTasks = /* @__PURE__ */ new Set();
|
|
6240
6329
|
var claudeTaskConcurrency = /* @__PURE__ */ new Map();
|
|
6241
6330
|
var MAX_CLAUDE_CONCURRENCY = 2;
|
|
6331
|
+
function claudePidFilePath() {
|
|
6332
|
+
return join6(homedir4(), ".augmented", "manager-claude-pids.json");
|
|
6333
|
+
}
|
|
6334
|
+
var inFlightClaudePids = /* @__PURE__ */ new Map();
|
|
6335
|
+
function registerClaudeSpawn(record) {
|
|
6336
|
+
inFlightClaudePids.set(record.pid, record);
|
|
6337
|
+
try {
|
|
6338
|
+
addSpawn(claudePidFilePath(), record);
|
|
6339
|
+
} catch (err) {
|
|
6340
|
+
log(`[drain] pid-file write failed: ${err.message}`);
|
|
6341
|
+
}
|
|
6342
|
+
}
|
|
6343
|
+
function unregisterClaudeSpawn(pid) {
|
|
6344
|
+
inFlightClaudePids.delete(pid);
|
|
6345
|
+
try {
|
|
6346
|
+
removeSpawn(claudePidFilePath(), pid);
|
|
6347
|
+
} catch {
|
|
6348
|
+
}
|
|
6349
|
+
}
|
|
6242
6350
|
var claudeSchedulerStates = /* @__PURE__ */ new Map();
|
|
6243
6351
|
async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData) {
|
|
6244
6352
|
const codeName = agent.code_name;
|
|
@@ -6510,9 +6618,9 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
6510
6618
|
try {
|
|
6511
6619
|
const claudeMdPath = join6(projectDir, "CLAUDE.md");
|
|
6512
6620
|
const serverNames = [];
|
|
6513
|
-
if (
|
|
6621
|
+
if (existsSync5(mcpConfigPath)) {
|
|
6514
6622
|
try {
|
|
6515
|
-
const d = JSON.parse(
|
|
6623
|
+
const d = JSON.parse(readFileSync7(mcpConfigPath, "utf-8"));
|
|
6516
6624
|
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
6517
6625
|
} catch {
|
|
6518
6626
|
}
|
|
@@ -6531,14 +6639,14 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
6531
6639
|
"--allowedTools",
|
|
6532
6640
|
allowedTools
|
|
6533
6641
|
];
|
|
6534
|
-
if (
|
|
6642
|
+
if (existsSync5(claudeMdPath)) {
|
|
6535
6643
|
claudeArgs.push("--system-prompt-file", claudeMdPath);
|
|
6536
6644
|
}
|
|
6537
6645
|
const childEnv = { ...process.env };
|
|
6538
6646
|
const envIntPath = join6(projectDir, ".env.integrations");
|
|
6539
|
-
if (
|
|
6647
|
+
if (existsSync5(envIntPath)) {
|
|
6540
6648
|
try {
|
|
6541
|
-
for (const line of
|
|
6649
|
+
for (const line of readFileSync7(envIntPath, "utf-8").split("\n")) {
|
|
6542
6650
|
if (!line || line.startsWith("#") || !line.includes("=")) continue;
|
|
6543
6651
|
const eqIdx = line.indexOf("=");
|
|
6544
6652
|
childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
|
|
@@ -6562,11 +6670,21 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
6562
6670
|
runId = startResult.run_id;
|
|
6563
6671
|
kanbanItemId = startResult.kanban_item_id;
|
|
6564
6672
|
if (runId) childEnv["AGT_RUN_ID"] = runId;
|
|
6673
|
+
const claudeKind = task.templateId === "kanban-work" ? "kanban-work" : "scheduled-task";
|
|
6565
6674
|
const { stdout, stderr } = await execFilePromiseLong(resolveClaudeBinary(), claudeArgs, {
|
|
6566
6675
|
cwd: projectDir,
|
|
6567
6676
|
timeout: 3e5,
|
|
6568
6677
|
stdin: "ignore",
|
|
6569
|
-
env: childEnv
|
|
6678
|
+
env: childEnv,
|
|
6679
|
+
onSpawn: (pid) => registerClaudeSpawn({
|
|
6680
|
+
pid,
|
|
6681
|
+
started_at: Date.now(),
|
|
6682
|
+
kind: claudeKind,
|
|
6683
|
+
agent_id: agentId,
|
|
6684
|
+
agent_code_name: codeName,
|
|
6685
|
+
kanban_card_id: kanbanItemId ?? void 0
|
|
6686
|
+
}),
|
|
6687
|
+
onExit: (pid) => unregisterClaudeSpawn(pid)
|
|
6570
6688
|
});
|
|
6571
6689
|
if (stderr) {
|
|
6572
6690
|
log(`[claude-scheduler] Task '${task.name}' stderr for '${codeName}': ${stderr.slice(0, 500)}`);
|
|
@@ -7340,9 +7458,9 @@ ${escapeXml(msg.content)}
|
|
|
7340
7458
|
const projDir = ccProjectDir(agent.codeName);
|
|
7341
7459
|
const mcpConfigPath = join6(projDir, ".mcp.json");
|
|
7342
7460
|
const serverNames = [];
|
|
7343
|
-
if (
|
|
7461
|
+
if (existsSync5(mcpConfigPath)) {
|
|
7344
7462
|
try {
|
|
7345
|
-
const d = JSON.parse(
|
|
7463
|
+
const d = JSON.parse(readFileSync7(mcpConfigPath, "utf-8"));
|
|
7346
7464
|
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
7347
7465
|
} catch {
|
|
7348
7466
|
}
|
|
@@ -7362,14 +7480,14 @@ ${escapeXml(msg.content)}
|
|
|
7362
7480
|
allowedTools
|
|
7363
7481
|
];
|
|
7364
7482
|
const chatClaudeMd = join6(projDir, "CLAUDE.md");
|
|
7365
|
-
if (
|
|
7483
|
+
if (existsSync5(chatClaudeMd)) {
|
|
7366
7484
|
chatArgs.push("--system-prompt-file", chatClaudeMd);
|
|
7367
7485
|
}
|
|
7368
7486
|
const envIntPath = join6(projDir, ".env.integrations");
|
|
7369
7487
|
const childEnv = { ...process.env };
|
|
7370
|
-
if (
|
|
7488
|
+
if (existsSync5(envIntPath)) {
|
|
7371
7489
|
try {
|
|
7372
|
-
for (const line of
|
|
7490
|
+
for (const line of readFileSync7(envIntPath, "utf-8").split("\n")) {
|
|
7373
7491
|
if (!line || line.startsWith("#") || !line.includes("=")) continue;
|
|
7374
7492
|
const eqIdx = line.indexOf("=");
|
|
7375
7493
|
childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
|
|
@@ -7382,7 +7500,19 @@ ${escapeXml(msg.content)}
|
|
|
7382
7500
|
} catch (err) {
|
|
7383
7501
|
throw new Error(`Auth resolve failed for '${agent.codeName}': ${err.message}`);
|
|
7384
7502
|
}
|
|
7385
|
-
const { stdout } = await execFilePromiseLong(resolveClaudeBinary(), chatArgs, {
|
|
7503
|
+
const { stdout } = await execFilePromiseLong(resolveClaudeBinary(), chatArgs, {
|
|
7504
|
+
cwd: projDir,
|
|
7505
|
+
stdin: "ignore",
|
|
7506
|
+
env: childEnv,
|
|
7507
|
+
onSpawn: (pid) => registerClaudeSpawn({
|
|
7508
|
+
pid,
|
|
7509
|
+
started_at: Date.now(),
|
|
7510
|
+
kind: "direct-chat",
|
|
7511
|
+
agent_id: agent.agentId,
|
|
7512
|
+
agent_code_name: agent.codeName
|
|
7513
|
+
}),
|
|
7514
|
+
onExit: (pid) => unregisterClaudeSpawn(pid)
|
|
7515
|
+
});
|
|
7386
7516
|
reply = stdout.trim() || "[No response from agent]";
|
|
7387
7517
|
} else {
|
|
7388
7518
|
const { stdout } = await execFilePromiseLong("openclaw", [
|
|
@@ -7734,8 +7864,8 @@ function getBuiltInSkillContent(skillId) {
|
|
|
7734
7864
|
join6(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "skills", skillId, "SKILL.md")
|
|
7735
7865
|
];
|
|
7736
7866
|
for (const candidate of candidates) {
|
|
7737
|
-
if (
|
|
7738
|
-
const content =
|
|
7867
|
+
if (existsSync5(candidate)) {
|
|
7868
|
+
const content = readFileSync7(candidate, "utf-8");
|
|
7739
7869
|
const files = [{ relativePath: "SKILL.md", content }];
|
|
7740
7870
|
builtInSkillCache.set(skillId, files);
|
|
7741
7871
|
return files;
|
|
@@ -7906,6 +8036,12 @@ async function execFilePromiseLong(cmd, args, opts) {
|
|
|
7906
8036
|
stdio: [opts?.stdin === "ignore" ? "ignore" : "pipe", "pipe", "pipe"],
|
|
7907
8037
|
...opts?.env ? { env: opts.env } : {}
|
|
7908
8038
|
});
|
|
8039
|
+
if (opts?.onSpawn && typeof child.pid === "number") {
|
|
8040
|
+
try {
|
|
8041
|
+
opts.onSpawn(child.pid);
|
|
8042
|
+
} catch {
|
|
8043
|
+
}
|
|
8044
|
+
}
|
|
7909
8045
|
let stdout = "";
|
|
7910
8046
|
let stderr = "";
|
|
7911
8047
|
child.stdout?.on("data", (d) => {
|
|
@@ -7920,6 +8056,12 @@ async function execFilePromiseLong(cmd, args, opts) {
|
|
|
7920
8056
|
}, opts?.timeout ?? 12e4);
|
|
7921
8057
|
child.on("close", (code) => {
|
|
7922
8058
|
clearTimeout(timer);
|
|
8059
|
+
if (opts?.onExit && typeof child.pid === "number") {
|
|
8060
|
+
try {
|
|
8061
|
+
opts.onExit(child.pid);
|
|
8062
|
+
} catch {
|
|
8063
|
+
}
|
|
8064
|
+
}
|
|
7923
8065
|
if (code !== 0) reject(new ChildProcessError(code, stdout, stderr));
|
|
7924
8066
|
else resolve({ stdout, stderr });
|
|
7925
8067
|
});
|
|
@@ -8688,14 +8830,14 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
8688
8830
|
}
|
|
8689
8831
|
pendingFreshMemorySync.delete(agent.agent_id);
|
|
8690
8832
|
}
|
|
8691
|
-
if (
|
|
8833
|
+
if (existsSync5(memoryDir)) {
|
|
8692
8834
|
const prevHashes = memoryFileHashes.get(agent.agent_id) ?? /* @__PURE__ */ new Map();
|
|
8693
8835
|
const currentHashes = /* @__PURE__ */ new Map();
|
|
8694
8836
|
const changedMemories = [];
|
|
8695
8837
|
for (const file of readdirSync3(memoryDir)) {
|
|
8696
8838
|
if (!file.endsWith(".md")) continue;
|
|
8697
8839
|
try {
|
|
8698
|
-
const raw =
|
|
8840
|
+
const raw = readFileSync7(join6(memoryDir, file), "utf-8");
|
|
8699
8841
|
const fileHash = createHash3("sha256").update(raw).digest("hex").slice(0, 16);
|
|
8700
8842
|
currentHashes.set(file, fileHash);
|
|
8701
8843
|
if (prevHashes.get(file) === fileHash) continue;
|
|
@@ -8720,7 +8862,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
8720
8862
|
} catch (err) {
|
|
8721
8863
|
for (const mem of changedMemories) {
|
|
8722
8864
|
for (const [file] of currentHashes) {
|
|
8723
|
-
const parsed = parseMemoryFile(
|
|
8865
|
+
const parsed = parseMemoryFile(readFileSync7(join6(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
|
|
8724
8866
|
if (parsed?.name === mem.name) currentHashes.delete(file);
|
|
8725
8867
|
}
|
|
8726
8868
|
}
|
|
@@ -8733,7 +8875,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
8733
8875
|
}
|
|
8734
8876
|
}
|
|
8735
8877
|
async function downloadMemories(agent, memoryDir, log2, { force }) {
|
|
8736
|
-
const localFiles =
|
|
8878
|
+
const localFiles = existsSync5(memoryDir) ? readdirSync3(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
|
|
8737
8879
|
const localListHash = createHash3("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
|
|
8738
8880
|
const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
|
|
8739
8881
|
const prevDownload = lastDownloadHash.get(agent.agent_id);
|
|
@@ -8748,7 +8890,7 @@ async function downloadMemories(agent, memoryDir, log2, { force }) {
|
|
|
8748
8890
|
lastDownloadHash.set(agent.agent_id, responseHash);
|
|
8749
8891
|
lastLocalFileHash.set(agent.agent_id, localListHash);
|
|
8750
8892
|
if (dbMemories.memories?.length) {
|
|
8751
|
-
|
|
8893
|
+
mkdirSync4(memoryDir, { recursive: true });
|
|
8752
8894
|
let written = 0;
|
|
8753
8895
|
let overwritten = 0;
|
|
8754
8896
|
for (let i = 0; i < dbMemories.memories.length; i++) {
|
|
@@ -8764,10 +8906,10 @@ description: ${JSON.stringify(mem.content.slice(0, 200))}
|
|
|
8764
8906
|
|
|
8765
8907
|
${mem.content}
|
|
8766
8908
|
`;
|
|
8767
|
-
if (
|
|
8909
|
+
if (existsSync5(filePath)) {
|
|
8768
8910
|
let existing = "";
|
|
8769
8911
|
try {
|
|
8770
|
-
existing =
|
|
8912
|
+
existing = readFileSync7(filePath, "utf-8");
|
|
8771
8913
|
} catch {
|
|
8772
8914
|
}
|
|
8773
8915
|
if (existing === desired) continue;
|
|
@@ -8791,7 +8933,7 @@ ${mem.content}
|
|
|
8791
8933
|
}
|
|
8792
8934
|
}
|
|
8793
8935
|
async function cleanupAgentFiles(codeName, agentDir) {
|
|
8794
|
-
if (
|
|
8936
|
+
if (existsSync5(agentDir)) {
|
|
8795
8937
|
try {
|
|
8796
8938
|
rmSync2(agentDir, { recursive: true, force: true });
|
|
8797
8939
|
log(`Removed provision directory for '${codeName}'`);
|
|
@@ -8947,9 +9089,24 @@ async function stopPolling(opts = {}) {
|
|
|
8947
9089
|
process.exit(opts.forcedExitCode ?? 1);
|
|
8948
9090
|
}, 15e3);
|
|
8949
9091
|
shutdownTimer.unref();
|
|
9092
|
+
const drainStartedAt = Date.now();
|
|
9093
|
+
const livePids = [...inFlightClaudePids.keys()];
|
|
9094
|
+
if (livePids.length > 0) {
|
|
9095
|
+
for (const pid of livePids) {
|
|
9096
|
+
try {
|
|
9097
|
+
process.kill(pid, "SIGTERM");
|
|
9098
|
+
} catch {
|
|
9099
|
+
}
|
|
9100
|
+
}
|
|
9101
|
+
log(formatDrainShutdownLine({ step: "sigterm-claude-pids", elapsedMs: Date.now() - drainStartedAt, killedPids: livePids }));
|
|
9102
|
+
} else {
|
|
9103
|
+
log(formatDrainShutdownLine({ step: "sigterm-claude-pids", elapsedMs: 0, note: "no in-flight claude -p children" }));
|
|
9104
|
+
}
|
|
9105
|
+
const subsysStartedAt = Date.now();
|
|
8950
9106
|
stopCaffeinate();
|
|
8951
9107
|
stopRealtimeChat();
|
|
8952
9108
|
stopGatewayPool();
|
|
9109
|
+
log(formatDrainShutdownLine({ step: "stop-subsystems", elapsedMs: Date.now() - subsysStartedAt }));
|
|
8953
9110
|
for (const codeName of [...scheduledRunsByCode.keys()]) {
|
|
8954
9111
|
closeScheduledRunsForCode(codeName, "cancelled", "manager shutdown");
|
|
8955
9112
|
}
|
|
@@ -8965,8 +9122,8 @@ function startManager(opts) {
|
|
|
8965
9122
|
config = opts;
|
|
8966
9123
|
try {
|
|
8967
9124
|
const stateFile = getStateFile();
|
|
8968
|
-
if (
|
|
8969
|
-
const raw =
|
|
9125
|
+
if (existsSync5(stateFile)) {
|
|
9126
|
+
const raw = readFileSync7(stateFile, "utf-8");
|
|
8970
9127
|
const parsed = JSON.parse(raw);
|
|
8971
9128
|
if (Array.isArray(parsed.agents)) {
|
|
8972
9129
|
state4.agents = parsed.agents;
|
|
@@ -8986,8 +9143,77 @@ function startManager(opts) {
|
|
|
8986
9143
|
);
|
|
8987
9144
|
deployMcpAssets();
|
|
8988
9145
|
reapOrphanChannelMcps({ log });
|
|
8989
|
-
void
|
|
8990
|
-
|
|
9146
|
+
void (async () => {
|
|
9147
|
+
try {
|
|
9148
|
+
await reapOrphanedClaudePids();
|
|
9149
|
+
} catch (err) {
|
|
9150
|
+
log(`[drain] boot reaper failed: ${err.message}`);
|
|
9151
|
+
}
|
|
9152
|
+
void ensureHostFrameworkBinaries();
|
|
9153
|
+
startPolling();
|
|
9154
|
+
})();
|
|
9155
|
+
}
|
|
9156
|
+
async function reapOrphanedClaudePids() {
|
|
9157
|
+
const path = claudePidFilePath();
|
|
9158
|
+
const file = readPidFile(path);
|
|
9159
|
+
if (file.spawns.length === 0) return;
|
|
9160
|
+
const looksLikeClaude = (pid) => {
|
|
9161
|
+
if (process.platform !== "linux") return true;
|
|
9162
|
+
try {
|
|
9163
|
+
const comm = readFileSync7(`/proc/${pid}/comm`, "utf-8").trim().toLowerCase();
|
|
9164
|
+
return comm.includes("claude");
|
|
9165
|
+
} catch {
|
|
9166
|
+
return false;
|
|
9167
|
+
}
|
|
9168
|
+
};
|
|
9169
|
+
const decision = decideReaperActions(
|
|
9170
|
+
file,
|
|
9171
|
+
(pid) => {
|
|
9172
|
+
try {
|
|
9173
|
+
process.kill(pid, 0);
|
|
9174
|
+
return true;
|
|
9175
|
+
} catch {
|
|
9176
|
+
return false;
|
|
9177
|
+
}
|
|
9178
|
+
},
|
|
9179
|
+
looksLikeClaude
|
|
9180
|
+
);
|
|
9181
|
+
for (const spawn of decision.toKill) {
|
|
9182
|
+
try {
|
|
9183
|
+
process.kill(spawn.pid, "SIGKILL");
|
|
9184
|
+
} catch (err) {
|
|
9185
|
+
log(`[drain] reaper SIGKILL pid=${spawn.pid} failed: ${err.message}`);
|
|
9186
|
+
}
|
|
9187
|
+
}
|
|
9188
|
+
for (const spawn of decision.pidReusedSkipped) {
|
|
9189
|
+
log(
|
|
9190
|
+
`[drain] reaper skipped pid=${spawn.pid} agent=${spawn.agent_code_name} card=${spawn.kanban_card_id ?? "none"} \u2014 PID reuse suspected (identity probe rejected)`
|
|
9191
|
+
);
|
|
9192
|
+
}
|
|
9193
|
+
let kanbanReset = 0;
|
|
9194
|
+
for (const spawn of decision.toResetKanban) {
|
|
9195
|
+
try {
|
|
9196
|
+
await api.post("/host/kanban", {
|
|
9197
|
+
agent_id: spawn.agent_id,
|
|
9198
|
+
update: [{ id: spawn.kanban_card_id, status: "backlog" }]
|
|
9199
|
+
});
|
|
9200
|
+
kanbanReset += 1;
|
|
9201
|
+
} catch (err) {
|
|
9202
|
+
log(`[drain] reaper kanban-reset failed for card=${spawn.kanban_card_id} agent=${spawn.agent_code_name}: ${err.message}`);
|
|
9203
|
+
}
|
|
9204
|
+
}
|
|
9205
|
+
log(formatReaperBootLine({
|
|
9206
|
+
totalRecorded: file.spawns.length,
|
|
9207
|
+
killed: decision.toKill.length,
|
|
9208
|
+
alreadyDead: decision.alreadyDead.length,
|
|
9209
|
+
pidReusedSkipped: decision.pidReusedSkipped.length,
|
|
9210
|
+
kanbanReset
|
|
9211
|
+
}));
|
|
9212
|
+
try {
|
|
9213
|
+
writePidFile(path, { version: 1, spawns: [] });
|
|
9214
|
+
} catch (err) {
|
|
9215
|
+
log(`[drain] reaper file truncate failed: ${err.message}`);
|
|
9216
|
+
}
|
|
8991
9217
|
}
|
|
8992
9218
|
async function ensureHostFrameworkBinaries() {
|
|
8993
9219
|
const apiKey = getApiKey();
|
|
@@ -9045,17 +9271,17 @@ function restartRunningChannelMcps(basenames) {
|
|
|
9045
9271
|
}
|
|
9046
9272
|
function deployMcpAssets() {
|
|
9047
9273
|
const targetDir = join6(homedir4(), ".augmented", "_mcp");
|
|
9048
|
-
|
|
9049
|
-
const moduleDir =
|
|
9274
|
+
mkdirSync4(targetDir, { recursive: true });
|
|
9275
|
+
const moduleDir = dirname3(fileURLToPath(import.meta.url));
|
|
9050
9276
|
let mcpSourceDir = "";
|
|
9051
9277
|
let dir = moduleDir;
|
|
9052
9278
|
for (let i = 0; i < 6; i++) {
|
|
9053
9279
|
const candidate = join6(dir, "dist", "mcp");
|
|
9054
|
-
if (
|
|
9280
|
+
if (existsSync5(join6(candidate, "index.js"))) {
|
|
9055
9281
|
mcpSourceDir = candidate;
|
|
9056
9282
|
break;
|
|
9057
9283
|
}
|
|
9058
|
-
const parent =
|
|
9284
|
+
const parent = dirname3(dir);
|
|
9059
9285
|
if (parent === dir) break;
|
|
9060
9286
|
dir = parent;
|
|
9061
9287
|
}
|
|
@@ -9066,8 +9292,8 @@ function deployMcpAssets() {
|
|
|
9066
9292
|
const changedBasenames = [];
|
|
9067
9293
|
const fileHash = (p) => {
|
|
9068
9294
|
try {
|
|
9069
|
-
if (!
|
|
9070
|
-
return createHash3("sha256").update(
|
|
9295
|
+
if (!existsSync5(p)) return null;
|
|
9296
|
+
return createHash3("sha256").update(readFileSync7(p)).digest("hex");
|
|
9071
9297
|
} catch {
|
|
9072
9298
|
return null;
|
|
9073
9299
|
}
|
|
@@ -9080,7 +9306,7 @@ function deployMcpAssets() {
|
|
|
9080
9306
|
for (const file of ["index.js", "slack-channel.js", "direct-chat-channel.js", "telegram-channel.js"]) {
|
|
9081
9307
|
const src = join6(mcpSourceDir, file);
|
|
9082
9308
|
const dst = join6(targetDir, file);
|
|
9083
|
-
if (!
|
|
9309
|
+
if (!existsSync5(src)) continue;
|
|
9084
9310
|
const before = fileHash(dst);
|
|
9085
9311
|
try {
|
|
9086
9312
|
copyFileSync(src, dst);
|
|
@@ -9100,13 +9326,13 @@ function deployMcpAssets() {
|
|
|
9100
9326
|
const localMcpPath = join6(targetDir, "index.js");
|
|
9101
9327
|
try {
|
|
9102
9328
|
const agentsDir = join6(homedir4(), ".augmented", "agents");
|
|
9103
|
-
if (
|
|
9329
|
+
if (existsSync5(agentsDir)) {
|
|
9104
9330
|
for (const entry of readdirSync3(agentsDir, { withFileTypes: true })) {
|
|
9105
9331
|
if (!entry.isDirectory()) continue;
|
|
9106
9332
|
for (const subdir of ["provision", "project"]) {
|
|
9107
9333
|
const mcpJsonPath = join6(agentsDir, entry.name, subdir, ".mcp.json");
|
|
9108
9334
|
try {
|
|
9109
|
-
const raw =
|
|
9335
|
+
const raw = readFileSync7(mcpJsonPath, "utf-8");
|
|
9110
9336
|
if (!raw.includes("@integrity-labs/augmented-mcp")) continue;
|
|
9111
9337
|
const mcpConfig = JSON.parse(raw);
|
|
9112
9338
|
const augServer = mcpConfig.mcpServers?.["augmented"];
|