clawmatrix 0.5.1 → 0.6.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/cli/bin/clawmatrix.mjs +411 -12
- package/cli/skills/clawmatrix/SKILL.md +49 -0
- package/package.json +1 -1
- package/src/api.ts +476 -3
- package/src/automation.ts +90 -1
- package/src/cluster-service.ts +24 -9
- package/src/config.ts +22 -16
- package/src/connection.ts +10 -0
- package/src/device-info.ts +10 -0
- package/src/health-tracker.ts +25 -5
- package/src/index.ts +285 -0
- package/src/knowledge-sync.ts +7 -0
- package/src/peer-manager.ts +41 -22
- package/src/router.ts +21 -3
- package/src/types.ts +1 -0
package/cli/bin/clawmatrix.mjs
CHANGED
|
@@ -645,27 +645,53 @@ async function cmdApprovalRevoke(nodeId) {
|
|
|
645
645
|
|
|
646
646
|
function cmdHelpJson() {
|
|
647
647
|
const commands = [
|
|
648
|
+
// Cluster
|
|
648
649
|
{ name: "status", args: "", options: ["--json"], description: "Show cluster topology and peer status" },
|
|
649
650
|
{ name: "peers", args: "", options: [], description: "List known peers (JSON)" },
|
|
650
651
|
{ name: "check", args: "<nodeId>", options: [], description: "Check if a specific node is reachable" },
|
|
652
|
+
{ name: "config", args: "", options: ["--json"], description: "Show local node configuration" },
|
|
653
|
+
{ name: "availability", args: "[range]", options: ["--json"], description: "Show node uptime (24h|7d|90d)" },
|
|
654
|
+
// Tools & Execution
|
|
651
655
|
{ name: "tools", args: "[node]", options: ["--json", "-v", "-f <keyword>", "-d <tool>"], description: "List available tools on remote nodes" },
|
|
652
656
|
{ name: "call", args: "<node> <tool> [json]", options: ["-t <ms>"], description: "Invoke a tool on a remote node" },
|
|
653
657
|
{ name: "batch", args: "<node> [json]", options: ["--no-stop-on-error", "-t <ms>"], description: "Invoke multiple tools in sequence" },
|
|
654
658
|
{ name: "models", args: "", options: ["--json", "-n <nodeId>"], description: "List all cluster models" },
|
|
655
|
-
|
|
659
|
+
// Agents
|
|
656
660
|
{ name: "send", args: "<node> <message>", options: [], description: "Send a message to a remote node" },
|
|
657
661
|
{ name: "handoff", args: "<task>", options: ["--agent <agent>", "--node <node>", "-t <ms>"], description: "Delegate a task to a remote agent" },
|
|
658
|
-
{ name: "acp", args: "<action>", options: ["-n <node>", "-a <agent>", "--session <id>", "--mode <mode>", "--cwd <dir>"], description: "Manage ACP agent sessions" },
|
|
662
|
+
{ name: "acp", args: "<action>", options: ["-n <node>", "-a <agent>", "--session <id>", "--mode <mode>", "--cwd <dir>"], description: "Manage ACP agent sessions (list|prompt|resume|cancel|close)" },
|
|
663
|
+
// Events & Automations
|
|
664
|
+
{ name: "events", args: "", options: ["--json", "-t <type>", "-s <source>", "-l <n>", "-a", "--consume <ids>"], description: "Query and consume events" },
|
|
665
|
+
{ name: "events ingest", args: "<json>", options: [], description: "Ingest events from external sources" },
|
|
666
|
+
{ name: "automations rules", args: "", options: ["--json"], description: "List automation rules" },
|
|
667
|
+
{ name: "automations history", args: "", options: ["--json", "--limit <n>"], description: "Show automation execution history" },
|
|
668
|
+
{ name: "automations run", args: "<ruleId>", options: ["--json"], description: "Manually trigger an automation rule" },
|
|
669
|
+
{ name: "automations replay", args: "<executionId>", options: ["--json"], description: "Re-run a historical execution" },
|
|
670
|
+
{ name: "automations save", args: "<json-file|->", options: [], description: "Save automation rules from JSON" },
|
|
671
|
+
// Infrastructure
|
|
659
672
|
{ name: "diagnostic", args: "<node>", options: ["--action status|exec", "-c <command>", "-t <seconds>"], description: "Diagnose a remote node via sentinel" },
|
|
660
673
|
{ name: "notify", args: "<title>", options: ["--detail <text>", "--progress <0-100>", "--action start|update|end", "--task-id <id>", "--tool <name>"], description: "Push notification to mobile devices" },
|
|
661
674
|
{ name: "terminal", args: "<node>", options: ["--shell <shell>", "--cwd <dir>", "--action list|close|read|input", "--session <id>"], description: "Open/manage remote terminal sessions" },
|
|
662
675
|
{ name: "transfer", args: "<node> <path> [remote]", options: ["--pull", "-t <ms>"], description: "Transfer files to/from a remote node" },
|
|
676
|
+
// Knowledge & Kanban
|
|
677
|
+
{ name: "kb files", args: "", options: ["--json"], description: "List all synced knowledge files" },
|
|
678
|
+
{ name: "kb history", args: "<path>", options: ["--json"], description: "Show change history for a synced file" },
|
|
679
|
+
{ name: "kb blame", args: "<path>", options: ["--json"], description: "Show line-by-line attribution for a synced file" },
|
|
680
|
+
{ name: "board", args: "", options: ["--json"], description: "Kanban board summary" },
|
|
681
|
+
{ name: "board list", args: "", options: ["--json", "--stage <s>", "--label <l>", "--priority <p>", "--node <n>"], description: "List kanban cards" },
|
|
682
|
+
{ name: "board create", args: "<title>", options: ["--desc <text>", "--priority <p>", "--labels <l1,l2>", "--node <n>", "--agent <a>"], description: "Create a new kanban card" },
|
|
683
|
+
{ name: "board get", args: "<cardId>", options: ["--json"], description: "Get kanban card details" },
|
|
684
|
+
{ name: "board update", args: "<cardId>", options: ["--title <t>", "--desc <d>", "--priority <p>", "--labels <l1,l2>"], description: "Update kanban card properties" },
|
|
685
|
+
{ name: "board claim", args: "<cardId>", options: ["--agent <a>"], description: "Claim a kanban card" },
|
|
686
|
+
{ name: "board move", args: "<cardId> <stage>", options: [], description: "Move kanban card to a stage" },
|
|
687
|
+
{ name: "board annotate", args: "<cardId> <text>", options: ["--type <t>"], description: "Add annotation to a kanban card" },
|
|
688
|
+
{ name: "board delete", args: "<cardId>", options: [], description: "Delete a kanban card" },
|
|
689
|
+
// Access Control
|
|
663
690
|
{ name: "approve", args: "<approvalId>", options: [], description: "Approve a pending peer" },
|
|
664
691
|
{ name: "deny", args: "<approvalId>", options: [], description: "Deny a pending peer" },
|
|
665
692
|
{ name: "approval list", args: "", options: [], description: "List approved/pending peers" },
|
|
666
693
|
{ name: "approval revoke", args: "<nodeId>", options: [], description: "Revoke an approved peer" },
|
|
667
|
-
|
|
668
|
-
{ name: "kb history", args: "<path>", options: ["--json"], description: "Show change history for a synced file" },
|
|
694
|
+
// Other
|
|
669
695
|
{ name: "install-skill", args: "", options: [], description: "Symlink skill into ~/.claude/skills/" },
|
|
670
696
|
];
|
|
671
697
|
console.log(jsonOut({ prefix: "clawmatrix", commands }));
|
|
@@ -945,7 +971,8 @@ async function cmdKb(args) {
|
|
|
945
971
|
|
|
946
972
|
Subcommands:
|
|
947
973
|
files [--json] List all synced files with metadata
|
|
948
|
-
history <path> [--json] Show change history for a synced file
|
|
974
|
+
history <path> [--json] Show change history for a synced file
|
|
975
|
+
blame <path> [--json] Show line-by-line attribution for a synced file`);
|
|
949
976
|
return;
|
|
950
977
|
}
|
|
951
978
|
|
|
@@ -1016,6 +1043,44 @@ Subcommands:
|
|
|
1016
1043
|
return;
|
|
1017
1044
|
}
|
|
1018
1045
|
|
|
1046
|
+
if (subCmd === "blame") {
|
|
1047
|
+
const filePath = subArgs.filter((a) => !a.startsWith("--"))[0];
|
|
1048
|
+
if (!filePath) { console.error("Usage: clawmatrix kb blame <path>"); process.exit(1); }
|
|
1049
|
+
const result = await callGateway("clawmatrix.kb.blame", { path: filePath });
|
|
1050
|
+
if (isJson) { console.log(jsonOut(result)); return; }
|
|
1051
|
+
|
|
1052
|
+
const blame = result?.blame;
|
|
1053
|
+
if (!Array.isArray(blame) || blame.length === 0) {
|
|
1054
|
+
console.log(`No blame data for: ${filePath}`);
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
if (!isTTY) {
|
|
1059
|
+
for (const b of blame) {
|
|
1060
|
+
const parts = [b.line !== undefined ? `L${b.line}` : "?"];
|
|
1061
|
+
if (b.nodeId) parts.push(`node=${b.nodeId}`);
|
|
1062
|
+
if (b.agentId) parts.push(`agent=${b.agentId}`);
|
|
1063
|
+
parts.push(`actor=${b.actor}`);
|
|
1064
|
+
parts.push(new Date(b.timestamp).toISOString());
|
|
1065
|
+
parts.push(b.content || "");
|
|
1066
|
+
console.log(parts.join(" | "));
|
|
1067
|
+
}
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
console.log();
|
|
1072
|
+
console.log(` ${cyan("◆")} ${bold("File Blame")} ${dim(filePath)}`);
|
|
1073
|
+
console.log(` ${bar}`);
|
|
1074
|
+
for (const b of blame) {
|
|
1075
|
+
const lineNum = b.line !== undefined ? dim(`L${String(b.line).padStart(4)}`) : dim(" ?");
|
|
1076
|
+
const attr = b.nodeId ? yellow(b.nodeId) : dim(b.actor || "?");
|
|
1077
|
+
const content = b.content || "";
|
|
1078
|
+
console.log(` ${bar} ${lineNum} ${attr} ${content}`);
|
|
1079
|
+
}
|
|
1080
|
+
console.log();
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1019
1084
|
console.error(`Unknown kb subcommand: ${subCmd}`);
|
|
1020
1085
|
process.exit(1);
|
|
1021
1086
|
} catch (e) {
|
|
@@ -1041,6 +1106,8 @@ Subcommands:
|
|
|
1041
1106
|
create <title> [--desc <text>] [--priority <p>] [--labels <l1,l2>]
|
|
1042
1107
|
Create a new card
|
|
1043
1108
|
get <cardId> Get card details
|
|
1109
|
+
update <cardId> [--title <t>] [--desc <d>] [--priority <p>] [--labels <l1,l2>]
|
|
1110
|
+
Update card properties
|
|
1044
1111
|
claim <cardId> [--agent <a>] Claim a backlog card
|
|
1045
1112
|
move <cardId> <stage> Move card to a stage (backlog|claimed|in_progress|review|done|archived)
|
|
1046
1113
|
annotate <cardId> <text> [--type <t>]
|
|
@@ -1150,6 +1217,29 @@ Subcommands:
|
|
|
1150
1217
|
return;
|
|
1151
1218
|
}
|
|
1152
1219
|
|
|
1220
|
+
if (subCmd === "update" || subCmd === "edit") {
|
|
1221
|
+
const cardId = subArgs.filter((a) => !a.startsWith("--"))[0];
|
|
1222
|
+
if (!cardId) { console.error("Usage: clawmatrix board update <cardId> [--title <t>] [--desc <d>] [--priority <p>] [--labels <l1,l2>]"); process.exit(1); }
|
|
1223
|
+
const params = { cardId };
|
|
1224
|
+
const titleIdx = subArgs.indexOf("--title");
|
|
1225
|
+
if (titleIdx !== -1) params.title = subArgs[titleIdx + 1];
|
|
1226
|
+
const descIdx = subArgs.indexOf("--desc");
|
|
1227
|
+
if (descIdx !== -1) params.description = subArgs[descIdx + 1];
|
|
1228
|
+
const prioIdx = subArgs.indexOf("--priority");
|
|
1229
|
+
if (prioIdx !== -1) params.priority = subArgs[prioIdx + 1];
|
|
1230
|
+
const labelsIdx = subArgs.indexOf("--labels");
|
|
1231
|
+
if (labelsIdx !== -1) params.labels = subArgs[labelsIdx + 1]?.split(",");
|
|
1232
|
+
const nodeIdx = subArgs.indexOf("--node");
|
|
1233
|
+
if (nodeIdx !== -1) params.targetNode = subArgs[nodeIdx + 1];
|
|
1234
|
+
const agentIdx = subArgs.indexOf("--agent");
|
|
1235
|
+
if (agentIdx !== -1) params.targetAgent = subArgs[agentIdx + 1];
|
|
1236
|
+
|
|
1237
|
+
const card = await callGateway("clawmatrix.board.update", params);
|
|
1238
|
+
if (isJson) { console.log(jsonOut(card)); return; }
|
|
1239
|
+
console.log(`Updated: ${card.id} — ${card.title}`);
|
|
1240
|
+
return;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1153
1243
|
if (subCmd === "claim") {
|
|
1154
1244
|
const cardId = subArgs.filter((a) => !a.startsWith("--"))[0];
|
|
1155
1245
|
if (!cardId) { console.error("Usage: clawmatrix board claim <cardId>"); process.exit(1); }
|
|
@@ -1226,35 +1316,338 @@ Subcommands:
|
|
|
1226
1316
|
}
|
|
1227
1317
|
}
|
|
1228
1318
|
|
|
1319
|
+
// ── Availability command ──────────────────────────────────────────
|
|
1320
|
+
|
|
1321
|
+
async function cmdAvailability(args) {
|
|
1322
|
+
const isJson = args.includes("--json");
|
|
1323
|
+
const range = args.find((a) => ["24h", "7d", "90d"].includes(a)) || "24h";
|
|
1324
|
+
|
|
1325
|
+
try {
|
|
1326
|
+
const data = await callGateway("clawmatrix.availability", { range });
|
|
1327
|
+
if (isJson) { console.log(jsonOut(data)); return; }
|
|
1328
|
+
|
|
1329
|
+
if (!isTTY) {
|
|
1330
|
+
console.log(`Range: ${data.range} | Bucket: ${data.bucketMinutes}min`);
|
|
1331
|
+
for (const node of (data.nodes || [])) {
|
|
1332
|
+
const uptimeStr = node.uptimeRatio !== undefined ? `${(node.uptimeRatio * 100).toFixed(1)}%` : "N/A";
|
|
1333
|
+
console.log(`${node.nodeId} | uptime: ${uptimeStr} | buckets: ${node.buckets?.length ?? 0}`);
|
|
1334
|
+
}
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
console.log();
|
|
1339
|
+
console.log(` ${cyan("◆")} ${bold("Availability")} ${dim(`range: ${data.range}, bucket: ${data.bucketMinutes}min`)}`);
|
|
1340
|
+
console.log(` ${bar}`);
|
|
1341
|
+
|
|
1342
|
+
for (const node of (data.nodes || [])) {
|
|
1343
|
+
const uptimeStr = node.uptimeRatio !== undefined ? `${(node.uptimeRatio * 100).toFixed(1)}%` : "N/A";
|
|
1344
|
+
const uptimeColor = node.uptimeRatio >= 0.99 ? green : node.uptimeRatio >= 0.9 ? yellow : red;
|
|
1345
|
+
console.log(` ${bar} ${bold(node.nodeId)} ${lbl("uptime")}${uptimeColor(uptimeStr)}`);
|
|
1346
|
+
|
|
1347
|
+
if (node.buckets?.length > 0) {
|
|
1348
|
+
// Render a compact timeline bar
|
|
1349
|
+
const chars = node.buckets.map((b) => {
|
|
1350
|
+
if (b.status === "up") return green("▓");
|
|
1351
|
+
if (b.status === "degraded") return yellow("▒");
|
|
1352
|
+
if (b.status === "down") return red("░");
|
|
1353
|
+
return dim("·");
|
|
1354
|
+
});
|
|
1355
|
+
// Show at most 60 chars for readability
|
|
1356
|
+
const maxWidth = 60;
|
|
1357
|
+
const step = Math.max(1, Math.ceil(chars.length / maxWidth));
|
|
1358
|
+
const compressed = [];
|
|
1359
|
+
for (let i = 0; i < chars.length; i += step) {
|
|
1360
|
+
compressed.push(chars[i]);
|
|
1361
|
+
}
|
|
1362
|
+
console.log(` ${bar} ${compressed.join("")}`);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
if (data.gaps?.length > 0) {
|
|
1367
|
+
console.log(` ${bar}`);
|
|
1368
|
+
console.log(` ${bar} ${bold("Gaps")}`);
|
|
1369
|
+
for (const gap of data.gaps.slice(0, 10)) {
|
|
1370
|
+
const from = new Date(gap.from).toLocaleString();
|
|
1371
|
+
const to = new Date(gap.to).toLocaleString();
|
|
1372
|
+
const dur = Math.round((gap.to - gap.from) / 60000);
|
|
1373
|
+
console.log(` ${bar} ${gap.nodeId} ${dim(from)} → ${dim(to)} ${red(`${dur}min`)}`);
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
console.log();
|
|
1377
|
+
} catch (err) {
|
|
1378
|
+
console.error(`Error: ${err.message || String(err)}`);
|
|
1379
|
+
process.exitCode = 1;
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
// ── Automations command ──────────────────────────────────────────
|
|
1384
|
+
|
|
1385
|
+
async function cmdAutomations(args) {
|
|
1386
|
+
const isJson = args.includes("--json");
|
|
1387
|
+
const subCmd = args[0];
|
|
1388
|
+
const subArgs = args.slice(1);
|
|
1389
|
+
|
|
1390
|
+
if (!subCmd || subCmd === "--help" || subCmd === "-h") {
|
|
1391
|
+
console.log(`Usage: clawmatrix automations [subcommand]
|
|
1392
|
+
|
|
1393
|
+
Subcommands:
|
|
1394
|
+
rules [--json] List all automation rules
|
|
1395
|
+
history [--limit <n>] [--json]
|
|
1396
|
+
Show execution history
|
|
1397
|
+
run <ruleId> [--json] Manually trigger a rule
|
|
1398
|
+
replay <executionId> [--json]
|
|
1399
|
+
Re-run a historical execution
|
|
1400
|
+
save <json-file|-> Save rules from JSON file or stdin`);
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
try {
|
|
1405
|
+
if (subCmd === "rules" || subCmd === "list") {
|
|
1406
|
+
const data = await callGateway("clawmatrix.automations.rules");
|
|
1407
|
+
if (isJson) { console.log(jsonOut(data)); return; }
|
|
1408
|
+
const rules = data.rules || [];
|
|
1409
|
+
if (rules.length === 0) { console.log("No automation rules configured."); return; }
|
|
1410
|
+
|
|
1411
|
+
if (!isTTY) {
|
|
1412
|
+
for (const r of rules) {
|
|
1413
|
+
const status = r.enabled ? "enabled" : "disabled";
|
|
1414
|
+
const trigger = r.trigger?.type || "unknown";
|
|
1415
|
+
console.log(`${r.id} | ${status} | trigger:${trigger} | ${r.name || "(unnamed)"}`);
|
|
1416
|
+
}
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
console.log();
|
|
1421
|
+
console.log(` ${cyan("◆")} ${bold("Automation Rules")} ${dim(`${rules.length} rule(s)`)}`);
|
|
1422
|
+
console.log(` ${bar}`);
|
|
1423
|
+
for (const r of rules) {
|
|
1424
|
+
const dot = r.enabled ? green("●") : red("○");
|
|
1425
|
+
const trigger = r.trigger?.type || "unknown";
|
|
1426
|
+
console.log(` ${bar} ${dot} ${bold(r.id)} ${r.name || dim("(unnamed)")} ${dim(`trigger: ${trigger}`)}`);
|
|
1427
|
+
}
|
|
1428
|
+
console.log();
|
|
1429
|
+
return;
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
if (subCmd === "history") {
|
|
1433
|
+
const lIdx = subArgs.indexOf("--limit") !== -1 ? subArgs.indexOf("--limit") : subArgs.indexOf("-l");
|
|
1434
|
+
const limit = lIdx !== -1 ? parseInt(subArgs[lIdx + 1], 10) : 20;
|
|
1435
|
+
const data = await callGateway("clawmatrix.automations.history", { limit });
|
|
1436
|
+
if (isJson) { console.log(jsonOut(data)); return; }
|
|
1437
|
+
const executions = data.executions || [];
|
|
1438
|
+
if (executions.length === 0) { console.log("No execution history."); return; }
|
|
1439
|
+
|
|
1440
|
+
if (!isTTY) {
|
|
1441
|
+
for (const ex of executions) {
|
|
1442
|
+
const ts = new Date(ex.startedAt || ex.ts).toISOString();
|
|
1443
|
+
const dur = ex.durationMs ? `${ex.durationMs}ms` : "?";
|
|
1444
|
+
console.log(`${ts} | ${ex.ruleId} | ${ex.status} | ${dur}`);
|
|
1445
|
+
}
|
|
1446
|
+
return;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
console.log();
|
|
1450
|
+
console.log(` ${cyan("◆")} ${bold("Execution History")} ${dim(`${executions.length} execution(s)`)}`);
|
|
1451
|
+
console.log(` ${bar}`);
|
|
1452
|
+
for (const ex of executions) {
|
|
1453
|
+
const ts = new Date(ex.startedAt || ex.ts).toLocaleString();
|
|
1454
|
+
const dur = ex.durationMs ? dim(`${ex.durationMs}ms`) : "";
|
|
1455
|
+
const statusColor = ex.status === "success" ? green : ex.status === "error" ? red : yellow;
|
|
1456
|
+
console.log(` ${bar} ${statusColor("●")} ${bold(ex.ruleId)} ${statusColor(ex.status)} ${dur} ${dim(ts)}`);
|
|
1457
|
+
if (ex.error) console.log(` ${bar} ${red(ex.error)}`);
|
|
1458
|
+
}
|
|
1459
|
+
console.log();
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
if (subCmd === "run") {
|
|
1464
|
+
const ruleId = subArgs.filter((a) => !a.startsWith("--"))[0];
|
|
1465
|
+
if (!ruleId) { console.error("Usage: clawmatrix automations run <ruleId>"); process.exit(1); }
|
|
1466
|
+
const data = await callGateway("clawmatrix.automations.run", { ruleId }, 60000);
|
|
1467
|
+
if (isJson) { console.log(jsonOut(data)); return; }
|
|
1468
|
+
const ex = data.execution;
|
|
1469
|
+
const statusColor = ex?.status === "success" ? green : red;
|
|
1470
|
+
console.log(`${statusColor("●")} Rule ${ruleId}: ${statusColor(ex?.status || "unknown")}${ex?.durationMs ? ` (${ex.durationMs}ms)` : ""}`);
|
|
1471
|
+
if (ex?.error) console.log(` ${red(ex.error)}`);
|
|
1472
|
+
return;
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
if (subCmd === "replay") {
|
|
1476
|
+
const executionId = subArgs.filter((a) => !a.startsWith("--"))[0];
|
|
1477
|
+
if (!executionId) { console.error("Usage: clawmatrix automations replay <executionId>"); process.exit(1); }
|
|
1478
|
+
const data = await callGateway("clawmatrix.automations.replay", { executionId }, 60000);
|
|
1479
|
+
if (isJson) { console.log(jsonOut(data)); return; }
|
|
1480
|
+
console.log(`Replayed: ${executionId} → ${data.execution?.status || "unknown"}`);
|
|
1481
|
+
return;
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
if (subCmd === "save") {
|
|
1485
|
+
let jsonStr = subArgs.filter((a) => !a.startsWith("--"))[0];
|
|
1486
|
+
if (jsonStr === "-" || (!jsonStr && !process.stdin.isTTY)) {
|
|
1487
|
+
const chunks = [];
|
|
1488
|
+
for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
|
|
1489
|
+
jsonStr = Buffer.concat(chunks).toString("utf-8").trim();
|
|
1490
|
+
} else if (jsonStr && !jsonStr.startsWith("[")) {
|
|
1491
|
+
// Treat as file path
|
|
1492
|
+
try { jsonStr = readFileSync(resolve(jsonStr), "utf-8"); } catch (e) {
|
|
1493
|
+
console.error(`Error: Could not read file: ${jsonStr}`);
|
|
1494
|
+
process.exit(1);
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
if (!jsonStr) { console.error("Usage: clawmatrix automations save <json-file|->"); process.exit(1); }
|
|
1498
|
+
const rules = JSON.parse(jsonStr);
|
|
1499
|
+
const data = await callGateway("clawmatrix.automations.save", { rules });
|
|
1500
|
+
console.log(`Saved ${data.count} rule(s)`);
|
|
1501
|
+
return;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
console.error(`Unknown automations subcommand: ${subCmd}`);
|
|
1505
|
+
process.exit(1);
|
|
1506
|
+
} catch (err) {
|
|
1507
|
+
console.error(`Error: ${err.message || String(err)}`);
|
|
1508
|
+
process.exitCode = 1;
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
// ── Config command ───────────────────────────────────────────────
|
|
1513
|
+
|
|
1514
|
+
async function cmdConfig(args) {
|
|
1515
|
+
const isJson = args.includes("--json");
|
|
1516
|
+
|
|
1517
|
+
try {
|
|
1518
|
+
const data = await callGateway("clawmatrix.config.get");
|
|
1519
|
+
if (isJson) { console.log(jsonOut(data)); return; }
|
|
1520
|
+
|
|
1521
|
+
if (!isTTY) {
|
|
1522
|
+
const parts = [`nodeId: ${data.nodeId}`];
|
|
1523
|
+
parts.push(`listen: ${data.listen !== false ? data.listen : "disabled"}`);
|
|
1524
|
+
parts.push(`e2ee: ${data.e2ee ?? false}`);
|
|
1525
|
+
parts.push(`tags: ${(data.tags || []).join(",") || "none"}`);
|
|
1526
|
+
parts.push(`agents: ${(data.agents || []).map((a) => a.id).join(",") || "none"}`);
|
|
1527
|
+
parts.push(`models: ${(data.models || []).map((m) => m.id).join(",") || "none"}`);
|
|
1528
|
+
parts.push(`toolProxy: ${data.toolProxy?.enabled ? "enabled" : "disabled"}`);
|
|
1529
|
+
parts.push(`terminal: ${data.terminal?.enabled ? "enabled" : "disabled"}`);
|
|
1530
|
+
parts.push(`acp: ${data.acp?.enabled ? "enabled" : "disabled"}`);
|
|
1531
|
+
parts.push(`knowledge: ${data.knowledge?.enabled ? "enabled" : "disabled"}`);
|
|
1532
|
+
parts.push(`proxyModels: ${data.proxyModels}`);
|
|
1533
|
+
parts.push(`peers: ${data.peers}`);
|
|
1534
|
+
console.log(parts.join(" | "));
|
|
1535
|
+
return;
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
console.log();
|
|
1539
|
+
console.log(` ${cyan("◆")} ${bold("ClawMatrix Config")}`);
|
|
1540
|
+
console.log(` ${bar}`);
|
|
1541
|
+
console.log(` ${bar} ${lbl("Node ID")}${bold(String(data.nodeId))}`);
|
|
1542
|
+
console.log(` ${bar} ${lbl("Listen")}${data.listen !== false ? `:${data.listen}` : dim("disabled")}`);
|
|
1543
|
+
console.log(` ${bar} ${lbl("E2EE")}${data.e2ee ? green("enabled") : dim("disabled")}`);
|
|
1544
|
+
console.log(` ${bar} ${lbl("Tags")}${(data.tags || []).join(dim(", ")) || dim("none")}`);
|
|
1545
|
+
console.log(` ${bar}`);
|
|
1546
|
+
console.log(` ${bar} ${bold("Agents")}`);
|
|
1547
|
+
for (const a of (data.agents || [])) {
|
|
1548
|
+
console.log(` ${bar} ${green("●")} ${a.id} ${dim(a.model || "")}`);
|
|
1549
|
+
}
|
|
1550
|
+
if ((data.agents || []).length === 0) console.log(` ${bar} ${dim("none")}`);
|
|
1551
|
+
console.log(` ${bar}`);
|
|
1552
|
+
console.log(` ${bar} ${bold("Models")}`);
|
|
1553
|
+
for (const m of (data.models || [])) {
|
|
1554
|
+
console.log(` ${bar} ${green("●")} ${m.id} ${dim(m.provider || "")}`);
|
|
1555
|
+
}
|
|
1556
|
+
if ((data.models || []).length === 0) console.log(` ${bar} ${dim("none")}`);
|
|
1557
|
+
console.log(` ${bar}`);
|
|
1558
|
+
console.log(` ${bar} ${bold("Features")}`);
|
|
1559
|
+
const feat = (name, enabled) => ` ${bar} ${enabled ? green("●") : red("○")} ${name}`;
|
|
1560
|
+
console.log(feat("Tool Proxy", data.toolProxy?.enabled));
|
|
1561
|
+
if (data.toolProxy?.enabled) {
|
|
1562
|
+
const allow = data.toolProxy.allow?.join(", ") || "*";
|
|
1563
|
+
const deny = data.toolProxy.deny?.length > 0 ? `, deny: ${data.toolProxy.deny.join(", ")}` : "";
|
|
1564
|
+
console.log(` ${bar} ${dim(`allow: ${allow}${deny}`)}`);
|
|
1565
|
+
}
|
|
1566
|
+
console.log(feat("Terminal", data.terminal?.enabled));
|
|
1567
|
+
console.log(feat("ACP", data.acp?.enabled));
|
|
1568
|
+
console.log(feat("Knowledge Sync", data.knowledge?.enabled));
|
|
1569
|
+
console.log(` ${bar}`);
|
|
1570
|
+
console.log(` ${bar} ${lbl("Proxy Models")}${data.proxyModels}`);
|
|
1571
|
+
console.log(` ${bar} ${lbl("Peers")}${data.peers}`);
|
|
1572
|
+
console.log();
|
|
1573
|
+
} catch (err) {
|
|
1574
|
+
console.error(`Error: ${err.message || String(err)}`);
|
|
1575
|
+
process.exitCode = 1;
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
// ── Events ingest subcommand ─────────────────────────────────────
|
|
1580
|
+
|
|
1581
|
+
async function cmdEventsIngest(args) {
|
|
1582
|
+
let jsonStr = args.find((a) => a.startsWith("[") || a.startsWith("{"));
|
|
1583
|
+
if (!jsonStr && !process.stdin.isTTY) {
|
|
1584
|
+
const chunks = [];
|
|
1585
|
+
for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
|
|
1586
|
+
jsonStr = Buffer.concat(chunks).toString("utf-8").trim();
|
|
1587
|
+
}
|
|
1588
|
+
if (!jsonStr) {
|
|
1589
|
+
console.error('Usage: clawmatrix events ingest \'[{"type":"x","source":"y","data":{}}]\' or pipe via stdin');
|
|
1590
|
+
process.exit(1);
|
|
1591
|
+
}
|
|
1592
|
+
try {
|
|
1593
|
+
const raw = JSON.parse(jsonStr);
|
|
1594
|
+
const events = Array.isArray(raw) ? raw : [raw];
|
|
1595
|
+
const result = await callGateway("clawmatrix.events.ingest", { events });
|
|
1596
|
+
console.log(jsonOut(result));
|
|
1597
|
+
} catch (err) {
|
|
1598
|
+
console.error(`Error: ${err.message || String(err)}`);
|
|
1599
|
+
process.exitCode = 1;
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1229
1603
|
const HELP = `ClawMatrix - Decentralized mesh cluster CLI
|
|
1230
1604
|
|
|
1231
1605
|
Usage: clawmatrix <command> [options]
|
|
1232
1606
|
|
|
1233
|
-
|
|
1607
|
+
Cluster:
|
|
1234
1608
|
status Show cluster topology and peer status
|
|
1609
|
+
peers List known peers (JSON)
|
|
1235
1610
|
check <nodeId> Check if a specific node is reachable
|
|
1611
|
+
config Show local node configuration
|
|
1612
|
+
availability [range] Show node uptime (24h|7d|90d)
|
|
1613
|
+
|
|
1614
|
+
Tools & Execution:
|
|
1236
1615
|
tools [node] List available tools on remote nodes
|
|
1237
1616
|
call <node> <tool> [p] Invoke a tool on a remote node
|
|
1238
1617
|
batch <node> [json] Invoke multiple tools in sequence
|
|
1239
1618
|
models List all models available across the cluster
|
|
1240
|
-
|
|
1619
|
+
|
|
1620
|
+
Agents:
|
|
1241
1621
|
send <node> <message> Send a message to a remote node
|
|
1242
1622
|
handoff <task> [opts] Delegate a task to a remote agent
|
|
1243
|
-
acp <action> [opts]
|
|
1623
|
+
acp <action> [opts] ACP sessions (list|prompt|resume|cancel|close)
|
|
1624
|
+
|
|
1625
|
+
Events & Automations:
|
|
1626
|
+
events Query, consume, or ingest events
|
|
1627
|
+
automations [subcmd] Manage automation rules (rules|history|run|replay|save)
|
|
1628
|
+
|
|
1629
|
+
Infrastructure:
|
|
1244
1630
|
diagnostic <node> Diagnose a remote node via sentinel
|
|
1245
|
-
notify <title> [opts] Push notification to mobile
|
|
1631
|
+
notify <title> [opts] Push notification to mobile (Dynamic Island)
|
|
1246
1632
|
terminal <node> Open/manage remote terminal sessions
|
|
1247
1633
|
transfer <node> <path> Transfer files to/from a remote node
|
|
1634
|
+
|
|
1635
|
+
Knowledge & Kanban:
|
|
1636
|
+
kb [subcommand] Knowledge sync (files|history|blame)
|
|
1637
|
+
board [subcommand] Kanban board (list|create|get|update|claim|move|annotate|delete)
|
|
1638
|
+
|
|
1639
|
+
Access Control:
|
|
1248
1640
|
approve <approvalId> Approve a pending peer join request
|
|
1249
1641
|
deny <approvalId> Deny a pending peer join request
|
|
1250
|
-
kb [subcommand] Knowledge sync (files|history)
|
|
1251
|
-
board [subcommand] Kanban board (list|create|get|claim|move|annotate|delete)
|
|
1252
1642
|
approval list|revoke Manage approved peers
|
|
1643
|
+
|
|
1644
|
+
Other:
|
|
1253
1645
|
install-skill Symlink skill into ~/.claude/skills/
|
|
1254
1646
|
help-json Structured command reference (JSON)
|
|
1255
1647
|
|
|
1256
1648
|
Options:
|
|
1257
1649
|
--help, -h Show this help message
|
|
1650
|
+
--json Output as JSON (most commands)
|
|
1258
1651
|
|
|
1259
1652
|
Run 'clawmatrix <command> --help' for command-specific options.`;
|
|
1260
1653
|
|
|
@@ -1273,7 +1666,10 @@ try {
|
|
|
1273
1666
|
case "call": await cmdCall(rest[0], rest[1], rest[2], rest); break;
|
|
1274
1667
|
case "batch": await cmdBatch(rest[0], rest.find((a) => a.startsWith("[") || a.startsWith("{")), rest); break;
|
|
1275
1668
|
case "models": await cmdModels(rest); break;
|
|
1276
|
-
case "events":
|
|
1669
|
+
case "events":
|
|
1670
|
+
if (rest[0] === "ingest") await cmdEventsIngest(rest.slice(1));
|
|
1671
|
+
else await cmdEvents(rest);
|
|
1672
|
+
break;
|
|
1277
1673
|
case "send": await cmdSend(rest[0], rest.slice(1).join(" ")); break;
|
|
1278
1674
|
case "handoff": await cmdHandoff(rest); break;
|
|
1279
1675
|
case "acp": await cmdAcp(rest[0], rest.slice(1)); break;
|
|
@@ -1283,6 +1679,9 @@ try {
|
|
|
1283
1679
|
case "transfer": await cmdTransfer(rest[0], rest[1], rest.slice(2)); break;
|
|
1284
1680
|
case "kb": case "knowledge": await cmdKb(rest); break;
|
|
1285
1681
|
case "board": await cmdBoard(rest); break;
|
|
1682
|
+
case "availability": await cmdAvailability(rest); break;
|
|
1683
|
+
case "automations": case "automation": await cmdAutomations(rest); break;
|
|
1684
|
+
case "config": await cmdConfig(rest); break;
|
|
1286
1685
|
case "approve": await cmdApprove(rest[0]); break;
|
|
1287
1686
|
case "deny": await cmdDeny(rest[0]); break;
|
|
1288
1687
|
case "approval":
|
|
@@ -18,7 +18,10 @@ All commands output LLM-friendly text (no ANSI colors) when stdout is not a TTY.
|
|
|
18
18
|
```bash
|
|
19
19
|
clawmatrix status # Cluster topology: peers, agents, models, tags
|
|
20
20
|
clawmatrix status --json # Structured JSON output
|
|
21
|
+
clawmatrix peers # List known peers (JSON)
|
|
21
22
|
clawmatrix check <nodeId> # Quick reachability check with latency
|
|
23
|
+
clawmatrix config # Show local node configuration
|
|
24
|
+
clawmatrix config --json # Config as JSON
|
|
22
25
|
```
|
|
23
26
|
|
|
24
27
|
Always start with `clawmatrix status` to understand the current topology before performing other operations.
|
|
@@ -55,6 +58,14 @@ clawmatrix models # All cluster models
|
|
|
55
58
|
clawmatrix models --node <nodeId> # Filter by node
|
|
56
59
|
```
|
|
57
60
|
|
|
61
|
+
## Availability
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
clawmatrix availability # Node uptime (default: 24h)
|
|
65
|
+
clawmatrix availability 7d # 7-day uptime
|
|
66
|
+
clawmatrix availability 90d --json # 90-day as JSON
|
|
67
|
+
```
|
|
68
|
+
|
|
58
69
|
## Events
|
|
59
70
|
|
|
60
71
|
```bash
|
|
@@ -63,6 +74,19 @@ clawmatrix events --type <type> # Filter by type
|
|
|
63
74
|
clawmatrix events --source <nodeId> # Filter by source node
|
|
64
75
|
clawmatrix events --consume <id1,id2> # Mark events as consumed
|
|
65
76
|
clawmatrix events --all # Include consumed events
|
|
77
|
+
clawmatrix events ingest '[{"type":"x","source":"y","data":{}}]' # Ingest events
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Automations
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
clawmatrix automations rules # List automation rules
|
|
84
|
+
clawmatrix automations history # Execution history
|
|
85
|
+
clawmatrix automations history --limit 5 # Limit results
|
|
86
|
+
clawmatrix automations run <ruleId> # Manually trigger a rule
|
|
87
|
+
clawmatrix automations replay <execId> # Re-run historical execution
|
|
88
|
+
clawmatrix automations save rules.json # Save rules from JSON file
|
|
89
|
+
cat rules.json | clawmatrix automations save - # Save from stdin
|
|
66
90
|
```
|
|
67
91
|
|
|
68
92
|
## Peer Approval
|
|
@@ -94,6 +118,31 @@ clawmatrix notify "iOS 构建" --action end --task-id <id>
|
|
|
94
118
|
|
|
95
119
|
Options: `--detail <text>`, `--progress <0-100>`, `--action start|update|end`, `--task-id <id>`, `--tool <name>`
|
|
96
120
|
|
|
121
|
+
## Knowledge Sync
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
clawmatrix kb files # List synced knowledge files
|
|
125
|
+
clawmatrix kb history <path> # File change history
|
|
126
|
+
clawmatrix kb blame <path> # Line-by-line attribution
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Kanban Board
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
clawmatrix board # Board summary
|
|
133
|
+
clawmatrix board list # List all cards
|
|
134
|
+
clawmatrix board list --stage in_progress --priority urgent # Filter cards
|
|
135
|
+
clawmatrix board create "Task title" --priority high --labels "bug,p0"
|
|
136
|
+
clawmatrix board get <cardId> # Card details
|
|
137
|
+
clawmatrix board update <cardId> --title "New title" --desc "Updated"
|
|
138
|
+
clawmatrix board claim <cardId> # Claim a card
|
|
139
|
+
clawmatrix board move <cardId> done # Move to stage
|
|
140
|
+
clawmatrix board annotate <cardId> "Progress note" --type progress
|
|
141
|
+
clawmatrix board delete <cardId> # Delete a card
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Stages: `backlog` → `claimed` → `in_progress` → `review` → `done` → `archived`
|
|
145
|
+
|
|
97
146
|
## Workflow
|
|
98
147
|
|
|
99
148
|
1. Run `clawmatrix status` to see the cluster topology
|
package/package.json
CHANGED