clawmatrix 0.4.2 → 0.5.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 +17 -21
- package/cli/bin/clawmatrix.mjs +300 -1
- package/package.json +8 -1
- package/src/acp-proxy.ts +122 -50
- package/src/{web.ts → api.ts} +646 -25
- package/src/audit.ts +37 -2
- package/src/auth.ts +5 -10
- package/src/automation.ts +625 -0
- package/src/cluster-service.ts +172 -16
- package/src/compat.ts +103 -0
- package/src/config.ts +75 -27
- package/src/connection.ts +215 -37
- package/src/crypto.ts +72 -5
- package/src/device-info.ts +21 -2
- package/src/file-transfer.ts +3 -2
- package/src/handoff.ts +90 -32
- package/src/health-tracker.ts +91 -356
- package/src/index.ts +421 -13
- package/src/kanban.ts +507 -0
- package/src/knowledge-sync.ts +158 -7
- package/src/local-tools.ts +65 -2
- package/src/log-replication.ts +198 -0
- package/src/model-proxy.ts +152 -60
- package/src/peer-approval.ts +3 -2
- package/src/peer-manager.ts +230 -44
- package/src/retry.ts +81 -0
- package/src/router.ts +152 -104
- package/src/sentinel.ts +85 -51
- package/src/store.ts +578 -0
- package/src/terminal.ts +17 -8
- package/src/tool-proxy.ts +6 -5
- package/src/tools/cluster-events.ts +6 -6
- package/src/tools/cluster-kanban.ts +345 -0
- package/src/tools/cluster-peers.ts +1 -1
- package/src/tools/cluster-query.ts +145 -0
- package/src/types.ts +95 -9
package/README.md
CHANGED
|
@@ -56,10 +56,7 @@ Edit `openclaw.json` to add the plugin configuration. All nodes share the same `
|
|
|
56
56
|
"nodeId": "cloud-01",
|
|
57
57
|
"secret": "your-shared-secret-min-16-chars",
|
|
58
58
|
"listen": true,
|
|
59
|
-
"listenPort": 19000
|
|
60
|
-
"agents": [
|
|
61
|
-
{ "id": "reviewer", "description": "Code review", "tags": ["review"] }
|
|
62
|
-
]
|
|
59
|
+
"listenPort": 19000
|
|
63
60
|
}
|
|
64
61
|
}
|
|
65
62
|
}
|
|
@@ -73,13 +70,12 @@ Edit `openclaw.json` to add the plugin configuration. All nodes share the same `
|
|
|
73
70
|
{
|
|
74
71
|
"nodeId": "office-01",
|
|
75
72
|
"secret": "your-shared-secret-min-16-chars",
|
|
76
|
-
"peers": [
|
|
77
|
-
"agents": [{ "id": "coder", "description": "Code development", "tags": ["coding"] }],
|
|
73
|
+
"peers": ["wss://cloud-01.example.com:19000"],
|
|
78
74
|
"models": [
|
|
79
75
|
{ "id": "claude-sonnet", "provider": "anthropic" },
|
|
80
76
|
{ "id": "deepseek-coder", "provider": "ollama" }
|
|
81
77
|
],
|
|
82
|
-
"toolProxy":
|
|
78
|
+
"toolProxy": true
|
|
83
79
|
}
|
|
84
80
|
```
|
|
85
81
|
|
|
@@ -89,17 +85,18 @@ Edit `openclaw.json` to add the plugin configuration. All nodes share the same `
|
|
|
89
85
|
{
|
|
90
86
|
"nodeId": "home-01",
|
|
91
87
|
"secret": "your-shared-secret-min-16-chars",
|
|
92
|
-
"peers": [
|
|
93
|
-
"agents": [{ "id": "assistant", "description": "Personal assistant", "tags": ["general"] }],
|
|
88
|
+
"peers": ["wss://cloud-01.example.com:19000"],
|
|
94
89
|
"proxyModels": [
|
|
95
90
|
{ "nodeId": "office-01", "models": [{ "id": "claude-sonnet" }] }
|
|
96
91
|
]
|
|
97
92
|
}
|
|
98
93
|
```
|
|
99
94
|
|
|
100
|
-
**
|
|
101
|
-
|
|
102
|
-
|
|
95
|
+
> **Notes**:
|
|
96
|
+
> - `agents` can be omitted — auto-discovered from OpenClaw's `agents.list` config.
|
|
97
|
+
> - `peers` accepts URL strings — nodeId is resolved from the handshake. Use `{ "nodeId": "x", "url": "wss://..." }` when you need a specific nodeId.
|
|
98
|
+
> - `toolProxy: true` is shorthand for `{ "enabled": true, "allow": ["*"] }`.
|
|
99
|
+
> - Nodes sharing models only need `{ id, provider }` in `models` — ClawMatrix auto-reads `baseUrl`/`apiKey` from OpenClaw's `models.providers`.
|
|
103
100
|
|
|
104
101
|
To use a cluster model: `/model <nodeId>/<modelId>`.
|
|
105
102
|
|
|
@@ -145,8 +142,8 @@ The target parameter supports nodeId (`"office-01"`) or tag queries (`"tags:codi
|
|
|
145
142
|
| `listen` | boolean | `false` | Accept inbound WS connections |
|
|
146
143
|
| `listenHost` | string | `"0.0.0.0"` | Listen address |
|
|
147
144
|
| `listenPort` | number | `0` (random) | Inbound WS port |
|
|
148
|
-
| `peers` | array | `[]` | Peers
|
|
149
|
-
| `agents` | array | `[]` | Local Agents: `{ id, description, tags }` |
|
|
145
|
+
| `peers` | array | `[]` | Peers: URL string or `{ nodeId, url }` (nodeId auto-resolved from handshake when using string form) |
|
|
146
|
+
| `agents` | array | `[]` | Local Agents: `{ id, description, tags }` (auto-discovered from OpenClaw if omitted) |
|
|
150
147
|
| `models` | array | `[]` | Models shared with the cluster: `{ id, provider }` (auto-reads OpenClaw provider config; optional `baseUrl`/`apiKey` override) |
|
|
151
148
|
| `proxyModels` | array | `[]` | Remote model groups to consume: `{ nodeId, models: [{ id }] }` |
|
|
152
149
|
| `tags` | array | `[]` | Free-form node tags |
|
|
@@ -165,20 +162,23 @@ The target parameter supports nodeId (`"office-01"`) or tag queries (`"tags:codi
|
|
|
165
162
|
|
|
166
163
|
### Tool Proxy
|
|
167
164
|
|
|
165
|
+
Accepts `true` (enable all), `false` (disable), or an object for fine-grained control.
|
|
166
|
+
|
|
168
167
|
| Field | Type | Default | Description |
|
|
169
168
|
|-------|------|---------|-------------|
|
|
170
|
-
| `toolProxy
|
|
169
|
+
| `toolProxy` | boolean \| object | — | `true` = enable all tools, `false` = disable |
|
|
170
|
+
| `toolProxy.enabled` | boolean | `false` | Allow remote tool execution (object form) |
|
|
171
171
|
| `toolProxy.allow` | array | `[]` | Allowed tool names; `["*"]` or `[]` means all |
|
|
172
172
|
| `toolProxy.deny` | array | `[]` | Denied tools (takes precedence over allow) |
|
|
173
173
|
| `toolProxy.maxOutputBytes` | number | `1048576` | Max tool output size (bytes) |
|
|
174
174
|
|
|
175
175
|
### Peer Approval
|
|
176
176
|
|
|
177
|
-
|
|
177
|
+
Accepts `"required"`, `"notify"`, `"none"`, a boolean, or an object. Default: `true` (required mode).
|
|
178
178
|
|
|
179
179
|
| Field | Type | Default | Description |
|
|
180
180
|
|-------|------|---------|-------------|
|
|
181
|
-
| `peerApproval` | boolean \| object | `true` | `
|
|
181
|
+
| `peerApproval` | string \| boolean \| object | `true` | `"required"` / `"notify"` / `"none"`, or `true`/`false`, or object |
|
|
182
182
|
| `peerApproval.enabled` | boolean | `false` | Enable peer approval (object form) |
|
|
183
183
|
| `peerApproval.mode` | string | `"notify"` | `"notify"` (log only) or `"required"` (block until approved) |
|
|
184
184
|
| `peerApproval.timeout` | number | `1200000` | Approval request timeout (ms) |
|
|
@@ -235,10 +235,6 @@ Peer approval accepts either a boolean shorthand or an object. The default is `t
|
|
|
235
235
|
|
|
236
236
|
### Web UI
|
|
237
237
|
|
|
238
|
-
| Field | Type | Default | Description |
|
|
239
|
-
|-------|------|---------|-------------|
|
|
240
|
-
| `web.enabled` | boolean | `false` | Enable HTTP web interface |
|
|
241
|
-
| `web.token` | string | required if enabled | Auth token (>= 8 characters) |
|
|
242
238
|
|
|
243
239
|
## Architecture
|
|
244
240
|
|
package/cli/bin/clawmatrix.mjs
CHANGED
|
@@ -607,7 +607,7 @@ async function cmdEvents(args) {
|
|
|
607
607
|
console.log();
|
|
608
608
|
} catch (err) {
|
|
609
609
|
const msg = err.message || String(err);
|
|
610
|
-
if (msg.includes("not
|
|
610
|
+
if (msg.includes("not available")) console.log("Events not available (listen mode not enabled in config).");
|
|
611
611
|
else console.log("Could not reach gateway. Is it running?");
|
|
612
612
|
}
|
|
613
613
|
}
|
|
@@ -664,6 +664,8 @@ function cmdHelpJson() {
|
|
|
664
664
|
{ name: "deny", args: "<approvalId>", options: [], description: "Deny a pending peer" },
|
|
665
665
|
{ name: "approval list", args: "", options: [], description: "List approved/pending peers" },
|
|
666
666
|
{ name: "approval revoke", args: "<nodeId>", options: [], description: "Revoke an approved peer" },
|
|
667
|
+
{ name: "kb files", args: "", options: ["--json"], description: "List all synced knowledge files" },
|
|
668
|
+
{ name: "kb history", args: "<path>", options: ["--json"], description: "Show change history for a synced file" },
|
|
667
669
|
{ name: "install-skill", args: "", options: [], description: "Symlink skill into ~/.claude/skills/" },
|
|
668
670
|
];
|
|
669
671
|
console.log(jsonOut({ prefix: "clawmatrix", commands }));
|
|
@@ -931,6 +933,299 @@ async function cmdTransfer(node, localPath, args) {
|
|
|
931
933
|
}
|
|
932
934
|
}
|
|
933
935
|
|
|
936
|
+
// ── Knowledge sync command ─────────────────────────────────────────
|
|
937
|
+
|
|
938
|
+
async function cmdKb(args) {
|
|
939
|
+
const isJson = args.includes("--json");
|
|
940
|
+
const subCmd = args[0];
|
|
941
|
+
const subArgs = args.slice(1);
|
|
942
|
+
|
|
943
|
+
if (!subCmd || subCmd === "--help" || subCmd === "-h") {
|
|
944
|
+
console.log(`Usage: clawmatrix kb [subcommand]
|
|
945
|
+
|
|
946
|
+
Subcommands:
|
|
947
|
+
files [--json] List all synced files with metadata
|
|
948
|
+
history <path> [--json] Show change history for a synced file`);
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
try {
|
|
953
|
+
if (subCmd === "files") {
|
|
954
|
+
const files = await callGateway("clawmatrix.kb.files", {});
|
|
955
|
+
if (isJson) { console.log(jsonOut(files)); return; }
|
|
956
|
+
if (!Array.isArray(files) || files.length === 0) {
|
|
957
|
+
console.log("No synced files.");
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
if (!isTTY) {
|
|
962
|
+
for (const f of files) {
|
|
963
|
+
console.log(`${f.path} | v${f.version} | ${new Date(f.updatedAt).toISOString()}${f.deleted ? " | deleted" : ""}`);
|
|
964
|
+
}
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
console.log();
|
|
969
|
+
console.log(` ${cyan("◆")} ${bold("Synced Files")} ${dim(`${files.length} file(s)`)}`);
|
|
970
|
+
console.log(` ${bar}`);
|
|
971
|
+
for (const f of files) {
|
|
972
|
+
const ts = new Date(f.updatedAt).toLocaleString();
|
|
973
|
+
const del = f.deleted ? red(" [deleted]") : "";
|
|
974
|
+
console.log(` ${bar} ${bold(f.path)} ${dim(`v${f.version}`)} ${dim(ts)}${del}`);
|
|
975
|
+
}
|
|
976
|
+
console.log();
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
if (subCmd === "history") {
|
|
981
|
+
const filePath = subArgs.filter((a) => !a.startsWith("--"))[0];
|
|
982
|
+
if (!filePath) { console.error("Usage: clawmatrix kb history <path>"); process.exit(1); }
|
|
983
|
+
const result = await callGateway("clawmatrix.kb.history", { path: filePath });
|
|
984
|
+
if (isJson) { console.log(jsonOut(result)); return; }
|
|
985
|
+
|
|
986
|
+
const history = result?.history;
|
|
987
|
+
if (!Array.isArray(history) || history.length === 0) {
|
|
988
|
+
console.log(`No history for: ${filePath}`);
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
if (!isTTY) {
|
|
993
|
+
for (const h of history) {
|
|
994
|
+
const parts = [new Date(h.timestamp).toISOString()];
|
|
995
|
+
if (h.nodeId) parts.push(`node=${h.nodeId}`);
|
|
996
|
+
if (h.agentId) parts.push(`agent=${h.agentId}`);
|
|
997
|
+
if (!h.nodeId && !h.agentId && h.message) parts.push(h.message);
|
|
998
|
+
parts.push(`actor=${h.actor}`);
|
|
999
|
+
console.log(parts.join(" | "));
|
|
1000
|
+
}
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
console.log();
|
|
1005
|
+
console.log(` ${cyan("◆")} ${bold("File History")} ${dim(filePath)} ${dim(`${history.length} change(s)`)}`);
|
|
1006
|
+
console.log(` ${bar}`);
|
|
1007
|
+
for (const h of history) {
|
|
1008
|
+
const ts = new Date(h.timestamp).toLocaleString();
|
|
1009
|
+
const attribution = [];
|
|
1010
|
+
if (h.nodeId) attribution.push(`node: ${h.nodeId}`);
|
|
1011
|
+
if (h.agentId) attribution.push(`agent: ${h.agentId}`);
|
|
1012
|
+
const attrStr = attribution.length > 0 ? yellow(attribution.join(", ")) : dim(h.message || h.actor);
|
|
1013
|
+
console.log(` ${bar} ${dim(ts)} ${attrStr}`);
|
|
1014
|
+
}
|
|
1015
|
+
console.log();
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
console.error(`Unknown kb subcommand: ${subCmd}`);
|
|
1020
|
+
process.exit(1);
|
|
1021
|
+
} catch (e) {
|
|
1022
|
+
console.error(`kb ${subCmd} failed: ${e.message}`);
|
|
1023
|
+
process.exitCode = 1;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
// ── Board (Kanban) command ─────────────────────────────────────────
|
|
1028
|
+
|
|
1029
|
+
async function cmdBoard(args) {
|
|
1030
|
+
const isJson = args.includes("--json");
|
|
1031
|
+
const subCmd = args[0];
|
|
1032
|
+
const subArgs = args.slice(1);
|
|
1033
|
+
|
|
1034
|
+
if (!subCmd || subCmd === "--help" || subCmd === "-h") {
|
|
1035
|
+
console.log(`Usage: clawmatrix board [subcommand]
|
|
1036
|
+
|
|
1037
|
+
Subcommands:
|
|
1038
|
+
(no args) Board summary
|
|
1039
|
+
list [--stage <s>] [--label <l>] [--priority <p>] [--json]
|
|
1040
|
+
List cards
|
|
1041
|
+
create <title> [--desc <text>] [--priority <p>] [--labels <l1,l2>]
|
|
1042
|
+
Create a new card
|
|
1043
|
+
get <cardId> Get card details
|
|
1044
|
+
claim <cardId> [--agent <a>] Claim a backlog card
|
|
1045
|
+
move <cardId> <stage> Move card to a stage (backlog|claimed|in_progress|review|done|archived)
|
|
1046
|
+
annotate <cardId> <text> [--type <t>]
|
|
1047
|
+
Add annotation (note|session_link|artifact|progress|error)
|
|
1048
|
+
delete <cardId> Delete a card`);
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
try {
|
|
1053
|
+
if (subCmd === "list") {
|
|
1054
|
+
const params = {};
|
|
1055
|
+
const stageIdx = subArgs.indexOf("--stage");
|
|
1056
|
+
if (stageIdx !== -1) params.stage = subArgs[stageIdx + 1];
|
|
1057
|
+
const labelIdx = subArgs.indexOf("--label");
|
|
1058
|
+
if (labelIdx !== -1) params.label = subArgs[labelIdx + 1];
|
|
1059
|
+
const prioIdx = subArgs.indexOf("--priority");
|
|
1060
|
+
if (prioIdx !== -1) params.priority = subArgs[prioIdx + 1];
|
|
1061
|
+
const nodeIdx = subArgs.indexOf("--node");
|
|
1062
|
+
if (nodeIdx !== -1) params.assignedNode = subArgs[nodeIdx + 1];
|
|
1063
|
+
|
|
1064
|
+
const cards = await callGateway("clawmatrix.board.list", params);
|
|
1065
|
+
if (isJson) { console.log(jsonOut(cards)); return; }
|
|
1066
|
+
if (!Array.isArray(cards) || cards.length === 0) {
|
|
1067
|
+
console.log("No cards found.");
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
if (!isTTY) {
|
|
1072
|
+
for (const c of cards) {
|
|
1073
|
+
const labels = c.labels?.length ? ` [${c.labels.join(",")}]` : "";
|
|
1074
|
+
const assigned = c.assignedNode ? ` → ${c.assignedNode}` : "";
|
|
1075
|
+
console.log(`${c.id} | ${c.stage} | ${c.priority} | ${c.title}${labels}${assigned}`);
|
|
1076
|
+
}
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
console.log();
|
|
1081
|
+
console.log(` ${cyan("◆")} ${bold("Kanban Board")} ${dim(`${cards.length} card(s)`)}`);
|
|
1082
|
+
console.log(` ${bar}`);
|
|
1083
|
+
const priorityColor = { urgent: red, high: yellow, medium: (s) => s, low: dim };
|
|
1084
|
+
for (const c of cards) {
|
|
1085
|
+
const pc = (priorityColor[c.priority] ?? ((s) => s));
|
|
1086
|
+
const labels = c.labels?.length ? dim(` [${c.labels.join(",")}]`) : "";
|
|
1087
|
+
const assigned = c.assignedNode ? dim(` → ${c.assignedNode}`) : "";
|
|
1088
|
+
console.log(` ${bar} ${pc(c.priority.padEnd(7))} ${bold(c.id)} ${c.title}${labels}${assigned} ${dim(c.stage)}`);
|
|
1089
|
+
}
|
|
1090
|
+
console.log();
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
if (subCmd === "create") {
|
|
1095
|
+
const title = subArgs.filter((a) => !a.startsWith("--"))[0];
|
|
1096
|
+
if (!title) { console.error("Usage: clawmatrix board create <title>"); process.exit(1); }
|
|
1097
|
+
const params = { title };
|
|
1098
|
+
const descIdx = subArgs.indexOf("--desc");
|
|
1099
|
+
if (descIdx !== -1) params.description = subArgs[descIdx + 1];
|
|
1100
|
+
const prioIdx = subArgs.indexOf("--priority");
|
|
1101
|
+
if (prioIdx !== -1) params.priority = subArgs[prioIdx + 1];
|
|
1102
|
+
const labelsIdx = subArgs.indexOf("--labels");
|
|
1103
|
+
if (labelsIdx !== -1) params.labels = subArgs[labelsIdx + 1]?.split(",");
|
|
1104
|
+
const nodeIdx = subArgs.indexOf("--node");
|
|
1105
|
+
if (nodeIdx !== -1) params.targetNode = subArgs[nodeIdx + 1];
|
|
1106
|
+
const agentIdx = subArgs.indexOf("--agent");
|
|
1107
|
+
if (agentIdx !== -1) params.targetAgent = subArgs[agentIdx + 1];
|
|
1108
|
+
|
|
1109
|
+
const card = await callGateway("clawmatrix.board.create", params);
|
|
1110
|
+
if (isJson) { console.log(jsonOut(card)); return; }
|
|
1111
|
+
console.log(`Created: ${card.id} — ${card.title}`);
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
if (subCmd === "get") {
|
|
1116
|
+
const cardId = subArgs.filter((a) => !a.startsWith("--"))[0];
|
|
1117
|
+
if (!cardId) { console.error("Usage: clawmatrix board get <cardId>"); process.exit(1); }
|
|
1118
|
+
const card = await callGateway("clawmatrix.board.get", { cardId });
|
|
1119
|
+
if (isJson) { console.log(jsonOut(card)); return; }
|
|
1120
|
+
|
|
1121
|
+
if (!isTTY) {
|
|
1122
|
+
console.log(`${card.id} | ${card.stage} | ${card.priority} | ${card.title}`);
|
|
1123
|
+
if (card.description) console.log(`Description: ${card.description}`);
|
|
1124
|
+
if (card.assignedNode) console.log(`Assigned: ${card.assignedNode} / ${card.assignedAgent || "?"}`);
|
|
1125
|
+
if (card.annotations?.length) {
|
|
1126
|
+
console.log(`Annotations: ${card.annotations.length}`);
|
|
1127
|
+
for (const a of card.annotations) {
|
|
1128
|
+
console.log(` [${a.type}] ${a.agent}@${a.nodeId}: ${a.content}`);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
return;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
console.log();
|
|
1135
|
+
console.log(` ${cyan("◆")} ${bold(card.id)} — ${card.title}`);
|
|
1136
|
+
console.log(` ${bar}`);
|
|
1137
|
+
console.log(` ${bar} ${lbl("Stage")} ${card.stage}`);
|
|
1138
|
+
console.log(` ${bar} ${lbl("Priority")} ${card.priority}`);
|
|
1139
|
+
if (card.description) console.log(` ${bar} ${lbl("Description")} ${card.description}`);
|
|
1140
|
+
if (card.assignedNode) console.log(` ${bar} ${lbl("Assigned")} ${card.assignedNode} / ${card.assignedAgent || "?"}`);
|
|
1141
|
+
if (card.labels?.length) console.log(` ${bar} ${lbl("Labels")} ${card.labels.join(", ")}`);
|
|
1142
|
+
if (card.annotations?.length) {
|
|
1143
|
+
console.log(` ${bar}`);
|
|
1144
|
+
console.log(` ${bar} ${bold("Annotations")} (${card.annotations.length})`);
|
|
1145
|
+
for (const a of card.annotations) {
|
|
1146
|
+
console.log(` ${bar} ${dim(`[${a.type}]`)} ${a.agent}: ${a.content}`);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
console.log();
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
if (subCmd === "claim") {
|
|
1154
|
+
const cardId = subArgs.filter((a) => !a.startsWith("--"))[0];
|
|
1155
|
+
if (!cardId) { console.error("Usage: clawmatrix board claim <cardId>"); process.exit(1); }
|
|
1156
|
+
const params = { cardId };
|
|
1157
|
+
const agentIdx = subArgs.indexOf("--agent");
|
|
1158
|
+
if (agentIdx !== -1) params.agent = subArgs[agentIdx + 1];
|
|
1159
|
+
const card = await callGateway("clawmatrix.board.claim", params);
|
|
1160
|
+
if (isJson) { console.log(jsonOut(card)); return; }
|
|
1161
|
+
console.log(`Claimed: ${card.id} → ${card.assignedNode} / ${card.assignedAgent}`);
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
if (subCmd === "move") {
|
|
1166
|
+
const positionals = subArgs.filter((a) => !a.startsWith("--"));
|
|
1167
|
+
const cardId = positionals[0];
|
|
1168
|
+
const stage = positionals[1];
|
|
1169
|
+
if (!cardId || !stage) { console.error("Usage: clawmatrix board move <cardId> <stage>"); process.exit(1); }
|
|
1170
|
+
const card = await callGateway("clawmatrix.board.move", { cardId, stage });
|
|
1171
|
+
if (isJson) { console.log(jsonOut(card)); return; }
|
|
1172
|
+
console.log(`Moved: ${card.id} → ${card.stage}`);
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
if (subCmd === "annotate") {
|
|
1177
|
+
const positionals = subArgs.filter((a) => !a.startsWith("--"));
|
|
1178
|
+
const cardId = positionals[0];
|
|
1179
|
+
const content = positionals.slice(1).join(" ");
|
|
1180
|
+
if (!cardId || !content) { console.error("Usage: clawmatrix board annotate <cardId> <text>"); process.exit(1); }
|
|
1181
|
+
const params = { cardId, content };
|
|
1182
|
+
const typeIdx = subArgs.indexOf("--type");
|
|
1183
|
+
if (typeIdx !== -1) params.annotationType = subArgs[typeIdx + 1];
|
|
1184
|
+
const card = await callGateway("clawmatrix.board.annotate", params);
|
|
1185
|
+
if (isJson) { console.log(jsonOut(card)); return; }
|
|
1186
|
+
console.log(`Annotated: ${card.id} (${card.annotations?.length ?? 0} annotations)`);
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
if (subCmd === "delete") {
|
|
1191
|
+
const cardId = subArgs.filter((a) => !a.startsWith("--"))[0];
|
|
1192
|
+
if (!cardId) { console.error("Usage: clawmatrix board delete <cardId>"); process.exit(1); }
|
|
1193
|
+
const result = await callGateway("clawmatrix.board.delete", { cardId });
|
|
1194
|
+
if (isJson) { console.log(jsonOut(result)); return; }
|
|
1195
|
+
console.log(result.deleted ? `Deleted: ${cardId}` : `Card not found: ${cardId}`);
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
// Default: board summary
|
|
1200
|
+
const summary = await callGateway("clawmatrix.board.summary", {});
|
|
1201
|
+
if (isJson) { console.log(jsonOut(summary)); return; }
|
|
1202
|
+
|
|
1203
|
+
if (!isTTY) {
|
|
1204
|
+
console.log(`Total: ${summary.total}`);
|
|
1205
|
+
for (const [stage, count] of Object.entries(summary.byStage || {})) {
|
|
1206
|
+
if (count > 0) console.log(` ${stage}: ${count}`);
|
|
1207
|
+
}
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
console.log();
|
|
1212
|
+
console.log(` ${cyan("◆")} ${bold("Kanban Board Summary")} ${dim(`${summary.total} card(s)`)}`);
|
|
1213
|
+
console.log(` ${bar}`);
|
|
1214
|
+
const stageLabels = { backlog: "Backlog", claimed: "Claimed", in_progress: "In Progress", review: "Review", done: "Done", archived: "Archived" };
|
|
1215
|
+
for (const [stage, label] of Object.entries(stageLabels)) {
|
|
1216
|
+
const count = summary.byStage?.[stage] ?? 0;
|
|
1217
|
+
if (count > 0 || stage !== "archived") {
|
|
1218
|
+
console.log(` ${bar} ${lbl(label)} ${count}`);
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
console.log();
|
|
1222
|
+
|
|
1223
|
+
} catch (e) {
|
|
1224
|
+
console.error(e.message);
|
|
1225
|
+
process.exitCode = 1;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
|
|
934
1229
|
const HELP = `ClawMatrix - Decentralized mesh cluster CLI
|
|
935
1230
|
|
|
936
1231
|
Usage: clawmatrix <command> [options]
|
|
@@ -952,6 +1247,8 @@ Commands:
|
|
|
952
1247
|
transfer <node> <path> Transfer files to/from a remote node
|
|
953
1248
|
approve <approvalId> Approve a pending peer join request
|
|
954
1249
|
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)
|
|
955
1252
|
approval list|revoke Manage approved peers
|
|
956
1253
|
install-skill Symlink skill into ~/.claude/skills/
|
|
957
1254
|
help-json Structured command reference (JSON)
|
|
@@ -984,6 +1281,8 @@ try {
|
|
|
984
1281
|
case "notify": await cmdNotify(rest[0], rest.slice(1)); break;
|
|
985
1282
|
case "terminal": await cmdTerminal(rest[0], rest.slice(1)); break;
|
|
986
1283
|
case "transfer": await cmdTransfer(rest[0], rest[1], rest.slice(2)); break;
|
|
1284
|
+
case "kb": case "knowledge": await cmdKb(rest); break;
|
|
1285
|
+
case "board": await cmdBoard(rest); break;
|
|
987
1286
|
case "approve": await cmdApprove(rest[0]); break;
|
|
988
1287
|
case "deny": await cmdDeny(rest[0]); break;
|
|
989
1288
|
case "approval":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clawmatrix",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Decentralized mesh cluster plugin for OpenClaw — inter-gateway communication, model proxy, task handoff, and tool proxy.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -36,13 +36,20 @@
|
|
|
36
36
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
37
37
|
"@automerge/automerge": "^3.2.4",
|
|
38
38
|
"@mariozechner/pi-coding-agent": ">=0.55.0",
|
|
39
|
+
"async-mutex": "^0.5.0",
|
|
40
|
+
"better-sqlite3": "^12.8.0",
|
|
41
|
+
"cockatiel": "^3.2.1",
|
|
42
|
+
"eventemitter3": "^5.0.4",
|
|
39
43
|
"ignore": "^7.0.5",
|
|
44
|
+
"lru-cache": "^11.2.7",
|
|
45
|
+
"nanoid": "^5.1.7",
|
|
40
46
|
"node-pty": "^1.0.0",
|
|
41
47
|
"picomatch": "^4.0.3",
|
|
42
48
|
"ws": "^8.19.0",
|
|
43
49
|
"zod": "^4.3.6"
|
|
44
50
|
},
|
|
45
51
|
"devDependencies": {
|
|
52
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
46
53
|
"@types/bun": "latest",
|
|
47
54
|
"@types/picomatch": "^4.0.2",
|
|
48
55
|
"@types/ws": "^8.18.1"
|