@integrity-labs/agt-cli 0.15.8 → 0.15.9
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 +3 -3
- package/dist/{chunk-32D5TUSD.js → chunk-C6UBNLUC.js} +117 -5
- package/dist/{chunk-32D5TUSD.js.map → chunk-C6UBNLUC.js.map} +1 -1
- package/dist/lib/manager-worker.js +348 -177
- package/dist/lib/manager-worker.js.map +1 -1
- package/mcp/slack-channel.js +388 -11
- package/mcp/telegram-channel.js +386 -8
- package/package.json +1 -1
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
resolveChannels,
|
|
23
23
|
resolveDmTarget,
|
|
24
24
|
wrapScheduledTaskPrompt
|
|
25
|
-
} from "../chunk-
|
|
25
|
+
} from "../chunk-C6UBNLUC.js";
|
|
26
26
|
import {
|
|
27
27
|
findTaskByTemplate,
|
|
28
28
|
getProjectDir,
|
|
@@ -46,11 +46,11 @@ import {
|
|
|
46
46
|
|
|
47
47
|
// src/lib/manager-worker.ts
|
|
48
48
|
import { createHash } from "crypto";
|
|
49
|
-
import { readFileSync, writeFileSync, appendFileSync, mkdirSync, chmodSync, existsSync, rmSync, readdirSync, statSync, unlinkSync, copyFileSync } from "fs";
|
|
49
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, appendFileSync, mkdirSync as mkdirSync2, chmodSync, existsSync as existsSync2, rmSync as rmSync2, readdirSync as readdirSync2, statSync, unlinkSync, copyFileSync } from "fs";
|
|
50
50
|
import https from "https";
|
|
51
51
|
import { execFileSync as syncExecFile } from "child_process";
|
|
52
|
-
import { join as
|
|
53
|
-
import { homedir as
|
|
52
|
+
import { join as join3, dirname } from "path";
|
|
53
|
+
import { homedir as homedir3 } from "os";
|
|
54
54
|
import { fileURLToPath } from "url";
|
|
55
55
|
|
|
56
56
|
// src/lib/plugin-context-render.ts
|
|
@@ -896,6 +896,135 @@ function withScheduleLinkFooter(opts) {
|
|
|
896
896
|
return appendScheduleLinkFooter(opts.body, url, opts.medium);
|
|
897
897
|
}
|
|
898
898
|
|
|
899
|
+
// src/lib/restart-flags.ts
|
|
900
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
901
|
+
import { homedir as homedir2 } from "os";
|
|
902
|
+
import { join as join2 } from "path";
|
|
903
|
+
import { randomUUID } from "crypto";
|
|
904
|
+
function restartFlagsDir() {
|
|
905
|
+
return join2(homedir2(), ".augmented", "restart-flags");
|
|
906
|
+
}
|
|
907
|
+
function flagPath(codeName) {
|
|
908
|
+
return join2(restartFlagsDir(), `${codeName}.flag`);
|
|
909
|
+
}
|
|
910
|
+
function readRestartFlags() {
|
|
911
|
+
const dir = restartFlagsDir();
|
|
912
|
+
if (!existsSync(dir)) return [];
|
|
913
|
+
const out = [];
|
|
914
|
+
for (const entry of readdirSync(dir)) {
|
|
915
|
+
if (!entry.endsWith(".flag")) continue;
|
|
916
|
+
try {
|
|
917
|
+
const raw = readFileSync(join2(dir, entry), "utf8");
|
|
918
|
+
const parsed = JSON.parse(raw);
|
|
919
|
+
if (typeof parsed.codeName !== "string" || parsed.codeName.length === 0) {
|
|
920
|
+
parsed.codeName = entry.replace(/\.flag$/, "");
|
|
921
|
+
}
|
|
922
|
+
out.push(parsed);
|
|
923
|
+
} catch {
|
|
924
|
+
out.push({
|
|
925
|
+
codeName: entry.replace(/\.flag$/, ""),
|
|
926
|
+
source: "unknown",
|
|
927
|
+
ts: Date.now()
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
return out;
|
|
932
|
+
}
|
|
933
|
+
function deleteRestartFlag(codeName) {
|
|
934
|
+
const path = flagPath(codeName);
|
|
935
|
+
if (existsSync(path)) {
|
|
936
|
+
rmSync(path, { force: true });
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
// src/lib/restart-handler.ts
|
|
941
|
+
async function processRestartFlags(opts) {
|
|
942
|
+
const flags = readRestartFlags();
|
|
943
|
+
if (flags.length === 0) return;
|
|
944
|
+
for (const flag of flags) {
|
|
945
|
+
try {
|
|
946
|
+
await processOne(flag, opts);
|
|
947
|
+
} catch (err) {
|
|
948
|
+
opts.log(`[restart-handler] Failed to process flag for '${flag.codeName}': ${err.message}`);
|
|
949
|
+
try {
|
|
950
|
+
await sendError(flag, opts, `\u274C Restart failed for \`${flag.codeName}\`: internal error. Check manager logs.`);
|
|
951
|
+
} catch (ackErr) {
|
|
952
|
+
opts.log(`[restart-handler] Error-ack send failed for '${flag.codeName}': ${ackErr.message}`);
|
|
953
|
+
}
|
|
954
|
+
} finally {
|
|
955
|
+
try {
|
|
956
|
+
deleteRestartFlag(flag.codeName);
|
|
957
|
+
} catch (err) {
|
|
958
|
+
opts.log(`[restart-handler] Failed to delete flag for '${flag.codeName}': ${err.message}`);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
async function processOne(flag, opts) {
|
|
964
|
+
const framework = opts.resolveFramework(flag.codeName);
|
|
965
|
+
if (framework === null) {
|
|
966
|
+
opts.log(`[restart-handler] Ignoring /restart for unknown agent '${flag.codeName}'`);
|
|
967
|
+
await sendError(flag, opts, `Agent \`${flag.codeName}\` is not managed by this host.`);
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
if (framework !== "claude-code") {
|
|
971
|
+
opts.log(`[restart-handler] Ignoring /restart for '${flag.codeName}' \u2014 framework is '${framework}', not claude-code`);
|
|
972
|
+
await sendError(flag, opts, `\`/restart\` is only supported for Claude Code agents (this agent runs on \`${framework}\`).`);
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
opts.log(`[restart-handler] Restarting tmux session for '${flag.codeName}' (source: ${flag.source})`);
|
|
976
|
+
opts.stopSession(flag.codeName);
|
|
977
|
+
await sendAck(flag, opts, `\u{1F504} Restart initiated for \`${flag.codeName}\` \u2014 the replacement session is being created.`);
|
|
978
|
+
}
|
|
979
|
+
async function sendAck(flag, opts, text) {
|
|
980
|
+
if (flag.source === "telegram") {
|
|
981
|
+
const tokens = opts.getTelegramTokens(flag.codeName);
|
|
982
|
+
if (!tokens) {
|
|
983
|
+
opts.log(`[restart-handler] No telegram tokens cached for '${flag.codeName}' \u2014 skipping ack`);
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
const chatId = flag.reply?.["chat_id"];
|
|
987
|
+
if (chatId == null) return;
|
|
988
|
+
const messageId = flag.reply?.["message_id"];
|
|
989
|
+
const body = { chat_id: chatId, text, parse_mode: "Markdown" };
|
|
990
|
+
if (messageId != null) body.reply_to_message_id = Number(messageId);
|
|
991
|
+
try {
|
|
992
|
+
const resp = await opts.sendTelegram(tokens.token, "sendMessage", body);
|
|
993
|
+
if (!resp.ok) {
|
|
994
|
+
opts.log(`[restart-handler] Telegram ack failed for '${flag.codeName}': ${resp.description ?? "unknown"}`);
|
|
995
|
+
}
|
|
996
|
+
} catch (err) {
|
|
997
|
+
opts.log(`[restart-handler] Telegram ack threw for '${flag.codeName}': ${err.message}`);
|
|
998
|
+
}
|
|
999
|
+
return;
|
|
1000
|
+
}
|
|
1001
|
+
if (flag.source === "slack") {
|
|
1002
|
+
const token = opts.getSlackToken(flag.codeName);
|
|
1003
|
+
if (!token) {
|
|
1004
|
+
opts.log(`[restart-handler] No slack token cached for '${flag.codeName}' \u2014 skipping ack`);
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
const channel = flag.reply?.["channel"];
|
|
1008
|
+
if (channel == null) return;
|
|
1009
|
+
const threadTs = flag.reply?.["thread_ts"];
|
|
1010
|
+
const body = { channel, text };
|
|
1011
|
+
if (threadTs != null) body.thread_ts = String(threadTs);
|
|
1012
|
+
try {
|
|
1013
|
+
const resp = await opts.sendSlack(token, body);
|
|
1014
|
+
if (!resp.ok) {
|
|
1015
|
+
opts.log(`[restart-handler] Slack ack failed for '${flag.codeName}': ${resp.error ?? "unknown"}`);
|
|
1016
|
+
}
|
|
1017
|
+
} catch (err) {
|
|
1018
|
+
opts.log(`[restart-handler] Slack ack threw for '${flag.codeName}': ${err.message}`);
|
|
1019
|
+
}
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
opts.log(`[restart-handler] Unknown ack source '${flag.source}' for '${flag.codeName}' \u2014 skipping`);
|
|
1023
|
+
}
|
|
1024
|
+
async function sendError(flag, opts, text) {
|
|
1025
|
+
await sendAck(flag, opts, text);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
899
1028
|
// src/lib/realtime-chat.ts
|
|
900
1029
|
import { createClient } from "@supabase/supabase-js";
|
|
901
1030
|
var client = null;
|
|
@@ -1220,8 +1349,8 @@ function stopRealtimeChat() {
|
|
|
1220
1349
|
var GATEWAY_PORT_BASE = 18800;
|
|
1221
1350
|
var GATEWAY_PORT_STEP = 10;
|
|
1222
1351
|
var GATEWAY_PORT_MAX = 18899;
|
|
1223
|
-
var AUGMENTED_DIR =
|
|
1224
|
-
var GATEWAY_PORTS_FILE =
|
|
1352
|
+
var AUGMENTED_DIR = join3(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
1353
|
+
var GATEWAY_PORTS_FILE = join3(AUGMENTED_DIR, "gateway-ports.json");
|
|
1225
1354
|
var CHANNEL_SWEEP_INTERVAL_MS = (() => {
|
|
1226
1355
|
const raw = parseInt(process.env["AGT_CHANNEL_SWEEP_INTERVAL_MS"] ?? "", 10);
|
|
1227
1356
|
if (!Number.isFinite(raw)) return 5 * 60 * 1e3;
|
|
@@ -1313,7 +1442,7 @@ function resolveBrewPath(execFileSync2) {
|
|
|
1313
1442
|
} catch {
|
|
1314
1443
|
}
|
|
1315
1444
|
const fallback = "/home/linuxbrew/.linuxbrew/bin/brew";
|
|
1316
|
-
if (
|
|
1445
|
+
if (existsSync2(fallback)) return fallback;
|
|
1317
1446
|
return null;
|
|
1318
1447
|
}
|
|
1319
1448
|
var toolkitCliEnsured = /* @__PURE__ */ new Set();
|
|
@@ -1459,7 +1588,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
1459
1588
|
}
|
|
1460
1589
|
return runAsync(brewPath, args, opts);
|
|
1461
1590
|
};
|
|
1462
|
-
let claudeExists =
|
|
1591
|
+
let claudeExists = existsSync2("/home/linuxbrew/.linuxbrew/bin/claude");
|
|
1463
1592
|
if (!claudeExists) {
|
|
1464
1593
|
try {
|
|
1465
1594
|
execFileSync2("which", ["claude"], { timeout: 5e3 });
|
|
@@ -1483,7 +1612,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
1483
1612
|
if (!process.env.PATH?.split(":").includes(brewBinDir)) {
|
|
1484
1613
|
process.env.PATH = `${brewBinDir}:${process.env.PATH ?? ""}`;
|
|
1485
1614
|
}
|
|
1486
|
-
if (
|
|
1615
|
+
if (existsSync2("/home/linuxbrew/.linuxbrew/bin/claude")) {
|
|
1487
1616
|
log("Claude Code installed successfully");
|
|
1488
1617
|
} else {
|
|
1489
1618
|
log("Claude Code install completed but binary not found at expected path \u2014 check brew logs");
|
|
@@ -1518,9 +1647,9 @@ async function checkAndUpdateCli() {
|
|
|
1518
1647
|
const isDevMode = cliPath.includes("/src/") || cliPath.includes("tsx");
|
|
1519
1648
|
if (isDevMode) return;
|
|
1520
1649
|
if (!isHomebrew && !cliPath.includes("node_modules")) return;
|
|
1521
|
-
const { homedir:
|
|
1650
|
+
const { homedir: homedir4 } = await import("os");
|
|
1522
1651
|
const { readFileSync: readF, writeFileSync: writeF } = await import("fs");
|
|
1523
|
-
const markerPath =
|
|
1652
|
+
const markerPath = join3(homedir4(), ".augmented", ".last-update-check");
|
|
1524
1653
|
try {
|
|
1525
1654
|
const lastCheck = parseInt(readF(markerPath, "utf-8").trim(), 10);
|
|
1526
1655
|
if (Date.now() - lastCheck < UPDATE_CHECK_INTERVAL_MS) return;
|
|
@@ -1603,12 +1732,12 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
1603
1732
|
throw new Error("claude_auth_mode=api_key but /host/exchange returned no decrypted key");
|
|
1604
1733
|
}
|
|
1605
1734
|
childEnv.ANTHROPIC_API_KEY = exchange.anthropicApiKey;
|
|
1606
|
-
const claudeDir =
|
|
1735
|
+
const claudeDir = join3(homedir3(), ".claude");
|
|
1607
1736
|
for (const filename of [".credentials.json", "credentials.json"]) {
|
|
1608
|
-
const p =
|
|
1609
|
-
if (
|
|
1737
|
+
const p = join3(claudeDir, filename);
|
|
1738
|
+
if (existsSync2(p)) {
|
|
1610
1739
|
try {
|
|
1611
|
-
|
|
1740
|
+
rmSync2(p, { force: true });
|
|
1612
1741
|
log(`[${label}] Removed ${p} (api_key mode \u2014 preventing OAuth fallback)`);
|
|
1613
1742
|
} catch {
|
|
1614
1743
|
}
|
|
@@ -1620,14 +1749,14 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
1620
1749
|
}
|
|
1621
1750
|
function loadGatewayPorts() {
|
|
1622
1751
|
try {
|
|
1623
|
-
return JSON.parse(
|
|
1752
|
+
return JSON.parse(readFileSync2(GATEWAY_PORTS_FILE, "utf-8"));
|
|
1624
1753
|
} catch {
|
|
1625
1754
|
return {};
|
|
1626
1755
|
}
|
|
1627
1756
|
}
|
|
1628
1757
|
function saveGatewayPorts(ports) {
|
|
1629
|
-
|
|
1630
|
-
|
|
1758
|
+
mkdirSync2(AUGMENTED_DIR, { recursive: true });
|
|
1759
|
+
writeFileSync2(GATEWAY_PORTS_FILE, JSON.stringify(ports, null, 2));
|
|
1631
1760
|
}
|
|
1632
1761
|
function allocatePort(codeName) {
|
|
1633
1762
|
const ports = loadGatewayPorts();
|
|
@@ -1650,12 +1779,12 @@ function freePort(codeName) {
|
|
|
1650
1779
|
}
|
|
1651
1780
|
}
|
|
1652
1781
|
function getStateFile() {
|
|
1653
|
-
return
|
|
1782
|
+
return join3(config?.configDir ?? join3(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
|
|
1654
1783
|
}
|
|
1655
1784
|
function send(msg) {
|
|
1656
1785
|
if (msg.type === "state-update") {
|
|
1657
1786
|
try {
|
|
1658
|
-
|
|
1787
|
+
writeFileSync2(getStateFile(), JSON.stringify(msg.state, null, 2));
|
|
1659
1788
|
} catch {
|
|
1660
1789
|
}
|
|
1661
1790
|
}
|
|
@@ -1685,9 +1814,9 @@ function log(msg) {
|
|
|
1685
1814
|
`);
|
|
1686
1815
|
try {
|
|
1687
1816
|
if (!managerLogPath) {
|
|
1688
|
-
managerLogPath =
|
|
1689
|
-
|
|
1690
|
-
if (!
|
|
1817
|
+
managerLogPath = join3(homedir3(), ".augmented", "manager.log");
|
|
1818
|
+
mkdirSync2(dirname(managerLogPath), { recursive: true });
|
|
1819
|
+
if (!existsSync2(managerLogPath)) {
|
|
1691
1820
|
appendFileSync(managerLogPath, "", { mode: 384 });
|
|
1692
1821
|
} else {
|
|
1693
1822
|
chmodSync(managerLogPath, 384);
|
|
@@ -1703,7 +1832,7 @@ function sha256(content) {
|
|
|
1703
1832
|
}
|
|
1704
1833
|
function hashFile(filePath) {
|
|
1705
1834
|
try {
|
|
1706
|
-
const content =
|
|
1835
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
1707
1836
|
return sha256(content);
|
|
1708
1837
|
} catch {
|
|
1709
1838
|
return null;
|
|
@@ -1727,13 +1856,13 @@ function parseSkillFrontmatter(content) {
|
|
|
1727
1856
|
return out;
|
|
1728
1857
|
}
|
|
1729
1858
|
async function refreshSkillsIndexInClaudeMd(configDir, codeName, log2) {
|
|
1730
|
-
const { readdirSync:
|
|
1731
|
-
const skillsDir =
|
|
1732
|
-
const claudeMdPath =
|
|
1859
|
+
const { readdirSync: readdirSync3, readFileSync: rfs, existsSync: ex, writeFileSync: writeFileSync3 } = await import("fs");
|
|
1860
|
+
const skillsDir = join3(configDir, codeName, "project", ".claude", "skills");
|
|
1861
|
+
const claudeMdPath = join3(configDir, codeName, "project", "CLAUDE.md");
|
|
1733
1862
|
if (!ex(skillsDir) || !ex(claudeMdPath)) return;
|
|
1734
1863
|
const entries = [];
|
|
1735
|
-
for (const dir of
|
|
1736
|
-
const skillFile =
|
|
1864
|
+
for (const dir of readdirSync3(skillsDir).sort()) {
|
|
1865
|
+
const skillFile = join3(skillsDir, dir, "SKILL.md");
|
|
1737
1866
|
if (!ex(skillFile)) continue;
|
|
1738
1867
|
try {
|
|
1739
1868
|
const { name, description } = parseSkillFrontmatter(rfs(skillFile, "utf-8"));
|
|
@@ -1775,16 +1904,16 @@ ${SKILLS_INDEX_END}`;
|
|
|
1775
1904
|
next = current.trimEnd() + "\n\n" + section + "\n";
|
|
1776
1905
|
}
|
|
1777
1906
|
if (next !== current) {
|
|
1778
|
-
|
|
1907
|
+
writeFileSync3(claudeMdPath, next, "utf-8");
|
|
1779
1908
|
log2(`Refreshed skills index in CLAUDE.md for '${codeName}' (${entries.length} skills)`);
|
|
1780
1909
|
}
|
|
1781
1910
|
}
|
|
1782
1911
|
async function migrateToProfiles() {
|
|
1783
1912
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
1784
|
-
const sharedConfigPath =
|
|
1913
|
+
const sharedConfigPath = join3(homeDir, ".openclaw", "openclaw.json");
|
|
1785
1914
|
let sharedConfig;
|
|
1786
1915
|
try {
|
|
1787
|
-
sharedConfig = JSON.parse(
|
|
1916
|
+
sharedConfig = JSON.parse(readFileSync2(sharedConfigPath, "utf-8"));
|
|
1788
1917
|
} catch {
|
|
1789
1918
|
return;
|
|
1790
1919
|
}
|
|
@@ -1797,19 +1926,19 @@ async function migrateToProfiles() {
|
|
|
1797
1926
|
const codeName = agentEntry["id"];
|
|
1798
1927
|
if (!codeName) continue;
|
|
1799
1928
|
if (codeName === "main") continue;
|
|
1800
|
-
const profileDir =
|
|
1801
|
-
if (
|
|
1929
|
+
const profileDir = join3(homeDir, `.openclaw-${codeName}`);
|
|
1930
|
+
if (existsSync2(join3(profileDir, "openclaw.json"))) continue;
|
|
1802
1931
|
log(`Migrating agent '${codeName}' to per-agent profile`);
|
|
1803
1932
|
if (adapter.seedProfileConfig) {
|
|
1804
1933
|
adapter.seedProfileConfig(codeName);
|
|
1805
1934
|
}
|
|
1806
|
-
const sharedAuthDir =
|
|
1807
|
-
const profileAuthDir =
|
|
1808
|
-
const authFile =
|
|
1809
|
-
if (
|
|
1810
|
-
|
|
1811
|
-
const authContent =
|
|
1812
|
-
|
|
1935
|
+
const sharedAuthDir = join3(homeDir, ".openclaw", "agents", codeName, "agent");
|
|
1936
|
+
const profileAuthDir = join3(profileDir, "agents", codeName, "agent");
|
|
1937
|
+
const authFile = join3(sharedAuthDir, "auth-profiles.json");
|
|
1938
|
+
if (existsSync2(authFile)) {
|
|
1939
|
+
mkdirSync2(profileAuthDir, { recursive: true });
|
|
1940
|
+
const authContent = readFileSync2(authFile, "utf-8");
|
|
1941
|
+
writeFileSync2(join3(profileAuthDir, "auth-profiles.json"), authContent);
|
|
1813
1942
|
}
|
|
1814
1943
|
allocatePort(codeName);
|
|
1815
1944
|
migrated++;
|
|
@@ -1847,7 +1976,7 @@ function readGatewayToken(codeName) {
|
|
|
1847
1976
|
}
|
|
1848
1977
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
1849
1978
|
try {
|
|
1850
|
-
const cfg = JSON.parse(
|
|
1979
|
+
const cfg = JSON.parse(readFileSync2(join3(homeDir, `.openclaw-${codeName}`, "openclaw.json"), "utf-8"));
|
|
1851
1980
|
return cfg?.gateway?.auth?.token;
|
|
1852
1981
|
} catch {
|
|
1853
1982
|
return void 0;
|
|
@@ -1856,10 +1985,10 @@ function readGatewayToken(codeName) {
|
|
|
1856
1985
|
var GATEWAY_HUNG_TIMEOUT_MS = 5 * 6e4;
|
|
1857
1986
|
function isGatewayHung(codeName) {
|
|
1858
1987
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
1859
|
-
const jobsPath =
|
|
1860
|
-
if (!
|
|
1988
|
+
const jobsPath = join3(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
1989
|
+
if (!existsSync2(jobsPath)) return false;
|
|
1861
1990
|
try {
|
|
1862
|
-
const data = JSON.parse(
|
|
1991
|
+
const data = JSON.parse(readFileSync2(jobsPath, "utf-8"));
|
|
1863
1992
|
const jobs = data.jobs ?? data;
|
|
1864
1993
|
if (!Array.isArray(jobs)) return false;
|
|
1865
1994
|
const now = Date.now();
|
|
@@ -1892,19 +2021,19 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
1892
2021
|
}
|
|
1893
2022
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
1894
2023
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
1895
|
-
const cronJobsPath =
|
|
2024
|
+
const cronJobsPath = join3(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
1896
2025
|
clearStaleCronRunState(cronJobsPath);
|
|
1897
2026
|
} else {
|
|
1898
2027
|
if (status.port) {
|
|
1899
2028
|
try {
|
|
1900
2029
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
1901
|
-
const configPath =
|
|
1902
|
-
if (
|
|
1903
|
-
const cfg = JSON.parse(
|
|
2030
|
+
const configPath = join3(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
2031
|
+
if (existsSync2(configPath)) {
|
|
2032
|
+
const cfg = JSON.parse(readFileSync2(configPath, "utf-8"));
|
|
1904
2033
|
if (cfg.gateway?.port !== status.port) {
|
|
1905
2034
|
if (!cfg.gateway) cfg.gateway = {};
|
|
1906
2035
|
cfg.gateway.port = status.port;
|
|
1907
|
-
|
|
2036
|
+
writeFileSync2(configPath, JSON.stringify(cfg, null, 2));
|
|
1908
2037
|
}
|
|
1909
2038
|
}
|
|
1910
2039
|
} catch {
|
|
@@ -1924,12 +2053,12 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
1924
2053
|
gatewaysStartedThisCycle.add(codeName);
|
|
1925
2054
|
try {
|
|
1926
2055
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
1927
|
-
const configPath =
|
|
1928
|
-
if (
|
|
1929
|
-
const cfg = JSON.parse(
|
|
2056
|
+
const configPath = join3(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
2057
|
+
if (existsSync2(configPath)) {
|
|
2058
|
+
const cfg = JSON.parse(readFileSync2(configPath, "utf-8"));
|
|
1930
2059
|
if (!cfg.gateway) cfg.gateway = {};
|
|
1931
2060
|
cfg.gateway.port = port;
|
|
1932
|
-
|
|
2061
|
+
writeFileSync2(configPath, JSON.stringify(cfg, null, 2));
|
|
1933
2062
|
}
|
|
1934
2063
|
} catch {
|
|
1935
2064
|
}
|
|
@@ -2012,6 +2141,48 @@ Automatic restart failed: ${err.message}`
|
|
|
2012
2141
|
async function pollCycle() {
|
|
2013
2142
|
if (!config) return;
|
|
2014
2143
|
if (restartAfterUpgrade) return;
|
|
2144
|
+
try {
|
|
2145
|
+
await processRestartFlags({
|
|
2146
|
+
log,
|
|
2147
|
+
resolveFramework: (codeName) => agentFrameworkCache.get(codeName) ?? null,
|
|
2148
|
+
stopSession: (codeName) => {
|
|
2149
|
+
stopPersistentSession(codeName, log);
|
|
2150
|
+
persistentSessionAgents.delete(codeName);
|
|
2151
|
+
claudeAuthTupleBySession.delete(codeName);
|
|
2152
|
+
},
|
|
2153
|
+
getTelegramTokens: (codeName) => {
|
|
2154
|
+
const t = agentChannelTokens.get(codeName);
|
|
2155
|
+
if (!t?.telegram) return null;
|
|
2156
|
+
return { token: t.telegram, allowedChats: t.telegramAllowedChats };
|
|
2157
|
+
},
|
|
2158
|
+
sendTelegram: (botToken, method, body) => telegramApiCall(botToken, method, body),
|
|
2159
|
+
getSlackToken: (codeName) => agentChannelTokens.get(codeName)?.slack ?? null,
|
|
2160
|
+
sendSlack: async (botToken, body) => {
|
|
2161
|
+
const controller = new AbortController();
|
|
2162
|
+
const timer = setTimeout(() => controller.abort(), 5e3);
|
|
2163
|
+
try {
|
|
2164
|
+
const res = await fetch("https://slack.com/api/chat.postMessage", {
|
|
2165
|
+
method: "POST",
|
|
2166
|
+
headers: {
|
|
2167
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
2168
|
+
Authorization: `Bearer ${botToken}`
|
|
2169
|
+
},
|
|
2170
|
+
body: JSON.stringify(body),
|
|
2171
|
+
signal: controller.signal
|
|
2172
|
+
});
|
|
2173
|
+
const data = await res.json();
|
|
2174
|
+
return data;
|
|
2175
|
+
} catch (err) {
|
|
2176
|
+
const isAbort = err.name === "AbortError";
|
|
2177
|
+
return { ok: false, error: isAbort ? "timeout" : err.message };
|
|
2178
|
+
} finally {
|
|
2179
|
+
clearTimeout(timer);
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
});
|
|
2183
|
+
} catch (err) {
|
|
2184
|
+
log(`[restart-handler] processRestartFlags threw: ${err.message}`);
|
|
2185
|
+
}
|
|
2015
2186
|
checkAndUpdateCli().catch((err) => log(`[self-update] Check failed: ${err.message}`));
|
|
2016
2187
|
try {
|
|
2017
2188
|
registeredAgentsCache.clear();
|
|
@@ -2135,7 +2306,7 @@ async function pollCycle() {
|
|
|
2135
2306
|
const adapter = resolveAgentFramework(prev.codeName);
|
|
2136
2307
|
await stopGatewayIfRunning(prev.codeName, adapter);
|
|
2137
2308
|
freePort(prev.codeName);
|
|
2138
|
-
const agentDir =
|
|
2309
|
+
const agentDir = join3(adapter.getAgentDir(prev.codeName), "provision");
|
|
2139
2310
|
await cleanupAgentFiles(prev.codeName, agentDir);
|
|
2140
2311
|
clearAgentCaches(prev.agentId, prev.codeName);
|
|
2141
2312
|
}
|
|
@@ -2215,7 +2386,7 @@ async function processAgent(agent, agentStates) {
|
|
|
2215
2386
|
}
|
|
2216
2387
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2217
2388
|
const adapter = resolveAgentFramework(agent.code_name);
|
|
2218
|
-
let agentDir =
|
|
2389
|
+
let agentDir = join3(adapter.getAgentDir(agent.code_name), "provision");
|
|
2219
2390
|
if (agent.status === "draft" || agent.status === "paused") {
|
|
2220
2391
|
log(`Agent '${agent.code_name}' is ${agent.status}, skipping provisioning`);
|
|
2221
2392
|
await stopGatewayIfRunning(agent.code_name, adapter);
|
|
@@ -2339,7 +2510,7 @@ async function processAgent(agent, agentStates) {
|
|
|
2339
2510
|
const frameworkId = refreshData.agent.framework ?? "openclaw";
|
|
2340
2511
|
agentFrameworkCache.set(agent.code_name, frameworkId);
|
|
2341
2512
|
const frameworkAdapter = getFramework(frameworkId);
|
|
2342
|
-
agentDir =
|
|
2513
|
+
agentDir = join3(frameworkAdapter.getAgentDir(agent.code_name), "provision");
|
|
2343
2514
|
cacheAgentDeliveryMetadata(agent.code_name, refreshData);
|
|
2344
2515
|
if (frameworkAdapter.seedProfileConfig) {
|
|
2345
2516
|
frameworkAdapter.seedProfileConfig(agent.code_name);
|
|
@@ -2367,9 +2538,9 @@ async function processAgent(agent, agentStates) {
|
|
|
2367
2538
|
try {
|
|
2368
2539
|
const artifacts = generateArtifacts(agent, refreshData, frameworkAdapter);
|
|
2369
2540
|
const changedFiles = [];
|
|
2370
|
-
|
|
2541
|
+
mkdirSync2(agentDir, { recursive: true });
|
|
2371
2542
|
for (const artifact of artifacts) {
|
|
2372
|
-
const filePath =
|
|
2543
|
+
const filePath = join3(agentDir, artifact.relativePath);
|
|
2373
2544
|
let existingHash;
|
|
2374
2545
|
let newHash;
|
|
2375
2546
|
let writeContent = artifact.content;
|
|
@@ -2381,8 +2552,8 @@ async function processAgent(agent, agentStates) {
|
|
|
2381
2552
|
};
|
|
2382
2553
|
newHash = sha256(stripDynamicSections(artifact.content));
|
|
2383
2554
|
try {
|
|
2384
|
-
const projectClaudeMd =
|
|
2385
|
-
const existing =
|
|
2555
|
+
const projectClaudeMd = join3(config.configDir, agent.code_name, "project", "CLAUDE.md");
|
|
2556
|
+
const existing = readFileSync2(projectClaudeMd, "utf-8");
|
|
2386
2557
|
existingHash = sha256(stripDynamicSections(existing));
|
|
2387
2558
|
} catch {
|
|
2388
2559
|
existingHash = null;
|
|
@@ -2400,7 +2571,7 @@ async function processAgent(agent, agentStates) {
|
|
|
2400
2571
|
const generatorKeys = Object.keys(generatorServers);
|
|
2401
2572
|
let existingRaw = "";
|
|
2402
2573
|
try {
|
|
2403
|
-
existingRaw =
|
|
2574
|
+
existingRaw = readFileSync2(filePath, "utf-8");
|
|
2404
2575
|
} catch {
|
|
2405
2576
|
}
|
|
2406
2577
|
const existingServers = parseMcp(existingRaw);
|
|
@@ -2422,22 +2593,22 @@ async function processAgent(agent, agentStates) {
|
|
|
2422
2593
|
}
|
|
2423
2594
|
}
|
|
2424
2595
|
if (changedFiles.length > 0) {
|
|
2425
|
-
const isFirst = !
|
|
2596
|
+
const isFirst = !existsSync2(join3(agentDir, "CHARTER.md"));
|
|
2426
2597
|
const verb = isFirst ? "Provisioning" : "Updating";
|
|
2427
2598
|
const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
|
|
2428
2599
|
log(`${verb} '${agent.code_name}': ${fileNames}`);
|
|
2429
2600
|
for (const file of changedFiles) {
|
|
2430
|
-
const filePath =
|
|
2431
|
-
|
|
2432
|
-
|
|
2601
|
+
const filePath = join3(agentDir, file.relativePath);
|
|
2602
|
+
mkdirSync2(dirname(filePath), { recursive: true });
|
|
2603
|
+
writeFileSync2(filePath, file.content);
|
|
2433
2604
|
}
|
|
2434
2605
|
try {
|
|
2435
|
-
const provSkillsDir =
|
|
2436
|
-
if (
|
|
2437
|
-
for (const folder of
|
|
2606
|
+
const provSkillsDir = join3(agentDir, ".claude", "skills");
|
|
2607
|
+
if (existsSync2(provSkillsDir)) {
|
|
2608
|
+
for (const folder of readdirSync2(provSkillsDir)) {
|
|
2438
2609
|
if (folder.startsWith("knowledge-")) {
|
|
2439
2610
|
try {
|
|
2440
|
-
|
|
2611
|
+
rmSync2(join3(provSkillsDir, folder), { recursive: true });
|
|
2441
2612
|
} catch {
|
|
2442
2613
|
}
|
|
2443
2614
|
}
|
|
@@ -2450,7 +2621,7 @@ async function processAgent(agent, agentStates) {
|
|
|
2450
2621
|
const trackedFiles2 = frameworkAdapter.driftTrackedFiles();
|
|
2451
2622
|
const hashes = /* @__PURE__ */ new Map();
|
|
2452
2623
|
for (const file of trackedFiles2) {
|
|
2453
|
-
const h = hashFile(
|
|
2624
|
+
const h = hashFile(join3(agentDir, file));
|
|
2454
2625
|
if (h) hashes.set(file, h);
|
|
2455
2626
|
}
|
|
2456
2627
|
writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -2494,10 +2665,10 @@ async function processAgent(agent, agentStates) {
|
|
|
2494
2665
|
}
|
|
2495
2666
|
let lastDriftCheckAt = now;
|
|
2496
2667
|
const written = writtenHashes.get(agent.agent_id);
|
|
2497
|
-
if (written &&
|
|
2668
|
+
if (written && existsSync2(agentDir)) {
|
|
2498
2669
|
const driftedFiles = [];
|
|
2499
2670
|
for (const [file, expectedHash] of written) {
|
|
2500
|
-
const localHash = hashFile(
|
|
2671
|
+
const localHash = hashFile(join3(agentDir, file));
|
|
2501
2672
|
if (localHash && localHash !== expectedHash) {
|
|
2502
2673
|
driftedFiles.push(file);
|
|
2503
2674
|
}
|
|
@@ -2508,7 +2679,7 @@ async function processAgent(agent, agentStates) {
|
|
|
2508
2679
|
try {
|
|
2509
2680
|
const localHashes = {};
|
|
2510
2681
|
for (const file of driftedFiles) {
|
|
2511
|
-
localHashes[file] = hashFile(
|
|
2682
|
+
localHashes[file] = hashFile(join3(agentDir, file));
|
|
2512
2683
|
}
|
|
2513
2684
|
await api.post("/host/drift", {
|
|
2514
2685
|
agent_id: agent.agent_id,
|
|
@@ -2591,9 +2762,9 @@ async function processAgent(agent, agentStates) {
|
|
|
2591
2762
|
const entry = refreshData.channel_configs[channelId];
|
|
2592
2763
|
if (!entry || entry.status !== "active" && entry.status !== "pending") continue;
|
|
2593
2764
|
try {
|
|
2594
|
-
const installedPath =
|
|
2595
|
-
if (
|
|
2596
|
-
const installed = JSON.parse(
|
|
2765
|
+
const installedPath = join3(homedir3(), ".claude", "plugins", "installed_plugins.json");
|
|
2766
|
+
if (existsSync2(installedPath)) {
|
|
2767
|
+
const installed = JSON.parse(readFileSync2(installedPath, "utf-8"));
|
|
2597
2768
|
const pluginKeys = Object.keys(installed.plugins ?? {});
|
|
2598
2769
|
if (pluginKeys.some((k) => k.startsWith(`${pluginName}@`))) continue;
|
|
2599
2770
|
}
|
|
@@ -2626,19 +2797,19 @@ async function processAgent(agent, agentStates) {
|
|
|
2626
2797
|
if (agentSessionMode === "persistent" && (agentFrameworkCache.get(agent.code_name) ?? "openclaw") === "claude-code") {
|
|
2627
2798
|
try {
|
|
2628
2799
|
const agentProvisionDir = agentDir;
|
|
2629
|
-
const projectDir =
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
const provisionMcpPath =
|
|
2633
|
-
const projectMcpPath =
|
|
2800
|
+
const projectDir = join3(homedir3(), ".augmented", agent.code_name, "project");
|
|
2801
|
+
mkdirSync2(agentProvisionDir, { recursive: true });
|
|
2802
|
+
mkdirSync2(projectDir, { recursive: true });
|
|
2803
|
+
const provisionMcpPath = join3(agentProvisionDir, ".mcp.json");
|
|
2804
|
+
const projectMcpPath = join3(projectDir, ".mcp.json");
|
|
2634
2805
|
let mcpConfig = { mcpServers: {} };
|
|
2635
2806
|
try {
|
|
2636
|
-
mcpConfig = JSON.parse(
|
|
2807
|
+
mcpConfig = JSON.parse(readFileSync2(provisionMcpPath, "utf-8"));
|
|
2637
2808
|
if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
|
|
2638
2809
|
} catch {
|
|
2639
2810
|
}
|
|
2640
|
-
const localDirectChatChannel =
|
|
2641
|
-
if (
|
|
2811
|
+
const localDirectChatChannel = join3(homedir3(), ".augmented", "_mcp", "direct-chat-channel.js");
|
|
2812
|
+
if (existsSync2(localDirectChatChannel) && !mcpConfig.mcpServers["direct-chat"]) {
|
|
2642
2813
|
mcpConfig.mcpServers["direct-chat"] = {
|
|
2643
2814
|
command: "node",
|
|
2644
2815
|
args: [localDirectChatChannel],
|
|
@@ -2649,14 +2820,14 @@ async function processAgent(agent, agentStates) {
|
|
|
2649
2820
|
}
|
|
2650
2821
|
};
|
|
2651
2822
|
const serialized = JSON.stringify(mcpConfig, null, 2);
|
|
2652
|
-
|
|
2653
|
-
|
|
2823
|
+
writeFileSync2(provisionMcpPath, serialized);
|
|
2824
|
+
writeFileSync2(projectMcpPath, serialized);
|
|
2654
2825
|
log(`Channel credentials written for '${agent.code_name}/direct-chat'`);
|
|
2655
2826
|
}
|
|
2656
|
-
const staleChannelsPath =
|
|
2657
|
-
if (
|
|
2827
|
+
const staleChannelsPath = join3(projectDir, ".mcp-channels.json");
|
|
2828
|
+
if (existsSync2(staleChannelsPath)) {
|
|
2658
2829
|
try {
|
|
2659
|
-
|
|
2830
|
+
rmSync2(staleChannelsPath, { force: true });
|
|
2660
2831
|
} catch {
|
|
2661
2832
|
}
|
|
2662
2833
|
}
|
|
@@ -2769,8 +2940,8 @@ async function processAgent(agent, agentStates) {
|
|
|
2769
2940
|
const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
|
|
2770
2941
|
if (mcpPath) {
|
|
2771
2942
|
try {
|
|
2772
|
-
const { readFileSync:
|
|
2773
|
-
const mcpConfig = JSON.parse(
|
|
2943
|
+
const { readFileSync: readFileSync3 } = await import("fs");
|
|
2944
|
+
const mcpConfig = JSON.parse(readFileSync3(mcpPath, "utf-8"));
|
|
2774
2945
|
if (mcpConfig.mcpServers) {
|
|
2775
2946
|
const managedPrefixes = [
|
|
2776
2947
|
"composio_",
|
|
@@ -2862,8 +3033,8 @@ async function processAgent(agent, agentStates) {
|
|
|
2862
3033
|
if (agent.status === "active") {
|
|
2863
3034
|
if (frameworkAdapter.installPlugin) {
|
|
2864
3035
|
try {
|
|
2865
|
-
const pluginPath =
|
|
2866
|
-
if (
|
|
3036
|
+
const pluginPath = join3(process.cwd(), "packages", "openclaw-plugin-augmented", "src", "index.ts");
|
|
3037
|
+
if (existsSync2(pluginPath)) {
|
|
2867
3038
|
frameworkAdapter.installPlugin(agent.code_name, "augmented", pluginPath, {
|
|
2868
3039
|
agtHost: requireHost(),
|
|
2869
3040
|
agtApiKey: getApiKey() ?? void 0,
|
|
@@ -2924,25 +3095,25 @@ async function processAgent(agent, agentStates) {
|
|
|
2924
3095
|
}
|
|
2925
3096
|
}
|
|
2926
3097
|
try {
|
|
2927
|
-
const { readdirSync:
|
|
2928
|
-
const { homedir:
|
|
3098
|
+
const { readdirSync: readdirSync3, rmSync: rmSync3 } = await import("fs");
|
|
3099
|
+
const { homedir: homedir4 } = await import("os");
|
|
2929
3100
|
const frameworkId2 = frameworkAdapter.id;
|
|
2930
3101
|
const candidateSkillDirs = [
|
|
2931
3102
|
// Claude Code — framework runtime tree
|
|
2932
|
-
|
|
3103
|
+
join3(homedir4(), ".augmented", agent.code_name, "skills"),
|
|
2933
3104
|
// Claude Code — project tree
|
|
2934
|
-
|
|
3105
|
+
join3(homedir4(), ".augmented", agent.code_name, "project", ".claude", "skills"),
|
|
2935
3106
|
// OpenClaw — framework runtime tree
|
|
2936
|
-
|
|
3107
|
+
join3(homedir4(), `.openclaw-${agent.code_name}`, "skills"),
|
|
2937
3108
|
// Defensive: legacy provision-side path, not currently an
|
|
2938
3109
|
// install target but cheap to sweep.
|
|
2939
|
-
|
|
3110
|
+
join3(agentDir, ".claude", "skills")
|
|
2940
3111
|
];
|
|
2941
|
-
const existingDirs = candidateSkillDirs.filter((d) =>
|
|
3112
|
+
const existingDirs = candidateSkillDirs.filter((d) => existsSync2(d));
|
|
2942
3113
|
const discoveredEntries = /* @__PURE__ */ new Set();
|
|
2943
3114
|
for (const dir of existingDirs) {
|
|
2944
3115
|
try {
|
|
2945
|
-
for (const entry of
|
|
3116
|
+
for (const entry of readdirSync3(dir)) {
|
|
2946
3117
|
if (entry.startsWith("plugin-") || entry.startsWith("integration-")) {
|
|
2947
3118
|
discoveredEntries.add(entry);
|
|
2948
3119
|
}
|
|
@@ -2952,9 +3123,9 @@ async function processAgent(agent, agentStates) {
|
|
|
2952
3123
|
}
|
|
2953
3124
|
const removeSkillFolder = (entry, reason) => {
|
|
2954
3125
|
for (const dir of existingDirs) {
|
|
2955
|
-
const p =
|
|
2956
|
-
if (
|
|
2957
|
-
|
|
3126
|
+
const p = join3(dir, entry);
|
|
3127
|
+
if (existsSync2(p)) {
|
|
3128
|
+
rmSync3(p, { recursive: true, force: true });
|
|
2958
3129
|
}
|
|
2959
3130
|
}
|
|
2960
3131
|
log(`Removed ${reason} '${entry}' for '${agent.code_name}' (framework=${frameworkId2})`);
|
|
@@ -3123,10 +3294,10 @@ async function processAgent(agent, agentStates) {
|
|
|
3123
3294
|
lastWorkTriggerAt.set(agent.code_name, triggerTs);
|
|
3124
3295
|
if (agentFw === "openclaw" && gatewayRunning && gatewayPort) {
|
|
3125
3296
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
3126
|
-
const jobsPath =
|
|
3127
|
-
if (
|
|
3297
|
+
const jobsPath = join3(homeDir, `.openclaw-${agent.code_name}`, "cron", "jobs.json");
|
|
3298
|
+
if (existsSync2(jobsPath)) {
|
|
3128
3299
|
try {
|
|
3129
|
-
const jobsData = JSON.parse(
|
|
3300
|
+
const jobsData = JSON.parse(readFileSync2(jobsPath, "utf-8"));
|
|
3130
3301
|
const kanbanJob = (jobsData.jobs ?? []).find(
|
|
3131
3302
|
(j) => typeof j.name === "string" && j.name.includes("kanban-work")
|
|
3132
3303
|
);
|
|
@@ -3252,10 +3423,10 @@ In progress for ${age} minutes \u2014 auto-failed`).catch(() => {
|
|
|
3252
3423
|
}
|
|
3253
3424
|
}
|
|
3254
3425
|
const trackedFiles = frameworkAdapter.driftTrackedFiles();
|
|
3255
|
-
if (trackedFiles.length > 0 &&
|
|
3426
|
+
if (trackedFiles.length > 0 && existsSync2(agentDir)) {
|
|
3256
3427
|
const hashes = /* @__PURE__ */ new Map();
|
|
3257
3428
|
for (const file of trackedFiles) {
|
|
3258
|
-
const h = hashFile(
|
|
3429
|
+
const h = hashFile(join3(agentDir, file));
|
|
3259
3430
|
if (h) hashes.set(file, h);
|
|
3260
3431
|
}
|
|
3261
3432
|
writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -3289,19 +3460,19 @@ function cleanupStaleSessions(codeName) {
|
|
|
3289
3460
|
lastCleanupAt.set(codeName, Date.now());
|
|
3290
3461
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
3291
3462
|
for (const agentDir of ["main", codeName]) {
|
|
3292
|
-
const sessionsDir =
|
|
3463
|
+
const sessionsDir = join3(homeDir, `.openclaw-${codeName}`, "agents", agentDir, "sessions");
|
|
3293
3464
|
cleanupCronSessions(sessionsDir, CRON_SESSION_KEEP_COUNT);
|
|
3294
3465
|
}
|
|
3295
|
-
const cronRunsDir =
|
|
3466
|
+
const cronRunsDir = join3(homeDir, `.openclaw-${codeName}`, "cron", "runs");
|
|
3296
3467
|
cleanupOldFiles(cronRunsDir, CRON_RUN_RETENTION_DAYS, ".jsonl");
|
|
3297
|
-
const cronJobsPath =
|
|
3468
|
+
const cronJobsPath = join3(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
3298
3469
|
clearStaleCronRunState(cronJobsPath);
|
|
3299
3470
|
}
|
|
3300
3471
|
function cleanupCronSessions(sessionsDir, keepCount) {
|
|
3301
|
-
const indexPath =
|
|
3302
|
-
if (!
|
|
3472
|
+
const indexPath = join3(sessionsDir, "sessions.json");
|
|
3473
|
+
if (!existsSync2(indexPath)) return;
|
|
3303
3474
|
try {
|
|
3304
|
-
const raw =
|
|
3475
|
+
const raw = readFileSync2(indexPath, "utf-8");
|
|
3305
3476
|
const index = JSON.parse(raw);
|
|
3306
3477
|
const cronRunKeys = Object.keys(index).filter((k) => k.includes(":cron:") && k.includes(":run:")).map((k) => ({
|
|
3307
3478
|
key: k,
|
|
@@ -3314,9 +3485,9 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
3314
3485
|
for (const entry of toDelete) {
|
|
3315
3486
|
delete index[entry.key];
|
|
3316
3487
|
if (entry.sessionId) {
|
|
3317
|
-
const sessionFile =
|
|
3488
|
+
const sessionFile = join3(sessionsDir, `${entry.sessionId}.jsonl`);
|
|
3318
3489
|
try {
|
|
3319
|
-
if (
|
|
3490
|
+
if (existsSync2(sessionFile)) {
|
|
3320
3491
|
unlinkSync(sessionFile);
|
|
3321
3492
|
deletedFiles++;
|
|
3322
3493
|
}
|
|
@@ -3336,8 +3507,8 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
3336
3507
|
delete index[parentKey];
|
|
3337
3508
|
if (parentSessionId) {
|
|
3338
3509
|
try {
|
|
3339
|
-
const f =
|
|
3340
|
-
if (
|
|
3510
|
+
const f = join3(sessionsDir, `${parentSessionId}.jsonl`);
|
|
3511
|
+
if (existsSync2(f)) {
|
|
3341
3512
|
unlinkSync(f);
|
|
3342
3513
|
deletedFiles++;
|
|
3343
3514
|
}
|
|
@@ -3346,7 +3517,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
3346
3517
|
}
|
|
3347
3518
|
}
|
|
3348
3519
|
}
|
|
3349
|
-
|
|
3520
|
+
writeFileSync2(indexPath, JSON.stringify(index));
|
|
3350
3521
|
if (toDelete.length > 0) {
|
|
3351
3522
|
log(`Cleaned ${toDelete.length} cron session(s) and ${deletedFiles} file(s) from ${sessionsDir}`);
|
|
3352
3523
|
}
|
|
@@ -3355,9 +3526,9 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
3355
3526
|
}
|
|
3356
3527
|
var STALE_RUN_TIMEOUT_MS = 5 * 6e4;
|
|
3357
3528
|
function clearStaleCronRunState(jobsPath) {
|
|
3358
|
-
if (!
|
|
3529
|
+
if (!existsSync2(jobsPath)) return;
|
|
3359
3530
|
try {
|
|
3360
|
-
const raw =
|
|
3531
|
+
const raw = readFileSync2(jobsPath, "utf-8");
|
|
3361
3532
|
const data = JSON.parse(raw);
|
|
3362
3533
|
const jobs = data.jobs ?? data;
|
|
3363
3534
|
if (!Array.isArray(jobs)) return;
|
|
@@ -3382,19 +3553,19 @@ function clearStaleCronRunState(jobsPath) {
|
|
|
3382
3553
|
}
|
|
3383
3554
|
}
|
|
3384
3555
|
if (changed) {
|
|
3385
|
-
|
|
3556
|
+
writeFileSync2(jobsPath, JSON.stringify(data, null, 2));
|
|
3386
3557
|
}
|
|
3387
3558
|
} catch {
|
|
3388
3559
|
}
|
|
3389
3560
|
}
|
|
3390
3561
|
function cleanupOldFiles(dir, maxAgeDays, ext) {
|
|
3391
|
-
if (!
|
|
3562
|
+
if (!existsSync2(dir)) return;
|
|
3392
3563
|
const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1e3;
|
|
3393
3564
|
let removed = 0;
|
|
3394
3565
|
try {
|
|
3395
|
-
for (const f of
|
|
3566
|
+
for (const f of readdirSync2(dir)) {
|
|
3396
3567
|
if (!f.endsWith(ext)) continue;
|
|
3397
|
-
const fullPath =
|
|
3568
|
+
const fullPath = join3(dir, f);
|
|
3398
3569
|
try {
|
|
3399
3570
|
const st = statSync(fullPath);
|
|
3400
3571
|
if (st.mtimeMs < cutoff) {
|
|
@@ -3521,18 +3692,18 @@ async function finishRun(runId, outcome, options = {}) {
|
|
|
3521
3692
|
}
|
|
3522
3693
|
async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
3523
3694
|
const projectDir = getProjectDir(codeName);
|
|
3524
|
-
const mcpConfigPath =
|
|
3695
|
+
const mcpConfigPath = join3(projectDir, ".mcp.json");
|
|
3525
3696
|
let runId = null;
|
|
3526
3697
|
let kanbanItemId = null;
|
|
3527
3698
|
let taskResult;
|
|
3528
3699
|
sanitizeMcpJson(mcpConfigPath, requireHost());
|
|
3529
3700
|
prompt = wrapScheduledTaskPrompt(prompt);
|
|
3530
3701
|
try {
|
|
3531
|
-
const claudeMdPath =
|
|
3702
|
+
const claudeMdPath = join3(projectDir, "CLAUDE.md");
|
|
3532
3703
|
const serverNames = [];
|
|
3533
|
-
if (
|
|
3704
|
+
if (existsSync2(mcpConfigPath)) {
|
|
3534
3705
|
try {
|
|
3535
|
-
const d = JSON.parse(
|
|
3706
|
+
const d = JSON.parse(readFileSync2(mcpConfigPath, "utf-8"));
|
|
3536
3707
|
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
3537
3708
|
} catch {
|
|
3538
3709
|
}
|
|
@@ -3550,14 +3721,14 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
3550
3721
|
"--allowedTools",
|
|
3551
3722
|
allowedTools
|
|
3552
3723
|
];
|
|
3553
|
-
if (
|
|
3724
|
+
if (existsSync2(claudeMdPath)) {
|
|
3554
3725
|
claudeArgs.push("--system-prompt-file", claudeMdPath);
|
|
3555
3726
|
}
|
|
3556
3727
|
const childEnv = { ...process.env };
|
|
3557
|
-
const envIntPath =
|
|
3558
|
-
if (
|
|
3728
|
+
const envIntPath = join3(projectDir, ".env.integrations");
|
|
3729
|
+
if (existsSync2(envIntPath)) {
|
|
3559
3730
|
try {
|
|
3560
|
-
for (const line of
|
|
3731
|
+
for (const line of readFileSync2(envIntPath, "utf-8").split("\n")) {
|
|
3561
3732
|
if (!line || line.startsWith("#") || !line.includes("=")) continue;
|
|
3562
3733
|
const eqIdx = line.indexOf("=");
|
|
3563
3734
|
childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
|
|
@@ -3739,8 +3910,8 @@ var claudeAuthTupleBySession = /* @__PURE__ */ new Map();
|
|
|
3739
3910
|
async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
3740
3911
|
const codeName = agent.code_name;
|
|
3741
3912
|
const projectDir = getProjectDir2(codeName);
|
|
3742
|
-
const mcpConfigPath =
|
|
3743
|
-
const claudeMdPath =
|
|
3913
|
+
const mcpConfigPath = join3(projectDir, ".mcp.json");
|
|
3914
|
+
const claudeMdPath = join3(projectDir, "CLAUDE.md");
|
|
3744
3915
|
const channelConfigs = refreshData.channel_configs;
|
|
3745
3916
|
const channels = [];
|
|
3746
3917
|
const devChannels = [];
|
|
@@ -4188,11 +4359,11 @@ async function processDirectChatMessage(agent, msg) {
|
|
|
4188
4359
|
if (fw === "claude-code") {
|
|
4189
4360
|
const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-CLSMSF5W.js");
|
|
4190
4361
|
const projDir = ccProjectDir(agent.codeName);
|
|
4191
|
-
const mcpConfigPath =
|
|
4362
|
+
const mcpConfigPath = join3(projDir, ".mcp.json");
|
|
4192
4363
|
const serverNames = [];
|
|
4193
|
-
if (
|
|
4364
|
+
if (existsSync2(mcpConfigPath)) {
|
|
4194
4365
|
try {
|
|
4195
|
-
const d = JSON.parse(
|
|
4366
|
+
const d = JSON.parse(readFileSync2(mcpConfigPath, "utf-8"));
|
|
4196
4367
|
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
4197
4368
|
} catch {
|
|
4198
4369
|
}
|
|
@@ -4209,15 +4380,15 @@ async function processDirectChatMessage(agent, msg) {
|
|
|
4209
4380
|
"--allowedTools",
|
|
4210
4381
|
allowedTools
|
|
4211
4382
|
];
|
|
4212
|
-
const chatClaudeMd =
|
|
4213
|
-
if (
|
|
4383
|
+
const chatClaudeMd = join3(projDir, "CLAUDE.md");
|
|
4384
|
+
if (existsSync2(chatClaudeMd)) {
|
|
4214
4385
|
chatArgs.push("--system-prompt-file", chatClaudeMd);
|
|
4215
4386
|
}
|
|
4216
|
-
const envIntPath =
|
|
4387
|
+
const envIntPath = join3(projDir, ".env.integrations");
|
|
4217
4388
|
const childEnv = { ...process.env };
|
|
4218
|
-
if (
|
|
4389
|
+
if (existsSync2(envIntPath)) {
|
|
4219
4390
|
try {
|
|
4220
|
-
for (const line of
|
|
4391
|
+
for (const line of readFileSync2(envIntPath, "utf-8").split("\n")) {
|
|
4221
4392
|
if (!line || line.startsWith("#") || !line.includes("=")) continue;
|
|
4222
4393
|
const eqIdx = line.indexOf("=");
|
|
4223
4394
|
childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
|
|
@@ -4498,12 +4669,12 @@ function getBuiltInSkillContent(skillId) {
|
|
|
4498
4669
|
if (builtInSkillCache.has(skillId)) return builtInSkillCache.get(skillId);
|
|
4499
4670
|
try {
|
|
4500
4671
|
const candidates = [
|
|
4501
|
-
|
|
4502
|
-
|
|
4672
|
+
join3(process.cwd(), "skills", skillId, "SKILL.md"),
|
|
4673
|
+
join3(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "skills", skillId, "SKILL.md")
|
|
4503
4674
|
];
|
|
4504
4675
|
for (const candidate of candidates) {
|
|
4505
|
-
if (
|
|
4506
|
-
const content =
|
|
4676
|
+
if (existsSync2(candidate)) {
|
|
4677
|
+
const content = readFileSync2(candidate, "utf-8");
|
|
4507
4678
|
const files = [{ relativePath: "SKILL.md", content }];
|
|
4508
4679
|
builtInSkillCache.set(skillId, files);
|
|
4509
4680
|
return files;
|
|
@@ -5245,16 +5416,16 @@ function parseMemoryFile(raw, fallbackName) {
|
|
|
5245
5416
|
};
|
|
5246
5417
|
}
|
|
5247
5418
|
async function syncMemories(agent, configDir, log2) {
|
|
5248
|
-
const projectDir =
|
|
5249
|
-
const memoryDir =
|
|
5250
|
-
if (
|
|
5419
|
+
const projectDir = join3(configDir, agent.code_name, "project");
|
|
5420
|
+
const memoryDir = join3(projectDir, "memory");
|
|
5421
|
+
if (existsSync2(memoryDir)) {
|
|
5251
5422
|
const prevHashes = memoryFileHashes.get(agent.agent_id) ?? /* @__PURE__ */ new Map();
|
|
5252
5423
|
const currentHashes = /* @__PURE__ */ new Map();
|
|
5253
5424
|
const changedMemories = [];
|
|
5254
|
-
for (const file of
|
|
5425
|
+
for (const file of readdirSync2(memoryDir)) {
|
|
5255
5426
|
if (!file.endsWith(".md")) continue;
|
|
5256
5427
|
try {
|
|
5257
|
-
const raw =
|
|
5428
|
+
const raw = readFileSync2(join3(memoryDir, file), "utf-8");
|
|
5258
5429
|
const fileHash = createHash("sha256").update(raw).digest("hex").slice(0, 16);
|
|
5259
5430
|
currentHashes.set(file, fileHash);
|
|
5260
5431
|
if (prevHashes.get(file) === fileHash) continue;
|
|
@@ -5279,7 +5450,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
5279
5450
|
} catch (err) {
|
|
5280
5451
|
for (const mem of changedMemories) {
|
|
5281
5452
|
for (const [file] of currentHashes) {
|
|
5282
|
-
const parsed = parseMemoryFile(
|
|
5453
|
+
const parsed = parseMemoryFile(readFileSync2(join3(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
|
|
5283
5454
|
if (parsed?.name === mem.name) currentHashes.delete(file);
|
|
5284
5455
|
}
|
|
5285
5456
|
}
|
|
@@ -5287,7 +5458,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
5287
5458
|
}
|
|
5288
5459
|
}
|
|
5289
5460
|
}
|
|
5290
|
-
const localFiles =
|
|
5461
|
+
const localFiles = existsSync2(memoryDir) ? readdirSync2(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
|
|
5291
5462
|
const localListHash = createHash("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
|
|
5292
5463
|
const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
|
|
5293
5464
|
const prevDownload = lastDownloadHash.get(agent.agent_id);
|
|
@@ -5302,7 +5473,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
5302
5473
|
lastDownloadHash.set(agent.agent_id, responseHash);
|
|
5303
5474
|
lastLocalFileHash.set(agent.agent_id, localListHash);
|
|
5304
5475
|
if (dbMemories.memories?.length) {
|
|
5305
|
-
|
|
5476
|
+
mkdirSync2(memoryDir, { recursive: true });
|
|
5306
5477
|
const existingFileSet = new Set(localFiles.map((f) => f.replace(/\.md$/, "").toLowerCase()));
|
|
5307
5478
|
let written = 0;
|
|
5308
5479
|
for (const mem of dbMemories.memories) {
|
|
@@ -5317,11 +5488,11 @@ description: ${JSON.stringify(mem.content.slice(0, 200))}
|
|
|
5317
5488
|
|
|
5318
5489
|
${mem.content}
|
|
5319
5490
|
`;
|
|
5320
|
-
|
|
5491
|
+
writeFileSync2(join3(memoryDir, `${slug}.md`), fileContent);
|
|
5321
5492
|
written++;
|
|
5322
5493
|
}
|
|
5323
5494
|
if (written > 0) {
|
|
5324
|
-
const updatedFiles =
|
|
5495
|
+
const updatedFiles = readdirSync2(memoryDir).filter((f) => f.endsWith(".md")).sort();
|
|
5325
5496
|
lastLocalFileHash.set(agent.agent_id, createHash("sha256").update(updatedFiles.join(",")).digest("hex").slice(0, 16));
|
|
5326
5497
|
log2(`Exported ${written} DB memories to local files for '${agent.code_name}'`);
|
|
5327
5498
|
}
|
|
@@ -5331,9 +5502,9 @@ ${mem.content}
|
|
|
5331
5502
|
}
|
|
5332
5503
|
}
|
|
5333
5504
|
async function cleanupAgentFiles(codeName, agentDir) {
|
|
5334
|
-
if (
|
|
5505
|
+
if (existsSync2(agentDir)) {
|
|
5335
5506
|
try {
|
|
5336
|
-
|
|
5507
|
+
rmSync2(agentDir, { recursive: true, force: true });
|
|
5337
5508
|
log(`Removed provision directory for '${codeName}'`);
|
|
5338
5509
|
} catch (err) {
|
|
5339
5510
|
log(`Failed to remove provision dir for '${codeName}': ${err.message}`);
|
|
@@ -5556,14 +5727,14 @@ function restartRunningChannelMcps(basenames) {
|
|
|
5556
5727
|
}
|
|
5557
5728
|
}
|
|
5558
5729
|
function deployMcpAssets() {
|
|
5559
|
-
const targetDir =
|
|
5560
|
-
|
|
5730
|
+
const targetDir = join3(homedir3(), ".augmented", "_mcp");
|
|
5731
|
+
mkdirSync2(targetDir, { recursive: true });
|
|
5561
5732
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
5562
5733
|
let mcpSourceDir = "";
|
|
5563
5734
|
let dir = moduleDir;
|
|
5564
5735
|
for (let i = 0; i < 6; i++) {
|
|
5565
|
-
const candidate =
|
|
5566
|
-
if (
|
|
5736
|
+
const candidate = join3(dir, "mcp");
|
|
5737
|
+
if (existsSync2(join3(candidate, "index.js"))) {
|
|
5567
5738
|
mcpSourceDir = candidate;
|
|
5568
5739
|
break;
|
|
5569
5740
|
}
|
|
@@ -5578,8 +5749,8 @@ function deployMcpAssets() {
|
|
|
5578
5749
|
const changedBasenames = [];
|
|
5579
5750
|
const fileHash = (p) => {
|
|
5580
5751
|
try {
|
|
5581
|
-
if (!
|
|
5582
|
-
return createHash("sha256").update(
|
|
5752
|
+
if (!existsSync2(p)) return null;
|
|
5753
|
+
return createHash("sha256").update(readFileSync2(p)).digest("hex");
|
|
5583
5754
|
} catch {
|
|
5584
5755
|
return null;
|
|
5585
5756
|
}
|
|
@@ -5590,9 +5761,9 @@ function deployMcpAssets() {
|
|
|
5590
5761
|
"telegram-channel.js"
|
|
5591
5762
|
]);
|
|
5592
5763
|
for (const file of ["index.js", "slack-channel.js", "direct-chat-channel.js", "telegram-channel.js"]) {
|
|
5593
|
-
const src =
|
|
5594
|
-
const dst =
|
|
5595
|
-
if (!
|
|
5764
|
+
const src = join3(mcpSourceDir, file);
|
|
5765
|
+
const dst = join3(targetDir, file);
|
|
5766
|
+
if (!existsSync2(src)) continue;
|
|
5596
5767
|
const before = fileHash(dst);
|
|
5597
5768
|
try {
|
|
5598
5769
|
copyFileSync(src, dst);
|
|
@@ -5609,23 +5780,23 @@ function deployMcpAssets() {
|
|
|
5609
5780
|
log(`[manager] Bundle(s) updated: ${changedBasenames.join(", ")} \u2014 signalling running instances to restart`);
|
|
5610
5781
|
restartRunningChannelMcps(changedBasenames);
|
|
5611
5782
|
}
|
|
5612
|
-
const localMcpPath =
|
|
5783
|
+
const localMcpPath = join3(targetDir, "index.js");
|
|
5613
5784
|
try {
|
|
5614
|
-
const agentsDir =
|
|
5615
|
-
if (
|
|
5616
|
-
for (const entry of
|
|
5785
|
+
const agentsDir = join3(homedir3(), ".augmented", "agents");
|
|
5786
|
+
if (existsSync2(agentsDir)) {
|
|
5787
|
+
for (const entry of readdirSync2(agentsDir, { withFileTypes: true })) {
|
|
5617
5788
|
if (!entry.isDirectory()) continue;
|
|
5618
5789
|
for (const subdir of ["provision", "project"]) {
|
|
5619
|
-
const mcpJsonPath =
|
|
5790
|
+
const mcpJsonPath = join3(agentsDir, entry.name, subdir, ".mcp.json");
|
|
5620
5791
|
try {
|
|
5621
|
-
const raw =
|
|
5792
|
+
const raw = readFileSync2(mcpJsonPath, "utf-8");
|
|
5622
5793
|
if (!raw.includes("@integrity-labs/augmented-mcp")) continue;
|
|
5623
5794
|
const mcpConfig = JSON.parse(raw);
|
|
5624
5795
|
const augServer = mcpConfig.mcpServers?.["augmented"];
|
|
5625
5796
|
if (!augServer) continue;
|
|
5626
5797
|
augServer.command = "node";
|
|
5627
5798
|
augServer.args = [localMcpPath];
|
|
5628
|
-
|
|
5799
|
+
writeFileSync2(mcpJsonPath, JSON.stringify(mcpConfig, null, 2));
|
|
5629
5800
|
log(`[manager] Patched ${entry.name}/${subdir}/.mcp.json: npx \u2192 node`);
|
|
5630
5801
|
} catch {
|
|
5631
5802
|
}
|