aoaoe 0.53.0 → 0.54.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 +5 -3
- package/dist/index.js +137 -8
- package/dist/types.d.ts +12 -0
- package/dist/types.js +27 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -240,6 +240,7 @@ commands:
|
|
|
240
240
|
init detect tools + sessions, import history, generate config
|
|
241
241
|
status quick daemon health check (is it running? what's it doing?)
|
|
242
242
|
config show the effective resolved config (defaults + file)
|
|
243
|
+
config --validate validate config + check tool availability
|
|
243
244
|
notify-test send a test notification to configured webhooks
|
|
244
245
|
task manage tasks and sessions (list, start, stop, new, rm, edit)
|
|
245
246
|
tasks show task progress (from aoaoe.tasks.json)
|
package/dist/config.d.ts
CHANGED
package/dist/config.js
CHANGED
|
@@ -274,7 +274,7 @@ export function parseCliArgs(argv) {
|
|
|
274
274
|
let initForce = false;
|
|
275
275
|
let runTaskCli = false;
|
|
276
276
|
let registerTitle;
|
|
277
|
-
const defaults = { overrides, help: false, version: false, register: false, testContext: false, runTest: false, showTasks: false, showHistory: false, showStatus: false, showConfig: false, notifyTest: false, runInit: false, initForce: false, runTaskCli: false };
|
|
277
|
+
const defaults = { overrides, help: false, version: false, register: false, testContext: false, runTest: false, showTasks: false, showHistory: false, showStatus: false, showConfig: false, configValidate: false, notifyTest: false, runInit: false, initForce: false, runTaskCli: false };
|
|
278
278
|
// check for subcommand as first non-flag arg
|
|
279
279
|
if (argv[2] === "test-context") {
|
|
280
280
|
return { ...defaults, testContext: true };
|
|
@@ -295,7 +295,8 @@ export function parseCliArgs(argv) {
|
|
|
295
295
|
return { ...defaults, showStatus: true };
|
|
296
296
|
}
|
|
297
297
|
if (argv[2] === "config") {
|
|
298
|
-
|
|
298
|
+
const validate = argv.includes("--validate") || argv.includes("-V");
|
|
299
|
+
return { ...defaults, showConfig: true, configValidate: validate };
|
|
299
300
|
}
|
|
300
301
|
if (argv[2] === "notify-test") {
|
|
301
302
|
return { ...defaults, notifyTest: true };
|
|
@@ -389,7 +390,7 @@ export function parseCliArgs(argv) {
|
|
|
389
390
|
break;
|
|
390
391
|
}
|
|
391
392
|
}
|
|
392
|
-
return { overrides, help, version, register: false, testContext: false, runTest: false, showTasks: false, showHistory: false, showStatus: false, showConfig: false, notifyTest: false, runInit: false, initForce: false, runTaskCli: false };
|
|
393
|
+
return { overrides, help, version, register: false, testContext: false, runTest: false, showTasks: false, showHistory: false, showStatus: false, showConfig: false, configValidate: false, notifyTest: false, runInit: false, initForce: false, runTaskCli: false };
|
|
393
394
|
}
|
|
394
395
|
export function printHelp() {
|
|
395
396
|
console.log(`aoaoe - autonomous supervisor for agent-of-empires sessions
|
|
@@ -407,6 +408,7 @@ commands:
|
|
|
407
408
|
(none) start the supervisor daemon (interactive TUI)
|
|
408
409
|
status quick daemon health check (is it running? what's it doing?)
|
|
409
410
|
config show the effective resolved config (defaults + file)
|
|
411
|
+
config --validate validate config file + check tool availability
|
|
410
412
|
notify-test send a test notification to configured webhooks
|
|
411
413
|
task manage tasks and sessions (list, start, stop, new, rm, edit)
|
|
412
414
|
tasks show task progress (from aoaoe.tasks.json)
|
package/dist/index.js
CHANGED
|
@@ -18,7 +18,7 @@ import { runTaskCli, handleTaskSlashCommand } from "./task-cli.js";
|
|
|
18
18
|
import { TUI } from "./tui.js";
|
|
19
19
|
import { isDaemonRunningFromState } from "./chat.js";
|
|
20
20
|
import { sendNotification, sendTestNotification } from "./notify.js";
|
|
21
|
-
import { actionSession, actionDetail } from "./types.js";
|
|
21
|
+
import { actionSession, actionDetail, toActionLogEntry } from "./types.js";
|
|
22
22
|
import { YELLOW, GREEN, DIM, BOLD, RED, RESET } from "./colors.js";
|
|
23
23
|
import { readFileSync, existsSync, statSync, mkdirSync, writeFileSync, chmodSync } from "node:fs";
|
|
24
24
|
import { resolve, dirname, join } from "node:path";
|
|
@@ -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, notifyTest, runInit, initForce, runTaskCli: isTaskCli, registerTitle } = parseCliArgs(process.argv);
|
|
31
|
+
const { overrides, help, version, register, testContext: isTestContext, runTest, showTasks, showHistory, showStatus, showConfig, configValidate, notifyTest, runInit, initForce, runTaskCli: isTaskCli, registerTitle } = parseCliArgs(process.argv);
|
|
32
32
|
if (help) {
|
|
33
33
|
printHelp();
|
|
34
34
|
process.exit(0);
|
|
@@ -73,9 +73,14 @@ async function main() {
|
|
|
73
73
|
showDaemonStatus();
|
|
74
74
|
return;
|
|
75
75
|
}
|
|
76
|
-
// `aoaoe config` -- show effective resolved config
|
|
76
|
+
// `aoaoe config` -- show effective resolved config (with optional --validate)
|
|
77
77
|
if (showConfig) {
|
|
78
|
-
|
|
78
|
+
if (configValidate) {
|
|
79
|
+
await runConfigValidation();
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
showEffectiveConfig();
|
|
83
|
+
}
|
|
79
84
|
return;
|
|
80
85
|
}
|
|
81
86
|
// `aoaoe notify-test` -- send a test notification to configured webhooks
|
|
@@ -1136,7 +1141,9 @@ async function showActionHistory() {
|
|
|
1136
1141
|
console.log(` ${"─".repeat(70)}`);
|
|
1137
1142
|
for (const line of recent) {
|
|
1138
1143
|
try {
|
|
1139
|
-
const entry = JSON.parse(line);
|
|
1144
|
+
const entry = toActionLogEntry(JSON.parse(line));
|
|
1145
|
+
if (!entry)
|
|
1146
|
+
continue; // skip malformed lines
|
|
1140
1147
|
const time = new Date(entry.timestamp).toLocaleTimeString();
|
|
1141
1148
|
const date = new Date(entry.timestamp).toLocaleDateString();
|
|
1142
1149
|
const icon = entry.success ? `${GREEN}+${RESET}` : `${RED}!${RESET}`;
|
|
@@ -1146,7 +1153,7 @@ async function showActionHistory() {
|
|
|
1146
1153
|
console.log(` ${icon} ${DIM}${date} ${time}${RESET} ${YELLOW}${actionName.padEnd(16)}${RESET} ${session.padEnd(10)} ${detail}`);
|
|
1147
1154
|
}
|
|
1148
1155
|
catch {
|
|
1149
|
-
// skip
|
|
1156
|
+
// skip unparseable JSON lines
|
|
1150
1157
|
}
|
|
1151
1158
|
}
|
|
1152
1159
|
console.log(` ${"─".repeat(70)}`);
|
|
@@ -1155,14 +1162,18 @@ async function showActionHistory() {
|
|
|
1155
1162
|
const actionCounts = new Map();
|
|
1156
1163
|
for (const line of lines) {
|
|
1157
1164
|
try {
|
|
1158
|
-
const e = JSON.parse(line);
|
|
1165
|
+
const e = toActionLogEntry(JSON.parse(line));
|
|
1166
|
+
if (!e)
|
|
1167
|
+
continue;
|
|
1159
1168
|
if (e.success)
|
|
1160
1169
|
successes++;
|
|
1161
1170
|
else
|
|
1162
1171
|
failures++;
|
|
1163
1172
|
actionCounts.set(e.action.action, (actionCounts.get(e.action.action) ?? 0) + 1);
|
|
1164
1173
|
}
|
|
1165
|
-
catch {
|
|
1174
|
+
catch {
|
|
1175
|
+
// skip unparseable JSON lines
|
|
1176
|
+
}
|
|
1166
1177
|
}
|
|
1167
1178
|
const breakdown = [...actionCounts.entries()].sort((a, b) => b[1] - a[1]).map(([k, v]) => `${k}: ${v}`).join(", ");
|
|
1168
1179
|
console.log(` total: ${lines.length} actions (${GREEN}${successes} ok${RESET}, ${RED}${failures} failed${RESET})`);
|
|
@@ -1234,6 +1245,124 @@ function showDaemonStatus() {
|
|
|
1234
1245
|
}
|
|
1235
1246
|
console.log("");
|
|
1236
1247
|
}
|
|
1248
|
+
// `aoaoe config --validate` -- validate config file, field values, and tool availability
|
|
1249
|
+
async function runConfigValidation() {
|
|
1250
|
+
const configPath = findConfigFile();
|
|
1251
|
+
let checks = 0;
|
|
1252
|
+
let passed = 0;
|
|
1253
|
+
let warnings = 0;
|
|
1254
|
+
console.log("");
|
|
1255
|
+
console.log(" aoaoe — config validation");
|
|
1256
|
+
console.log(` ${"─".repeat(50)}`);
|
|
1257
|
+
// 1. config file exists
|
|
1258
|
+
checks++;
|
|
1259
|
+
if (configPath) {
|
|
1260
|
+
console.log(` ${GREEN}✓${RESET} config file found: ${configPath}`);
|
|
1261
|
+
passed++;
|
|
1262
|
+
}
|
|
1263
|
+
else {
|
|
1264
|
+
console.log(` ${YELLOW}!${RESET} no config file found (using defaults)`);
|
|
1265
|
+
console.log(` ${DIM}run 'aoaoe init' to create one${RESET}`);
|
|
1266
|
+
warnings++;
|
|
1267
|
+
}
|
|
1268
|
+
// 2. config parses + validates
|
|
1269
|
+
checks++;
|
|
1270
|
+
let config;
|
|
1271
|
+
try {
|
|
1272
|
+
const configResult = loadConfig();
|
|
1273
|
+
config = configResult;
|
|
1274
|
+
console.log(` ${GREEN}✓${RESET} config valid (all field values OK)`);
|
|
1275
|
+
passed++;
|
|
1276
|
+
}
|
|
1277
|
+
catch (err) {
|
|
1278
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1279
|
+
console.log(` ${RED}✗${RESET} config validation failed:`);
|
|
1280
|
+
for (const line of msg.split("\n")) {
|
|
1281
|
+
console.log(` ${line}`);
|
|
1282
|
+
}
|
|
1283
|
+
console.log("");
|
|
1284
|
+
console.log(` ${passed}/${checks} checks passed, fix config errors and retry`);
|
|
1285
|
+
console.log("");
|
|
1286
|
+
process.exit(1);
|
|
1287
|
+
return; // unreachable, but satisfies TypeScript
|
|
1288
|
+
}
|
|
1289
|
+
// 3. required tools on PATH
|
|
1290
|
+
const tools = [
|
|
1291
|
+
{ name: "aoe", label: "agent-of-empires CLI" },
|
|
1292
|
+
{ name: "tmux", label: "terminal multiplexer" },
|
|
1293
|
+
];
|
|
1294
|
+
if (config.reasoner === "opencode") {
|
|
1295
|
+
tools.push({ name: "opencode", label: "OpenCode CLI" });
|
|
1296
|
+
}
|
|
1297
|
+
else if (config.reasoner === "claude-code") {
|
|
1298
|
+
tools.push({ name: "claude", label: "Claude Code CLI" });
|
|
1299
|
+
}
|
|
1300
|
+
for (const tool of tools) {
|
|
1301
|
+
checks++;
|
|
1302
|
+
try {
|
|
1303
|
+
const { execFile: execFileCb } = await import("node:child_process");
|
|
1304
|
+
const { promisify } = await import("node:util");
|
|
1305
|
+
const execFileAsync = promisify(execFileCb);
|
|
1306
|
+
await execFileAsync("which", [tool.name]);
|
|
1307
|
+
console.log(` ${GREEN}✓${RESET} ${tool.name} found on PATH (${tool.label})`);
|
|
1308
|
+
passed++;
|
|
1309
|
+
}
|
|
1310
|
+
catch {
|
|
1311
|
+
console.log(` ${RED}✗${RESET} ${tool.name} not found on PATH (${tool.label})`);
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
// 4. notifications config check
|
|
1315
|
+
checks++;
|
|
1316
|
+
if (config.notifications) {
|
|
1317
|
+
const hasWebhook = !!config.notifications.webhookUrl;
|
|
1318
|
+
const hasSlack = !!config.notifications.slackWebhookUrl;
|
|
1319
|
+
if (hasWebhook || hasSlack) {
|
|
1320
|
+
const targets = [hasWebhook && "webhook", hasSlack && "Slack"].filter(Boolean).join(" + ");
|
|
1321
|
+
console.log(` ${GREEN}✓${RESET} notifications configured (${targets})`);
|
|
1322
|
+
console.log(` ${DIM}run 'aoaoe notify-test' to verify delivery${RESET}`);
|
|
1323
|
+
passed++;
|
|
1324
|
+
}
|
|
1325
|
+
else {
|
|
1326
|
+
console.log(` ${YELLOW}!${RESET} notifications block exists but no webhook URLs configured`);
|
|
1327
|
+
warnings++;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
else {
|
|
1331
|
+
console.log(` ${DIM}○${RESET} notifications not configured (optional)`);
|
|
1332
|
+
passed++; // not configured is fine — it's optional
|
|
1333
|
+
}
|
|
1334
|
+
// 5. sessionDirs validation — check that mapped dirs exist
|
|
1335
|
+
if (config.sessionDirs && Object.keys(config.sessionDirs).length > 0) {
|
|
1336
|
+
const basePath = process.cwd();
|
|
1337
|
+
for (const [title, dir] of Object.entries(config.sessionDirs)) {
|
|
1338
|
+
checks++;
|
|
1339
|
+
const resolved = dir.startsWith("/") ? dir : resolve(basePath, dir);
|
|
1340
|
+
if (existsSync(resolved)) {
|
|
1341
|
+
console.log(` ${GREEN}✓${RESET} sessionDirs.${title} → ${resolved}`);
|
|
1342
|
+
passed++;
|
|
1343
|
+
}
|
|
1344
|
+
else {
|
|
1345
|
+
console.log(` ${YELLOW}!${RESET} sessionDirs.${title} → ${resolved} (not found)`);
|
|
1346
|
+
warnings++;
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
// summary
|
|
1351
|
+
const failed = checks - passed - warnings;
|
|
1352
|
+
console.log("");
|
|
1353
|
+
if (failed === 0 && warnings === 0) {
|
|
1354
|
+
console.log(` ${GREEN}${BOLD}all ${checks} checks passed${RESET}`);
|
|
1355
|
+
}
|
|
1356
|
+
else if (failed === 0) {
|
|
1357
|
+
console.log(` ${passed}/${checks} passed, ${YELLOW}${warnings} warning(s)${RESET}`);
|
|
1358
|
+
}
|
|
1359
|
+
else {
|
|
1360
|
+
console.log(` ${passed}/${checks} passed, ${RED}${failed} failed${RESET}${warnings > 0 ? `, ${YELLOW}${warnings} warning(s)${RESET}` : ""}`);
|
|
1361
|
+
}
|
|
1362
|
+
console.log("");
|
|
1363
|
+
if (failed > 0)
|
|
1364
|
+
process.exit(1);
|
|
1365
|
+
}
|
|
1237
1366
|
// `aoaoe config` -- show the effective resolved config (defaults + file + any notes)
|
|
1238
1367
|
function showEffectiveConfig() {
|
|
1239
1368
|
const configPath = findConfigFile();
|
package/dist/types.d.ts
CHANGED
|
@@ -175,4 +175,16 @@ export declare function toAoeSessionList(raw: unknown): Array<{
|
|
|
175
175
|
title: string;
|
|
176
176
|
}>;
|
|
177
177
|
export declare function toReasonerBackend(raw: string): ReasonerBackend;
|
|
178
|
+
export interface ActionLogEntry {
|
|
179
|
+
timestamp: number;
|
|
180
|
+
action: {
|
|
181
|
+
action: string;
|
|
182
|
+
session?: string;
|
|
183
|
+
text?: string;
|
|
184
|
+
title?: string;
|
|
185
|
+
};
|
|
186
|
+
success: boolean;
|
|
187
|
+
detail: string;
|
|
188
|
+
}
|
|
189
|
+
export declare function toActionLogEntry(raw: unknown): ActionLogEntry | null;
|
|
178
190
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.js
CHANGED
|
@@ -112,4 +112,31 @@ export function toReasonerBackend(raw) {
|
|
|
112
112
|
return raw;
|
|
113
113
|
throw new Error(`--reasoner must be "opencode" or "claude-code", got "${raw}"`);
|
|
114
114
|
}
|
|
115
|
+
export function toActionLogEntry(raw) {
|
|
116
|
+
if (!raw || typeof raw !== "object")
|
|
117
|
+
return null;
|
|
118
|
+
const obj = raw;
|
|
119
|
+
if (typeof obj.timestamp !== "number")
|
|
120
|
+
return null;
|
|
121
|
+
if (typeof obj.success !== "boolean")
|
|
122
|
+
return null;
|
|
123
|
+
if (typeof obj.detail !== "string")
|
|
124
|
+
obj.detail = "";
|
|
125
|
+
if (!obj.action || typeof obj.action !== "object")
|
|
126
|
+
return null;
|
|
127
|
+
const action = obj.action;
|
|
128
|
+
if (typeof action.action !== "string")
|
|
129
|
+
return null;
|
|
130
|
+
return {
|
|
131
|
+
timestamp: obj.timestamp,
|
|
132
|
+
action: {
|
|
133
|
+
action: action.action,
|
|
134
|
+
session: typeof action.session === "string" ? action.session : undefined,
|
|
135
|
+
text: typeof action.text === "string" ? action.text : undefined,
|
|
136
|
+
title: typeof action.title === "string" ? action.title : undefined,
|
|
137
|
+
},
|
|
138
|
+
success: obj.success,
|
|
139
|
+
detail: typeof obj.detail === "string" ? obj.detail : "",
|
|
140
|
+
};
|
|
141
|
+
}
|
|
115
142
|
//# sourceMappingURL=types.js.map
|