@integrity-labs/agt-cli 0.9.0 → 0.9.2
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-55TMBRXT.js → chunk-AMB6FJLZ.js} +12 -1
- package/dist/chunk-AMB6FJLZ.js.map +1 -0
- package/dist/chunk-JOAT4JQN.js +460 -0
- package/dist/chunk-JOAT4JQN.js.map +1 -0
- package/dist/lib/manager-worker.js +176 -511
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/persistent-session-M6GXAEQF.js +25 -0
- package/dist/persistent-session-M6GXAEQF.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-55TMBRXT.js.map +0 -1
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
provisionStopHook,
|
|
10
10
|
requireHost,
|
|
11
11
|
resolveChannels
|
|
12
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-AMB6FJLZ.js";
|
|
13
13
|
import {
|
|
14
14
|
findTaskByTemplate,
|
|
15
15
|
getProjectDir,
|
|
@@ -18,48 +18,24 @@ import {
|
|
|
18
18
|
markTaskFired,
|
|
19
19
|
syncTasksToScheduler
|
|
20
20
|
} from "../chunk-2TSCVXHE.js";
|
|
21
|
+
import {
|
|
22
|
+
getProjectDir as getProjectDir2,
|
|
23
|
+
injectMessage,
|
|
24
|
+
isSessionHealthy,
|
|
25
|
+
resetRestartCount,
|
|
26
|
+
sanitizeMcpJson,
|
|
27
|
+
startPersistentSession,
|
|
28
|
+
stopAllSessionsAndWait,
|
|
29
|
+
stopPersistentSession
|
|
30
|
+
} from "../chunk-JOAT4JQN.js";
|
|
21
31
|
|
|
22
32
|
// src/lib/manager-worker.ts
|
|
23
33
|
import { createHash } from "crypto";
|
|
24
|
-
import { readFileSync
|
|
34
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, rmSync, readdirSync, statSync, unlinkSync, copyFileSync } from "fs";
|
|
25
35
|
import https from "https";
|
|
26
|
-
import { join
|
|
27
|
-
import { homedir
|
|
28
|
-
import { fileURLToPath
|
|
29
|
-
|
|
30
|
-
// src/lib/mcp-sanitize.ts
|
|
31
|
-
import { readFileSync, writeFileSync } from "fs";
|
|
32
|
-
function sanitizeMcpJson(mcpConfigPath, apiHost) {
|
|
33
|
-
try {
|
|
34
|
-
const mcpRaw = JSON.parse(readFileSync(mcpConfigPath, "utf-8"));
|
|
35
|
-
const servers = mcpRaw.mcpServers;
|
|
36
|
-
if (!servers) return false;
|
|
37
|
-
let changed = false;
|
|
38
|
-
for (const [key, val] of Object.entries(servers)) {
|
|
39
|
-
if (typeof val?.url !== "string") continue;
|
|
40
|
-
if (val.url.startsWith("/")) {
|
|
41
|
-
if (apiHost) {
|
|
42
|
-
val.url = `${apiHost}${val.url}`;
|
|
43
|
-
changed = true;
|
|
44
|
-
} else {
|
|
45
|
-
delete servers[key];
|
|
46
|
-
changed = true;
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
const url = val.url;
|
|
51
|
-
delete val.url;
|
|
52
|
-
delete val.type;
|
|
53
|
-
val.command = "npx";
|
|
54
|
-
val.args = ["-y", "mcp-remote", url, "--allow-http"];
|
|
55
|
-
changed = true;
|
|
56
|
-
}
|
|
57
|
-
if (changed) writeFileSync(mcpConfigPath, JSON.stringify(mcpRaw, null, 2));
|
|
58
|
-
return changed;
|
|
59
|
-
} catch {
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
36
|
+
import { join, dirname } from "path";
|
|
37
|
+
import { homedir } from "os";
|
|
38
|
+
import { fileURLToPath } from "url";
|
|
63
39
|
|
|
64
40
|
// src/lib/gateway-client.ts
|
|
65
41
|
import { EventEmitter } from "events";
|
|
@@ -330,345 +306,6 @@ var GatewayClientPool = class extends EventEmitter {
|
|
|
330
306
|
}
|
|
331
307
|
};
|
|
332
308
|
|
|
333
|
-
// src/lib/persistent-session.ts
|
|
334
|
-
import { spawn, execSync, execFileSync } from "child_process";
|
|
335
|
-
import { join, dirname } from "path";
|
|
336
|
-
import { homedir } from "os";
|
|
337
|
-
import { existsSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync } from "fs";
|
|
338
|
-
import { fileURLToPath } from "url";
|
|
339
|
-
function collectMcpServerNames(mcpConfigPath, channelsConfigPath) {
|
|
340
|
-
const names = [];
|
|
341
|
-
for (const path of [mcpConfigPath, channelsConfigPath]) {
|
|
342
|
-
if (!existsSync(path)) continue;
|
|
343
|
-
try {
|
|
344
|
-
const data = JSON.parse(readFileSync2(path, "utf-8"));
|
|
345
|
-
const servers = data.mcpServers;
|
|
346
|
-
if (servers) names.push(...Object.keys(servers));
|
|
347
|
-
} catch {
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
return names;
|
|
351
|
-
}
|
|
352
|
-
var _acpxBin = null;
|
|
353
|
-
function getAcpxBin() {
|
|
354
|
-
if (_acpxBin) return _acpxBin;
|
|
355
|
-
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
356
|
-
let dir = moduleDir;
|
|
357
|
-
for (let i = 0; i < 6; i++) {
|
|
358
|
-
const candidate = join(dir, "node_modules", ".bin", "acpx");
|
|
359
|
-
if (existsSync(candidate)) {
|
|
360
|
-
_acpxBin = candidate;
|
|
361
|
-
return _acpxBin;
|
|
362
|
-
}
|
|
363
|
-
const parent = dirname(dir);
|
|
364
|
-
if (parent === dir) break;
|
|
365
|
-
dir = parent;
|
|
366
|
-
}
|
|
367
|
-
try {
|
|
368
|
-
execSync("which acpx", { stdio: "ignore" });
|
|
369
|
-
_acpxBin = "acpx";
|
|
370
|
-
return _acpxBin;
|
|
371
|
-
} catch {
|
|
372
|
-
return "";
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
var sessions = /* @__PURE__ */ new Map();
|
|
376
|
-
function startPersistentSession(config2) {
|
|
377
|
-
const existing = sessions.get(config2.codeName);
|
|
378
|
-
if (existing && existing.status === "running") {
|
|
379
|
-
return existing;
|
|
380
|
-
}
|
|
381
|
-
const restartCount = existing?.restartCount ?? 0;
|
|
382
|
-
if (existing?.status === "crashed" && existing.startedAt) {
|
|
383
|
-
const backoffMs = Math.min(5e3 * Math.pow(2, restartCount), 6e4);
|
|
384
|
-
if (Date.now() - existing.startedAt < backoffMs) {
|
|
385
|
-
return existing;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
const session = {
|
|
389
|
-
codeName: config2.codeName,
|
|
390
|
-
startedAt: null,
|
|
391
|
-
restartCount,
|
|
392
|
-
status: "starting"
|
|
393
|
-
};
|
|
394
|
-
sessions.set(config2.codeName, session);
|
|
395
|
-
spawnSession(config2, session);
|
|
396
|
-
return session;
|
|
397
|
-
}
|
|
398
|
-
function spawnSession(config2, session) {
|
|
399
|
-
const { codeName, projectDir, mcpConfigPath, claudeMdPath, channels, devChannels, apiHost, log: log2 } = config2;
|
|
400
|
-
const tmuxSession = `agt-${codeName}`;
|
|
401
|
-
log2(`[persistent-session] Starting tmux session '${tmuxSession}' for '${codeName}'`);
|
|
402
|
-
try {
|
|
403
|
-
sanitizeMcpJson(mcpConfigPath, apiHost);
|
|
404
|
-
writeAcpxConfig(config2);
|
|
405
|
-
try {
|
|
406
|
-
execSync(`tmux kill-session -t ${tmuxSession} 2>/dev/null`, { stdio: "ignore" });
|
|
407
|
-
} catch {
|
|
408
|
-
}
|
|
409
|
-
const args = [];
|
|
410
|
-
if (channels.length > 0) args.push("--channels", ...channels);
|
|
411
|
-
if (devChannels.length > 0) args.push("--dangerously-load-development-channels", ...devChannels);
|
|
412
|
-
args.push("--mcp-config", mcpConfigPath);
|
|
413
|
-
const channelsConfigPath = join(projectDir, ".mcp-channels.json");
|
|
414
|
-
if (existsSync(channelsConfigPath)) args.push("--mcp-config", channelsConfigPath);
|
|
415
|
-
if (existsSync(claudeMdPath)) args.push("--system-prompt-file", claudeMdPath);
|
|
416
|
-
args.push("--allow-dangerously-skip-permissions");
|
|
417
|
-
args.push("--dangerously-skip-permissions");
|
|
418
|
-
args.push("--strict-mcp-config");
|
|
419
|
-
args.push("--name", tmuxSession);
|
|
420
|
-
const mcpServerNames = collectMcpServerNames(mcpConfigPath, channelsConfigPath);
|
|
421
|
-
const mcpPatterns = mcpServerNames.map((name) => `mcp__${name.replace(/-/g, "_")}__*`);
|
|
422
|
-
const allowedTools = [
|
|
423
|
-
...mcpPatterns,
|
|
424
|
-
"Bash",
|
|
425
|
-
"Read",
|
|
426
|
-
"Write",
|
|
427
|
-
"Edit",
|
|
428
|
-
"Grep",
|
|
429
|
-
"Glob",
|
|
430
|
-
"Agent",
|
|
431
|
-
"Skill"
|
|
432
|
-
].join(",");
|
|
433
|
-
args.push("--allowedTools", allowedTools);
|
|
434
|
-
let envPrefix = "";
|
|
435
|
-
const envIntegrationsPath = join(projectDir, ".env.integrations");
|
|
436
|
-
if (existsSync(envIntegrationsPath)) {
|
|
437
|
-
try {
|
|
438
|
-
const envContent = readFileSync2(envIntegrationsPath, "utf-8");
|
|
439
|
-
const envVars = envContent.split("\n").filter((line) => line && !line.startsWith("#") && line.includes("=")).map((line) => {
|
|
440
|
-
const eqIdx = line.indexOf("=");
|
|
441
|
-
const key = line.slice(0, eqIdx);
|
|
442
|
-
const value = line.slice(eqIdx + 1);
|
|
443
|
-
return `${key}=${JSON.stringify(value)}`;
|
|
444
|
-
}).join(" ");
|
|
445
|
-
if (envVars) envPrefix = `${envVars} `;
|
|
446
|
-
} catch {
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
const initPrompt = "You are now online. Wait for messages from your channels (Telegram, Slack) and respond to them. Use your kanban tools to track work.";
|
|
450
|
-
const claudeCmd = `${envPrefix}claude ${JSON.stringify(initPrompt)} ${args.map((a) => a.includes(" ") || a.includes("*") ? JSON.stringify(a) : a).join(" ")}`;
|
|
451
|
-
const child = spawn("tmux", [
|
|
452
|
-
"new-session",
|
|
453
|
-
"-d",
|
|
454
|
-
"-s",
|
|
455
|
-
tmuxSession,
|
|
456
|
-
"-c",
|
|
457
|
-
projectDir,
|
|
458
|
-
claudeCmd
|
|
459
|
-
], {
|
|
460
|
-
cwd: projectDir,
|
|
461
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
462
|
-
env: process.env
|
|
463
|
-
});
|
|
464
|
-
child.on("close", (code) => {
|
|
465
|
-
if (code !== 0) {
|
|
466
|
-
log2(`[persistent-session] Failed to create tmux session for '${codeName}' (exit ${code})`);
|
|
467
|
-
session.status = "crashed";
|
|
468
|
-
session.startedAt = Date.now();
|
|
469
|
-
session.restartCount++;
|
|
470
|
-
return;
|
|
471
|
-
}
|
|
472
|
-
log2(`[persistent-session] tmux session '${tmuxSession}' created for '${codeName}'`);
|
|
473
|
-
acceptDialogs(tmuxSession, codeName, log2).catch(() => {
|
|
474
|
-
});
|
|
475
|
-
});
|
|
476
|
-
child.on("error", (err) => {
|
|
477
|
-
log2(`[persistent-session] Failed to start tmux for '${codeName}': ${err.message}`);
|
|
478
|
-
session.status = "crashed";
|
|
479
|
-
session.startedAt = Date.now();
|
|
480
|
-
session.restartCount++;
|
|
481
|
-
});
|
|
482
|
-
session.startedAt = Date.now();
|
|
483
|
-
session.status = "running";
|
|
484
|
-
session.restartCount = 0;
|
|
485
|
-
} catch (err) {
|
|
486
|
-
log2(`[persistent-session] Failed to start session for '${codeName}': ${err.message}`);
|
|
487
|
-
session.status = "crashed";
|
|
488
|
-
session.startedAt = Date.now();
|
|
489
|
-
session.restartCount++;
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
async function acceptDialogs(tmuxSession, codeName, log2) {
|
|
493
|
-
for (let i = 0; i < 15; i++) {
|
|
494
|
-
await new Promise((r) => setTimeout(r, 2e3));
|
|
495
|
-
try {
|
|
496
|
-
const screen = execSync(`tmux capture-pane -t ${tmuxSession} -p 2>/dev/null`, { encoding: "utf-8" });
|
|
497
|
-
if (screen.includes("Yes, I trust this folder")) {
|
|
498
|
-
execSync(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: "ignore" });
|
|
499
|
-
log2(`[persistent-session] Auto-accepted workspace trust for '${codeName}'`);
|
|
500
|
-
continue;
|
|
501
|
-
}
|
|
502
|
-
if (screen.includes("I am using this for local development")) {
|
|
503
|
-
execSync(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: "ignore" });
|
|
504
|
-
log2(`[persistent-session] Auto-accepted dev channels for '${codeName}'`);
|
|
505
|
-
continue;
|
|
506
|
-
}
|
|
507
|
-
if (screen.includes("Enter to confirm") && screen.includes("MCP")) {
|
|
508
|
-
execSync(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: "ignore" });
|
|
509
|
-
log2(`[persistent-session] Auto-accepted MCP servers for '${codeName}'`);
|
|
510
|
-
continue;
|
|
511
|
-
}
|
|
512
|
-
if (screen.includes("Yes, I accept") && screen.includes("Bypass Permissions")) {
|
|
513
|
-
execSync(`tmux send-keys -t ${tmuxSession} 2`, { stdio: "ignore" });
|
|
514
|
-
await new Promise((r) => setTimeout(r, 300));
|
|
515
|
-
execSync(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: "ignore" });
|
|
516
|
-
log2(`[persistent-session] Auto-accepted bypass permissions for '${codeName}'`);
|
|
517
|
-
continue;
|
|
518
|
-
}
|
|
519
|
-
if (screen.includes("\u276F") && !screen.includes("Enter to confirm")) {
|
|
520
|
-
log2(`[persistent-session] Session ready for '${codeName}' \u2014 no more dialogs`);
|
|
521
|
-
break;
|
|
522
|
-
}
|
|
523
|
-
} catch {
|
|
524
|
-
break;
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
async function injectMessage(codeName, type, content, meta, log2) {
|
|
529
|
-
const _log = log2 ?? ((_) => {
|
|
530
|
-
});
|
|
531
|
-
const session = sessions.get(codeName);
|
|
532
|
-
if (!session || session.status !== "running") {
|
|
533
|
-
_log(`[inject] SKIP '${codeName}' \u2014 session ${session ? `status=${session.status}` : "not found in Map"}`);
|
|
534
|
-
return false;
|
|
535
|
-
}
|
|
536
|
-
const prefix = meta?.task_name ? `[Task: ${meta.task_name}] ` : "";
|
|
537
|
-
const text = prefix + content;
|
|
538
|
-
const projectDir = getProjectDir2(codeName);
|
|
539
|
-
const acpx = getAcpxBin();
|
|
540
|
-
if (acpx) {
|
|
541
|
-
try {
|
|
542
|
-
const tmpDir = join(projectDir, ".claude");
|
|
543
|
-
mkdirSync(tmpDir, { recursive: true });
|
|
544
|
-
const tmpFile = join(tmpDir, ".agt-inject-prompt.txt");
|
|
545
|
-
writeFileSync2(tmpFile, text);
|
|
546
|
-
_log(`[inject] acpx exec (fire-and-forget): cwd=${projectDir}, file=${tmpFile}`);
|
|
547
|
-
const child = spawn(acpx, ["claude", "exec", "-f", tmpFile], {
|
|
548
|
-
cwd: projectDir,
|
|
549
|
-
stdio: "ignore",
|
|
550
|
-
detached: true
|
|
551
|
-
});
|
|
552
|
-
child.on("error", (err) => {
|
|
553
|
-
_log(`[inject] acpx spawn error for '${codeName}': ${err.message}`);
|
|
554
|
-
});
|
|
555
|
-
child.unref();
|
|
556
|
-
return true;
|
|
557
|
-
} catch (err) {
|
|
558
|
-
_log(`[inject] acpx exec failed for '${codeName}': ${err.message}`);
|
|
559
|
-
}
|
|
560
|
-
} else {
|
|
561
|
-
_log(`[inject] acpx binary not found \u2014 falling back to tmux send-keys`);
|
|
562
|
-
}
|
|
563
|
-
try {
|
|
564
|
-
execFileSync("tmux", ["send-keys", "-t", `agt-${codeName}`, text, "Enter"], { stdio: "ignore" });
|
|
565
|
-
_log(`[inject] tmux send-keys sent for '${codeName}' \u2014 unverified (returning false)`);
|
|
566
|
-
return false;
|
|
567
|
-
} catch (err) {
|
|
568
|
-
_log(`[inject] tmux send-keys failed for '${codeName}': ${err.message}`);
|
|
569
|
-
return false;
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
function stopPersistentSession(codeName, log2) {
|
|
573
|
-
const session = sessions.get(codeName);
|
|
574
|
-
if (!session) return;
|
|
575
|
-
log2(`[persistent-session] Stopping session for '${codeName}'`);
|
|
576
|
-
session.status = "stopped";
|
|
577
|
-
try {
|
|
578
|
-
execSync(`tmux kill-session -t agt-${codeName} 2>/dev/null`, { stdio: "ignore" });
|
|
579
|
-
} catch {
|
|
580
|
-
}
|
|
581
|
-
try {
|
|
582
|
-
const acpx = getAcpxBin();
|
|
583
|
-
if (acpx) {
|
|
584
|
-
execFileSync(acpx, ["claude", "sessions", "close", `agt-${codeName}`], {
|
|
585
|
-
cwd: getProjectDir2(codeName),
|
|
586
|
-
timeout: 5e3,
|
|
587
|
-
stdio: "ignore"
|
|
588
|
-
});
|
|
589
|
-
}
|
|
590
|
-
} catch {
|
|
591
|
-
}
|
|
592
|
-
sessions.delete(codeName);
|
|
593
|
-
}
|
|
594
|
-
function isSessionHealthy(codeName) {
|
|
595
|
-
const tmuxSession = `agt-${codeName}`;
|
|
596
|
-
try {
|
|
597
|
-
execSync(`tmux has-session -t ${tmuxSession} 2>/dev/null`, { stdio: "ignore" });
|
|
598
|
-
} catch {
|
|
599
|
-
const session2 = sessions.get(codeName);
|
|
600
|
-
if (session2 && session2.status === "running") {
|
|
601
|
-
session2.status = "crashed";
|
|
602
|
-
}
|
|
603
|
-
return false;
|
|
604
|
-
}
|
|
605
|
-
if (!sessions.has(codeName)) {
|
|
606
|
-
sessions.set(codeName, {
|
|
607
|
-
codeName,
|
|
608
|
-
startedAt: Date.now(),
|
|
609
|
-
restartCount: 0,
|
|
610
|
-
status: "running"
|
|
611
|
-
});
|
|
612
|
-
}
|
|
613
|
-
const session = sessions.get(codeName);
|
|
614
|
-
if (session.status !== "running") {
|
|
615
|
-
session.status = "running";
|
|
616
|
-
}
|
|
617
|
-
return true;
|
|
618
|
-
}
|
|
619
|
-
function resetRestartCount(codeName) {
|
|
620
|
-
const session = sessions.get(codeName);
|
|
621
|
-
if (session) session.restartCount = 0;
|
|
622
|
-
}
|
|
623
|
-
async function stopAllSessionsAndWait(log2, opts) {
|
|
624
|
-
const codeNames = [...sessions.keys()];
|
|
625
|
-
if (codeNames.length === 0) return;
|
|
626
|
-
for (const codeName of codeNames) {
|
|
627
|
-
stopPersistentSession(codeName, log2);
|
|
628
|
-
}
|
|
629
|
-
await new Promise((resolve) => setTimeout(resolve, Math.min(opts.timeoutMs, 2e3)));
|
|
630
|
-
}
|
|
631
|
-
function getProjectDir2(codeName) {
|
|
632
|
-
return join(homedir(), ".augmented", codeName, "project");
|
|
633
|
-
}
|
|
634
|
-
function writeAcpxConfig(config2) {
|
|
635
|
-
const { projectDir, mcpConfigPath, claudeMdPath, channels, devChannels } = config2;
|
|
636
|
-
const claudeArgs = [];
|
|
637
|
-
if (channels.length > 0) claudeArgs.push("--channels", ...channels);
|
|
638
|
-
if (devChannels.length > 0) claudeArgs.push("--dangerously-load-development-channels", ...devChannels);
|
|
639
|
-
claudeArgs.push("--mcp-config", mcpConfigPath);
|
|
640
|
-
const channelsConfigPath = join(projectDir, ".mcp-channels.json");
|
|
641
|
-
if (existsSync(channelsConfigPath)) claudeArgs.push("--mcp-config", channelsConfigPath);
|
|
642
|
-
if (existsSync(claudeMdPath)) claudeArgs.push("--system-prompt-file", claudeMdPath);
|
|
643
|
-
claudeArgs.push("--allow-dangerously-skip-permissions");
|
|
644
|
-
claudeArgs.push("--dangerously-skip-permissions");
|
|
645
|
-
claudeArgs.push("--strict-mcp-config");
|
|
646
|
-
const mcpServerNames2 = collectMcpServerNames(mcpConfigPath, channelsConfigPath);
|
|
647
|
-
const mcpPatterns2 = mcpServerNames2.map((name) => `mcp__${name.replace(/-/g, "_")}__*`);
|
|
648
|
-
const allowedTools2 = [...mcpPatterns2, "Bash", "Read", "Write", "Edit", "Grep", "Glob", "Agent", "Skill"].join(",");
|
|
649
|
-
claudeArgs.push("--allowedTools", allowedTools2);
|
|
650
|
-
const acpCmd = `npx -y @agentclientprotocol/claude-agent-acp ${claudeArgs.map((a) => a.includes(" ") || a.includes("*") ? JSON.stringify(a) : a).join(" ")}`;
|
|
651
|
-
const envIntegrationsPath = join(projectDir, ".env.integrations");
|
|
652
|
-
const wrapperPath = join(projectDir, ".claude", "acpx-agent.sh");
|
|
653
|
-
const wrapperLines = ["#!/usr/bin/env bash"];
|
|
654
|
-
if (existsSync(envIntegrationsPath)) {
|
|
655
|
-
wrapperLines.push(`set -a`, `source ${JSON.stringify(envIntegrationsPath)}`, `set +a`);
|
|
656
|
-
}
|
|
657
|
-
wrapperLines.push(`exec ${acpCmd}`);
|
|
658
|
-
mkdirSync(join(projectDir, ".claude"), { recursive: true });
|
|
659
|
-
writeFileSync2(wrapperPath, wrapperLines.join("\n") + "\n", { mode: 493 });
|
|
660
|
-
const acpxConfig = {
|
|
661
|
-
defaultAgent: "claude",
|
|
662
|
-
defaultPermissions: "approve-all",
|
|
663
|
-
agents: {
|
|
664
|
-
claude: {
|
|
665
|
-
command: wrapperPath
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
};
|
|
669
|
-
writeFileSync2(join(projectDir, ".acpxrc.json"), JSON.stringify(acpxConfig, null, 2));
|
|
670
|
-
}
|
|
671
|
-
|
|
672
309
|
// src/lib/realtime-chat.ts
|
|
673
310
|
import { createClient } from "@supabase/supabase-js";
|
|
674
311
|
var client = null;
|
|
@@ -911,8 +548,8 @@ function stopRealtimeChat() {
|
|
|
911
548
|
var GATEWAY_PORT_BASE = 18800;
|
|
912
549
|
var GATEWAY_PORT_STEP = 10;
|
|
913
550
|
var GATEWAY_PORT_MAX = 18899;
|
|
914
|
-
var AUGMENTED_DIR =
|
|
915
|
-
var GATEWAY_PORTS_FILE =
|
|
551
|
+
var AUGMENTED_DIR = join(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
552
|
+
var GATEWAY_PORTS_FILE = join(AUGMENTED_DIR, "gateway-ports.json");
|
|
916
553
|
var config = null;
|
|
917
554
|
var running = false;
|
|
918
555
|
var pollTimer = null;
|
|
@@ -989,24 +626,24 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
989
626
|
if (frameworkId !== "claude-code") return;
|
|
990
627
|
if (frameworkBinaryChecked.has(frameworkId)) return;
|
|
991
628
|
frameworkBinaryChecked.add(frameworkId);
|
|
992
|
-
const { execFileSync
|
|
629
|
+
const { execFileSync } = await import("child_process");
|
|
993
630
|
let brewPath;
|
|
994
631
|
try {
|
|
995
|
-
brewPath =
|
|
632
|
+
brewPath = execFileSync("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
996
633
|
} catch {
|
|
997
634
|
log("Homebrew not found \u2014 cannot auto-install/upgrade Claude Code. Install manually: https://claude.ai/download");
|
|
998
635
|
return;
|
|
999
636
|
}
|
|
1000
637
|
let claudeExists = false;
|
|
1001
638
|
try {
|
|
1002
|
-
|
|
639
|
+
execFileSync("which", ["claude"], { timeout: 5e3 });
|
|
1003
640
|
claudeExists = true;
|
|
1004
641
|
} catch {
|
|
1005
642
|
}
|
|
1006
643
|
if (!claudeExists) {
|
|
1007
644
|
log("Claude Code binary not found \u2014 installing via Homebrew...");
|
|
1008
645
|
try {
|
|
1009
|
-
|
|
646
|
+
execFileSync(brewPath, ["install", "--cask", "claude-code"], {
|
|
1010
647
|
timeout: 12e4,
|
|
1011
648
|
stdio: "pipe"
|
|
1012
649
|
});
|
|
@@ -1015,7 +652,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
1015
652
|
return;
|
|
1016
653
|
}
|
|
1017
654
|
try {
|
|
1018
|
-
|
|
655
|
+
execFileSync("which", ["claude"], { timeout: 5e3 });
|
|
1019
656
|
log("Claude Code installed successfully");
|
|
1020
657
|
} catch {
|
|
1021
658
|
log("Claude Code install completed but binary not found on PATH \u2014 you may need to restart your terminal");
|
|
@@ -1023,7 +660,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
1023
660
|
} else {
|
|
1024
661
|
log("Checking for Claude Code updates...");
|
|
1025
662
|
try {
|
|
1026
|
-
const output =
|
|
663
|
+
const output = execFileSync(brewPath, ["upgrade", "--cask", "claude-code"], {
|
|
1027
664
|
timeout: 12e4,
|
|
1028
665
|
stdio: "pipe"
|
|
1029
666
|
}).toString();
|
|
@@ -1041,7 +678,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
1041
678
|
}
|
|
1042
679
|
}
|
|
1043
680
|
}
|
|
1044
|
-
agentRuntimeAuthenticated = checkClaudeAuth(
|
|
681
|
+
agentRuntimeAuthenticated = checkClaudeAuth(execFileSync);
|
|
1045
682
|
}
|
|
1046
683
|
var UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
1047
684
|
var selfUpdateChecked = false;
|
|
@@ -1053,23 +690,23 @@ async function checkAndUpdateCli() {
|
|
|
1053
690
|
const isDevMode = cliPath.includes("/src/") || cliPath.includes("tsx");
|
|
1054
691
|
if (isDevMode) return;
|
|
1055
692
|
if (!isHomebrew && !cliPath.includes("node_modules")) return;
|
|
1056
|
-
const { homedir:
|
|
693
|
+
const { homedir: homedir2 } = await import("os");
|
|
1057
694
|
const { readFileSync: readF, writeFileSync: writeF } = await import("fs");
|
|
1058
|
-
const markerPath =
|
|
695
|
+
const markerPath = join(homedir2(), ".augmented", ".last-update-check");
|
|
1059
696
|
try {
|
|
1060
697
|
const lastCheck = parseInt(readF(markerPath, "utf-8").trim(), 10);
|
|
1061
698
|
if (Date.now() - lastCheck < UPDATE_CHECK_INTERVAL_MS) return;
|
|
1062
699
|
} catch {
|
|
1063
700
|
}
|
|
1064
|
-
const { execFileSync
|
|
701
|
+
const { execFileSync } = await import("child_process");
|
|
1065
702
|
let brewPath;
|
|
1066
703
|
try {
|
|
1067
|
-
brewPath =
|
|
704
|
+
brewPath = execFileSync("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
1068
705
|
} catch {
|
|
1069
706
|
return;
|
|
1070
707
|
}
|
|
1071
708
|
try {
|
|
1072
|
-
const outdated =
|
|
709
|
+
const outdated = execFileSync(brewPath, ["outdated", "--json=v2"], {
|
|
1073
710
|
timeout: 3e4,
|
|
1074
711
|
encoding: "utf-8"
|
|
1075
712
|
});
|
|
@@ -1080,7 +717,7 @@ async function checkAndUpdateCli() {
|
|
|
1080
717
|
const latest = agtOutdated.current_version ?? "unknown";
|
|
1081
718
|
log(`[self-update] agt CLI update available: ${installed} \u2192 ${latest}. Upgrading...`);
|
|
1082
719
|
try {
|
|
1083
|
-
|
|
720
|
+
execFileSync(brewPath, ["upgrade", "integrity-labs/tap/agt"], {
|
|
1084
721
|
timeout: 12e4,
|
|
1085
722
|
stdio: "pipe"
|
|
1086
723
|
});
|
|
@@ -1096,9 +733,9 @@ async function checkAndUpdateCli() {
|
|
|
1096
733
|
} catch {
|
|
1097
734
|
}
|
|
1098
735
|
}
|
|
1099
|
-
function checkClaudeAuth(
|
|
736
|
+
function checkClaudeAuth(execFileSync) {
|
|
1100
737
|
try {
|
|
1101
|
-
const authOutput =
|
|
738
|
+
const authOutput = execFileSync("claude", ["auth", "status"], { timeout: 1e4, stdio: "pipe" }).toString().trim();
|
|
1102
739
|
let loggedIn = null;
|
|
1103
740
|
try {
|
|
1104
741
|
const parsed = JSON.parse(authOutput);
|
|
@@ -1123,14 +760,14 @@ function checkClaudeAuth(execFileSync2) {
|
|
|
1123
760
|
}
|
|
1124
761
|
function loadGatewayPorts() {
|
|
1125
762
|
try {
|
|
1126
|
-
return JSON.parse(
|
|
763
|
+
return JSON.parse(readFileSync(GATEWAY_PORTS_FILE, "utf-8"));
|
|
1127
764
|
} catch {
|
|
1128
765
|
return {};
|
|
1129
766
|
}
|
|
1130
767
|
}
|
|
1131
768
|
function saveGatewayPorts(ports) {
|
|
1132
|
-
|
|
1133
|
-
|
|
769
|
+
mkdirSync(AUGMENTED_DIR, { recursive: true });
|
|
770
|
+
writeFileSync(GATEWAY_PORTS_FILE, JSON.stringify(ports, null, 2));
|
|
1134
771
|
}
|
|
1135
772
|
function allocatePort(codeName) {
|
|
1136
773
|
const ports = loadGatewayPorts();
|
|
@@ -1152,11 +789,11 @@ function freePort(codeName) {
|
|
|
1152
789
|
saveGatewayPorts(ports);
|
|
1153
790
|
}
|
|
1154
791
|
}
|
|
1155
|
-
var STATE_FILE =
|
|
792
|
+
var STATE_FILE = join(process.env["HOME"] ?? "/tmp", ".augmented", "manager-state.json");
|
|
1156
793
|
function send(msg) {
|
|
1157
794
|
if (msg.type === "state-update") {
|
|
1158
795
|
try {
|
|
1159
|
-
|
|
796
|
+
writeFileSync(STATE_FILE, JSON.stringify(msg.state, null, 2));
|
|
1160
797
|
} catch {
|
|
1161
798
|
}
|
|
1162
799
|
}
|
|
@@ -1178,7 +815,7 @@ function sha256(content) {
|
|
|
1178
815
|
}
|
|
1179
816
|
function hashFile(filePath) {
|
|
1180
817
|
try {
|
|
1181
|
-
const content =
|
|
818
|
+
const content = readFileSync(filePath, "utf-8");
|
|
1182
819
|
return sha256(content);
|
|
1183
820
|
} catch {
|
|
1184
821
|
return null;
|
|
@@ -1186,10 +823,10 @@ function hashFile(filePath) {
|
|
|
1186
823
|
}
|
|
1187
824
|
async function migrateToProfiles() {
|
|
1188
825
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
1189
|
-
const sharedConfigPath =
|
|
826
|
+
const sharedConfigPath = join(homeDir, ".openclaw", "openclaw.json");
|
|
1190
827
|
let sharedConfig;
|
|
1191
828
|
try {
|
|
1192
|
-
sharedConfig = JSON.parse(
|
|
829
|
+
sharedConfig = JSON.parse(readFileSync(sharedConfigPath, "utf-8"));
|
|
1193
830
|
} catch {
|
|
1194
831
|
return;
|
|
1195
832
|
}
|
|
@@ -1202,19 +839,19 @@ async function migrateToProfiles() {
|
|
|
1202
839
|
const codeName = agentEntry["id"];
|
|
1203
840
|
if (!codeName) continue;
|
|
1204
841
|
if (codeName === "main") continue;
|
|
1205
|
-
const profileDir =
|
|
1206
|
-
if (
|
|
842
|
+
const profileDir = join(homeDir, `.openclaw-${codeName}`);
|
|
843
|
+
if (existsSync(join(profileDir, "openclaw.json"))) continue;
|
|
1207
844
|
log(`Migrating agent '${codeName}' to per-agent profile`);
|
|
1208
845
|
if (adapter.seedProfileConfig) {
|
|
1209
846
|
adapter.seedProfileConfig(codeName);
|
|
1210
847
|
}
|
|
1211
|
-
const sharedAuthDir =
|
|
1212
|
-
const profileAuthDir =
|
|
1213
|
-
const authFile =
|
|
1214
|
-
if (
|
|
1215
|
-
|
|
1216
|
-
const authContent =
|
|
1217
|
-
|
|
848
|
+
const sharedAuthDir = join(homeDir, ".openclaw", "agents", codeName, "agent");
|
|
849
|
+
const profileAuthDir = join(profileDir, "agents", codeName, "agent");
|
|
850
|
+
const authFile = join(sharedAuthDir, "auth-profiles.json");
|
|
851
|
+
if (existsSync(authFile)) {
|
|
852
|
+
mkdirSync(profileAuthDir, { recursive: true });
|
|
853
|
+
const authContent = readFileSync(authFile, "utf-8");
|
|
854
|
+
writeFileSync(join(profileAuthDir, "auth-profiles.json"), authContent);
|
|
1218
855
|
}
|
|
1219
856
|
allocatePort(codeName);
|
|
1220
857
|
migrated++;
|
|
@@ -1252,7 +889,7 @@ function readGatewayToken(codeName) {
|
|
|
1252
889
|
}
|
|
1253
890
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
1254
891
|
try {
|
|
1255
|
-
const cfg = JSON.parse(
|
|
892
|
+
const cfg = JSON.parse(readFileSync(join(homeDir, `.openclaw-${codeName}`, "openclaw.json"), "utf-8"));
|
|
1256
893
|
return cfg?.gateway?.auth?.token;
|
|
1257
894
|
} catch {
|
|
1258
895
|
return void 0;
|
|
@@ -1261,10 +898,10 @@ function readGatewayToken(codeName) {
|
|
|
1261
898
|
var GATEWAY_HUNG_TIMEOUT_MS = 5 * 6e4;
|
|
1262
899
|
function isGatewayHung(codeName) {
|
|
1263
900
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
1264
|
-
const jobsPath =
|
|
1265
|
-
if (!
|
|
901
|
+
const jobsPath = join(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
902
|
+
if (!existsSync(jobsPath)) return false;
|
|
1266
903
|
try {
|
|
1267
|
-
const data = JSON.parse(
|
|
904
|
+
const data = JSON.parse(readFileSync(jobsPath, "utf-8"));
|
|
1268
905
|
const jobs = data.jobs ?? data;
|
|
1269
906
|
if (!Array.isArray(jobs)) return false;
|
|
1270
907
|
const now = Date.now();
|
|
@@ -1297,19 +934,19 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
1297
934
|
}
|
|
1298
935
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
1299
936
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
1300
|
-
const cronJobsPath =
|
|
937
|
+
const cronJobsPath = join(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
1301
938
|
clearStaleCronRunState(cronJobsPath);
|
|
1302
939
|
} else {
|
|
1303
940
|
if (status.port) {
|
|
1304
941
|
try {
|
|
1305
942
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
1306
|
-
const configPath =
|
|
1307
|
-
if (
|
|
1308
|
-
const cfg = JSON.parse(
|
|
943
|
+
const configPath = join(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
944
|
+
if (existsSync(configPath)) {
|
|
945
|
+
const cfg = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
1309
946
|
if (cfg.gateway?.port !== status.port) {
|
|
1310
947
|
if (!cfg.gateway) cfg.gateway = {};
|
|
1311
948
|
cfg.gateway.port = status.port;
|
|
1312
|
-
|
|
949
|
+
writeFileSync(configPath, JSON.stringify(cfg, null, 2));
|
|
1313
950
|
}
|
|
1314
951
|
}
|
|
1315
952
|
} catch {
|
|
@@ -1329,12 +966,12 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
1329
966
|
gatewaysStartedThisCycle.add(codeName);
|
|
1330
967
|
try {
|
|
1331
968
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
1332
|
-
const configPath =
|
|
1333
|
-
if (
|
|
1334
|
-
const cfg = JSON.parse(
|
|
969
|
+
const configPath = join(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
970
|
+
if (existsSync(configPath)) {
|
|
971
|
+
const cfg = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
1335
972
|
if (!cfg.gateway) cfg.gateway = {};
|
|
1336
973
|
cfg.gateway.port = port;
|
|
1337
|
-
|
|
974
|
+
writeFileSync(configPath, JSON.stringify(cfg, null, 2));
|
|
1338
975
|
}
|
|
1339
976
|
} catch {
|
|
1340
977
|
}
|
|
@@ -1438,11 +1075,39 @@ async function pollCycle() {
|
|
|
1438
1075
|
}
|
|
1439
1076
|
try {
|
|
1440
1077
|
const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
|
|
1078
|
+
const { collectDiagnostics } = await import("../persistent-session-M6GXAEQF.js");
|
|
1079
|
+
const diagCodeNames = [...persistentSessionAgents];
|
|
1080
|
+
const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
|
|
1081
|
+
let tailscaleHostname;
|
|
1082
|
+
try {
|
|
1083
|
+
const { execSync: es } = await import("child_process");
|
|
1084
|
+
const tsJson = es("tailscale status --self --json 2>/dev/null", {
|
|
1085
|
+
encoding: "utf-8",
|
|
1086
|
+
timeout: 3e3
|
|
1087
|
+
}).trim();
|
|
1088
|
+
const ts = JSON.parse(tsJson);
|
|
1089
|
+
tailscaleHostname = ts.Self?.DNSName?.replace(/\.$/, "") || void 0;
|
|
1090
|
+
} catch {
|
|
1091
|
+
try {
|
|
1092
|
+
const { execSync: es } = await import("child_process");
|
|
1093
|
+
tailscaleHostname = es("hostname", { encoding: "utf-8", timeout: 1e3 }).trim();
|
|
1094
|
+
} catch {
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
let osUsername;
|
|
1098
|
+
try {
|
|
1099
|
+
const { userInfo } = await import("os");
|
|
1100
|
+
osUsername = userInfo().username;
|
|
1101
|
+
} catch {
|
|
1102
|
+
}
|
|
1441
1103
|
await api.post("/host/heartbeat", {
|
|
1442
1104
|
host_id: hostId,
|
|
1443
1105
|
framework_version: cachedFrameworkVersion ?? void 0,
|
|
1444
1106
|
host_security: detectHostSecurity() ?? void 0,
|
|
1445
|
-
agent_runtime_authenticated: agentRuntimeAuthenticated
|
|
1107
|
+
agent_runtime_authenticated: agentRuntimeAuthenticated,
|
|
1108
|
+
agent_diagnostics: agentDiagnostics,
|
|
1109
|
+
hostname: tailscaleHostname,
|
|
1110
|
+
os_username: osUsername
|
|
1446
1111
|
});
|
|
1447
1112
|
} catch (err) {
|
|
1448
1113
|
log(`Heartbeat failed: ${err.message}`);
|
|
@@ -1501,7 +1166,7 @@ async function pollCycle() {
|
|
|
1501
1166
|
const adapter = resolveAgentFramework(prev.codeName);
|
|
1502
1167
|
await stopGatewayIfRunning(prev.codeName, adapter);
|
|
1503
1168
|
freePort(prev.codeName);
|
|
1504
|
-
const agentDir =
|
|
1169
|
+
const agentDir = join(config.configDir, prev.codeName, "provision");
|
|
1505
1170
|
await cleanupAgentFiles(prev.codeName, agentDir);
|
|
1506
1171
|
clearAgentCaches(prev.agentId, prev.codeName);
|
|
1507
1172
|
}
|
|
@@ -1568,7 +1233,7 @@ async function processAgent(agent, agentStates) {
|
|
|
1568
1233
|
agentFrameworkCache.set(agent.code_name, agent.framework);
|
|
1569
1234
|
}
|
|
1570
1235
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1571
|
-
const agentDir =
|
|
1236
|
+
const agentDir = join(config.configDir, agent.code_name, "provision");
|
|
1572
1237
|
const adapter = resolveAgentFramework(agent.code_name);
|
|
1573
1238
|
if (agent.status === "draft" || agent.status === "paused") {
|
|
1574
1239
|
log(`Agent '${agent.code_name}' is ${agent.status}, skipping provisioning`);
|
|
@@ -1719,9 +1384,9 @@ async function processAgent(agent, agentStates) {
|
|
|
1719
1384
|
try {
|
|
1720
1385
|
const artifacts = generateArtifacts(agent, refreshData, frameworkAdapter);
|
|
1721
1386
|
const changedFiles = [];
|
|
1722
|
-
|
|
1387
|
+
mkdirSync(agentDir, { recursive: true });
|
|
1723
1388
|
for (const artifact of artifacts) {
|
|
1724
|
-
const filePath =
|
|
1389
|
+
const filePath = join(agentDir, artifact.relativePath);
|
|
1725
1390
|
const newHash = sha256(artifact.content);
|
|
1726
1391
|
const existingHash = hashFile(filePath);
|
|
1727
1392
|
if (newHash !== existingHash) {
|
|
@@ -1729,19 +1394,19 @@ async function processAgent(agent, agentStates) {
|
|
|
1729
1394
|
}
|
|
1730
1395
|
}
|
|
1731
1396
|
if (changedFiles.length > 0) {
|
|
1732
|
-
const isFirst = !
|
|
1397
|
+
const isFirst = !existsSync(join(agentDir, "CHARTER.md"));
|
|
1733
1398
|
const verb = isFirst ? "Provisioning" : "Updating";
|
|
1734
1399
|
const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
|
|
1735
1400
|
log(`${verb} '${agent.code_name}': ${fileNames}`);
|
|
1736
1401
|
for (const file of changedFiles) {
|
|
1737
|
-
|
|
1402
|
+
writeFileSync(join(agentDir, file.relativePath), file.content);
|
|
1738
1403
|
}
|
|
1739
1404
|
lastProvisionAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1740
1405
|
knownVersions.set(agent.agent_id, { charterVersion, toolsVersion });
|
|
1741
1406
|
const trackedFiles = frameworkAdapter.driftTrackedFiles();
|
|
1742
1407
|
const hashes = /* @__PURE__ */ new Map();
|
|
1743
1408
|
for (const file of trackedFiles) {
|
|
1744
|
-
const h = hashFile(
|
|
1409
|
+
const h = hashFile(join(agentDir, file));
|
|
1745
1410
|
if (h) hashes.set(file, h);
|
|
1746
1411
|
}
|
|
1747
1412
|
writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -1785,10 +1450,10 @@ async function processAgent(agent, agentStates) {
|
|
|
1785
1450
|
}
|
|
1786
1451
|
let lastDriftCheckAt = now;
|
|
1787
1452
|
const written = writtenHashes.get(agent.agent_id);
|
|
1788
|
-
if (written &&
|
|
1453
|
+
if (written && existsSync(agentDir)) {
|
|
1789
1454
|
const driftedFiles = [];
|
|
1790
1455
|
for (const [file, expectedHash] of written) {
|
|
1791
|
-
const localHash = hashFile(
|
|
1456
|
+
const localHash = hashFile(join(agentDir, file));
|
|
1792
1457
|
if (localHash && localHash !== expectedHash) {
|
|
1793
1458
|
driftedFiles.push(file);
|
|
1794
1459
|
}
|
|
@@ -1799,7 +1464,7 @@ async function processAgent(agent, agentStates) {
|
|
|
1799
1464
|
try {
|
|
1800
1465
|
const localHashes = {};
|
|
1801
1466
|
for (const file of driftedFiles) {
|
|
1802
|
-
localHashes[file] = hashFile(
|
|
1467
|
+
localHashes[file] = hashFile(join(agentDir, file));
|
|
1803
1468
|
}
|
|
1804
1469
|
await api.post("/host/drift", {
|
|
1805
1470
|
agent_id: agent.agent_id,
|
|
@@ -1952,11 +1617,11 @@ async function processAgent(agent, agentStates) {
|
|
|
1952
1617
|
}
|
|
1953
1618
|
if (frameworkAdapter.removeMcpServer) {
|
|
1954
1619
|
try {
|
|
1955
|
-
const { readFileSync:
|
|
1956
|
-
const { join:
|
|
1957
|
-
const { homedir:
|
|
1958
|
-
const mcpPath =
|
|
1959
|
-
const mcpConfig = JSON.parse(
|
|
1620
|
+
const { readFileSync: readFileSync2 } = await import("fs");
|
|
1621
|
+
const { join: join2 } = await import("path");
|
|
1622
|
+
const { homedir: homedir2 } = await import("os");
|
|
1623
|
+
const mcpPath = join2(homedir2(), ".augmented", "agents", agent.code_name, "provision", ".mcp.json");
|
|
1624
|
+
const mcpConfig = JSON.parse(readFileSync2(mcpPath, "utf-8"));
|
|
1960
1625
|
if (mcpConfig.mcpServers) {
|
|
1961
1626
|
const managedPrefixes = ["composio-", "one-", "nango-", "paragon-"];
|
|
1962
1627
|
for (const key of Object.keys(mcpConfig.mcpServers)) {
|
|
@@ -2009,18 +1674,18 @@ async function processAgent(agent, agentStates) {
|
|
|
2009
1674
|
"@tobilu/qmd"
|
|
2010
1675
|
]);
|
|
2011
1676
|
if (intHash !== prevIntHash) {
|
|
2012
|
-
const { execFileSync
|
|
1677
|
+
const { execFileSync } = await import("child_process");
|
|
2013
1678
|
for (const tool of capData.cliTools) {
|
|
2014
1679
|
if (!ALLOWED_CLI_PACKAGES.has(tool.package)) {
|
|
2015
1680
|
log(`Skipping CLI tool '${tool.package}' for '${agent.code_name}' \u2014 not on the allowed packages list`);
|
|
2016
1681
|
continue;
|
|
2017
1682
|
}
|
|
2018
1683
|
try {
|
|
2019
|
-
|
|
1684
|
+
execFileSync("which", [tool.binary], { stdio: "ignore" });
|
|
2020
1685
|
} catch {
|
|
2021
1686
|
log(`Installing CLI tool '${tool.package}' for '${agent.code_name}'...`);
|
|
2022
1687
|
try {
|
|
2023
|
-
|
|
1688
|
+
execFileSync("npm", ["install", "-g", tool.package], { stdio: "ignore", timeout: 6e4 });
|
|
2024
1689
|
log(`CLI tool '${tool.binary}' installed successfully`);
|
|
2025
1690
|
} catch (installErr) {
|
|
2026
1691
|
log(`Failed to install CLI tool '${tool.package}': ${installErr.message}`);
|
|
@@ -2028,8 +1693,8 @@ async function processAgent(agent, agentStates) {
|
|
|
2028
1693
|
}
|
|
2029
1694
|
if (tool.binary === "qmd") {
|
|
2030
1695
|
try {
|
|
2031
|
-
const agentDir2 =
|
|
2032
|
-
|
|
1696
|
+
const agentDir2 = join(process.env["HOME"] ?? "/tmp", ".augmented", agent.code_name);
|
|
1697
|
+
execFileSync("qmd", ["collection", "add", agent.code_name, "project"], {
|
|
2033
1698
|
stdio: "ignore",
|
|
2034
1699
|
timeout: 3e4,
|
|
2035
1700
|
cwd: agentDir2
|
|
@@ -2089,8 +1754,8 @@ async function processAgent(agent, agentStates) {
|
|
|
2089
1754
|
if (agent.status === "active") {
|
|
2090
1755
|
if (frameworkAdapter.installPlugin) {
|
|
2091
1756
|
try {
|
|
2092
|
-
const pluginPath =
|
|
2093
|
-
if (
|
|
1757
|
+
const pluginPath = join(process.cwd(), "packages", "openclaw-plugin-augmented", "src", "index.ts");
|
|
1758
|
+
if (existsSync(pluginPath)) {
|
|
2094
1759
|
frameworkAdapter.installPlugin(agent.code_name, "augmented", pluginPath, {
|
|
2095
1760
|
agtHost: requireHost(),
|
|
2096
1761
|
agtApiKey: getApiKey() ?? void 0,
|
|
@@ -2205,10 +1870,10 @@ async function processAgent(agent, agentStates) {
|
|
|
2205
1870
|
lastWorkTriggerAt.set(agent.code_name, triggerTs);
|
|
2206
1871
|
if (agentFw === "openclaw" && gatewayRunning && gatewayPort) {
|
|
2207
1872
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
2208
|
-
const jobsPath =
|
|
2209
|
-
if (
|
|
1873
|
+
const jobsPath = join(homeDir, `.openclaw-${agent.code_name}`, "cron", "jobs.json");
|
|
1874
|
+
if (existsSync(jobsPath)) {
|
|
2210
1875
|
try {
|
|
2211
|
-
const jobsData = JSON.parse(
|
|
1876
|
+
const jobsData = JSON.parse(readFileSync(jobsPath, "utf-8"));
|
|
2212
1877
|
const kanbanJob = (jobsData.jobs ?? []).find(
|
|
2213
1878
|
(j) => typeof j.name === "string" && j.name.includes("kanban-work")
|
|
2214
1879
|
);
|
|
@@ -2353,19 +2018,19 @@ function cleanupStaleSessions(codeName) {
|
|
|
2353
2018
|
lastCleanupAt.set(codeName, Date.now());
|
|
2354
2019
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
2355
2020
|
for (const agentDir of ["main", codeName]) {
|
|
2356
|
-
const sessionsDir =
|
|
2021
|
+
const sessionsDir = join(homeDir, `.openclaw-${codeName}`, "agents", agentDir, "sessions");
|
|
2357
2022
|
cleanupCronSessions(sessionsDir, CRON_SESSION_KEEP_COUNT);
|
|
2358
2023
|
}
|
|
2359
|
-
const cronRunsDir =
|
|
2024
|
+
const cronRunsDir = join(homeDir, `.openclaw-${codeName}`, "cron", "runs");
|
|
2360
2025
|
cleanupOldFiles(cronRunsDir, CRON_RUN_RETENTION_DAYS, ".jsonl");
|
|
2361
|
-
const cronJobsPath =
|
|
2026
|
+
const cronJobsPath = join(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
2362
2027
|
clearStaleCronRunState(cronJobsPath);
|
|
2363
2028
|
}
|
|
2364
2029
|
function cleanupCronSessions(sessionsDir, keepCount) {
|
|
2365
|
-
const indexPath =
|
|
2366
|
-
if (!
|
|
2030
|
+
const indexPath = join(sessionsDir, "sessions.json");
|
|
2031
|
+
if (!existsSync(indexPath)) return;
|
|
2367
2032
|
try {
|
|
2368
|
-
const raw =
|
|
2033
|
+
const raw = readFileSync(indexPath, "utf-8");
|
|
2369
2034
|
const index = JSON.parse(raw);
|
|
2370
2035
|
const cronRunKeys = Object.keys(index).filter((k) => k.includes(":cron:") && k.includes(":run:")).map((k) => ({
|
|
2371
2036
|
key: k,
|
|
@@ -2378,9 +2043,9 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
2378
2043
|
for (const entry of toDelete) {
|
|
2379
2044
|
delete index[entry.key];
|
|
2380
2045
|
if (entry.sessionId) {
|
|
2381
|
-
const sessionFile =
|
|
2046
|
+
const sessionFile = join(sessionsDir, `${entry.sessionId}.jsonl`);
|
|
2382
2047
|
try {
|
|
2383
|
-
if (
|
|
2048
|
+
if (existsSync(sessionFile)) {
|
|
2384
2049
|
unlinkSync(sessionFile);
|
|
2385
2050
|
deletedFiles++;
|
|
2386
2051
|
}
|
|
@@ -2400,8 +2065,8 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
2400
2065
|
delete index[parentKey];
|
|
2401
2066
|
if (parentSessionId) {
|
|
2402
2067
|
try {
|
|
2403
|
-
const f =
|
|
2404
|
-
if (
|
|
2068
|
+
const f = join(sessionsDir, `${parentSessionId}.jsonl`);
|
|
2069
|
+
if (existsSync(f)) {
|
|
2405
2070
|
unlinkSync(f);
|
|
2406
2071
|
deletedFiles++;
|
|
2407
2072
|
}
|
|
@@ -2410,7 +2075,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
2410
2075
|
}
|
|
2411
2076
|
}
|
|
2412
2077
|
}
|
|
2413
|
-
|
|
2078
|
+
writeFileSync(indexPath, JSON.stringify(index));
|
|
2414
2079
|
if (toDelete.length > 0) {
|
|
2415
2080
|
log(`Cleaned ${toDelete.length} cron session(s) and ${deletedFiles} file(s) from ${sessionsDir}`);
|
|
2416
2081
|
}
|
|
@@ -2419,9 +2084,9 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
2419
2084
|
}
|
|
2420
2085
|
var STALE_RUN_TIMEOUT_MS = 5 * 6e4;
|
|
2421
2086
|
function clearStaleCronRunState(jobsPath) {
|
|
2422
|
-
if (!
|
|
2087
|
+
if (!existsSync(jobsPath)) return;
|
|
2423
2088
|
try {
|
|
2424
|
-
const raw =
|
|
2089
|
+
const raw = readFileSync(jobsPath, "utf-8");
|
|
2425
2090
|
const data = JSON.parse(raw);
|
|
2426
2091
|
const jobs = data.jobs ?? data;
|
|
2427
2092
|
if (!Array.isArray(jobs)) return;
|
|
@@ -2446,19 +2111,19 @@ function clearStaleCronRunState(jobsPath) {
|
|
|
2446
2111
|
}
|
|
2447
2112
|
}
|
|
2448
2113
|
if (changed) {
|
|
2449
|
-
|
|
2114
|
+
writeFileSync(jobsPath, JSON.stringify(data, null, 2));
|
|
2450
2115
|
}
|
|
2451
2116
|
} catch {
|
|
2452
2117
|
}
|
|
2453
2118
|
}
|
|
2454
2119
|
function cleanupOldFiles(dir, maxAgeDays, ext) {
|
|
2455
|
-
if (!
|
|
2120
|
+
if (!existsSync(dir)) return;
|
|
2456
2121
|
const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1e3;
|
|
2457
2122
|
let removed = 0;
|
|
2458
2123
|
try {
|
|
2459
2124
|
for (const f of readdirSync(dir)) {
|
|
2460
2125
|
if (!f.endsWith(ext)) continue;
|
|
2461
|
-
const fullPath =
|
|
2126
|
+
const fullPath = join(dir, f);
|
|
2462
2127
|
try {
|
|
2463
2128
|
const st = statSync(fullPath);
|
|
2464
2129
|
if (st.mtimeMs < cutoff) {
|
|
@@ -2549,16 +2214,16 @@ async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData
|
|
|
2549
2214
|
}
|
|
2550
2215
|
async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
2551
2216
|
const projectDir = getProjectDir(codeName);
|
|
2552
|
-
const mcpConfigPath =
|
|
2217
|
+
const mcpConfigPath = join(projectDir, ".mcp.json");
|
|
2553
2218
|
sanitizeMcpJson(mcpConfigPath, requireHost());
|
|
2554
2219
|
try {
|
|
2555
|
-
const claudeMdPath =
|
|
2220
|
+
const claudeMdPath = join(projectDir, "CLAUDE.md");
|
|
2556
2221
|
const serverNames = [];
|
|
2557
|
-
const channelsConfigPath =
|
|
2222
|
+
const channelsConfigPath = join(projectDir, ".mcp-channels.json");
|
|
2558
2223
|
for (const p of [mcpConfigPath, channelsConfigPath]) {
|
|
2559
|
-
if (
|
|
2224
|
+
if (existsSync(p)) {
|
|
2560
2225
|
try {
|
|
2561
|
-
const d = JSON.parse(
|
|
2226
|
+
const d = JSON.parse(readFileSync(p, "utf-8"));
|
|
2562
2227
|
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
2563
2228
|
} catch {
|
|
2564
2229
|
}
|
|
@@ -2577,15 +2242,15 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
2577
2242
|
"--allowedTools",
|
|
2578
2243
|
allowedTools
|
|
2579
2244
|
];
|
|
2580
|
-
if (
|
|
2581
|
-
if (
|
|
2245
|
+
if (existsSync(channelsConfigPath)) claudeArgs.push("--mcp-config", channelsConfigPath);
|
|
2246
|
+
if (existsSync(claudeMdPath)) {
|
|
2582
2247
|
claudeArgs.push("--system-prompt-file", claudeMdPath);
|
|
2583
2248
|
}
|
|
2584
2249
|
const childEnv = { ...process.env };
|
|
2585
|
-
const envIntPath =
|
|
2586
|
-
if (
|
|
2250
|
+
const envIntPath = join(projectDir, ".env.integrations");
|
|
2251
|
+
if (existsSync(envIntPath)) {
|
|
2587
2252
|
try {
|
|
2588
|
-
for (const line of
|
|
2253
|
+
for (const line of readFileSync(envIntPath, "utf-8").split("\n")) {
|
|
2589
2254
|
if (!line || line.startsWith("#") || !line.includes("=")) continue;
|
|
2590
2255
|
const eqIdx = line.indexOf("=");
|
|
2591
2256
|
childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
|
|
@@ -2688,8 +2353,8 @@ var persistentSessionAgents = /* @__PURE__ */ new Set();
|
|
|
2688
2353
|
async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
2689
2354
|
const codeName = agent.code_name;
|
|
2690
2355
|
const projectDir = getProjectDir2(codeName);
|
|
2691
|
-
const mcpConfigPath =
|
|
2692
|
-
const claudeMdPath =
|
|
2356
|
+
const mcpConfigPath = join(projectDir, ".mcp.json");
|
|
2357
|
+
const claudeMdPath = join(projectDir, "CLAUDE.md");
|
|
2693
2358
|
const channelConfigs = refreshData.channel_configs;
|
|
2694
2359
|
const channels = [];
|
|
2695
2360
|
const devChannels = [];
|
|
@@ -2705,8 +2370,8 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
2705
2370
|
}
|
|
2706
2371
|
}
|
|
2707
2372
|
if (!agentRuntimeAuthenticated) {
|
|
2708
|
-
const { execFileSync
|
|
2709
|
-
agentRuntimeAuthenticated = checkClaudeAuth(
|
|
2373
|
+
const { execFileSync } = await import("child_process");
|
|
2374
|
+
agentRuntimeAuthenticated = checkClaudeAuth(execFileSync);
|
|
2710
2375
|
if (!agentRuntimeAuthenticated) {
|
|
2711
2376
|
log(`[persistent-session] Skipping '${codeName}' \u2014 Claude Code not authenticated`);
|
|
2712
2377
|
return;
|
|
@@ -3022,13 +2687,13 @@ async function processDirectChatMessage(agent, msg) {
|
|
|
3022
2687
|
if (fw === "claude-code") {
|
|
3023
2688
|
const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-VFBZFE6U.js");
|
|
3024
2689
|
const projDir = ccProjectDir(agent.codeName);
|
|
3025
|
-
const mcpConfigPath =
|
|
3026
|
-
const channelsConfigPath =
|
|
2690
|
+
const mcpConfigPath = join(projDir, ".mcp.json");
|
|
2691
|
+
const channelsConfigPath = join(projDir, ".mcp-channels.json");
|
|
3027
2692
|
const serverNames = [];
|
|
3028
2693
|
for (const p of [mcpConfigPath, channelsConfigPath]) {
|
|
3029
|
-
if (
|
|
2694
|
+
if (existsSync(p)) {
|
|
3030
2695
|
try {
|
|
3031
|
-
const d = JSON.parse(
|
|
2696
|
+
const d = JSON.parse(readFileSync(p, "utf-8"));
|
|
3032
2697
|
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
3033
2698
|
} catch {
|
|
3034
2699
|
}
|
|
@@ -3047,16 +2712,16 @@ async function processDirectChatMessage(agent, msg) {
|
|
|
3047
2712
|
"--allowedTools",
|
|
3048
2713
|
allowedTools
|
|
3049
2714
|
];
|
|
3050
|
-
if (
|
|
3051
|
-
const chatClaudeMd =
|
|
3052
|
-
if (
|
|
2715
|
+
if (existsSync(channelsConfigPath)) chatArgs.push("--mcp-config", channelsConfigPath);
|
|
2716
|
+
const chatClaudeMd = join(projDir, "CLAUDE.md");
|
|
2717
|
+
if (existsSync(chatClaudeMd)) {
|
|
3053
2718
|
chatArgs.push("--system-prompt-file", chatClaudeMd);
|
|
3054
2719
|
}
|
|
3055
|
-
const envIntPath =
|
|
2720
|
+
const envIntPath = join(projDir, ".env.integrations");
|
|
3056
2721
|
const childEnv = { ...process.env };
|
|
3057
|
-
if (
|
|
2722
|
+
if (existsSync(envIntPath)) {
|
|
3058
2723
|
try {
|
|
3059
|
-
for (const line of
|
|
2724
|
+
for (const line of readFileSync(envIntPath, "utf-8").split("\n")) {
|
|
3060
2725
|
if (!line || line.startsWith("#") || !line.includes("=")) continue;
|
|
3061
2726
|
const eqIdx = line.indexOf("=");
|
|
3062
2727
|
childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
|
|
@@ -3328,12 +2993,12 @@ function getBuiltInSkillContent(skillId) {
|
|
|
3328
2993
|
if (builtInSkillCache.has(skillId)) return builtInSkillCache.get(skillId);
|
|
3329
2994
|
try {
|
|
3330
2995
|
const candidates = [
|
|
3331
|
-
|
|
3332
|
-
|
|
2996
|
+
join(process.cwd(), "skills", skillId, "SKILL.md"),
|
|
2997
|
+
join(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "skills", skillId, "SKILL.md")
|
|
3333
2998
|
];
|
|
3334
2999
|
for (const candidate of candidates) {
|
|
3335
|
-
if (
|
|
3336
|
-
const content =
|
|
3000
|
+
if (existsSync(candidate)) {
|
|
3001
|
+
const content = readFileSync(candidate, "utf-8");
|
|
3337
3002
|
const files = [{ relativePath: "SKILL.md", content }];
|
|
3338
3003
|
builtInSkillCache.set(skillId, files);
|
|
3339
3004
|
return files;
|
|
@@ -3749,7 +3414,7 @@ function generateArtifacts(agent, refreshData, adapter) {
|
|
|
3749
3414
|
return provisionOutput.artifacts;
|
|
3750
3415
|
}
|
|
3751
3416
|
async function cleanupAgentFiles(codeName, agentDir) {
|
|
3752
|
-
if (
|
|
3417
|
+
if (existsSync(agentDir)) {
|
|
3753
3418
|
try {
|
|
3754
3419
|
rmSync(agentDir, { recursive: true, force: true });
|
|
3755
3420
|
log(`Removed provision directory for '${codeName}'`);
|
|
@@ -3808,8 +3473,8 @@ var caffeinateProc = null;
|
|
|
3808
3473
|
async function startCaffeinate() {
|
|
3809
3474
|
if (process.platform !== "darwin") return;
|
|
3810
3475
|
try {
|
|
3811
|
-
const { spawn
|
|
3812
|
-
caffeinateProc =
|
|
3476
|
+
const { spawn } = await import("child_process");
|
|
3477
|
+
caffeinateProc = spawn("caffeinate", ["-dims"], {
|
|
3813
3478
|
stdio: "ignore",
|
|
3814
3479
|
detached: false
|
|
3815
3480
|
});
|
|
@@ -3860,8 +3525,8 @@ async function killAllAgtTmuxSessions() {
|
|
|
3860
3525
|
["list-sessions", "-F", "#{session_name}"],
|
|
3861
3526
|
{ timeout: 2e3, stdin: "ignore" }
|
|
3862
3527
|
);
|
|
3863
|
-
const
|
|
3864
|
-
for (const session of
|
|
3528
|
+
const sessions = output.trim().split("\n").filter((s) => s.startsWith("agt-"));
|
|
3529
|
+
for (const session of sessions) {
|
|
3865
3530
|
try {
|
|
3866
3531
|
await execFilePromiseLong("tmux", ["kill-session", "-t", session], {
|
|
3867
3532
|
timeout: 2e3,
|
|
@@ -3871,8 +3536,8 @@ async function killAllAgtTmuxSessions() {
|
|
|
3871
3536
|
} catch {
|
|
3872
3537
|
}
|
|
3873
3538
|
}
|
|
3874
|
-
if (
|
|
3875
|
-
log(`Cleaned up ${
|
|
3539
|
+
if (sessions.length > 0) {
|
|
3540
|
+
log(`Cleaned up ${sessions.length} agt-* tmux session(s)`);
|
|
3876
3541
|
}
|
|
3877
3542
|
} catch {
|
|
3878
3543
|
}
|
|
@@ -3904,18 +3569,18 @@ function startManager(opts) {
|
|
|
3904
3569
|
startPolling();
|
|
3905
3570
|
}
|
|
3906
3571
|
function deployMcpAssets() {
|
|
3907
|
-
const targetDir =
|
|
3908
|
-
|
|
3909
|
-
const moduleDir =
|
|
3572
|
+
const targetDir = join(homedir(), ".augmented", "_mcp");
|
|
3573
|
+
mkdirSync(targetDir, { recursive: true });
|
|
3574
|
+
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
3910
3575
|
let mcpSourceDir = "";
|
|
3911
3576
|
let dir = moduleDir;
|
|
3912
3577
|
for (let i = 0; i < 6; i++) {
|
|
3913
|
-
const candidate =
|
|
3914
|
-
if (
|
|
3578
|
+
const candidate = join(dir, "mcp");
|
|
3579
|
+
if (existsSync(join(candidate, "index.js"))) {
|
|
3915
3580
|
mcpSourceDir = candidate;
|
|
3916
3581
|
break;
|
|
3917
3582
|
}
|
|
3918
|
-
const parent =
|
|
3583
|
+
const parent = dirname(dir);
|
|
3919
3584
|
if (parent === dir) break;
|
|
3920
3585
|
dir = parent;
|
|
3921
3586
|
}
|
|
@@ -3924,9 +3589,9 @@ function deployMcpAssets() {
|
|
|
3924
3589
|
return;
|
|
3925
3590
|
}
|
|
3926
3591
|
for (const file of ["index.js", "slack-channel.js"]) {
|
|
3927
|
-
const src =
|
|
3928
|
-
const dst =
|
|
3929
|
-
if (!
|
|
3592
|
+
const src = join(mcpSourceDir, file);
|
|
3593
|
+
const dst = join(targetDir, file);
|
|
3594
|
+
if (!existsSync(src)) continue;
|
|
3930
3595
|
try {
|
|
3931
3596
|
copyFileSync(src, dst);
|
|
3932
3597
|
} catch (err) {
|