@integrity-labs/agt-cli 0.7.8 → 0.7.10
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-4I4QZRBQ.js → chunk-5UGOY3IV.js} +12 -9
- package/dist/chunk-5UGOY3IV.js.map +1 -0
- package/dist/{chunk-B47JW4MM.js → chunk-X3FLX6EO.js} +6 -8
- package/dist/chunk-X3FLX6EO.js.map +1 -0
- package/dist/{claude-scheduler-APXMZEK4.js → claude-scheduler-V2QNX3Z4.js} +2 -2
- package/dist/lib/manager-worker.js +197 -219
- package/dist/lib/manager-worker.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-4I4QZRBQ.js.map +0 -1
- package/dist/chunk-B47JW4MM.js.map +0 -1
- /package/dist/{claude-scheduler-APXMZEK4.js.map → claude-scheduler-V2QNX3Z4.js.map} +0 -0
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
provisionStopHook,
|
|
10
10
|
requireHost,
|
|
11
11
|
resolveChannels
|
|
12
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-X3FLX6EO.js";
|
|
13
13
|
import {
|
|
14
14
|
findTaskByTemplate,
|
|
15
15
|
getProjectDir,
|
|
@@ -17,11 +17,11 @@ import {
|
|
|
17
17
|
loadSchedulerState,
|
|
18
18
|
markTaskFired,
|
|
19
19
|
syncTasksToScheduler
|
|
20
|
-
} from "../chunk-
|
|
20
|
+
} from "../chunk-5UGOY3IV.js";
|
|
21
21
|
|
|
22
22
|
// src/lib/manager-worker.ts
|
|
23
23
|
import { createHash } from "crypto";
|
|
24
|
-
import { readFileSync as readFileSync3, writeFileSync as
|
|
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
|
|
|
@@ -327,55 +327,30 @@ var GatewayClientPool = class extends EventEmitter {
|
|
|
327
327
|
};
|
|
328
328
|
|
|
329
329
|
// src/lib/persistent-session.ts
|
|
330
|
-
import {
|
|
330
|
+
import { execFileSync } from "child_process";
|
|
331
331
|
import { join } from "path";
|
|
332
332
|
import { homedir } from "os";
|
|
333
|
-
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
333
|
+
import { existsSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
334
334
|
var sessions = /* @__PURE__ */ new Map();
|
|
335
|
-
function
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
return existing;
|
|
339
|
-
}
|
|
340
|
-
const session = {
|
|
341
|
-
codeName: config2.codeName,
|
|
342
|
-
process: null,
|
|
343
|
-
taskChannelPort: null,
|
|
344
|
-
startedAt: null,
|
|
345
|
-
restartCount: existing?.restartCount ?? 0,
|
|
346
|
-
status: "starting"
|
|
347
|
-
};
|
|
348
|
-
sessions.set(config2.codeName, session);
|
|
349
|
-
spawnSession(config2, session);
|
|
350
|
-
return session;
|
|
351
|
-
}
|
|
352
|
-
function spawnSession(config2, session) {
|
|
353
|
-
const { codeName, projectDir, mcpConfigPath, claudeMdPath, channels, devChannels, log: log2 } = config2;
|
|
354
|
-
sanitizeMcpJson(mcpConfigPath);
|
|
355
|
-
const args = [];
|
|
335
|
+
function writeAcpxConfig(config2) {
|
|
336
|
+
const { projectDir, mcpConfigPath, claudeMdPath, channels, devChannels } = config2;
|
|
337
|
+
const claudeArgs = [];
|
|
356
338
|
if (channels.length > 0) {
|
|
357
|
-
|
|
339
|
+
claudeArgs.push("--channels", ...channels);
|
|
358
340
|
}
|
|
359
341
|
if (devChannels.length > 0) {
|
|
360
|
-
|
|
342
|
+
claudeArgs.push("--dangerously-load-development-channels", ...devChannels);
|
|
361
343
|
}
|
|
362
|
-
|
|
344
|
+
claudeArgs.push("--mcp-config", mcpConfigPath);
|
|
363
345
|
const channelsConfigPath = join(projectDir, ".mcp-channels.json");
|
|
364
346
|
if (existsSync(channelsConfigPath)) {
|
|
365
|
-
|
|
347
|
+
claudeArgs.push("--mcp-config", channelsConfigPath);
|
|
366
348
|
}
|
|
367
349
|
if (existsSync(claudeMdPath)) {
|
|
368
|
-
|
|
369
|
-
}
|
|
370
|
-
args.push("--allow-dangerously-skip-permissions");
|
|
371
|
-
args.push("--dangerously-skip-permissions");
|
|
372
|
-
args.push("--name", `agt-${codeName}`);
|
|
373
|
-
log2(`[persistent-session] Starting session for '${codeName}' with args: ${args.join(" ")}`);
|
|
374
|
-
const tmuxSession = `agt-${codeName}`;
|
|
375
|
-
try {
|
|
376
|
-
execSync(`tmux kill-session -t ${tmuxSession} 2>/dev/null`, { stdio: "ignore" });
|
|
377
|
-
} catch {
|
|
350
|
+
claudeArgs.push("--system-prompt-file", claudeMdPath);
|
|
378
351
|
}
|
|
352
|
+
claudeArgs.push("--allow-dangerously-skip-permissions");
|
|
353
|
+
claudeArgs.push("--dangerously-skip-permissions");
|
|
379
354
|
const envIntegrationsPath = join(projectDir, ".env.integrations");
|
|
380
355
|
let envPrefix = "";
|
|
381
356
|
if (existsSync(envIntegrationsPath)) {
|
|
@@ -385,138 +360,154 @@ function spawnSession(config2, session) {
|
|
|
385
360
|
const eqIdx = line.indexOf("=");
|
|
386
361
|
const key = line.slice(0, eqIdx);
|
|
387
362
|
const value = line.slice(eqIdx + 1);
|
|
388
|
-
return `${key}=${JSON.stringify(value)}`;
|
|
363
|
+
return `${key}=${value.includes(" ") ? JSON.stringify(value) : value}`;
|
|
389
364
|
}).join(" ");
|
|
390
365
|
if (envVars) envPrefix = `env ${envVars} `;
|
|
391
366
|
} catch {
|
|
392
367
|
}
|
|
393
368
|
}
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
tmuxSession,
|
|
401
|
-
"-c",
|
|
402
|
-
projectDir,
|
|
403
|
-
claudeCmd
|
|
404
|
-
], {
|
|
405
|
-
cwd: projectDir,
|
|
406
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
407
|
-
env: process.env
|
|
408
|
-
});
|
|
409
|
-
session.process = child;
|
|
410
|
-
session.startedAt = Date.now();
|
|
411
|
-
session.status = "running";
|
|
412
|
-
child.on("close", (code) => {
|
|
413
|
-
if (code !== 0) {
|
|
414
|
-
log2(`[persistent-session] Failed to create tmux session for '${codeName}' (exit ${code})`);
|
|
415
|
-
session.status = "crashed";
|
|
416
|
-
session.process = null;
|
|
417
|
-
return;
|
|
418
|
-
}
|
|
419
|
-
log2(`[persistent-session] tmux session '${tmuxSession}' created for '${codeName}'`);
|
|
420
|
-
const acceptDialogs = async () => {
|
|
421
|
-
for (let i = 0; i < 15; i++) {
|
|
422
|
-
await new Promise((r) => setTimeout(r, 2e3));
|
|
423
|
-
try {
|
|
424
|
-
const { execSync: es } = await import("child_process");
|
|
425
|
-
const screen = es(`tmux capture-pane -t ${tmuxSession} -p 2>/dev/null`, { encoding: "utf-8" });
|
|
426
|
-
if (screen.includes("Yes, I trust this folder")) {
|
|
427
|
-
es(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: "ignore" });
|
|
428
|
-
log2(`[persistent-session] Auto-accepted workspace trust for '${codeName}'`);
|
|
429
|
-
continue;
|
|
430
|
-
}
|
|
431
|
-
if (screen.includes("I am using this for local development")) {
|
|
432
|
-
es(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: "ignore" });
|
|
433
|
-
log2(`[persistent-session] Auto-accepted dev channels for '${codeName}'`);
|
|
434
|
-
continue;
|
|
435
|
-
}
|
|
436
|
-
if (screen.includes("Enter to confirm") && screen.includes("MCP")) {
|
|
437
|
-
es(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: "ignore" });
|
|
438
|
-
log2(`[persistent-session] Auto-accepted MCP servers for '${codeName}'`);
|
|
439
|
-
continue;
|
|
440
|
-
}
|
|
441
|
-
if (screen.includes("Yes, I accept") && screen.includes("Bypass Permissions")) {
|
|
442
|
-
es(`tmux send-keys -t ${tmuxSession} 2`, { stdio: "ignore" });
|
|
443
|
-
await new Promise((r) => setTimeout(r, 300));
|
|
444
|
-
es(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: "ignore" });
|
|
445
|
-
log2(`[persistent-session] Auto-accepted bypass permissions for '${codeName}'`);
|
|
446
|
-
continue;
|
|
447
|
-
}
|
|
448
|
-
if (screen.includes("\u276F") && !screen.includes("Enter to confirm")) {
|
|
449
|
-
log2(`[persistent-session] Session ready for '${codeName}' \u2014 no more dialogs`);
|
|
450
|
-
break;
|
|
451
|
-
}
|
|
452
|
-
} catch {
|
|
453
|
-
break;
|
|
454
|
-
}
|
|
369
|
+
const acpxConfig = {
|
|
370
|
+
defaultAgent: "claude",
|
|
371
|
+
defaultPermissions: "approve-all",
|
|
372
|
+
agents: {
|
|
373
|
+
claude: {
|
|
374
|
+
command: `${envPrefix}npx -y @agentclientprotocol/claude-agent-acp ${claudeArgs.join(" ")}`
|
|
455
375
|
}
|
|
456
|
-
}
|
|
457
|
-
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
writeFileSync2(join(projectDir, ".acpxrc.json"), JSON.stringify(acpxConfig, null, 2));
|
|
379
|
+
}
|
|
380
|
+
function startPersistentSession(config2) {
|
|
381
|
+
const existing = sessions.get(config2.codeName);
|
|
382
|
+
if (existing && existing.status === "running") {
|
|
383
|
+
return existing;
|
|
384
|
+
}
|
|
385
|
+
const restartCount = existing?.restartCount ?? 0;
|
|
386
|
+
if (existing?.status === "crashed" && existing.startedAt) {
|
|
387
|
+
const backoffMs = Math.min(5e3 * Math.pow(2, restartCount), 6e4);
|
|
388
|
+
if (Date.now() - existing.startedAt < backoffMs) {
|
|
389
|
+
return existing;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
const session = {
|
|
393
|
+
codeName: config2.codeName,
|
|
394
|
+
startedAt: null,
|
|
395
|
+
restartCount,
|
|
396
|
+
status: "starting"
|
|
397
|
+
};
|
|
398
|
+
sessions.set(config2.codeName, session);
|
|
399
|
+
spawnSession(config2, session);
|
|
400
|
+
return session;
|
|
401
|
+
}
|
|
402
|
+
function spawnSession(config2, session) {
|
|
403
|
+
const { codeName, projectDir, mcpConfigPath, apiHost, log: log2 } = config2;
|
|
404
|
+
const sessionName = `agt-${codeName}`;
|
|
405
|
+
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.";
|
|
406
|
+
log2(`[persistent-session] Starting acpx session '${sessionName}' for '${codeName}'`);
|
|
407
|
+
try {
|
|
408
|
+
sanitizeMcpJson(mcpConfigPath, apiHost);
|
|
409
|
+
writeAcpxConfig(config2);
|
|
410
|
+
execFileSync("acpx", [
|
|
411
|
+
"claude",
|
|
412
|
+
"sessions",
|
|
413
|
+
"ensure",
|
|
414
|
+
"--name",
|
|
415
|
+
sessionName
|
|
416
|
+
], {
|
|
417
|
+
cwd: projectDir,
|
|
418
|
+
timeout: 3e4,
|
|
419
|
+
stdio: "ignore"
|
|
458
420
|
});
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
421
|
+
execFileSync("acpx", [
|
|
422
|
+
"claude",
|
|
423
|
+
"-s",
|
|
424
|
+
sessionName,
|
|
425
|
+
"--no-wait",
|
|
426
|
+
"--",
|
|
427
|
+
initPrompt
|
|
428
|
+
], {
|
|
429
|
+
cwd: projectDir,
|
|
430
|
+
timeout: 3e4,
|
|
431
|
+
stdio: "ignore"
|
|
432
|
+
});
|
|
433
|
+
session.startedAt = Date.now();
|
|
434
|
+
session.status = "running";
|
|
435
|
+
session.restartCount = 0;
|
|
436
|
+
log2(`[persistent-session] Session '${sessionName}' started for '${codeName}'`);
|
|
437
|
+
} catch (err) {
|
|
438
|
+
log2(`[persistent-session] Failed to start acpx session for '${codeName}': ${err.message}`);
|
|
462
439
|
session.status = "crashed";
|
|
463
|
-
|
|
440
|
+
session.startedAt = Date.now();
|
|
441
|
+
session.restartCount++;
|
|
442
|
+
}
|
|
464
443
|
}
|
|
465
444
|
function stopPersistentSession(codeName, log2) {
|
|
466
445
|
const session = sessions.get(codeName);
|
|
467
446
|
if (!session) return;
|
|
468
447
|
log2(`[persistent-session] Stopping session for '${codeName}'`);
|
|
469
448
|
session.status = "stopped";
|
|
449
|
+
const projectDir = getProjectDir2(codeName);
|
|
470
450
|
try {
|
|
471
|
-
|
|
451
|
+
execFileSync("acpx", ["claude", "sessions", "close", `agt-${codeName}`], {
|
|
452
|
+
cwd: projectDir,
|
|
453
|
+
timeout: 1e4,
|
|
454
|
+
stdio: "ignore"
|
|
455
|
+
});
|
|
472
456
|
} catch {
|
|
473
457
|
}
|
|
474
|
-
|
|
475
|
-
|
|
458
|
+
try {
|
|
459
|
+
execFileSync("tmux", ["kill-session", "-t", `agt-${codeName}`], { stdio: "ignore" });
|
|
460
|
+
} catch {
|
|
476
461
|
}
|
|
477
|
-
setTimeout(() => {
|
|
478
|
-
if (session.process && !session.process.killed) {
|
|
479
|
-
session.process.kill("SIGKILL");
|
|
480
|
-
}
|
|
481
|
-
}, 1e4);
|
|
482
462
|
sessions.delete(codeName);
|
|
483
463
|
}
|
|
484
464
|
async function injectMessage(codeName, type, content, meta) {
|
|
485
465
|
const session = sessions.get(codeName);
|
|
486
|
-
if (!session || session.status !== "running"
|
|
466
|
+
if (!session || session.status !== "running") {
|
|
487
467
|
return false;
|
|
488
468
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
headers: { "Content-Type": "application/json" },
|
|
494
|
-
body: JSON.stringify({ type, content, meta }),
|
|
495
|
-
signal: AbortSignal.timeout(1e4)
|
|
496
|
-
});
|
|
497
|
-
if (res.ok) return true;
|
|
498
|
-
} catch {
|
|
499
|
-
}
|
|
500
|
-
}
|
|
469
|
+
const projectDir = getProjectDir2(codeName);
|
|
470
|
+
const sessionName = `agt-${codeName}`;
|
|
471
|
+
const prefix = meta?.task_name ? `[Task: ${meta.task_name}] ` : "";
|
|
472
|
+
const text = prefix + content;
|
|
501
473
|
try {
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
474
|
+
execFileSync("acpx", [
|
|
475
|
+
"claude",
|
|
476
|
+
"-s",
|
|
477
|
+
sessionName,
|
|
478
|
+
"--no-wait",
|
|
479
|
+
"--",
|
|
480
|
+
text
|
|
481
|
+
], {
|
|
482
|
+
cwd: projectDir,
|
|
483
|
+
timeout: 15e3,
|
|
484
|
+
stdio: "ignore"
|
|
485
|
+
});
|
|
506
486
|
return true;
|
|
507
487
|
} catch {
|
|
508
488
|
return false;
|
|
509
489
|
}
|
|
510
|
-
return false;
|
|
511
490
|
}
|
|
512
491
|
function isSessionHealthy(codeName) {
|
|
513
492
|
const session = sessions.get(codeName);
|
|
514
493
|
if (!session || session.status !== "running") return false;
|
|
494
|
+
const projectDir = getProjectDir2(codeName);
|
|
515
495
|
try {
|
|
516
|
-
|
|
517
|
-
|
|
496
|
+
const output = execFileSync("acpx", [
|
|
497
|
+
"claude",
|
|
498
|
+
"-s",
|
|
499
|
+
`agt-${codeName}`,
|
|
500
|
+
"status"
|
|
501
|
+
], {
|
|
502
|
+
cwd: projectDir,
|
|
503
|
+
timeout: 1e4,
|
|
504
|
+
encoding: "utf-8"
|
|
505
|
+
});
|
|
506
|
+
return output.includes("status: running") || output.includes("status: idle");
|
|
518
507
|
} catch {
|
|
519
508
|
session.status = "crashed";
|
|
509
|
+
session.startedAt = Date.now();
|
|
510
|
+
session.restartCount++;
|
|
520
511
|
return false;
|
|
521
512
|
}
|
|
522
513
|
}
|
|
@@ -527,28 +518,10 @@ function resetRestartCount(codeName) {
|
|
|
527
518
|
async function stopAllSessionsAndWait(log2, opts) {
|
|
528
519
|
const codeNames = [...sessions.keys()];
|
|
529
520
|
if (codeNames.length === 0) return;
|
|
530
|
-
const exitPromises = [];
|
|
531
521
|
for (const codeName of codeNames) {
|
|
532
|
-
const session = sessions.get(codeName);
|
|
533
|
-
const proc = session?.process;
|
|
534
522
|
stopPersistentSession(codeName, log2);
|
|
535
|
-
if (proc && !proc.killed && proc.exitCode === null) {
|
|
536
|
-
exitPromises.push(
|
|
537
|
-
new Promise((resolve) => {
|
|
538
|
-
proc.on("close", resolve);
|
|
539
|
-
proc.on("error", resolve);
|
|
540
|
-
})
|
|
541
|
-
);
|
|
542
|
-
}
|
|
543
523
|
}
|
|
544
|
-
|
|
545
|
-
await Promise.race([
|
|
546
|
-
Promise.all(exitPromises),
|
|
547
|
-
new Promise((resolve) => setTimeout(() => {
|
|
548
|
-
log2(`[persistent-session] Shutdown timeout (${opts.timeoutMs}ms), force-killing remaining sessions`);
|
|
549
|
-
resolve();
|
|
550
|
-
}, opts.timeoutMs))
|
|
551
|
-
]);
|
|
524
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(opts.timeoutMs, 2e3)));
|
|
552
525
|
}
|
|
553
526
|
function getProjectDir2(codeName) {
|
|
554
527
|
return join(homedir(), ".augmented", codeName, "project");
|
|
@@ -874,16 +847,16 @@ var lastVersionCheckAt = 0;
|
|
|
874
847
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
875
848
|
async function ensureBrewDependency(binary, formula) {
|
|
876
849
|
if (frameworkBinaryChecked.has(`dep:${binary}`)) return true;
|
|
877
|
-
const { execFileSync } = await import("child_process");
|
|
850
|
+
const { execFileSync: execFileSync2 } = await import("child_process");
|
|
878
851
|
try {
|
|
879
|
-
|
|
852
|
+
execFileSync2("which", [binary], { timeout: 5e3 });
|
|
880
853
|
frameworkBinaryChecked.add(`dep:${binary}`);
|
|
881
854
|
return true;
|
|
882
855
|
} catch {
|
|
883
856
|
}
|
|
884
857
|
let brewPath;
|
|
885
858
|
try {
|
|
886
|
-
brewPath =
|
|
859
|
+
brewPath = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
887
860
|
} catch {
|
|
888
861
|
log(`${binary} not found and Homebrew not available \u2014 install manually: brew install ${formula}`);
|
|
889
862
|
frameworkBinaryChecked.add(`dep:${binary}`);
|
|
@@ -891,14 +864,14 @@ async function ensureBrewDependency(binary, formula) {
|
|
|
891
864
|
}
|
|
892
865
|
log(`${binary} not found \u2014 installing via Homebrew...`);
|
|
893
866
|
try {
|
|
894
|
-
|
|
867
|
+
execFileSync2(brewPath, ["install", formula], { timeout: 12e4, stdio: "pipe" });
|
|
895
868
|
} catch (err) {
|
|
896
869
|
log(`Failed to install ${formula}: ${err.message}`);
|
|
897
870
|
frameworkBinaryChecked.add(`dep:${binary}`);
|
|
898
871
|
return false;
|
|
899
872
|
}
|
|
900
873
|
try {
|
|
901
|
-
|
|
874
|
+
execFileSync2("which", [binary], { timeout: 5e3 });
|
|
902
875
|
log(`${binary} installed successfully`);
|
|
903
876
|
frameworkBinaryChecked.add(`dep:${binary}`);
|
|
904
877
|
return true;
|
|
@@ -913,24 +886,24 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
913
886
|
if (frameworkBinaryChecked.has(frameworkId)) return;
|
|
914
887
|
frameworkBinaryChecked.add(frameworkId);
|
|
915
888
|
await ensureBrewDependency("tmux", "tmux");
|
|
916
|
-
const { execFileSync } = await import("child_process");
|
|
889
|
+
const { execFileSync: execFileSync2 } = await import("child_process");
|
|
917
890
|
let brewPath;
|
|
918
891
|
try {
|
|
919
|
-
brewPath =
|
|
892
|
+
brewPath = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
920
893
|
} catch {
|
|
921
894
|
log("Homebrew not found \u2014 cannot auto-install/upgrade Claude Code. Install manually: https://claude.ai/download");
|
|
922
895
|
return;
|
|
923
896
|
}
|
|
924
897
|
let claudeExists = false;
|
|
925
898
|
try {
|
|
926
|
-
|
|
899
|
+
execFileSync2("which", ["claude"], { timeout: 5e3 });
|
|
927
900
|
claudeExists = true;
|
|
928
901
|
} catch {
|
|
929
902
|
}
|
|
930
903
|
if (!claudeExists) {
|
|
931
904
|
log("Claude Code binary not found \u2014 installing via Homebrew...");
|
|
932
905
|
try {
|
|
933
|
-
|
|
906
|
+
execFileSync2(brewPath, ["install", "--cask", "claude-code"], {
|
|
934
907
|
timeout: 12e4,
|
|
935
908
|
stdio: "pipe"
|
|
936
909
|
});
|
|
@@ -939,7 +912,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
939
912
|
return;
|
|
940
913
|
}
|
|
941
914
|
try {
|
|
942
|
-
|
|
915
|
+
execFileSync2("which", ["claude"], { timeout: 5e3 });
|
|
943
916
|
log("Claude Code installed successfully");
|
|
944
917
|
} catch {
|
|
945
918
|
log("Claude Code install completed but binary not found on PATH \u2014 you may need to restart your terminal");
|
|
@@ -947,7 +920,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
947
920
|
} else {
|
|
948
921
|
log("Checking for Claude Code updates...");
|
|
949
922
|
try {
|
|
950
|
-
const output =
|
|
923
|
+
const output = execFileSync2(brewPath, ["upgrade", "--cask", "claude-code"], {
|
|
951
924
|
timeout: 12e4,
|
|
952
925
|
stdio: "pipe"
|
|
953
926
|
}).toString();
|
|
@@ -965,11 +938,11 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
965
938
|
}
|
|
966
939
|
}
|
|
967
940
|
}
|
|
968
|
-
agentRuntimeAuthenticated = checkClaudeAuth(
|
|
941
|
+
agentRuntimeAuthenticated = checkClaudeAuth(execFileSync2);
|
|
969
942
|
}
|
|
970
|
-
function checkClaudeAuth(
|
|
943
|
+
function checkClaudeAuth(execFileSync2) {
|
|
971
944
|
try {
|
|
972
|
-
const authOutput =
|
|
945
|
+
const authOutput = execFileSync2("claude", ["auth", "status"], { timeout: 1e4, stdio: "pipe" }).toString().trim();
|
|
973
946
|
let loggedIn = null;
|
|
974
947
|
try {
|
|
975
948
|
const parsed = JSON.parse(authOutput);
|
|
@@ -1000,8 +973,8 @@ function loadGatewayPorts() {
|
|
|
1000
973
|
}
|
|
1001
974
|
}
|
|
1002
975
|
function saveGatewayPorts(ports) {
|
|
1003
|
-
|
|
1004
|
-
|
|
976
|
+
mkdirSync2(AUGMENTED_DIR, { recursive: true });
|
|
977
|
+
writeFileSync3(GATEWAY_PORTS_FILE, JSON.stringify(ports, null, 2));
|
|
1005
978
|
}
|
|
1006
979
|
function allocatePort(codeName) {
|
|
1007
980
|
const ports = loadGatewayPorts();
|
|
@@ -1027,7 +1000,7 @@ var STATE_FILE = join2(process.env["HOME"] ?? "/tmp", ".augmented", "manager-sta
|
|
|
1027
1000
|
function send(msg) {
|
|
1028
1001
|
if (msg.type === "state-update") {
|
|
1029
1002
|
try {
|
|
1030
|
-
|
|
1003
|
+
writeFileSync3(STATE_FILE, JSON.stringify(msg.state, null, 2));
|
|
1031
1004
|
} catch {
|
|
1032
1005
|
}
|
|
1033
1006
|
}
|
|
@@ -1083,9 +1056,9 @@ async function migrateToProfiles() {
|
|
|
1083
1056
|
const profileAuthDir = join2(profileDir, "agents", codeName, "agent");
|
|
1084
1057
|
const authFile = join2(sharedAuthDir, "auth-profiles.json");
|
|
1085
1058
|
if (existsSync2(authFile)) {
|
|
1086
|
-
|
|
1059
|
+
mkdirSync2(profileAuthDir, { recursive: true });
|
|
1087
1060
|
const authContent = readFileSync3(authFile, "utf-8");
|
|
1088
|
-
|
|
1061
|
+
writeFileSync3(join2(profileAuthDir, "auth-profiles.json"), authContent);
|
|
1089
1062
|
}
|
|
1090
1063
|
allocatePort(codeName);
|
|
1091
1064
|
migrated++;
|
|
@@ -1180,7 +1153,7 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
1180
1153
|
if (cfg.gateway?.port !== status.port) {
|
|
1181
1154
|
if (!cfg.gateway) cfg.gateway = {};
|
|
1182
1155
|
cfg.gateway.port = status.port;
|
|
1183
|
-
|
|
1156
|
+
writeFileSync3(configPath, JSON.stringify(cfg, null, 2));
|
|
1184
1157
|
}
|
|
1185
1158
|
}
|
|
1186
1159
|
} catch {
|
|
@@ -1205,7 +1178,7 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
1205
1178
|
const cfg = JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
1206
1179
|
if (!cfg.gateway) cfg.gateway = {};
|
|
1207
1180
|
cfg.gateway.port = port;
|
|
1208
|
-
|
|
1181
|
+
writeFileSync3(configPath, JSON.stringify(cfg, null, 2));
|
|
1209
1182
|
}
|
|
1210
1183
|
} catch {
|
|
1211
1184
|
}
|
|
@@ -1590,7 +1563,7 @@ async function processAgent(agent, agentStates) {
|
|
|
1590
1563
|
try {
|
|
1591
1564
|
const artifacts = generateArtifacts(agent, refreshData, frameworkAdapter);
|
|
1592
1565
|
const changedFiles = [];
|
|
1593
|
-
|
|
1566
|
+
mkdirSync2(agentDir, { recursive: true });
|
|
1594
1567
|
for (const artifact of artifacts) {
|
|
1595
1568
|
const filePath = join2(agentDir, artifact.relativePath);
|
|
1596
1569
|
const newHash = sha256(artifact.content);
|
|
@@ -1605,7 +1578,7 @@ async function processAgent(agent, agentStates) {
|
|
|
1605
1578
|
const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
|
|
1606
1579
|
log(`${verb} '${agent.code_name}': ${fileNames}`);
|
|
1607
1580
|
for (const file of changedFiles) {
|
|
1608
|
-
|
|
1581
|
+
writeFileSync3(join2(agentDir, file.relativePath), file.content);
|
|
1609
1582
|
}
|
|
1610
1583
|
lastProvisionAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1611
1584
|
knownVersions.set(agent.agent_id, { charterVersion, toolsVersion });
|
|
@@ -1839,10 +1812,10 @@ async function processAgent(agent, agentStates) {
|
|
|
1839
1812
|
const hasLcm = integrations.some((i) => i.definition_id === "lossless-claw");
|
|
1840
1813
|
if (hasLcm && !losslessClawInstalled.get(agent.code_name)) {
|
|
1841
1814
|
try {
|
|
1842
|
-
const { execFileSync } = await import("child_process");
|
|
1815
|
+
const { execFileSync: execFileSync2 } = await import("child_process");
|
|
1843
1816
|
let pluginList;
|
|
1844
1817
|
try {
|
|
1845
|
-
pluginList =
|
|
1818
|
+
pluginList = execFileSync2(
|
|
1846
1819
|
"openclaw",
|
|
1847
1820
|
["--profile", agent.code_name, "plugins", "list", "--json"],
|
|
1848
1821
|
{ timeout: 15e3 }
|
|
@@ -1856,7 +1829,7 @@ async function processAgent(agent, agentStates) {
|
|
|
1856
1829
|
);
|
|
1857
1830
|
if (!alreadyInstalled) {
|
|
1858
1831
|
log(`Installing lossless-claw plugin for '${agent.code_name}'...`);
|
|
1859
|
-
|
|
1832
|
+
execFileSync2(
|
|
1860
1833
|
"openclaw",
|
|
1861
1834
|
["--profile", agent.code_name, "plugins", "install", "@martian-engineering/lossless-claw"],
|
|
1862
1835
|
{ stdio: "ignore", timeout: 6e4 }
|
|
@@ -1903,18 +1876,18 @@ async function processAgent(agent, agentStates) {
|
|
|
1903
1876
|
"@tobilu/qmd"
|
|
1904
1877
|
]);
|
|
1905
1878
|
if (intHash !== prevIntHash) {
|
|
1906
|
-
const { execFileSync } = await import("child_process");
|
|
1879
|
+
const { execFileSync: execFileSync2 } = await import("child_process");
|
|
1907
1880
|
for (const tool of capData.cliTools) {
|
|
1908
1881
|
if (!ALLOWED_CLI_PACKAGES.has(tool.package)) {
|
|
1909
1882
|
log(`Skipping CLI tool '${tool.package}' for '${agent.code_name}' \u2014 not on the allowed packages list`);
|
|
1910
1883
|
continue;
|
|
1911
1884
|
}
|
|
1912
1885
|
try {
|
|
1913
|
-
|
|
1886
|
+
execFileSync2("which", [tool.binary], { stdio: "ignore" });
|
|
1914
1887
|
} catch {
|
|
1915
1888
|
log(`Installing CLI tool '${tool.package}' for '${agent.code_name}'...`);
|
|
1916
1889
|
try {
|
|
1917
|
-
|
|
1890
|
+
execFileSync2("npm", ["install", "-g", tool.package], { stdio: "ignore", timeout: 6e4 });
|
|
1918
1891
|
log(`CLI tool '${tool.binary}' installed successfully`);
|
|
1919
1892
|
} catch (installErr) {
|
|
1920
1893
|
log(`Failed to install CLI tool '${tool.package}': ${installErr.message}`);
|
|
@@ -1923,7 +1896,7 @@ async function processAgent(agent, agentStates) {
|
|
|
1923
1896
|
if (tool.binary === "qmd") {
|
|
1924
1897
|
try {
|
|
1925
1898
|
const agentDir2 = join2(process.env["HOME"] ?? "/tmp", ".augmented", agent.code_name);
|
|
1926
|
-
|
|
1899
|
+
execFileSync2("qmd", ["collection", "add", agent.code_name, "project"], {
|
|
1927
1900
|
stdio: "ignore",
|
|
1928
1901
|
timeout: 3e4,
|
|
1929
1902
|
cwd: agentDir2
|
|
@@ -2304,7 +2277,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
2304
2277
|
}
|
|
2305
2278
|
}
|
|
2306
2279
|
}
|
|
2307
|
-
|
|
2280
|
+
writeFileSync3(indexPath, JSON.stringify(index));
|
|
2308
2281
|
if (toDelete.length > 0) {
|
|
2309
2282
|
log(`Cleaned ${toDelete.length} cron session(s) and ${deletedFiles} file(s) from ${sessionsDir}`);
|
|
2310
2283
|
}
|
|
@@ -2340,7 +2313,7 @@ function clearStaleCronRunState(jobsPath) {
|
|
|
2340
2313
|
}
|
|
2341
2314
|
}
|
|
2342
2315
|
if (changed) {
|
|
2343
|
-
|
|
2316
|
+
writeFileSync3(jobsPath, JSON.stringify(data, null, 2));
|
|
2344
2317
|
}
|
|
2345
2318
|
} catch {
|
|
2346
2319
|
}
|
|
@@ -2563,8 +2536,8 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
2563
2536
|
}
|
|
2564
2537
|
}
|
|
2565
2538
|
if (!agentRuntimeAuthenticated) {
|
|
2566
|
-
const { execFileSync } = await import("child_process");
|
|
2567
|
-
agentRuntimeAuthenticated = checkClaudeAuth(
|
|
2539
|
+
const { execFileSync: execFileSync2 } = await import("child_process");
|
|
2540
|
+
agentRuntimeAuthenticated = checkClaudeAuth(execFileSync2);
|
|
2568
2541
|
if (!agentRuntimeAuthenticated) {
|
|
2569
2542
|
log(`[persistent-session] Skipping '${codeName}' \u2014 Claude Code not authenticated`);
|
|
2570
2543
|
return;
|
|
@@ -2587,12 +2560,39 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
2587
2560
|
claudeMdPath,
|
|
2588
2561
|
channels,
|
|
2589
2562
|
devChannels,
|
|
2563
|
+
apiHost: requireHost(),
|
|
2590
2564
|
log
|
|
2591
2565
|
});
|
|
2592
2566
|
persistentSessionAgents.add(codeName);
|
|
2593
2567
|
return;
|
|
2594
2568
|
}
|
|
2595
2569
|
resetRestartCount(codeName);
|
|
2570
|
+
const stableTasksHash = createHash("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
|
|
2571
|
+
const prevHash = knownTasksHashes.get(agent.agent_id);
|
|
2572
|
+
if (stableTasksHash !== prevHash) {
|
|
2573
|
+
const taskInputs = tasks.map((t) => ({
|
|
2574
|
+
id: t.id,
|
|
2575
|
+
template_id: t.template_id,
|
|
2576
|
+
name: t.name,
|
|
2577
|
+
schedule_kind: t.schedule_kind,
|
|
2578
|
+
schedule_expr: t.schedule_expr ?? null,
|
|
2579
|
+
schedule_every: t.schedule_every ?? null,
|
|
2580
|
+
schedule_at: t.schedule_at ?? null,
|
|
2581
|
+
timezone: t.timezone ?? "UTC",
|
|
2582
|
+
prompt: t.prompt ?? "",
|
|
2583
|
+
session_target: t.session_target ?? "isolated",
|
|
2584
|
+
delivery_mode: t.delivery_mode ?? "none",
|
|
2585
|
+
delivery_channel: t.delivery_channel ?? null,
|
|
2586
|
+
delivery_to: t.delivery_to ?? null,
|
|
2587
|
+
enabled: t.enabled ?? true
|
|
2588
|
+
}));
|
|
2589
|
+
const schedulerState = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
|
|
2590
|
+
claudeSchedulerStates.set(codeName, schedulerState);
|
|
2591
|
+
knownTasksHashes.set(agent.agent_id, stableTasksHash);
|
|
2592
|
+
log(`[persistent-session] Tasks synced for '${codeName}' (${taskInputs.length} task(s))`);
|
|
2593
|
+
} else if (!claudeSchedulerStates.has(codeName)) {
|
|
2594
|
+
claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
|
|
2595
|
+
}
|
|
2596
2596
|
const state2 = claudeSchedulerStates.get(codeName);
|
|
2597
2597
|
if (state2) {
|
|
2598
2598
|
const ready = getReadyTasks(state2);
|
|
@@ -2624,8 +2624,8 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
2624
2624
|
const markerDir = join2(projectDir2, ".claude");
|
|
2625
2625
|
const markerPath = join2(markerDir, ".agt-pending-task.json");
|
|
2626
2626
|
try {
|
|
2627
|
-
|
|
2628
|
-
|
|
2627
|
+
mkdirSync2(markerDir, { recursive: true });
|
|
2628
|
+
writeFileSync3(markerPath, JSON.stringify({
|
|
2629
2629
|
agent_id: agent.agent_id,
|
|
2630
2630
|
task_id: task.taskId,
|
|
2631
2631
|
template_id: task.templateId,
|
|
@@ -2643,35 +2643,13 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
2643
2643
|
if (injected) {
|
|
2644
2644
|
const updated = markTaskFired(codeName, task.taskId, "ok");
|
|
2645
2645
|
claudeSchedulerStates.set(codeName, updated);
|
|
2646
|
-
log(`[persistent-session] Task '${task.name}' injected, next fire at ${new Date(updated.tasks[task.taskId]?.nextFireAt ?? 0).toISOString()}`);
|
|
2646
|
+
log(`[persistent-session] Task '${task.name}' injected for '${codeName}', next fire at ${new Date(updated.tasks[task.taskId]?.nextFireAt ?? 0).toISOString()}`);
|
|
2647
|
+
} else {
|
|
2648
|
+
log(`[persistent-session] Task '${task.name}' injection FAILED for '${codeName}' \u2014 injectMessage returned false`);
|
|
2647
2649
|
}
|
|
2648
2650
|
break;
|
|
2649
2651
|
}
|
|
2650
2652
|
}
|
|
2651
|
-
const stableTasksHash = createHash("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
|
|
2652
|
-
const prevHash = knownTasksHashes.get(agent.agent_id);
|
|
2653
|
-
if (stableTasksHash !== prevHash) {
|
|
2654
|
-
const taskInputs = tasks.map((t) => ({
|
|
2655
|
-
id: t.id,
|
|
2656
|
-
template_id: t.template_id,
|
|
2657
|
-
name: t.name,
|
|
2658
|
-
schedule_kind: t.schedule_kind,
|
|
2659
|
-
schedule_expr: t.schedule_expr ?? null,
|
|
2660
|
-
schedule_every: t.schedule_every ?? null,
|
|
2661
|
-
schedule_at: t.schedule_at ?? null,
|
|
2662
|
-
timezone: t.timezone ?? "UTC",
|
|
2663
|
-
prompt: t.prompt ?? "",
|
|
2664
|
-
session_target: t.session_target ?? "isolated",
|
|
2665
|
-
delivery_mode: t.delivery_mode ?? "none",
|
|
2666
|
-
delivery_channel: t.delivery_channel ?? null,
|
|
2667
|
-
delivery_to: t.delivery_to ?? null,
|
|
2668
|
-
enabled: t.enabled ?? true
|
|
2669
|
-
}));
|
|
2670
|
-
const schedulerState = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
|
|
2671
|
-
claudeSchedulerStates.set(codeName, schedulerState);
|
|
2672
|
-
knownTasksHashes.set(agent.agent_id, stableTasksHash);
|
|
2673
|
-
log(`[persistent-session] Tasks synced for '${codeName}' (${taskInputs.length} task(s))`);
|
|
2674
|
-
}
|
|
2675
2653
|
}
|
|
2676
2654
|
var realtimeStarted = false;
|
|
2677
2655
|
var realtimeDriftStarted = false;
|
|
@@ -2886,7 +2864,7 @@ async function processDirectChatMessage(agent, msg) {
|
|
|
2886
2864
|
try {
|
|
2887
2865
|
let reply;
|
|
2888
2866
|
if (fw === "claude-code") {
|
|
2889
|
-
const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-
|
|
2867
|
+
const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-V2QNX3Z4.js");
|
|
2890
2868
|
const projDir = ccProjectDir(agent.codeName);
|
|
2891
2869
|
const chatArgs = [
|
|
2892
2870
|
"-p",
|
|
@@ -3645,8 +3623,8 @@ var caffeinateProc = null;
|
|
|
3645
3623
|
async function startCaffeinate() {
|
|
3646
3624
|
if (process.platform !== "darwin") return;
|
|
3647
3625
|
try {
|
|
3648
|
-
const { spawn
|
|
3649
|
-
caffeinateProc =
|
|
3626
|
+
const { spawn } = await import("child_process");
|
|
3627
|
+
caffeinateProc = spawn("caffeinate", ["-dims"], {
|
|
3650
3628
|
stdio: "ignore",
|
|
3651
3629
|
detached: false
|
|
3652
3630
|
});
|