@integrity-labs/agt-cli 0.7.13 → 0.8.1
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/bin/agt.js.map +1 -1
- package/dist/{chunk-X3FLX6EO.js → chunk-55TMBRXT.js} +47 -86
- package/dist/chunk-55TMBRXT.js.map +1 -0
- package/dist/lib/manager-worker.js +317 -175
- package/dist/lib/manager-worker.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-X3FLX6EO.js.map +0 -1
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
provisionStopHook,
|
|
10
10
|
requireHost,
|
|
11
11
|
resolveChannels
|
|
12
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-55TMBRXT.js";
|
|
13
13
|
import {
|
|
14
14
|
findTaskByTemplate,
|
|
15
15
|
getProjectDir,
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
|
|
22
22
|
// src/lib/manager-worker.ts
|
|
23
23
|
import { createHash } from "crypto";
|
|
24
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync, existsSync as existsSync2, rmSync, readdirSync, statSync, unlinkSync } from "fs";
|
|
24
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync2, rmSync, readdirSync, statSync, unlinkSync } from "fs";
|
|
25
25
|
import https from "https";
|
|
26
26
|
import { join as join2 } from "path";
|
|
27
27
|
|
|
@@ -45,10 +45,12 @@ function sanitizeMcpJson(mcpConfigPath, apiHost) {
|
|
|
45
45
|
continue;
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
const url = val.url;
|
|
49
|
+
delete val.url;
|
|
50
|
+
delete val.type;
|
|
51
|
+
val.command = "npx";
|
|
52
|
+
val.args = ["-y", "mcp-remote", url, "--allow-http"];
|
|
53
|
+
changed = true;
|
|
52
54
|
}
|
|
53
55
|
if (changed) writeFileSync(mcpConfigPath, JSON.stringify(mcpRaw, null, 2));
|
|
54
56
|
return changed;
|
|
@@ -327,20 +329,31 @@ var GatewayClientPool = class extends EventEmitter {
|
|
|
327
329
|
};
|
|
328
330
|
|
|
329
331
|
// src/lib/persistent-session.ts
|
|
330
|
-
import {
|
|
332
|
+
import { spawn, execSync, execFileSync } from "child_process";
|
|
331
333
|
import { join, dirname } from "path";
|
|
332
334
|
import { homedir } from "os";
|
|
333
|
-
import { existsSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
335
|
+
import { existsSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync } from "fs";
|
|
334
336
|
import { fileURLToPath } from "url";
|
|
337
|
+
function collectMcpServerNames(mcpConfigPath, channelsConfigPath) {
|
|
338
|
+
const names = [];
|
|
339
|
+
for (const path of [mcpConfigPath, channelsConfigPath]) {
|
|
340
|
+
if (!existsSync(path)) continue;
|
|
341
|
+
try {
|
|
342
|
+
const data = JSON.parse(readFileSync2(path, "utf-8"));
|
|
343
|
+
const servers = data.mcpServers;
|
|
344
|
+
if (servers) names.push(...Object.keys(servers));
|
|
345
|
+
} catch {
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return names;
|
|
349
|
+
}
|
|
335
350
|
var _acpxBin = null;
|
|
336
351
|
function getAcpxBin() {
|
|
337
352
|
if (_acpxBin) return _acpxBin;
|
|
338
353
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
339
354
|
for (const candidate of [
|
|
340
355
|
join(moduleDir, "..", "..", "node_modules", ".bin", "acpx"),
|
|
341
|
-
// dist/lib/ → package root
|
|
342
356
|
join(moduleDir, "..", "..", "..", "..", "node_modules", ".bin", "acpx")
|
|
343
|
-
// hoisted to monorepo root
|
|
344
357
|
]) {
|
|
345
358
|
if (existsSync(candidate)) {
|
|
346
359
|
_acpxBin = candidate;
|
|
@@ -352,55 +365,10 @@ function getAcpxBin() {
|
|
|
352
365
|
_acpxBin = "acpx";
|
|
353
366
|
return _acpxBin;
|
|
354
367
|
} catch {
|
|
355
|
-
|
|
368
|
+
return "";
|
|
356
369
|
}
|
|
357
370
|
}
|
|
358
371
|
var sessions = /* @__PURE__ */ new Map();
|
|
359
|
-
function writeAcpxConfig(config2) {
|
|
360
|
-
const { projectDir, mcpConfigPath, claudeMdPath, channels, devChannels } = config2;
|
|
361
|
-
const claudeArgs = [];
|
|
362
|
-
if (channels.length > 0) {
|
|
363
|
-
claudeArgs.push("--channels", ...channels);
|
|
364
|
-
}
|
|
365
|
-
if (devChannels.length > 0) {
|
|
366
|
-
claudeArgs.push("--dangerously-load-development-channels", ...devChannels);
|
|
367
|
-
}
|
|
368
|
-
claudeArgs.push("--mcp-config", mcpConfigPath);
|
|
369
|
-
const channelsConfigPath = join(projectDir, ".mcp-channels.json");
|
|
370
|
-
if (existsSync(channelsConfigPath)) {
|
|
371
|
-
claudeArgs.push("--mcp-config", channelsConfigPath);
|
|
372
|
-
}
|
|
373
|
-
if (existsSync(claudeMdPath)) {
|
|
374
|
-
claudeArgs.push("--system-prompt-file", claudeMdPath);
|
|
375
|
-
}
|
|
376
|
-
claudeArgs.push("--allow-dangerously-skip-permissions");
|
|
377
|
-
claudeArgs.push("--dangerously-skip-permissions");
|
|
378
|
-
const envIntegrationsPath = join(projectDir, ".env.integrations");
|
|
379
|
-
let envPrefix = "";
|
|
380
|
-
if (existsSync(envIntegrationsPath)) {
|
|
381
|
-
try {
|
|
382
|
-
const envContent = readFileSync2(envIntegrationsPath, "utf-8");
|
|
383
|
-
const envVars = envContent.split("\n").filter((line) => line && !line.startsWith("#") && line.includes("=")).map((line) => {
|
|
384
|
-
const eqIdx = line.indexOf("=");
|
|
385
|
-
const key = line.slice(0, eqIdx);
|
|
386
|
-
const value = line.slice(eqIdx + 1);
|
|
387
|
-
return `${key}=${value.includes(" ") ? JSON.stringify(value) : value}`;
|
|
388
|
-
}).join(" ");
|
|
389
|
-
if (envVars) envPrefix = `env ${envVars} `;
|
|
390
|
-
} catch {
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
const acpxConfig = {
|
|
394
|
-
defaultAgent: "claude",
|
|
395
|
-
defaultPermissions: "approve-all",
|
|
396
|
-
agents: {
|
|
397
|
-
claude: {
|
|
398
|
-
command: `${envPrefix}npx -y @agentclientprotocol/claude-agent-acp ${claudeArgs.join(" ")}`
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
};
|
|
402
|
-
writeFileSync2(join(projectDir, ".acpxrc.json"), JSON.stringify(acpxConfig, null, 2));
|
|
403
|
-
}
|
|
404
372
|
function startPersistentSession(config2) {
|
|
405
373
|
const existing = sessions.get(config2.codeName);
|
|
406
374
|
if (existing && existing.status === "running") {
|
|
@@ -424,116 +392,220 @@ function startPersistentSession(config2) {
|
|
|
424
392
|
return session;
|
|
425
393
|
}
|
|
426
394
|
function spawnSession(config2, session) {
|
|
427
|
-
const { codeName, projectDir, mcpConfigPath, apiHost, log: log2 } = config2;
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
log2(`[persistent-session] Starting acpx session '${sessionName}' for '${codeName}'`);
|
|
395
|
+
const { codeName, projectDir, mcpConfigPath, claudeMdPath, channels, devChannels, apiHost, log: log2 } = config2;
|
|
396
|
+
const tmuxSession = `agt-${codeName}`;
|
|
397
|
+
log2(`[persistent-session] Starting tmux session '${tmuxSession}' for '${codeName}'`);
|
|
431
398
|
try {
|
|
432
399
|
sanitizeMcpJson(mcpConfigPath, apiHost);
|
|
433
400
|
writeAcpxConfig(config2);
|
|
434
|
-
|
|
435
|
-
"
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
401
|
+
try {
|
|
402
|
+
execSync(`tmux kill-session -t ${tmuxSession} 2>/dev/null`, { stdio: "ignore" });
|
|
403
|
+
} catch {
|
|
404
|
+
}
|
|
405
|
+
const args = [];
|
|
406
|
+
if (channels.length > 0) args.push("--channels", ...channels);
|
|
407
|
+
if (devChannels.length > 0) args.push("--dangerously-load-development-channels", ...devChannels);
|
|
408
|
+
args.push("--mcp-config", mcpConfigPath);
|
|
409
|
+
const channelsConfigPath = join(projectDir, ".mcp-channels.json");
|
|
410
|
+
if (existsSync(channelsConfigPath)) args.push("--mcp-config", channelsConfigPath);
|
|
411
|
+
if (existsSync(claudeMdPath)) args.push("--system-prompt-file", claudeMdPath);
|
|
412
|
+
args.push("--allow-dangerously-skip-permissions");
|
|
413
|
+
args.push("--dangerously-skip-permissions");
|
|
414
|
+
args.push("--strict-mcp-config");
|
|
415
|
+
args.push("--name", tmuxSession);
|
|
416
|
+
const mcpServerNames = collectMcpServerNames(mcpConfigPath, channelsConfigPath);
|
|
417
|
+
const mcpPatterns = mcpServerNames.map((name) => `mcp__${name.replace(/-/g, "_")}__*`);
|
|
418
|
+
const allowedTools = [
|
|
419
|
+
...mcpPatterns,
|
|
420
|
+
"Bash",
|
|
421
|
+
"Read",
|
|
422
|
+
"Write",
|
|
423
|
+
"Edit",
|
|
424
|
+
"Grep",
|
|
425
|
+
"Glob",
|
|
426
|
+
"Agent",
|
|
427
|
+
"Skill"
|
|
428
|
+
].join(",");
|
|
429
|
+
args.push("--allowedTools", allowedTools);
|
|
430
|
+
let envPrefix = "CLAUDE_CODE_SIMPLE=1 ";
|
|
431
|
+
const envIntegrationsPath = join(projectDir, ".env.integrations");
|
|
432
|
+
if (existsSync(envIntegrationsPath)) {
|
|
433
|
+
try {
|
|
434
|
+
const envContent = readFileSync2(envIntegrationsPath, "utf-8");
|
|
435
|
+
const envVars = envContent.split("\n").filter((line) => line && !line.startsWith("#") && line.includes("=")).map((line) => {
|
|
436
|
+
const eqIdx = line.indexOf("=");
|
|
437
|
+
const key = line.slice(0, eqIdx);
|
|
438
|
+
const value = line.slice(eqIdx + 1);
|
|
439
|
+
return `${key}=${JSON.stringify(value)}`;
|
|
440
|
+
}).join(" ");
|
|
441
|
+
if (envVars) envPrefix = `env ${envVars} `;
|
|
442
|
+
} catch {
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
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.";
|
|
446
|
+
const claudeCmd = `${envPrefix}claude ${JSON.stringify(initPrompt)} ${args.map((a) => a.includes(" ") || a.includes("*") ? JSON.stringify(a) : a).join(" ")}`;
|
|
447
|
+
const child = spawn("tmux", [
|
|
448
|
+
"new-session",
|
|
449
|
+
"-d",
|
|
447
450
|
"-s",
|
|
448
|
-
|
|
449
|
-
"
|
|
450
|
-
|
|
451
|
-
|
|
451
|
+
tmuxSession,
|
|
452
|
+
"-c",
|
|
453
|
+
projectDir,
|
|
454
|
+
claudeCmd
|
|
452
455
|
], {
|
|
453
456
|
cwd: projectDir,
|
|
454
|
-
|
|
455
|
-
|
|
457
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
458
|
+
env: process.env
|
|
459
|
+
});
|
|
460
|
+
child.on("close", (code) => {
|
|
461
|
+
if (code !== 0) {
|
|
462
|
+
log2(`[persistent-session] Failed to create tmux session for '${codeName}' (exit ${code})`);
|
|
463
|
+
session.status = "crashed";
|
|
464
|
+
session.startedAt = Date.now();
|
|
465
|
+
session.restartCount++;
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
log2(`[persistent-session] tmux session '${tmuxSession}' created for '${codeName}'`);
|
|
469
|
+
acceptDialogs(tmuxSession, codeName, log2).catch(() => {
|
|
470
|
+
});
|
|
471
|
+
});
|
|
472
|
+
child.on("error", (err) => {
|
|
473
|
+
log2(`[persistent-session] Failed to start tmux for '${codeName}': ${err.message}`);
|
|
474
|
+
session.status = "crashed";
|
|
475
|
+
session.startedAt = Date.now();
|
|
476
|
+
session.restartCount++;
|
|
456
477
|
});
|
|
457
478
|
session.startedAt = Date.now();
|
|
458
479
|
session.status = "running";
|
|
459
480
|
session.restartCount = 0;
|
|
460
|
-
log2(`[persistent-session] Session '${sessionName}' started for '${codeName}'`);
|
|
461
481
|
} catch (err) {
|
|
462
|
-
log2(`[persistent-session] Failed to start
|
|
482
|
+
log2(`[persistent-session] Failed to start session for '${codeName}': ${err.message}`);
|
|
463
483
|
session.status = "crashed";
|
|
464
484
|
session.startedAt = Date.now();
|
|
465
485
|
session.restartCount++;
|
|
466
486
|
}
|
|
467
487
|
}
|
|
468
|
-
function
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
488
|
+
async function acceptDialogs(tmuxSession, codeName, log2) {
|
|
489
|
+
for (let i = 0; i < 15; i++) {
|
|
490
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
491
|
+
try {
|
|
492
|
+
const screen = execSync(`tmux capture-pane -t ${tmuxSession} -p 2>/dev/null`, { encoding: "utf-8" });
|
|
493
|
+
if (screen.includes("Yes, I trust this folder")) {
|
|
494
|
+
execSync(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: "ignore" });
|
|
495
|
+
log2(`[persistent-session] Auto-accepted workspace trust for '${codeName}'`);
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
if (screen.includes("I am using this for local development")) {
|
|
499
|
+
execSync(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: "ignore" });
|
|
500
|
+
log2(`[persistent-session] Auto-accepted dev channels for '${codeName}'`);
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
503
|
+
if (screen.includes("Enter to confirm") && screen.includes("MCP")) {
|
|
504
|
+
execSync(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: "ignore" });
|
|
505
|
+
log2(`[persistent-session] Auto-accepted MCP servers for '${codeName}'`);
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
if (screen.includes("Yes, I accept") && screen.includes("Bypass Permissions")) {
|
|
509
|
+
execSync(`tmux send-keys -t ${tmuxSession} 2`, { stdio: "ignore" });
|
|
510
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
511
|
+
execSync(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: "ignore" });
|
|
512
|
+
log2(`[persistent-session] Auto-accepted bypass permissions for '${codeName}'`);
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
if (screen.includes("\u276F") && !screen.includes("Enter to confirm")) {
|
|
516
|
+
log2(`[persistent-session] Session ready for '${codeName}' \u2014 no more dialogs`);
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
519
|
+
} catch {
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
485
522
|
}
|
|
486
|
-
sessions.delete(codeName);
|
|
487
523
|
}
|
|
488
524
|
async function injectMessage(codeName, type, content, meta) {
|
|
489
525
|
const session = sessions.get(codeName);
|
|
490
526
|
if (!session || session.status !== "running") {
|
|
491
527
|
return false;
|
|
492
528
|
}
|
|
493
|
-
const projectDir = getProjectDir2(codeName);
|
|
494
|
-
const sessionName = `agt-${codeName}`;
|
|
495
529
|
const prefix = meta?.task_name ? `[Task: ${meta.task_name}] ` : "";
|
|
496
530
|
const text = prefix + content;
|
|
531
|
+
const projectDir = getProjectDir2(codeName);
|
|
532
|
+
const acpx = getAcpxBin();
|
|
533
|
+
if (acpx) {
|
|
534
|
+
try {
|
|
535
|
+
const tmpDir = join(projectDir, ".claude");
|
|
536
|
+
mkdirSync(tmpDir, { recursive: true });
|
|
537
|
+
const tmpFile = join(tmpDir, ".agt-inject-prompt.txt");
|
|
538
|
+
writeFileSync2(tmpFile, text);
|
|
539
|
+
execFileSync(acpx, [
|
|
540
|
+
"claude",
|
|
541
|
+
"exec",
|
|
542
|
+
"--cwd",
|
|
543
|
+
projectDir,
|
|
544
|
+
"-f",
|
|
545
|
+
tmpFile
|
|
546
|
+
], {
|
|
547
|
+
cwd: projectDir,
|
|
548
|
+
timeout: 3e5,
|
|
549
|
+
// 5 min for task execution
|
|
550
|
+
stdio: "ignore"
|
|
551
|
+
});
|
|
552
|
+
return true;
|
|
553
|
+
} catch {
|
|
554
|
+
}
|
|
555
|
+
}
|
|
497
556
|
try {
|
|
498
|
-
execFileSync(
|
|
499
|
-
|
|
500
|
-
"-s",
|
|
501
|
-
sessionName,
|
|
502
|
-
"--no-wait",
|
|
503
|
-
"--",
|
|
504
|
-
text
|
|
505
|
-
], {
|
|
506
|
-
cwd: projectDir,
|
|
507
|
-
timeout: 15e3,
|
|
508
|
-
stdio: "ignore"
|
|
509
|
-
});
|
|
510
|
-
return true;
|
|
557
|
+
execFileSync("tmux", ["send-keys", "-t", `agt-${codeName}`, text, "Enter"], { stdio: "ignore" });
|
|
558
|
+
return false;
|
|
511
559
|
} catch {
|
|
512
560
|
return false;
|
|
513
561
|
}
|
|
514
562
|
}
|
|
515
|
-
function
|
|
563
|
+
function stopPersistentSession(codeName, log2) {
|
|
516
564
|
const session = sessions.get(codeName);
|
|
517
|
-
if (!session
|
|
518
|
-
|
|
565
|
+
if (!session) return;
|
|
566
|
+
log2(`[persistent-session] Stopping session for '${codeName}'`);
|
|
567
|
+
session.status = "stopped";
|
|
519
568
|
try {
|
|
520
|
-
|
|
521
|
-
"claude",
|
|
522
|
-
"-s",
|
|
523
|
-
`agt-${codeName}`,
|
|
524
|
-
"status"
|
|
525
|
-
], {
|
|
526
|
-
cwd: projectDir,
|
|
527
|
-
timeout: 1e4,
|
|
528
|
-
encoding: "utf-8"
|
|
529
|
-
});
|
|
530
|
-
return output.includes("status: running") || output.includes("status: idle");
|
|
569
|
+
execSync(`tmux kill-session -t agt-${codeName} 2>/dev/null`, { stdio: "ignore" });
|
|
531
570
|
} catch {
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
571
|
+
}
|
|
572
|
+
try {
|
|
573
|
+
const acpx = getAcpxBin();
|
|
574
|
+
if (acpx) {
|
|
575
|
+
execFileSync(acpx, ["claude", "sessions", "close", `agt-${codeName}`], {
|
|
576
|
+
cwd: getProjectDir2(codeName),
|
|
577
|
+
timeout: 5e3,
|
|
578
|
+
stdio: "ignore"
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
} catch {
|
|
582
|
+
}
|
|
583
|
+
sessions.delete(codeName);
|
|
584
|
+
}
|
|
585
|
+
function isSessionHealthy(codeName) {
|
|
586
|
+
const tmuxSession = `agt-${codeName}`;
|
|
587
|
+
try {
|
|
588
|
+
execSync(`tmux has-session -t ${tmuxSession} 2>/dev/null`, { stdio: "ignore" });
|
|
589
|
+
} catch {
|
|
590
|
+
const session2 = sessions.get(codeName);
|
|
591
|
+
if (session2 && session2.status === "running") {
|
|
592
|
+
session2.status = "crashed";
|
|
593
|
+
}
|
|
535
594
|
return false;
|
|
536
595
|
}
|
|
596
|
+
if (!sessions.has(codeName)) {
|
|
597
|
+
sessions.set(codeName, {
|
|
598
|
+
codeName,
|
|
599
|
+
startedAt: Date.now(),
|
|
600
|
+
restartCount: 0,
|
|
601
|
+
status: "running"
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
const session = sessions.get(codeName);
|
|
605
|
+
if (session.status !== "running") {
|
|
606
|
+
session.status = "running";
|
|
607
|
+
}
|
|
608
|
+
return true;
|
|
537
609
|
}
|
|
538
610
|
function resetRestartCount(codeName) {
|
|
539
611
|
const session = sessions.get(codeName);
|
|
@@ -550,6 +622,47 @@ async function stopAllSessionsAndWait(log2, opts) {
|
|
|
550
622
|
function getProjectDir2(codeName) {
|
|
551
623
|
return join(homedir(), ".augmented", codeName, "project");
|
|
552
624
|
}
|
|
625
|
+
function writeAcpxConfig(config2) {
|
|
626
|
+
const { projectDir, mcpConfigPath, claudeMdPath, channels, devChannels } = config2;
|
|
627
|
+
const claudeArgs = [];
|
|
628
|
+
if (channels.length > 0) claudeArgs.push("--channels", ...channels);
|
|
629
|
+
if (devChannels.length > 0) claudeArgs.push("--dangerously-load-development-channels", ...devChannels);
|
|
630
|
+
claudeArgs.push("--mcp-config", mcpConfigPath);
|
|
631
|
+
const channelsConfigPath = join(projectDir, ".mcp-channels.json");
|
|
632
|
+
if (existsSync(channelsConfigPath)) claudeArgs.push("--mcp-config", channelsConfigPath);
|
|
633
|
+
if (existsSync(claudeMdPath)) claudeArgs.push("--system-prompt-file", claudeMdPath);
|
|
634
|
+
claudeArgs.push("--allow-dangerously-skip-permissions");
|
|
635
|
+
claudeArgs.push("--dangerously-skip-permissions");
|
|
636
|
+
const mcpServerNames2 = collectMcpServerNames(mcpConfigPath, channelsConfigPath);
|
|
637
|
+
const mcpPatterns2 = mcpServerNames2.map((name) => `mcp__${name.replace(/-/g, "_")}__*`);
|
|
638
|
+
const allowedTools2 = [...mcpPatterns2, "Bash", "Read", "Write", "Edit", "Grep", "Glob", "Agent", "Skill"].join(",");
|
|
639
|
+
claudeArgs.push("--allowedTools", allowedTools2);
|
|
640
|
+
let envPrefix = "";
|
|
641
|
+
const envIntegrationsPath = join(projectDir, ".env.integrations");
|
|
642
|
+
if (existsSync(envIntegrationsPath)) {
|
|
643
|
+
try {
|
|
644
|
+
const envContent = readFileSync2(envIntegrationsPath, "utf-8");
|
|
645
|
+
const envVars = envContent.split("\n").filter((line) => line && !line.startsWith("#") && line.includes("=")).map((line) => {
|
|
646
|
+
const eqIdx = line.indexOf("=");
|
|
647
|
+
const key = line.slice(0, eqIdx);
|
|
648
|
+
const value = line.slice(eqIdx + 1);
|
|
649
|
+
return `${key}=${JSON.stringify(value)}`;
|
|
650
|
+
}).join(" ");
|
|
651
|
+
if (envVars) envPrefix = `env ${envVars} `;
|
|
652
|
+
} catch {
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
const acpxConfig = {
|
|
656
|
+
defaultAgent: "claude",
|
|
657
|
+
defaultPermissions: "approve-all",
|
|
658
|
+
agents: {
|
|
659
|
+
claude: {
|
|
660
|
+
command: `${envPrefix}npx -y @agentclientprotocol/claude-agent-acp ${claudeArgs.map((a) => a.includes(" ") || a.includes("*") ? JSON.stringify(a) : a).join(" ")}`
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
writeFileSync2(join(projectDir, ".acpxrc.json"), JSON.stringify(acpxConfig, null, 2));
|
|
665
|
+
}
|
|
553
666
|
|
|
554
667
|
// src/lib/realtime-chat.ts
|
|
555
668
|
import { createClient } from "@supabase/supabase-js";
|
|
@@ -807,7 +920,6 @@ var knownChannelConfigHashes = /* @__PURE__ */ new Map();
|
|
|
807
920
|
var knownModels = /* @__PURE__ */ new Map();
|
|
808
921
|
var knownTasksHashes = /* @__PURE__ */ new Map();
|
|
809
922
|
var knownIntegrationHashes = /* @__PURE__ */ new Map();
|
|
810
|
-
var losslessClawInstalled = /* @__PURE__ */ new Map();
|
|
811
923
|
var knownSkillHashes = /* @__PURE__ */ new Map();
|
|
812
924
|
var lastCronRunTs = /* @__PURE__ */ new Map();
|
|
813
925
|
var lastWorkTriggerAt = /* @__PURE__ */ new Map();
|
|
@@ -848,7 +960,6 @@ function clearAgentCaches(agentId, codeName) {
|
|
|
848
960
|
knownModels.delete(agentId);
|
|
849
961
|
knownTasksHashes.delete(agentId);
|
|
850
962
|
knownIntegrationHashes.delete(agentId);
|
|
851
|
-
losslessClawInstalled.delete(codeName);
|
|
852
963
|
agentDisplayNames.delete(codeName);
|
|
853
964
|
codeNameToAgentId.delete(codeName);
|
|
854
965
|
agentFrameworkCache.delete(codeName);
|
|
@@ -927,6 +1038,59 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
927
1038
|
}
|
|
928
1039
|
agentRuntimeAuthenticated = checkClaudeAuth(execFileSync2);
|
|
929
1040
|
}
|
|
1041
|
+
var UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
1042
|
+
var selfUpdateChecked = false;
|
|
1043
|
+
async function checkAndUpdateCli() {
|
|
1044
|
+
if (selfUpdateChecked) return;
|
|
1045
|
+
selfUpdateChecked = true;
|
|
1046
|
+
const cliPath = process.argv[1] ?? "";
|
|
1047
|
+
const isHomebrew = cliPath.includes("/Cellar/") || cliPath.includes("/homebrew/");
|
|
1048
|
+
const isDevMode = cliPath.includes("/src/") || cliPath.includes("tsx");
|
|
1049
|
+
if (isDevMode) return;
|
|
1050
|
+
if (!isHomebrew && !cliPath.includes("node_modules")) return;
|
|
1051
|
+
const { homedir: homedir2 } = await import("os");
|
|
1052
|
+
const { readFileSync: readF, writeFileSync: writeF } = await import("fs");
|
|
1053
|
+
const markerPath = join2(homedir2(), ".augmented", ".last-update-check");
|
|
1054
|
+
try {
|
|
1055
|
+
const lastCheck = parseInt(readF(markerPath, "utf-8").trim(), 10);
|
|
1056
|
+
if (Date.now() - lastCheck < UPDATE_CHECK_INTERVAL_MS) return;
|
|
1057
|
+
} catch {
|
|
1058
|
+
}
|
|
1059
|
+
const { execFileSync: execFileSync2 } = await import("child_process");
|
|
1060
|
+
let brewPath;
|
|
1061
|
+
try {
|
|
1062
|
+
brewPath = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
1063
|
+
} catch {
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
try {
|
|
1067
|
+
const outdated = execFileSync2(brewPath, ["outdated", "--json=v2"], {
|
|
1068
|
+
timeout: 3e4,
|
|
1069
|
+
encoding: "utf-8"
|
|
1070
|
+
});
|
|
1071
|
+
const data = JSON.parse(outdated);
|
|
1072
|
+
const agtOutdated = data.formulae?.find((f) => f.name === "agt" || f.name === "integrity-labs/tap/agt");
|
|
1073
|
+
if (agtOutdated) {
|
|
1074
|
+
const installed = agtOutdated.installed_versions?.[0] ?? "unknown";
|
|
1075
|
+
const latest = agtOutdated.current_version ?? "unknown";
|
|
1076
|
+
log(`[self-update] agt CLI update available: ${installed} \u2192 ${latest}. Upgrading...`);
|
|
1077
|
+
try {
|
|
1078
|
+
execFileSync2(brewPath, ["upgrade", "integrity-labs/tap/agt"], {
|
|
1079
|
+
timeout: 12e4,
|
|
1080
|
+
stdio: "pipe"
|
|
1081
|
+
});
|
|
1082
|
+
log(`[self-update] agt CLI upgraded to ${latest}. Restart the manager to use the new version.`);
|
|
1083
|
+
} catch (err) {
|
|
1084
|
+
log(`[self-update] Upgrade failed: ${err.message}`);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
} catch {
|
|
1088
|
+
}
|
|
1089
|
+
try {
|
|
1090
|
+
writeF(markerPath, String(Date.now()));
|
|
1091
|
+
} catch {
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
930
1094
|
function checkClaudeAuth(execFileSync2) {
|
|
931
1095
|
try {
|
|
932
1096
|
const authOutput = execFileSync2("claude", ["auth", "status"], { timeout: 1e4, stdio: "pipe" }).toString().trim();
|
|
@@ -960,7 +1124,7 @@ function loadGatewayPorts() {
|
|
|
960
1124
|
}
|
|
961
1125
|
}
|
|
962
1126
|
function saveGatewayPorts(ports) {
|
|
963
|
-
|
|
1127
|
+
mkdirSync2(AUGMENTED_DIR, { recursive: true });
|
|
964
1128
|
writeFileSync3(GATEWAY_PORTS_FILE, JSON.stringify(ports, null, 2));
|
|
965
1129
|
}
|
|
966
1130
|
function allocatePort(codeName) {
|
|
@@ -1043,7 +1207,7 @@ async function migrateToProfiles() {
|
|
|
1043
1207
|
const profileAuthDir = join2(profileDir, "agents", codeName, "agent");
|
|
1044
1208
|
const authFile = join2(sharedAuthDir, "auth-profiles.json");
|
|
1045
1209
|
if (existsSync2(authFile)) {
|
|
1046
|
-
|
|
1210
|
+
mkdirSync2(profileAuthDir, { recursive: true });
|
|
1047
1211
|
const authContent = readFileSync3(authFile, "utf-8");
|
|
1048
1212
|
writeFileSync3(join2(profileAuthDir, "auth-profiles.json"), authContent);
|
|
1049
1213
|
}
|
|
@@ -1550,7 +1714,7 @@ async function processAgent(agent, agentStates) {
|
|
|
1550
1714
|
try {
|
|
1551
1715
|
const artifacts = generateArtifacts(agent, refreshData, frameworkAdapter);
|
|
1552
1716
|
const changedFiles = [];
|
|
1553
|
-
|
|
1717
|
+
mkdirSync2(agentDir, { recursive: true });
|
|
1554
1718
|
for (const artifact of artifacts) {
|
|
1555
1719
|
const filePath = join2(agentDir, artifact.relativePath);
|
|
1556
1720
|
const newHash = sha256(artifact.content);
|
|
@@ -1757,6 +1921,14 @@ async function processAgent(agent, agentStates) {
|
|
|
1757
1921
|
}
|
|
1758
1922
|
knownIntegrationHashes.set(agent.agent_id, intHash);
|
|
1759
1923
|
log(`Integrations provisioned for '${agent.code_name}' (${integrations.length} integration(s))`);
|
|
1924
|
+
const fw = agentFrameworkCache.get(agent.code_name) ?? "openclaw";
|
|
1925
|
+
if (fw === "claude-code" && isSessionHealthy(agent.code_name)) {
|
|
1926
|
+
const names = integrations.map((i) => i.display_name || i.definition_id).join(", ");
|
|
1927
|
+
injectMessage(agent.code_name, "system", `Your integrations have been updated. You now have access to: ${names}. Re-read your CLAUDE.md for details.`, {
|
|
1928
|
+
task_name: "integration-update"
|
|
1929
|
+
}).catch(() => {
|
|
1930
|
+
});
|
|
1931
|
+
}
|
|
1760
1932
|
const managedIntegrations = integrations.filter((i) => i.auth_type === "managed");
|
|
1761
1933
|
if (managedIntegrations.length > 0 && frameworkAdapter.writeMcpServer) {
|
|
1762
1934
|
try {
|
|
@@ -1766,11 +1938,12 @@ async function processAgent(agent, agentStates) {
|
|
|
1766
1938
|
if (tk.agent_id !== agent.agent_id) continue;
|
|
1767
1939
|
const serverId = tk.toolkit_id.replace(/\//g, "-");
|
|
1768
1940
|
expectedServerIds.add(serverId);
|
|
1941
|
+
const mcpUrl = tk.mcp_url;
|
|
1942
|
+
const mcpHeaders = tk.mcp_headers;
|
|
1769
1943
|
const proxyUrl = tk.proxy_url.startsWith("/") ? `${requireHost()}${tk.proxy_url}` : tk.proxy_url;
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
});
|
|
1773
|
-
log(`[managed-toolkit] ${agent.code_name}: ${tk.toolkit_name} \u2192 ${proxyUrl}`);
|
|
1944
|
+
const url = mcpUrl ?? proxyUrl;
|
|
1945
|
+
frameworkAdapter.writeMcpServer(agent.code_name, serverId, { url, headers: mcpHeaders });
|
|
1946
|
+
log(`[managed-toolkit] ${agent.code_name}: ${tk.toolkit_name} \u2192 ${url}`);
|
|
1774
1947
|
}
|
|
1775
1948
|
if (frameworkAdapter.removeMcpServer) {
|
|
1776
1949
|
try {
|
|
@@ -1796,38 +1969,6 @@ async function processAgent(agent, agentStates) {
|
|
|
1796
1969
|
}
|
|
1797
1970
|
}
|
|
1798
1971
|
needsGatewayRestart = true;
|
|
1799
|
-
const hasLcm = integrations.some((i) => i.definition_id === "lossless-claw");
|
|
1800
|
-
if (hasLcm && !losslessClawInstalled.get(agent.code_name)) {
|
|
1801
|
-
try {
|
|
1802
|
-
const { execFileSync: execFileSync2 } = await import("child_process");
|
|
1803
|
-
let pluginList;
|
|
1804
|
-
try {
|
|
1805
|
-
pluginList = execFileSync2(
|
|
1806
|
-
"openclaw",
|
|
1807
|
-
["--profile", agent.code_name, "plugins", "list", "--json"],
|
|
1808
|
-
{ timeout: 15e3 }
|
|
1809
|
-
).toString();
|
|
1810
|
-
} catch {
|
|
1811
|
-
pluginList = "[]";
|
|
1812
|
-
}
|
|
1813
|
-
const installed = JSON.parse(pluginList);
|
|
1814
|
-
const alreadyInstalled = installed.some(
|
|
1815
|
-
(p) => p.name === "lossless-claw" || p.name === "@martian-engineering/lossless-claw"
|
|
1816
|
-
);
|
|
1817
|
-
if (!alreadyInstalled) {
|
|
1818
|
-
log(`Installing lossless-claw plugin for '${agent.code_name}'...`);
|
|
1819
|
-
execFileSync2(
|
|
1820
|
-
"openclaw",
|
|
1821
|
-
["--profile", agent.code_name, "plugins", "install", "@martian-engineering/lossless-claw"],
|
|
1822
|
-
{ stdio: "ignore", timeout: 6e4 }
|
|
1823
|
-
);
|
|
1824
|
-
log(`lossless-claw plugin installed for '${agent.code_name}'`);
|
|
1825
|
-
}
|
|
1826
|
-
losslessClawInstalled.set(agent.code_name, true);
|
|
1827
|
-
} catch (pluginErr) {
|
|
1828
|
-
log(`lossless-claw plugin install failed for '${agent.code_name}': ${pluginErr.message}`);
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1831
1972
|
}
|
|
1832
1973
|
const resolvedDefIds = new Set(integrations.map((i) => i.definition_id));
|
|
1833
1974
|
for (const capId of resolvedDefIds) {
|
|
@@ -2611,7 +2752,7 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
2611
2752
|
const markerDir = join2(projectDir2, ".claude");
|
|
2612
2753
|
const markerPath = join2(markerDir, ".agt-pending-task.json");
|
|
2613
2754
|
try {
|
|
2614
|
-
|
|
2755
|
+
mkdirSync2(markerDir, { recursive: true });
|
|
2615
2756
|
writeFileSync3(markerPath, JSON.stringify({
|
|
2616
2757
|
agent_id: agent.agent_id,
|
|
2617
2758
|
task_id: task.taskId,
|
|
@@ -3610,8 +3751,8 @@ var caffeinateProc = null;
|
|
|
3610
3751
|
async function startCaffeinate() {
|
|
3611
3752
|
if (process.platform !== "darwin") return;
|
|
3612
3753
|
try {
|
|
3613
|
-
const { spawn } = await import("child_process");
|
|
3614
|
-
caffeinateProc =
|
|
3754
|
+
const { spawn: spawn2 } = await import("child_process");
|
|
3755
|
+
caffeinateProc = spawn2("caffeinate", ["-dims"], {
|
|
3615
3756
|
stdio: "ignore",
|
|
3616
3757
|
detached: false
|
|
3617
3758
|
});
|
|
@@ -3641,6 +3782,7 @@ function startPolling() {
|
|
|
3641
3782
|
running = true;
|
|
3642
3783
|
void startCaffeinate();
|
|
3643
3784
|
log(`Starting poll loop (interval=${config.intervalMs}ms, configDir=${config.configDir})`);
|
|
3785
|
+
checkAndUpdateCli().catch((err) => log(`[self-update] Check failed: ${err.message}`));
|
|
3644
3786
|
void migrateToProfiles().then(() => {
|
|
3645
3787
|
startGatewayPool();
|
|
3646
3788
|
return pollCycle();
|