@integrity-labs/agt-cli 0.7.9 → 0.7.11
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 +2 -2
- 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/{claude-scheduler-APXMZEK4.js → claude-scheduler-V2QNX3Z4.js} +2 -2
- package/dist/lib/manager-worker.js +214 -214
- package/dist/lib/manager-worker.js.map +1 -1
- package/package.json +2 -1
- package/dist/chunk-4I4QZRBQ.js.map +0 -1
- /package/dist/{claude-scheduler-APXMZEK4.js.map → claude-scheduler-V2QNX3Z4.js.map} +0 -0
|
@@ -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, 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,53 @@ var GatewayClientPool = class extends EventEmitter {
|
|
|
327
327
|
};
|
|
328
328
|
|
|
329
329
|
// src/lib/persistent-session.ts
|
|
330
|
-
import {
|
|
331
|
-
import { join } from "path";
|
|
330
|
+
import { execFileSync, execSync } from "child_process";
|
|
331
|
+
import { join, dirname } from "path";
|
|
332
332
|
import { homedir } from "os";
|
|
333
|
-
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
if (
|
|
338
|
-
|
|
333
|
+
import { existsSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
334
|
+
import { fileURLToPath } from "url";
|
|
335
|
+
var _acpxBin = null;
|
|
336
|
+
function getAcpxBin() {
|
|
337
|
+
if (_acpxBin) return _acpxBin;
|
|
338
|
+
const localBin = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "node_modules", ".bin", "acpx");
|
|
339
|
+
if (existsSync(localBin)) {
|
|
340
|
+
_acpxBin = localBin;
|
|
341
|
+
return _acpxBin;
|
|
342
|
+
}
|
|
343
|
+
try {
|
|
344
|
+
execSync("which acpx", { stdio: "ignore" });
|
|
345
|
+
_acpxBin = "acpx";
|
|
346
|
+
return _acpxBin;
|
|
347
|
+
} catch {
|
|
348
|
+
}
|
|
349
|
+
try {
|
|
350
|
+
execSync("npm install -g acpx@latest", { stdio: "ignore", timeout: 6e4 });
|
|
351
|
+
_acpxBin = "acpx";
|
|
352
|
+
return _acpxBin;
|
|
353
|
+
} catch {
|
|
354
|
+
throw new Error("acpx not found and auto-install failed. Run: npm install -g acpx");
|
|
339
355
|
}
|
|
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
356
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
const
|
|
357
|
+
var sessions = /* @__PURE__ */ new Map();
|
|
358
|
+
function writeAcpxConfig(config2) {
|
|
359
|
+
const { projectDir, mcpConfigPath, claudeMdPath, channels, devChannels } = config2;
|
|
360
|
+
const claudeArgs = [];
|
|
356
361
|
if (channels.length > 0) {
|
|
357
|
-
|
|
362
|
+
claudeArgs.push("--channels", ...channels);
|
|
358
363
|
}
|
|
359
364
|
if (devChannels.length > 0) {
|
|
360
|
-
|
|
365
|
+
claudeArgs.push("--dangerously-load-development-channels", ...devChannels);
|
|
361
366
|
}
|
|
362
|
-
|
|
367
|
+
claudeArgs.push("--mcp-config", mcpConfigPath);
|
|
363
368
|
const channelsConfigPath = join(projectDir, ".mcp-channels.json");
|
|
364
369
|
if (existsSync(channelsConfigPath)) {
|
|
365
|
-
|
|
370
|
+
claudeArgs.push("--mcp-config", channelsConfigPath);
|
|
366
371
|
}
|
|
367
372
|
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 {
|
|
373
|
+
claudeArgs.push("--system-prompt-file", claudeMdPath);
|
|
378
374
|
}
|
|
375
|
+
claudeArgs.push("--allow-dangerously-skip-permissions");
|
|
376
|
+
claudeArgs.push("--dangerously-skip-permissions");
|
|
379
377
|
const envIntegrationsPath = join(projectDir, ".env.integrations");
|
|
380
378
|
let envPrefix = "";
|
|
381
379
|
if (existsSync(envIntegrationsPath)) {
|
|
@@ -385,138 +383,154 @@ function spawnSession(config2, session) {
|
|
|
385
383
|
const eqIdx = line.indexOf("=");
|
|
386
384
|
const key = line.slice(0, eqIdx);
|
|
387
385
|
const value = line.slice(eqIdx + 1);
|
|
388
|
-
return `${key}=${JSON.stringify(value)}`;
|
|
386
|
+
return `${key}=${value.includes(" ") ? JSON.stringify(value) : value}`;
|
|
389
387
|
}).join(" ");
|
|
390
388
|
if (envVars) envPrefix = `env ${envVars} `;
|
|
391
389
|
} catch {
|
|
392
390
|
}
|
|
393
391
|
}
|
|
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
|
-
}
|
|
392
|
+
const acpxConfig = {
|
|
393
|
+
defaultAgent: "claude",
|
|
394
|
+
defaultPermissions: "approve-all",
|
|
395
|
+
agents: {
|
|
396
|
+
claude: {
|
|
397
|
+
command: `${envPrefix}npx -y @agentclientprotocol/claude-agent-acp ${claudeArgs.join(" ")}`
|
|
455
398
|
}
|
|
456
|
-
}
|
|
457
|
-
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
writeFileSync2(join(projectDir, ".acpxrc.json"), JSON.stringify(acpxConfig, null, 2));
|
|
402
|
+
}
|
|
403
|
+
function startPersistentSession(config2) {
|
|
404
|
+
const existing = sessions.get(config2.codeName);
|
|
405
|
+
if (existing && existing.status === "running") {
|
|
406
|
+
return existing;
|
|
407
|
+
}
|
|
408
|
+
const restartCount = existing?.restartCount ?? 0;
|
|
409
|
+
if (existing?.status === "crashed" && existing.startedAt) {
|
|
410
|
+
const backoffMs = Math.min(5e3 * Math.pow(2, restartCount), 6e4);
|
|
411
|
+
if (Date.now() - existing.startedAt < backoffMs) {
|
|
412
|
+
return existing;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
const session = {
|
|
416
|
+
codeName: config2.codeName,
|
|
417
|
+
startedAt: null,
|
|
418
|
+
restartCount,
|
|
419
|
+
status: "starting"
|
|
420
|
+
};
|
|
421
|
+
sessions.set(config2.codeName, session);
|
|
422
|
+
spawnSession(config2, session);
|
|
423
|
+
return session;
|
|
424
|
+
}
|
|
425
|
+
function spawnSession(config2, session) {
|
|
426
|
+
const { codeName, projectDir, mcpConfigPath, apiHost, log: log2 } = config2;
|
|
427
|
+
const sessionName = `agt-${codeName}`;
|
|
428
|
+
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.";
|
|
429
|
+
log2(`[persistent-session] Starting acpx session '${sessionName}' for '${codeName}'`);
|
|
430
|
+
try {
|
|
431
|
+
sanitizeMcpJson(mcpConfigPath, apiHost);
|
|
432
|
+
writeAcpxConfig(config2);
|
|
433
|
+
execFileSync(getAcpxBin(), [
|
|
434
|
+
"claude",
|
|
435
|
+
"sessions",
|
|
436
|
+
"ensure",
|
|
437
|
+
"--name",
|
|
438
|
+
sessionName
|
|
439
|
+
], {
|
|
440
|
+
cwd: projectDir,
|
|
441
|
+
timeout: 3e4,
|
|
442
|
+
stdio: "ignore"
|
|
458
443
|
});
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
444
|
+
execFileSync(getAcpxBin(), [
|
|
445
|
+
"claude",
|
|
446
|
+
"-s",
|
|
447
|
+
sessionName,
|
|
448
|
+
"--no-wait",
|
|
449
|
+
"--",
|
|
450
|
+
initPrompt
|
|
451
|
+
], {
|
|
452
|
+
cwd: projectDir,
|
|
453
|
+
timeout: 3e4,
|
|
454
|
+
stdio: "ignore"
|
|
455
|
+
});
|
|
456
|
+
session.startedAt = Date.now();
|
|
457
|
+
session.status = "running";
|
|
458
|
+
session.restartCount = 0;
|
|
459
|
+
log2(`[persistent-session] Session '${sessionName}' started for '${codeName}'`);
|
|
460
|
+
} catch (err) {
|
|
461
|
+
log2(`[persistent-session] Failed to start acpx session for '${codeName}': ${err.message}`);
|
|
462
462
|
session.status = "crashed";
|
|
463
|
-
|
|
463
|
+
session.startedAt = Date.now();
|
|
464
|
+
session.restartCount++;
|
|
465
|
+
}
|
|
464
466
|
}
|
|
465
467
|
function stopPersistentSession(codeName, log2) {
|
|
466
468
|
const session = sessions.get(codeName);
|
|
467
469
|
if (!session) return;
|
|
468
470
|
log2(`[persistent-session] Stopping session for '${codeName}'`);
|
|
469
471
|
session.status = "stopped";
|
|
472
|
+
const projectDir = getProjectDir2(codeName);
|
|
470
473
|
try {
|
|
471
|
-
|
|
474
|
+
execFileSync(getAcpxBin(), ["claude", "sessions", "close", `agt-${codeName}`], {
|
|
475
|
+
cwd: projectDir,
|
|
476
|
+
timeout: 1e4,
|
|
477
|
+
stdio: "ignore"
|
|
478
|
+
});
|
|
472
479
|
} catch {
|
|
473
480
|
}
|
|
474
|
-
|
|
475
|
-
|
|
481
|
+
try {
|
|
482
|
+
execFileSync("tmux", ["kill-session", "-t", `agt-${codeName}`], { stdio: "ignore" });
|
|
483
|
+
} catch {
|
|
476
484
|
}
|
|
477
|
-
setTimeout(() => {
|
|
478
|
-
if (session.process && !session.process.killed) {
|
|
479
|
-
session.process.kill("SIGKILL");
|
|
480
|
-
}
|
|
481
|
-
}, 1e4);
|
|
482
485
|
sessions.delete(codeName);
|
|
483
486
|
}
|
|
484
487
|
async function injectMessage(codeName, type, content, meta) {
|
|
485
488
|
const session = sessions.get(codeName);
|
|
486
|
-
if (!session || session.status !== "running"
|
|
489
|
+
if (!session || session.status !== "running") {
|
|
487
490
|
return false;
|
|
488
491
|
}
|
|
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
|
-
}
|
|
492
|
+
const projectDir = getProjectDir2(codeName);
|
|
493
|
+
const sessionName = `agt-${codeName}`;
|
|
494
|
+
const prefix = meta?.task_name ? `[Task: ${meta.task_name}] ` : "";
|
|
495
|
+
const text = prefix + content;
|
|
501
496
|
try {
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
497
|
+
execFileSync(getAcpxBin(), [
|
|
498
|
+
"claude",
|
|
499
|
+
"-s",
|
|
500
|
+
sessionName,
|
|
501
|
+
"--no-wait",
|
|
502
|
+
"--",
|
|
503
|
+
text
|
|
504
|
+
], {
|
|
505
|
+
cwd: projectDir,
|
|
506
|
+
timeout: 15e3,
|
|
507
|
+
stdio: "ignore"
|
|
508
|
+
});
|
|
506
509
|
return true;
|
|
507
510
|
} catch {
|
|
508
511
|
return false;
|
|
509
512
|
}
|
|
510
|
-
return false;
|
|
511
513
|
}
|
|
512
514
|
function isSessionHealthy(codeName) {
|
|
513
515
|
const session = sessions.get(codeName);
|
|
514
516
|
if (!session || session.status !== "running") return false;
|
|
517
|
+
const projectDir = getProjectDir2(codeName);
|
|
515
518
|
try {
|
|
516
|
-
|
|
517
|
-
|
|
519
|
+
const output = execFileSync(getAcpxBin(), [
|
|
520
|
+
"claude",
|
|
521
|
+
"-s",
|
|
522
|
+
`agt-${codeName}`,
|
|
523
|
+
"status"
|
|
524
|
+
], {
|
|
525
|
+
cwd: projectDir,
|
|
526
|
+
timeout: 1e4,
|
|
527
|
+
encoding: "utf-8"
|
|
528
|
+
});
|
|
529
|
+
return output.includes("status: running") || output.includes("status: idle");
|
|
518
530
|
} catch {
|
|
519
531
|
session.status = "crashed";
|
|
532
|
+
session.startedAt = Date.now();
|
|
533
|
+
session.restartCount++;
|
|
520
534
|
return false;
|
|
521
535
|
}
|
|
522
536
|
}
|
|
@@ -527,28 +541,10 @@ function resetRestartCount(codeName) {
|
|
|
527
541
|
async function stopAllSessionsAndWait(log2, opts) {
|
|
528
542
|
const codeNames = [...sessions.keys()];
|
|
529
543
|
if (codeNames.length === 0) return;
|
|
530
|
-
const exitPromises = [];
|
|
531
544
|
for (const codeName of codeNames) {
|
|
532
|
-
const session = sessions.get(codeName);
|
|
533
|
-
const proc = session?.process;
|
|
534
545
|
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
546
|
}
|
|
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
|
-
]);
|
|
547
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(opts.timeoutMs, 2e3)));
|
|
552
548
|
}
|
|
553
549
|
function getProjectDir2(codeName) {
|
|
554
550
|
return join(homedir(), ".augmented", codeName, "project");
|
|
@@ -874,16 +870,16 @@ var lastVersionCheckAt = 0;
|
|
|
874
870
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
875
871
|
async function ensureBrewDependency(binary, formula) {
|
|
876
872
|
if (frameworkBinaryChecked.has(`dep:${binary}`)) return true;
|
|
877
|
-
const { execFileSync } = await import("child_process");
|
|
873
|
+
const { execFileSync: execFileSync2 } = await import("child_process");
|
|
878
874
|
try {
|
|
879
|
-
|
|
875
|
+
execFileSync2("which", [binary], { timeout: 5e3 });
|
|
880
876
|
frameworkBinaryChecked.add(`dep:${binary}`);
|
|
881
877
|
return true;
|
|
882
878
|
} catch {
|
|
883
879
|
}
|
|
884
880
|
let brewPath;
|
|
885
881
|
try {
|
|
886
|
-
brewPath =
|
|
882
|
+
brewPath = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
887
883
|
} catch {
|
|
888
884
|
log(`${binary} not found and Homebrew not available \u2014 install manually: brew install ${formula}`);
|
|
889
885
|
frameworkBinaryChecked.add(`dep:${binary}`);
|
|
@@ -891,14 +887,14 @@ async function ensureBrewDependency(binary, formula) {
|
|
|
891
887
|
}
|
|
892
888
|
log(`${binary} not found \u2014 installing via Homebrew...`);
|
|
893
889
|
try {
|
|
894
|
-
|
|
890
|
+
execFileSync2(brewPath, ["install", formula], { timeout: 12e4, stdio: "pipe" });
|
|
895
891
|
} catch (err) {
|
|
896
892
|
log(`Failed to install ${formula}: ${err.message}`);
|
|
897
893
|
frameworkBinaryChecked.add(`dep:${binary}`);
|
|
898
894
|
return false;
|
|
899
895
|
}
|
|
900
896
|
try {
|
|
901
|
-
|
|
897
|
+
execFileSync2("which", [binary], { timeout: 5e3 });
|
|
902
898
|
log(`${binary} installed successfully`);
|
|
903
899
|
frameworkBinaryChecked.add(`dep:${binary}`);
|
|
904
900
|
return true;
|
|
@@ -913,24 +909,24 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
913
909
|
if (frameworkBinaryChecked.has(frameworkId)) return;
|
|
914
910
|
frameworkBinaryChecked.add(frameworkId);
|
|
915
911
|
await ensureBrewDependency("tmux", "tmux");
|
|
916
|
-
const { execFileSync } = await import("child_process");
|
|
912
|
+
const { execFileSync: execFileSync2 } = await import("child_process");
|
|
917
913
|
let brewPath;
|
|
918
914
|
try {
|
|
919
|
-
brewPath =
|
|
915
|
+
brewPath = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
920
916
|
} catch {
|
|
921
917
|
log("Homebrew not found \u2014 cannot auto-install/upgrade Claude Code. Install manually: https://claude.ai/download");
|
|
922
918
|
return;
|
|
923
919
|
}
|
|
924
920
|
let claudeExists = false;
|
|
925
921
|
try {
|
|
926
|
-
|
|
922
|
+
execFileSync2("which", ["claude"], { timeout: 5e3 });
|
|
927
923
|
claudeExists = true;
|
|
928
924
|
} catch {
|
|
929
925
|
}
|
|
930
926
|
if (!claudeExists) {
|
|
931
927
|
log("Claude Code binary not found \u2014 installing via Homebrew...");
|
|
932
928
|
try {
|
|
933
|
-
|
|
929
|
+
execFileSync2(brewPath, ["install", "--cask", "claude-code"], {
|
|
934
930
|
timeout: 12e4,
|
|
935
931
|
stdio: "pipe"
|
|
936
932
|
});
|
|
@@ -939,7 +935,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
939
935
|
return;
|
|
940
936
|
}
|
|
941
937
|
try {
|
|
942
|
-
|
|
938
|
+
execFileSync2("which", ["claude"], { timeout: 5e3 });
|
|
943
939
|
log("Claude Code installed successfully");
|
|
944
940
|
} catch {
|
|
945
941
|
log("Claude Code install completed but binary not found on PATH \u2014 you may need to restart your terminal");
|
|
@@ -947,7 +943,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
947
943
|
} else {
|
|
948
944
|
log("Checking for Claude Code updates...");
|
|
949
945
|
try {
|
|
950
|
-
const output =
|
|
946
|
+
const output = execFileSync2(brewPath, ["upgrade", "--cask", "claude-code"], {
|
|
951
947
|
timeout: 12e4,
|
|
952
948
|
stdio: "pipe"
|
|
953
949
|
}).toString();
|
|
@@ -965,11 +961,11 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
965
961
|
}
|
|
966
962
|
}
|
|
967
963
|
}
|
|
968
|
-
agentRuntimeAuthenticated = checkClaudeAuth(
|
|
964
|
+
agentRuntimeAuthenticated = checkClaudeAuth(execFileSync2);
|
|
969
965
|
}
|
|
970
|
-
function checkClaudeAuth(
|
|
966
|
+
function checkClaudeAuth(execFileSync2) {
|
|
971
967
|
try {
|
|
972
|
-
const authOutput =
|
|
968
|
+
const authOutput = execFileSync2("claude", ["auth", "status"], { timeout: 1e4, stdio: "pipe" }).toString().trim();
|
|
973
969
|
let loggedIn = null;
|
|
974
970
|
try {
|
|
975
971
|
const parsed = JSON.parse(authOutput);
|
|
@@ -1001,7 +997,7 @@ function loadGatewayPorts() {
|
|
|
1001
997
|
}
|
|
1002
998
|
function saveGatewayPorts(ports) {
|
|
1003
999
|
mkdirSync(AUGMENTED_DIR, { recursive: true });
|
|
1004
|
-
|
|
1000
|
+
writeFileSync3(GATEWAY_PORTS_FILE, JSON.stringify(ports, null, 2));
|
|
1005
1001
|
}
|
|
1006
1002
|
function allocatePort(codeName) {
|
|
1007
1003
|
const ports = loadGatewayPorts();
|
|
@@ -1027,7 +1023,7 @@ var STATE_FILE = join2(process.env["HOME"] ?? "/tmp", ".augmented", "manager-sta
|
|
|
1027
1023
|
function send(msg) {
|
|
1028
1024
|
if (msg.type === "state-update") {
|
|
1029
1025
|
try {
|
|
1030
|
-
|
|
1026
|
+
writeFileSync3(STATE_FILE, JSON.stringify(msg.state, null, 2));
|
|
1031
1027
|
} catch {
|
|
1032
1028
|
}
|
|
1033
1029
|
}
|
|
@@ -1085,7 +1081,7 @@ async function migrateToProfiles() {
|
|
|
1085
1081
|
if (existsSync2(authFile)) {
|
|
1086
1082
|
mkdirSync(profileAuthDir, { recursive: true });
|
|
1087
1083
|
const authContent = readFileSync3(authFile, "utf-8");
|
|
1088
|
-
|
|
1084
|
+
writeFileSync3(join2(profileAuthDir, "auth-profiles.json"), authContent);
|
|
1089
1085
|
}
|
|
1090
1086
|
allocatePort(codeName);
|
|
1091
1087
|
migrated++;
|
|
@@ -1180,7 +1176,7 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
1180
1176
|
if (cfg.gateway?.port !== status.port) {
|
|
1181
1177
|
if (!cfg.gateway) cfg.gateway = {};
|
|
1182
1178
|
cfg.gateway.port = status.port;
|
|
1183
|
-
|
|
1179
|
+
writeFileSync3(configPath, JSON.stringify(cfg, null, 2));
|
|
1184
1180
|
}
|
|
1185
1181
|
}
|
|
1186
1182
|
} catch {
|
|
@@ -1205,7 +1201,7 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
1205
1201
|
const cfg = JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
1206
1202
|
if (!cfg.gateway) cfg.gateway = {};
|
|
1207
1203
|
cfg.gateway.port = port;
|
|
1208
|
-
|
|
1204
|
+
writeFileSync3(configPath, JSON.stringify(cfg, null, 2));
|
|
1209
1205
|
}
|
|
1210
1206
|
} catch {
|
|
1211
1207
|
}
|
|
@@ -1605,7 +1601,7 @@ async function processAgent(agent, agentStates) {
|
|
|
1605
1601
|
const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
|
|
1606
1602
|
log(`${verb} '${agent.code_name}': ${fileNames}`);
|
|
1607
1603
|
for (const file of changedFiles) {
|
|
1608
|
-
|
|
1604
|
+
writeFileSync3(join2(agentDir, file.relativePath), file.content);
|
|
1609
1605
|
}
|
|
1610
1606
|
lastProvisionAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1611
1607
|
knownVersions.set(agent.agent_id, { charterVersion, toolsVersion });
|
|
@@ -1839,10 +1835,10 @@ async function processAgent(agent, agentStates) {
|
|
|
1839
1835
|
const hasLcm = integrations.some((i) => i.definition_id === "lossless-claw");
|
|
1840
1836
|
if (hasLcm && !losslessClawInstalled.get(agent.code_name)) {
|
|
1841
1837
|
try {
|
|
1842
|
-
const { execFileSync } = await import("child_process");
|
|
1838
|
+
const { execFileSync: execFileSync2 } = await import("child_process");
|
|
1843
1839
|
let pluginList;
|
|
1844
1840
|
try {
|
|
1845
|
-
pluginList =
|
|
1841
|
+
pluginList = execFileSync2(
|
|
1846
1842
|
"openclaw",
|
|
1847
1843
|
["--profile", agent.code_name, "plugins", "list", "--json"],
|
|
1848
1844
|
{ timeout: 15e3 }
|
|
@@ -1856,7 +1852,7 @@ async function processAgent(agent, agentStates) {
|
|
|
1856
1852
|
);
|
|
1857
1853
|
if (!alreadyInstalled) {
|
|
1858
1854
|
log(`Installing lossless-claw plugin for '${agent.code_name}'...`);
|
|
1859
|
-
|
|
1855
|
+
execFileSync2(
|
|
1860
1856
|
"openclaw",
|
|
1861
1857
|
["--profile", agent.code_name, "plugins", "install", "@martian-engineering/lossless-claw"],
|
|
1862
1858
|
{ stdio: "ignore", timeout: 6e4 }
|
|
@@ -1903,18 +1899,18 @@ async function processAgent(agent, agentStates) {
|
|
|
1903
1899
|
"@tobilu/qmd"
|
|
1904
1900
|
]);
|
|
1905
1901
|
if (intHash !== prevIntHash) {
|
|
1906
|
-
const { execFileSync } = await import("child_process");
|
|
1902
|
+
const { execFileSync: execFileSync2 } = await import("child_process");
|
|
1907
1903
|
for (const tool of capData.cliTools) {
|
|
1908
1904
|
if (!ALLOWED_CLI_PACKAGES.has(tool.package)) {
|
|
1909
1905
|
log(`Skipping CLI tool '${tool.package}' for '${agent.code_name}' \u2014 not on the allowed packages list`);
|
|
1910
1906
|
continue;
|
|
1911
1907
|
}
|
|
1912
1908
|
try {
|
|
1913
|
-
|
|
1909
|
+
execFileSync2("which", [tool.binary], { stdio: "ignore" });
|
|
1914
1910
|
} catch {
|
|
1915
1911
|
log(`Installing CLI tool '${tool.package}' for '${agent.code_name}'...`);
|
|
1916
1912
|
try {
|
|
1917
|
-
|
|
1913
|
+
execFileSync2("npm", ["install", "-g", tool.package], { stdio: "ignore", timeout: 6e4 });
|
|
1918
1914
|
log(`CLI tool '${tool.binary}' installed successfully`);
|
|
1919
1915
|
} catch (installErr) {
|
|
1920
1916
|
log(`Failed to install CLI tool '${tool.package}': ${installErr.message}`);
|
|
@@ -1923,7 +1919,7 @@ async function processAgent(agent, agentStates) {
|
|
|
1923
1919
|
if (tool.binary === "qmd") {
|
|
1924
1920
|
try {
|
|
1925
1921
|
const agentDir2 = join2(process.env["HOME"] ?? "/tmp", ".augmented", agent.code_name);
|
|
1926
|
-
|
|
1922
|
+
execFileSync2("qmd", ["collection", "add", agent.code_name, "project"], {
|
|
1927
1923
|
stdio: "ignore",
|
|
1928
1924
|
timeout: 3e4,
|
|
1929
1925
|
cwd: agentDir2
|
|
@@ -2304,7 +2300,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
2304
2300
|
}
|
|
2305
2301
|
}
|
|
2306
2302
|
}
|
|
2307
|
-
|
|
2303
|
+
writeFileSync3(indexPath, JSON.stringify(index));
|
|
2308
2304
|
if (toDelete.length > 0) {
|
|
2309
2305
|
log(`Cleaned ${toDelete.length} cron session(s) and ${deletedFiles} file(s) from ${sessionsDir}`);
|
|
2310
2306
|
}
|
|
@@ -2340,7 +2336,7 @@ function clearStaleCronRunState(jobsPath) {
|
|
|
2340
2336
|
}
|
|
2341
2337
|
}
|
|
2342
2338
|
if (changed) {
|
|
2343
|
-
|
|
2339
|
+
writeFileSync3(jobsPath, JSON.stringify(data, null, 2));
|
|
2344
2340
|
}
|
|
2345
2341
|
} catch {
|
|
2346
2342
|
}
|
|
@@ -2563,8 +2559,8 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
2563
2559
|
}
|
|
2564
2560
|
}
|
|
2565
2561
|
if (!agentRuntimeAuthenticated) {
|
|
2566
|
-
const { execFileSync } = await import("child_process");
|
|
2567
|
-
agentRuntimeAuthenticated = checkClaudeAuth(
|
|
2562
|
+
const { execFileSync: execFileSync2 } = await import("child_process");
|
|
2563
|
+
agentRuntimeAuthenticated = checkClaudeAuth(execFileSync2);
|
|
2568
2564
|
if (!agentRuntimeAuthenticated) {
|
|
2569
2565
|
log(`[persistent-session] Skipping '${codeName}' \u2014 Claude Code not authenticated`);
|
|
2570
2566
|
return;
|
|
@@ -2594,6 +2590,32 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
2594
2590
|
return;
|
|
2595
2591
|
}
|
|
2596
2592
|
resetRestartCount(codeName);
|
|
2593
|
+
const stableTasksHash = createHash("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
|
|
2594
|
+
const prevHash = knownTasksHashes.get(agent.agent_id);
|
|
2595
|
+
if (stableTasksHash !== prevHash) {
|
|
2596
|
+
const taskInputs = tasks.map((t) => ({
|
|
2597
|
+
id: t.id,
|
|
2598
|
+
template_id: t.template_id,
|
|
2599
|
+
name: t.name,
|
|
2600
|
+
schedule_kind: t.schedule_kind,
|
|
2601
|
+
schedule_expr: t.schedule_expr ?? null,
|
|
2602
|
+
schedule_every: t.schedule_every ?? null,
|
|
2603
|
+
schedule_at: t.schedule_at ?? null,
|
|
2604
|
+
timezone: t.timezone ?? "UTC",
|
|
2605
|
+
prompt: t.prompt ?? "",
|
|
2606
|
+
session_target: t.session_target ?? "isolated",
|
|
2607
|
+
delivery_mode: t.delivery_mode ?? "none",
|
|
2608
|
+
delivery_channel: t.delivery_channel ?? null,
|
|
2609
|
+
delivery_to: t.delivery_to ?? null,
|
|
2610
|
+
enabled: t.enabled ?? true
|
|
2611
|
+
}));
|
|
2612
|
+
const schedulerState = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
|
|
2613
|
+
claudeSchedulerStates.set(codeName, schedulerState);
|
|
2614
|
+
knownTasksHashes.set(agent.agent_id, stableTasksHash);
|
|
2615
|
+
log(`[persistent-session] Tasks synced for '${codeName}' (${taskInputs.length} task(s))`);
|
|
2616
|
+
} else if (!claudeSchedulerStates.has(codeName)) {
|
|
2617
|
+
claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
|
|
2618
|
+
}
|
|
2597
2619
|
const state2 = claudeSchedulerStates.get(codeName);
|
|
2598
2620
|
if (state2) {
|
|
2599
2621
|
const ready = getReadyTasks(state2);
|
|
@@ -2626,7 +2648,7 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
2626
2648
|
const markerPath = join2(markerDir, ".agt-pending-task.json");
|
|
2627
2649
|
try {
|
|
2628
2650
|
mkdirSync(markerDir, { recursive: true });
|
|
2629
|
-
|
|
2651
|
+
writeFileSync3(markerPath, JSON.stringify({
|
|
2630
2652
|
agent_id: agent.agent_id,
|
|
2631
2653
|
task_id: task.taskId,
|
|
2632
2654
|
template_id: task.templateId,
|
|
@@ -2644,35 +2666,13 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
2644
2666
|
if (injected) {
|
|
2645
2667
|
const updated = markTaskFired(codeName, task.taskId, "ok");
|
|
2646
2668
|
claudeSchedulerStates.set(codeName, updated);
|
|
2647
|
-
log(`[persistent-session] Task '${task.name}' injected, next fire at ${new Date(updated.tasks[task.taskId]?.nextFireAt ?? 0).toISOString()}`);
|
|
2669
|
+
log(`[persistent-session] Task '${task.name}' injected for '${codeName}', next fire at ${new Date(updated.tasks[task.taskId]?.nextFireAt ?? 0).toISOString()}`);
|
|
2670
|
+
} else {
|
|
2671
|
+
log(`[persistent-session] Task '${task.name}' injection FAILED for '${codeName}' \u2014 injectMessage returned false`);
|
|
2648
2672
|
}
|
|
2649
2673
|
break;
|
|
2650
2674
|
}
|
|
2651
2675
|
}
|
|
2652
|
-
const stableTasksHash = createHash("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
|
|
2653
|
-
const prevHash = knownTasksHashes.get(agent.agent_id);
|
|
2654
|
-
if (stableTasksHash !== prevHash) {
|
|
2655
|
-
const taskInputs = tasks.map((t) => ({
|
|
2656
|
-
id: t.id,
|
|
2657
|
-
template_id: t.template_id,
|
|
2658
|
-
name: t.name,
|
|
2659
|
-
schedule_kind: t.schedule_kind,
|
|
2660
|
-
schedule_expr: t.schedule_expr ?? null,
|
|
2661
|
-
schedule_every: t.schedule_every ?? null,
|
|
2662
|
-
schedule_at: t.schedule_at ?? null,
|
|
2663
|
-
timezone: t.timezone ?? "UTC",
|
|
2664
|
-
prompt: t.prompt ?? "",
|
|
2665
|
-
session_target: t.session_target ?? "isolated",
|
|
2666
|
-
delivery_mode: t.delivery_mode ?? "none",
|
|
2667
|
-
delivery_channel: t.delivery_channel ?? null,
|
|
2668
|
-
delivery_to: t.delivery_to ?? null,
|
|
2669
|
-
enabled: t.enabled ?? true
|
|
2670
|
-
}));
|
|
2671
|
-
const schedulerState = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
|
|
2672
|
-
claudeSchedulerStates.set(codeName, schedulerState);
|
|
2673
|
-
knownTasksHashes.set(agent.agent_id, stableTasksHash);
|
|
2674
|
-
log(`[persistent-session] Tasks synced for '${codeName}' (${taskInputs.length} task(s))`);
|
|
2675
|
-
}
|
|
2676
2676
|
}
|
|
2677
2677
|
var realtimeStarted = false;
|
|
2678
2678
|
var realtimeDriftStarted = false;
|
|
@@ -2887,7 +2887,7 @@ async function processDirectChatMessage(agent, msg) {
|
|
|
2887
2887
|
try {
|
|
2888
2888
|
let reply;
|
|
2889
2889
|
if (fw === "claude-code") {
|
|
2890
|
-
const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-
|
|
2890
|
+
const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-V2QNX3Z4.js");
|
|
2891
2891
|
const projDir = ccProjectDir(agent.codeName);
|
|
2892
2892
|
const chatArgs = [
|
|
2893
2893
|
"-p",
|
|
@@ -3646,8 +3646,8 @@ var caffeinateProc = null;
|
|
|
3646
3646
|
async function startCaffeinate() {
|
|
3647
3647
|
if (process.platform !== "darwin") return;
|
|
3648
3648
|
try {
|
|
3649
|
-
const { spawn
|
|
3650
|
-
caffeinateProc =
|
|
3649
|
+
const { spawn } = await import("child_process");
|
|
3650
|
+
caffeinateProc = spawn("caffeinate", ["-dims"], {
|
|
3651
3651
|
stdio: "ignore",
|
|
3652
3652
|
detached: false
|
|
3653
3653
|
});
|