aoaoe 0.55.0 → 0.56.0
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/README.md +1 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.js +6 -2
- package/dist/index.js +204 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -243,6 +243,7 @@ commands:
|
|
|
243
243
|
config --validate validate config + check tool availability
|
|
244
244
|
config --diff show only fields that differ from defaults
|
|
245
245
|
notify-test send a test notification to configured webhooks
|
|
246
|
+
doctor comprehensive health check (config, tools, daemon, disk)
|
|
246
247
|
task manage tasks and sessions (list, start, stop, new, rm, edit)
|
|
247
248
|
tasks show task progress (from aoaoe.tasks.json)
|
|
248
249
|
history review recent actions (from ~/.aoaoe/actions.log)
|
package/dist/config.d.ts
CHANGED
package/dist/config.js
CHANGED
|
@@ -299,7 +299,7 @@ export function parseCliArgs(argv) {
|
|
|
299
299
|
let initForce = false;
|
|
300
300
|
let runTaskCli = false;
|
|
301
301
|
let registerTitle;
|
|
302
|
-
const defaults = { overrides, help: false, version: false, register: false, testContext: false, runTest: false, showTasks: false, showHistory: false, showStatus: false, showConfig: false, configValidate: false, configDiff: false, notifyTest: false, runInit: false, initForce: false, runTaskCli: false };
|
|
302
|
+
const defaults = { overrides, help: false, version: false, register: false, testContext: false, runTest: false, showTasks: false, showHistory: false, showStatus: false, showConfig: false, configValidate: false, configDiff: false, notifyTest: false, runDoctor: false, runInit: false, initForce: false, runTaskCli: false };
|
|
303
303
|
// check for subcommand as first non-flag arg
|
|
304
304
|
if (argv[2] === "test-context") {
|
|
305
305
|
return { ...defaults, testContext: true };
|
|
@@ -327,6 +327,9 @@ export function parseCliArgs(argv) {
|
|
|
327
327
|
if (argv[2] === "notify-test") {
|
|
328
328
|
return { ...defaults, notifyTest: true };
|
|
329
329
|
}
|
|
330
|
+
if (argv[2] === "doctor") {
|
|
331
|
+
return { ...defaults, runDoctor: true };
|
|
332
|
+
}
|
|
330
333
|
if (argv[2] === "init") {
|
|
331
334
|
const force = argv.includes("--force") || argv.includes("-f");
|
|
332
335
|
return { ...defaults, runInit: true, initForce: force };
|
|
@@ -416,7 +419,7 @@ export function parseCliArgs(argv) {
|
|
|
416
419
|
break;
|
|
417
420
|
}
|
|
418
421
|
}
|
|
419
|
-
return { overrides, help, version, register: false, testContext: false, runTest: false, showTasks: false, showHistory: false, showStatus: false, showConfig: false, configValidate: false, configDiff: false, notifyTest: false, runInit: false, initForce: false, runTaskCli: false };
|
|
422
|
+
return { overrides, help, version, register: false, testContext: false, runTest: false, showTasks: false, showHistory: false, showStatus: false, showConfig: false, configValidate: false, configDiff: false, notifyTest: false, runDoctor: false, runInit: false, initForce: false, runTaskCli: false };
|
|
420
423
|
}
|
|
421
424
|
export function printHelp() {
|
|
422
425
|
console.log(`aoaoe - autonomous supervisor for agent-of-empires sessions
|
|
@@ -437,6 +440,7 @@ commands:
|
|
|
437
440
|
config --validate validate config + check tool availability
|
|
438
441
|
config --diff show only fields that differ from defaults
|
|
439
442
|
notify-test send a test notification to configured webhooks
|
|
443
|
+
doctor comprehensive health check (config, tools, daemon, disk)
|
|
440
444
|
task manage tasks and sessions (list, start, stop, new, rm, edit)
|
|
441
445
|
tasks show task progress (from aoaoe.tasks.json)
|
|
442
446
|
history review recent actions (from ~/.aoaoe/actions.log)
|
package/dist/index.js
CHANGED
|
@@ -28,7 +28,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
28
28
|
const AOAOE_DIR = join(homedir(), ".aoaoe"); // watch dir for wakeable sleep
|
|
29
29
|
const INPUT_FILE = join(AOAOE_DIR, "pending-input.txt"); // file IPC from chat.ts
|
|
30
30
|
async function main() {
|
|
31
|
-
const { overrides, help, version, register, testContext: isTestContext, runTest, showTasks, showHistory, showStatus, showConfig, configValidate, configDiff, notifyTest, runInit, initForce, runTaskCli: isTaskCli, registerTitle } = parseCliArgs(process.argv);
|
|
31
|
+
const { overrides, help, version, register, testContext: isTestContext, runTest, showTasks, showHistory, showStatus, showConfig, configValidate, configDiff, notifyTest, runDoctor, runInit, initForce, runTaskCli: isTaskCli, registerTitle } = parseCliArgs(process.argv);
|
|
32
32
|
if (help) {
|
|
33
33
|
printHelp();
|
|
34
34
|
process.exit(0);
|
|
@@ -91,6 +91,11 @@ async function main() {
|
|
|
91
91
|
await runNotifyTest();
|
|
92
92
|
return;
|
|
93
93
|
}
|
|
94
|
+
// `aoaoe doctor` -- comprehensive health check
|
|
95
|
+
if (runDoctor) {
|
|
96
|
+
await runDoctorCheck();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
94
99
|
// `aoaoe task` -- task management CLI
|
|
95
100
|
if (isTaskCli) {
|
|
96
101
|
await runTaskCli(process.argv);
|
|
@@ -1402,6 +1407,204 @@ async function runConfigValidation() {
|
|
|
1402
1407
|
if (failed > 0)
|
|
1403
1408
|
process.exit(1);
|
|
1404
1409
|
}
|
|
1410
|
+
// `aoaoe doctor` -- comprehensive health check: config, tools, daemon, disk, sessions
|
|
1411
|
+
async function runDoctorCheck() {
|
|
1412
|
+
const pkg = readPkgVersion();
|
|
1413
|
+
let checks = 0;
|
|
1414
|
+
let passed = 0;
|
|
1415
|
+
let warnings = 0;
|
|
1416
|
+
console.log("");
|
|
1417
|
+
console.log(` aoaoe${pkg ? ` v${pkg}` : ""} — doctor`);
|
|
1418
|
+
console.log(` ${"─".repeat(50)}`);
|
|
1419
|
+
// ── 1. config ──────────────────────────────────────────────────────────
|
|
1420
|
+
console.log(`\n ${BOLD}config${RESET}`);
|
|
1421
|
+
const configPath = findConfigFile();
|
|
1422
|
+
checks++;
|
|
1423
|
+
if (configPath) {
|
|
1424
|
+
console.log(` ${GREEN}✓${RESET} config file: ${configPath}`);
|
|
1425
|
+
passed++;
|
|
1426
|
+
}
|
|
1427
|
+
else {
|
|
1428
|
+
console.log(` ${YELLOW}!${RESET} no config file (using defaults)`);
|
|
1429
|
+
warnings++;
|
|
1430
|
+
}
|
|
1431
|
+
let config;
|
|
1432
|
+
checks++;
|
|
1433
|
+
try {
|
|
1434
|
+
config = loadConfig();
|
|
1435
|
+
console.log(` ${GREEN}✓${RESET} config validates OK`);
|
|
1436
|
+
passed++;
|
|
1437
|
+
}
|
|
1438
|
+
catch (err) {
|
|
1439
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1440
|
+
console.log(` ${RED}✗${RESET} config invalid: ${msg.split("\n")[0]}`);
|
|
1441
|
+
// use defaults to continue checking other things
|
|
1442
|
+
config = loadConfig({});
|
|
1443
|
+
}
|
|
1444
|
+
// ── 2. tools ───────────────────────────────────────────────────────────
|
|
1445
|
+
console.log(`\n ${BOLD}tools${RESET}`);
|
|
1446
|
+
const toolChecks = [
|
|
1447
|
+
{ cmd: "node", label: "Node.js", versionArg: ["--version"], required: true },
|
|
1448
|
+
{ cmd: "aoe", label: "agent-of-empires", versionArg: ["--version"], required: true },
|
|
1449
|
+
{ cmd: "tmux", label: "terminal multiplexer", versionArg: ["-V"], required: true },
|
|
1450
|
+
];
|
|
1451
|
+
if (config.reasoner === "opencode") {
|
|
1452
|
+
toolChecks.push({ cmd: "opencode", label: "OpenCode CLI", versionArg: ["version"], required: true });
|
|
1453
|
+
}
|
|
1454
|
+
else {
|
|
1455
|
+
toolChecks.push({ cmd: "claude", label: "Claude Code CLI", versionArg: ["--version"], required: true });
|
|
1456
|
+
}
|
|
1457
|
+
for (const tool of toolChecks) {
|
|
1458
|
+
checks++;
|
|
1459
|
+
try {
|
|
1460
|
+
const result = await shellExec(tool.cmd, tool.versionArg);
|
|
1461
|
+
const ver = result.stdout.trim().split("\n")[0].slice(0, 60) || result.stderr.trim().split("\n")[0].slice(0, 60);
|
|
1462
|
+
console.log(` ${GREEN}✓${RESET} ${tool.cmd} — ${ver}`);
|
|
1463
|
+
passed++;
|
|
1464
|
+
}
|
|
1465
|
+
catch {
|
|
1466
|
+
if (tool.required) {
|
|
1467
|
+
console.log(` ${RED}✗${RESET} ${tool.cmd} not found (${tool.label})`);
|
|
1468
|
+
}
|
|
1469
|
+
else {
|
|
1470
|
+
console.log(` ${YELLOW}!${RESET} ${tool.cmd} not found (${tool.label}, optional)`);
|
|
1471
|
+
warnings++;
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
// ── 3. reasoner server ─────────────────────────────────────────────────
|
|
1476
|
+
if (config.reasoner === "opencode") {
|
|
1477
|
+
console.log(`\n ${BOLD}reasoner${RESET}`);
|
|
1478
|
+
checks++;
|
|
1479
|
+
try {
|
|
1480
|
+
const resp = await fetch(`http://127.0.0.1:${config.opencode.port}/health`, {
|
|
1481
|
+
signal: AbortSignal.timeout(3000),
|
|
1482
|
+
});
|
|
1483
|
+
if (resp.ok) {
|
|
1484
|
+
console.log(` ${GREEN}✓${RESET} opencode serve responding on port ${config.opencode.port}`);
|
|
1485
|
+
passed++;
|
|
1486
|
+
}
|
|
1487
|
+
else {
|
|
1488
|
+
console.log(` ${YELLOW}!${RESET} opencode serve on port ${config.opencode.port} returned ${resp.status}`);
|
|
1489
|
+
warnings++;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
catch {
|
|
1493
|
+
console.log(` ${RED}✗${RESET} opencode serve not responding on port ${config.opencode.port}`);
|
|
1494
|
+
console.log(` ${DIM}start with: opencode serve --port ${config.opencode.port}${RESET}`);
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
// ── 4. daemon ──────────────────────────────────────────────────────────
|
|
1498
|
+
console.log(`\n ${BOLD}daemon${RESET}`);
|
|
1499
|
+
const state = readState();
|
|
1500
|
+
const daemonRunning = isDaemonRunningFromState(state);
|
|
1501
|
+
checks++;
|
|
1502
|
+
if (daemonRunning && state) {
|
|
1503
|
+
console.log(` ${GREEN}✓${RESET} daemon running (poll #${state.pollCount}, phase: ${state.phase})`);
|
|
1504
|
+
console.log(` ${state.sessions.length} session(s) monitored`);
|
|
1505
|
+
passed++;
|
|
1506
|
+
}
|
|
1507
|
+
else {
|
|
1508
|
+
console.log(` ${DIM}○${RESET} daemon not running`);
|
|
1509
|
+
passed++; // not running is fine for doctor — just informational
|
|
1510
|
+
}
|
|
1511
|
+
// lock file check
|
|
1512
|
+
const lockPath = join(homedir(), ".aoaoe", "daemon.lock");
|
|
1513
|
+
if (existsSync(lockPath) && !daemonRunning) {
|
|
1514
|
+
checks++;
|
|
1515
|
+
console.log(` ${YELLOW}!${RESET} stale lock file found: ${lockPath}`);
|
|
1516
|
+
console.log(` ${DIM}remove with: rm ${lockPath}${RESET}`);
|
|
1517
|
+
warnings++;
|
|
1518
|
+
}
|
|
1519
|
+
// ── 5. disk / data ─────────────────────────────────────────────────────
|
|
1520
|
+
console.log(`\n ${BOLD}data${RESET}`);
|
|
1521
|
+
const aoaoeDir = join(homedir(), ".aoaoe");
|
|
1522
|
+
if (existsSync(aoaoeDir)) {
|
|
1523
|
+
checks++;
|
|
1524
|
+
try {
|
|
1525
|
+
const files = await import("node:fs").then(fs => fs.readdirSync(aoaoeDir));
|
|
1526
|
+
let totalSize = 0;
|
|
1527
|
+
for (const f of files) {
|
|
1528
|
+
try {
|
|
1529
|
+
totalSize += statSync(join(aoaoeDir, f)).size;
|
|
1530
|
+
}
|
|
1531
|
+
catch { /* skip unreadable */ }
|
|
1532
|
+
}
|
|
1533
|
+
const sizeStr = totalSize < 1024 ? `${totalSize}B` :
|
|
1534
|
+
totalSize < 1_048_576 ? `${(totalSize / 1024).toFixed(1)}KB` :
|
|
1535
|
+
`${(totalSize / 1_048_576).toFixed(1)}MB`;
|
|
1536
|
+
console.log(` ${GREEN}✓${RESET} ~/.aoaoe/ — ${files.length} files, ${sizeStr}`);
|
|
1537
|
+
passed++;
|
|
1538
|
+
}
|
|
1539
|
+
catch {
|
|
1540
|
+
console.log(` ${YELLOW}!${RESET} could not read ~/.aoaoe/`);
|
|
1541
|
+
warnings++;
|
|
1542
|
+
}
|
|
1543
|
+
// actions log stats
|
|
1544
|
+
const actionsPath = join(aoaoeDir, "actions.log");
|
|
1545
|
+
if (existsSync(actionsPath)) {
|
|
1546
|
+
checks++;
|
|
1547
|
+
try {
|
|
1548
|
+
const content = readFileSync(actionsPath, "utf-8").trim();
|
|
1549
|
+
const lineCount = content ? content.split("\n").length : 0;
|
|
1550
|
+
const size = statSync(actionsPath).size;
|
|
1551
|
+
const sizeStr = size < 1024 ? `${size}B` : `${(size / 1024).toFixed(1)}KB`;
|
|
1552
|
+
console.log(` ${GREEN}✓${RESET} actions.log — ${lineCount} entries, ${sizeStr}`);
|
|
1553
|
+
passed++;
|
|
1554
|
+
}
|
|
1555
|
+
catch {
|
|
1556
|
+
console.log(` ${YELLOW}!${RESET} actions.log unreadable`);
|
|
1557
|
+
warnings++;
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
else {
|
|
1562
|
+
console.log(` ${DIM}○${RESET} ~/.aoaoe/ does not exist yet (run 'aoaoe init')`);
|
|
1563
|
+
}
|
|
1564
|
+
// ── 6. aoe sessions ───────────────────────────────────────────────────
|
|
1565
|
+
console.log(`\n ${BOLD}sessions${RESET}`);
|
|
1566
|
+
checks++;
|
|
1567
|
+
try {
|
|
1568
|
+
const listResult = await shellExec("aoe", ["list", "--json"]);
|
|
1569
|
+
if (listResult.exitCode === 0 && listResult.stdout.trim()) {
|
|
1570
|
+
const sessions = JSON.parse(listResult.stdout);
|
|
1571
|
+
if (Array.isArray(sessions) && sessions.length > 0) {
|
|
1572
|
+
console.log(` ${GREEN}✓${RESET} ${sessions.length} aoe session(s) found`);
|
|
1573
|
+
for (const s of sessions.slice(0, 5)) {
|
|
1574
|
+
console.log(` ${DIM}${s.title ?? s.id} (${s.tool ?? "?"})${RESET}`);
|
|
1575
|
+
}
|
|
1576
|
+
if (sessions.length > 5)
|
|
1577
|
+
console.log(` ${DIM}...and ${sessions.length - 5} more${RESET}`);
|
|
1578
|
+
passed++;
|
|
1579
|
+
}
|
|
1580
|
+
else {
|
|
1581
|
+
console.log(` ${DIM}○${RESET} no aoe sessions (start some with 'aoe add')`);
|
|
1582
|
+
passed++;
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
else {
|
|
1586
|
+
console.log(` ${YELLOW}!${RESET} aoe list returned non-zero (is aoe running?)`);
|
|
1587
|
+
warnings++;
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
catch {
|
|
1591
|
+
console.log(` ${RED}✗${RESET} could not run 'aoe list --json'`);
|
|
1592
|
+
}
|
|
1593
|
+
// ── summary ────────────────────────────────────────────────────────────
|
|
1594
|
+
const failed = checks - passed - warnings;
|
|
1595
|
+
console.log("");
|
|
1596
|
+
console.log(` ${"─".repeat(50)}`);
|
|
1597
|
+
if (failed === 0 && warnings === 0) {
|
|
1598
|
+
console.log(` ${GREEN}${BOLD}all ${checks} checks passed${RESET} — looking healthy`);
|
|
1599
|
+
}
|
|
1600
|
+
else if (failed === 0) {
|
|
1601
|
+
console.log(` ${passed}/${checks} passed, ${YELLOW}${warnings} warning(s)${RESET}`);
|
|
1602
|
+
}
|
|
1603
|
+
else {
|
|
1604
|
+
console.log(` ${passed}/${checks} passed, ${RED}${failed} failed${RESET}${warnings > 0 ? `, ${YELLOW}${warnings} warning(s)${RESET}` : ""}`);
|
|
1605
|
+
}
|
|
1606
|
+
console.log("");
|
|
1607
|
+
}
|
|
1405
1608
|
// `aoaoe config --diff` -- show only fields that differ from defaults
|
|
1406
1609
|
function showConfigDiff() {
|
|
1407
1610
|
const configPath = findConfigFile();
|