clawmatrix 0.4.2 → 0.5.1
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 +225 -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 +237 -47
- package/src/retry.ts +81 -0
- package/src/router.ts +152 -104
- package/src/sentinel.ts +86 -52
- 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
|
@@ -8,7 +8,7 @@ export function createClusterEventsTool(): AnyAgentTool {
|
|
|
8
8
|
description:
|
|
9
9
|
"Query and consume events from external sources (iOS Shortcuts, webhooks, etc.). " +
|
|
10
10
|
"Common types: message_received, location_change, battery_status. " +
|
|
11
|
-
"Events are persistent until consumed. Requires
|
|
11
|
+
"Events are persistent until consumed. Requires listen mode (listen: true).",
|
|
12
12
|
parameters: {
|
|
13
13
|
type: "object",
|
|
14
14
|
properties: {
|
|
@@ -58,10 +58,10 @@ export function createClusterEventsTool(): AnyAgentTool {
|
|
|
58
58
|
|
|
59
59
|
try {
|
|
60
60
|
const runtime = getClusterRuntime();
|
|
61
|
-
const
|
|
62
|
-
if (!
|
|
61
|
+
const apiHandler = runtime.apiHandler;
|
|
62
|
+
if (!apiHandler) {
|
|
63
63
|
return {
|
|
64
|
-
content: [{ type: "text" as const, text: "Event ingestion requires
|
|
64
|
+
content: [{ type: "text" as const, text: "Event ingestion requires listen mode (listen: true in config)" }],
|
|
65
65
|
details: { error: true },
|
|
66
66
|
};
|
|
67
67
|
}
|
|
@@ -73,7 +73,7 @@ export function createClusterEventsTool(): AnyAgentTool {
|
|
|
73
73
|
details: { error: true },
|
|
74
74
|
};
|
|
75
75
|
}
|
|
76
|
-
const consumed =
|
|
76
|
+
const consumed = apiHandler.consumeEvents(ids);
|
|
77
77
|
return {
|
|
78
78
|
content: [{ type: "text" as const, text: `Consumed ${consumed} event(s)` }],
|
|
79
79
|
details: { consumed },
|
|
@@ -81,7 +81,7 @@ export function createClusterEventsTool(): AnyAgentTool {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
// action === "query"
|
|
84
|
-
const events =
|
|
84
|
+
const events = apiHandler.queryEvents({
|
|
85
85
|
type,
|
|
86
86
|
source,
|
|
87
87
|
since,
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import type { AnyAgentTool } from "openclaw/plugin-sdk";
|
|
2
|
+
import { getClusterRuntime } from "../cluster-service.ts";
|
|
3
|
+
import type { CardStage, CardPriority, CardAnnotation } from "../types.ts";
|
|
4
|
+
|
|
5
|
+
export function createClusterKanbanTool(): AnyAgentTool {
|
|
6
|
+
return {
|
|
7
|
+
name: "cluster_kanban",
|
|
8
|
+
label: "Cluster Kanban Board",
|
|
9
|
+
description:
|
|
10
|
+
"Manage the distributed kanban board for tracking work items across the cluster. " +
|
|
11
|
+
"Actions: " +
|
|
12
|
+
'"create" — create a new card. ' +
|
|
13
|
+
'"list" — list cards (optionally filtered by stage, label, node, priority). ' +
|
|
14
|
+
'"get" — get card details by ID. ' +
|
|
15
|
+
'"claim" — claim a backlog card for execution. ' +
|
|
16
|
+
'"move" — move a card to a different stage. ' +
|
|
17
|
+
'"annotate" — add a note, progress update, artifact link, or error to a card. ' +
|
|
18
|
+
'"update" — update card fields (title, description, priority, labels, etc.). ' +
|
|
19
|
+
'"delete" — delete a card. ' +
|
|
20
|
+
'"summary" — get board summary with counts by stage and priority.',
|
|
21
|
+
parameters: {
|
|
22
|
+
type: "object",
|
|
23
|
+
properties: {
|
|
24
|
+
action: {
|
|
25
|
+
type: "string",
|
|
26
|
+
enum: ["create", "list", "get", "claim", "move", "annotate", "update", "delete", "summary"],
|
|
27
|
+
description: 'Action to perform. Default: "list"',
|
|
28
|
+
},
|
|
29
|
+
cardId: {
|
|
30
|
+
type: "string",
|
|
31
|
+
description: "Card ID (e.g. CM-abc1-1) for get, claim, move, annotate, update, delete",
|
|
32
|
+
},
|
|
33
|
+
title: {
|
|
34
|
+
type: "string",
|
|
35
|
+
description: "Card title (for create or update)",
|
|
36
|
+
},
|
|
37
|
+
description: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: "Card description in Markdown (for create or update)",
|
|
40
|
+
},
|
|
41
|
+
stage: {
|
|
42
|
+
type: "string",
|
|
43
|
+
enum: ["backlog", "claimed", "in_progress", "review", "done", "archived"],
|
|
44
|
+
description: "Target stage (for move) or filter (for list)",
|
|
45
|
+
},
|
|
46
|
+
priority: {
|
|
47
|
+
type: "string",
|
|
48
|
+
enum: ["low", "medium", "high", "urgent"],
|
|
49
|
+
description: "Card priority (for create, update, or list filter)",
|
|
50
|
+
},
|
|
51
|
+
targetNode: {
|
|
52
|
+
type: "string",
|
|
53
|
+
description: "Preferred node ID for execution (for create or update)",
|
|
54
|
+
},
|
|
55
|
+
targetAgent: {
|
|
56
|
+
type: "string",
|
|
57
|
+
description: 'Preferred agent ID or "tags:<tag>" (for create or update)',
|
|
58
|
+
},
|
|
59
|
+
cwd: {
|
|
60
|
+
type: "string",
|
|
61
|
+
description: "Working directory for the task (for create or update)",
|
|
62
|
+
},
|
|
63
|
+
labels: {
|
|
64
|
+
type: "array",
|
|
65
|
+
items: { type: "string" },
|
|
66
|
+
description: "Labels for filtering (for create, update, or list filter)",
|
|
67
|
+
},
|
|
68
|
+
assignedNode: {
|
|
69
|
+
type: "string",
|
|
70
|
+
description: "Filter by assigned node (for list)",
|
|
71
|
+
},
|
|
72
|
+
agent: {
|
|
73
|
+
type: "string",
|
|
74
|
+
description: "Agent name (for claim or annotate)",
|
|
75
|
+
},
|
|
76
|
+
annotationType: {
|
|
77
|
+
type: "string",
|
|
78
|
+
enum: ["note", "session_link", "artifact", "progress", "error"],
|
|
79
|
+
description: "Type of annotation (for annotate action)",
|
|
80
|
+
},
|
|
81
|
+
content: {
|
|
82
|
+
type: "string",
|
|
83
|
+
description: "Annotation content (for annotate action)",
|
|
84
|
+
},
|
|
85
|
+
handoffId: {
|
|
86
|
+
type: "string",
|
|
87
|
+
description: "Associated handoff ID (for update)",
|
|
88
|
+
},
|
|
89
|
+
acpSessionId: {
|
|
90
|
+
type: "string",
|
|
91
|
+
description: "Associated ACP session ID (for update)",
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
required: [],
|
|
95
|
+
},
|
|
96
|
+
async execute(_toolCallId, params) {
|
|
97
|
+
const {
|
|
98
|
+
action = "list",
|
|
99
|
+
cardId,
|
|
100
|
+
title,
|
|
101
|
+
description,
|
|
102
|
+
stage,
|
|
103
|
+
priority,
|
|
104
|
+
targetNode,
|
|
105
|
+
targetAgent,
|
|
106
|
+
cwd,
|
|
107
|
+
labels,
|
|
108
|
+
assignedNode,
|
|
109
|
+
agent,
|
|
110
|
+
annotationType,
|
|
111
|
+
content,
|
|
112
|
+
handoffId,
|
|
113
|
+
acpSessionId,
|
|
114
|
+
} = params as {
|
|
115
|
+
action?: string;
|
|
116
|
+
cardId?: string;
|
|
117
|
+
title?: string;
|
|
118
|
+
description?: string;
|
|
119
|
+
stage?: CardStage;
|
|
120
|
+
priority?: CardPriority;
|
|
121
|
+
targetNode?: string;
|
|
122
|
+
targetAgent?: string;
|
|
123
|
+
cwd?: string;
|
|
124
|
+
labels?: string[];
|
|
125
|
+
assignedNode?: string;
|
|
126
|
+
agent?: string;
|
|
127
|
+
annotationType?: CardAnnotation["type"];
|
|
128
|
+
content?: string;
|
|
129
|
+
handoffId?: string;
|
|
130
|
+
acpSessionId?: string;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const runtime = getClusterRuntime();
|
|
135
|
+
const km = runtime.kanbanManager;
|
|
136
|
+
|
|
137
|
+
if (!km) {
|
|
138
|
+
return {
|
|
139
|
+
content: [{ type: "text" as const, text: "Kanban board not available (kanban.enabled is false in config)" }],
|
|
140
|
+
details: { error: true },
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (action === "summary") {
|
|
145
|
+
const summary = km.getSummary();
|
|
146
|
+
return {
|
|
147
|
+
content: [{ type: "text" as const, text: JSON.stringify(summary) }],
|
|
148
|
+
details: summary,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (action === "list") {
|
|
153
|
+
const cards = km.listCards({
|
|
154
|
+
stage,
|
|
155
|
+
label: labels?.[0],
|
|
156
|
+
assignedNode,
|
|
157
|
+
priority,
|
|
158
|
+
});
|
|
159
|
+
// Return compact list (no annotations to save tokens)
|
|
160
|
+
const compact = cards.map((c) => ({
|
|
161
|
+
id: c.id,
|
|
162
|
+
title: c.title,
|
|
163
|
+
stage: c.stage,
|
|
164
|
+
priority: c.priority,
|
|
165
|
+
assignedNode: c.assignedNode,
|
|
166
|
+
assignedAgent: c.assignedAgent,
|
|
167
|
+
labels: c.labels,
|
|
168
|
+
createdAt: c.createdAt,
|
|
169
|
+
updatedAt: c.updatedAt,
|
|
170
|
+
}));
|
|
171
|
+
return {
|
|
172
|
+
content: [{ type: "text" as const, text: JSON.stringify(compact) }],
|
|
173
|
+
details: compact,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (action === "create") {
|
|
178
|
+
if (!title) {
|
|
179
|
+
return {
|
|
180
|
+
content: [{ type: "text" as const, text: "title is required for create action" }],
|
|
181
|
+
details: { error: true },
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
const card = km.createCard({
|
|
185
|
+
title,
|
|
186
|
+
description,
|
|
187
|
+
priority,
|
|
188
|
+
targetNode,
|
|
189
|
+
targetAgent,
|
|
190
|
+
cwd,
|
|
191
|
+
labels,
|
|
192
|
+
});
|
|
193
|
+
return {
|
|
194
|
+
content: [{ type: "text" as const, text: JSON.stringify(card) }],
|
|
195
|
+
details: card,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (action === "get") {
|
|
200
|
+
if (!cardId) {
|
|
201
|
+
return {
|
|
202
|
+
content: [{ type: "text" as const, text: "cardId is required for get action" }],
|
|
203
|
+
details: { error: true },
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
const card = km.getCard(cardId);
|
|
207
|
+
if (!card) {
|
|
208
|
+
return {
|
|
209
|
+
content: [{ type: "text" as const, text: `Card not found: ${cardId}` }],
|
|
210
|
+
details: { error: true },
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
content: [{ type: "text" as const, text: JSON.stringify(card) }],
|
|
215
|
+
details: card,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (action === "claim") {
|
|
220
|
+
if (!cardId) {
|
|
221
|
+
return {
|
|
222
|
+
content: [{ type: "text" as const, text: "cardId is required for claim action" }],
|
|
223
|
+
details: { error: true },
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
const card = km.claimCard(
|
|
227
|
+
cardId,
|
|
228
|
+
runtime.config.nodeId,
|
|
229
|
+
agent ?? runtime.config.agents[0]?.id ?? "unknown",
|
|
230
|
+
);
|
|
231
|
+
if (!card) {
|
|
232
|
+
return {
|
|
233
|
+
content: [{ type: "text" as const, text: `Cannot claim card ${cardId} (not found or not in backlog)` }],
|
|
234
|
+
details: { error: true },
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
return {
|
|
238
|
+
content: [{ type: "text" as const, text: JSON.stringify(card) }],
|
|
239
|
+
details: card,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (action === "move") {
|
|
244
|
+
if (!cardId || !stage) {
|
|
245
|
+
return {
|
|
246
|
+
content: [{ type: "text" as const, text: "cardId and stage are required for move action" }],
|
|
247
|
+
details: { error: true },
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
const card = km.moveCard(cardId, stage);
|
|
251
|
+
if (!card) {
|
|
252
|
+
return {
|
|
253
|
+
content: [{ type: "text" as const, text: `Cannot move card ${cardId} (not found or invalid stage)` }],
|
|
254
|
+
details: { error: true },
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
content: [{ type: "text" as const, text: JSON.stringify(card) }],
|
|
259
|
+
details: card,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (action === "annotate") {
|
|
264
|
+
if (!cardId || !content) {
|
|
265
|
+
return {
|
|
266
|
+
content: [{ type: "text" as const, text: "cardId and content are required for annotate action" }],
|
|
267
|
+
details: { error: true },
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
const card = km.annotateCard(cardId, {
|
|
271
|
+
nodeId: runtime.config.nodeId,
|
|
272
|
+
agent: agent ?? runtime.config.agents[0]?.id ?? "unknown",
|
|
273
|
+
type: annotationType ?? "note",
|
|
274
|
+
content,
|
|
275
|
+
});
|
|
276
|
+
if (!card) {
|
|
277
|
+
return {
|
|
278
|
+
content: [{ type: "text" as const, text: `Card not found: ${cardId}` }],
|
|
279
|
+
details: { error: true },
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
content: [{ type: "text" as const, text: JSON.stringify(card) }],
|
|
284
|
+
details: card,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (action === "update") {
|
|
289
|
+
if (!cardId) {
|
|
290
|
+
return {
|
|
291
|
+
content: [{ type: "text" as const, text: "cardId is required for update action" }],
|
|
292
|
+
details: { error: true },
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
const card = km.updateCard(cardId, {
|
|
296
|
+
title,
|
|
297
|
+
description,
|
|
298
|
+
priority,
|
|
299
|
+
targetNode,
|
|
300
|
+
targetAgent,
|
|
301
|
+
cwd,
|
|
302
|
+
labels,
|
|
303
|
+
handoffId,
|
|
304
|
+
acpSessionId,
|
|
305
|
+
});
|
|
306
|
+
if (!card) {
|
|
307
|
+
return {
|
|
308
|
+
content: [{ type: "text" as const, text: `Card not found: ${cardId}` }],
|
|
309
|
+
details: { error: true },
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
return {
|
|
313
|
+
content: [{ type: "text" as const, text: JSON.stringify(card) }],
|
|
314
|
+
details: card,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (action === "delete") {
|
|
319
|
+
if (!cardId) {
|
|
320
|
+
return {
|
|
321
|
+
content: [{ type: "text" as const, text: "cardId is required for delete action" }],
|
|
322
|
+
details: { error: true },
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
const ok = km.deleteCard(cardId);
|
|
326
|
+
const result = { deleted: ok, cardId };
|
|
327
|
+
return {
|
|
328
|
+
content: [{ type: "text" as const, text: JSON.stringify(result) }],
|
|
329
|
+
details: result,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return {
|
|
334
|
+
content: [{ type: "text" as const, text: `Unknown action: ${action}` }],
|
|
335
|
+
details: { error: true },
|
|
336
|
+
};
|
|
337
|
+
} catch (err) {
|
|
338
|
+
return {
|
|
339
|
+
content: [{ type: "text" as const, text: `Kanban error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
340
|
+
details: { error: true },
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
};
|
|
345
|
+
}
|
|
@@ -82,7 +82,7 @@ export function createClusterPeersTool(): AnyAgentTool {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
// Include satellite nodes (minimal fields — no agents/models)
|
|
85
|
-
const satellites = runtime.
|
|
85
|
+
const satellites = runtime.apiHandler?.getSatelliteContexts() ?? runtime.peerManager.satelliteContexts;
|
|
86
86
|
for (const sat of satellites) {
|
|
87
87
|
if (Date.now() - sat.ts >= 600_000) continue;
|
|
88
88
|
peers.push({
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { AnyAgentTool } from "openclaw/plugin-sdk";
|
|
2
|
+
import { getClusterRuntime } from "../cluster-service.ts";
|
|
3
|
+
|
|
4
|
+
export function createClusterQueryTool(): AnyAgentTool {
|
|
5
|
+
return {
|
|
6
|
+
name: "cluster_query",
|
|
7
|
+
label: "Cluster Query",
|
|
8
|
+
description:
|
|
9
|
+
"Query replicated data from the local SQLite store: audit_log (security events), " +
|
|
10
|
+
"health_events (node lifecycle & peer connectivity), handoff_history (task execution records). " +
|
|
11
|
+
"Data is replicated across peers via log sync, so each node has a cluster-wide view.",
|
|
12
|
+
parameters: {
|
|
13
|
+
type: "object",
|
|
14
|
+
properties: {
|
|
15
|
+
table: {
|
|
16
|
+
type: "string",
|
|
17
|
+
enum: ["audit_log", "health_events", "handoff_history"],
|
|
18
|
+
description: "Which replicated table to query",
|
|
19
|
+
},
|
|
20
|
+
since: {
|
|
21
|
+
type: "number",
|
|
22
|
+
description: "Events after this unix timestamp (ms). Shorthand: use negative values for relative offsets, e.g. -3600000 for last hour",
|
|
23
|
+
},
|
|
24
|
+
node_id: {
|
|
25
|
+
type: "string",
|
|
26
|
+
description: "Filter by originating node ID",
|
|
27
|
+
},
|
|
28
|
+
event: {
|
|
29
|
+
type: "string",
|
|
30
|
+
description: "Filter audit_log by event type (conn_open, auth_failure, peer_join, etc.)",
|
|
31
|
+
},
|
|
32
|
+
type: {
|
|
33
|
+
type: "string",
|
|
34
|
+
description: "Filter health_events by type (start, stop, peer_online, peer_offline)",
|
|
35
|
+
},
|
|
36
|
+
peer: {
|
|
37
|
+
type: "string",
|
|
38
|
+
description: "Filter health_events by peer node ID",
|
|
39
|
+
},
|
|
40
|
+
agent: {
|
|
41
|
+
type: "string",
|
|
42
|
+
description: "Filter handoff_history by agent name",
|
|
43
|
+
},
|
|
44
|
+
status: {
|
|
45
|
+
type: "string",
|
|
46
|
+
description: "Filter handoff_history by status (completed, failed)",
|
|
47
|
+
},
|
|
48
|
+
limit: {
|
|
49
|
+
type: "number",
|
|
50
|
+
description: "Max rows to return (default 50)",
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
required: ["table"],
|
|
54
|
+
},
|
|
55
|
+
async execute(_toolCallId, params) {
|
|
56
|
+
const {
|
|
57
|
+
table, since, node_id, event, type, peer, agent, status, limit,
|
|
58
|
+
} = params as {
|
|
59
|
+
table: "audit_log" | "health_events" | "handoff_history";
|
|
60
|
+
since?: number;
|
|
61
|
+
node_id?: string;
|
|
62
|
+
event?: string;
|
|
63
|
+
type?: string;
|
|
64
|
+
peer?: string;
|
|
65
|
+
agent?: string;
|
|
66
|
+
status?: string;
|
|
67
|
+
limit?: number;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const runtime = getClusterRuntime();
|
|
72
|
+
const store = runtime.store;
|
|
73
|
+
if (!store) {
|
|
74
|
+
return {
|
|
75
|
+
content: [{ type: "text" as const, text: "Store not initialized" }],
|
|
76
|
+
details: { error: true },
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Handle relative since values (negative = offset from now)
|
|
81
|
+
const resolvedSince = since != null && since < 0 ? Date.now() + since : since;
|
|
82
|
+
const maxRows = Math.max(1, Math.min(limit ?? 50, 500));
|
|
83
|
+
|
|
84
|
+
let rows: unknown[];
|
|
85
|
+
let summary: string;
|
|
86
|
+
|
|
87
|
+
switch (table) {
|
|
88
|
+
case "audit_log": {
|
|
89
|
+
const result = store.queryAudit({
|
|
90
|
+
since: resolvedSince,
|
|
91
|
+
nodeId: node_id,
|
|
92
|
+
event,
|
|
93
|
+
limit: maxRows,
|
|
94
|
+
});
|
|
95
|
+
rows = result;
|
|
96
|
+
summary = `${result.length} audit event(s)`;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
case "health_events": {
|
|
100
|
+
const result = store.queryHealth({
|
|
101
|
+
since: resolvedSince,
|
|
102
|
+
nodeId: node_id,
|
|
103
|
+
type,
|
|
104
|
+
peer,
|
|
105
|
+
limit: maxRows,
|
|
106
|
+
});
|
|
107
|
+
rows = result;
|
|
108
|
+
summary = `${result.length} health event(s)`;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
case "handoff_history": {
|
|
112
|
+
const result = store.queryHandoff({
|
|
113
|
+
since: resolvedSince,
|
|
114
|
+
nodeId: node_id,
|
|
115
|
+
agent,
|
|
116
|
+
status,
|
|
117
|
+
limit: maxRows,
|
|
118
|
+
});
|
|
119
|
+
rows = result;
|
|
120
|
+
summary = `${result.length} handoff record(s)`;
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (rows.length === 0) {
|
|
126
|
+
return {
|
|
127
|
+
content: [{ type: "text" as const, text: `No ${table} records found matching filters` }],
|
|
128
|
+
details: { count: 0, table },
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const text = `${summary}\n${JSON.stringify(rows, null, 2)}`;
|
|
133
|
+
return {
|
|
134
|
+
content: [{ type: "text" as const, text }],
|
|
135
|
+
details: { count: rows.length, table },
|
|
136
|
+
};
|
|
137
|
+
} catch (err) {
|
|
138
|
+
return {
|
|
139
|
+
content: [{ type: "text" as const, text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
|
|
140
|
+
details: { error: true },
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -89,6 +89,14 @@ export interface PeerSync extends ClusterFrame {
|
|
|
89
89
|
payload: {
|
|
90
90
|
peers: PeerInfo[];
|
|
91
91
|
satellites?: SatelliteContext[];
|
|
92
|
+
/** Sync version number for delta sync. */
|
|
93
|
+
version?: number;
|
|
94
|
+
/** Delta: newly added or changed peers since last sync. */
|
|
95
|
+
added?: PeerInfo[];
|
|
96
|
+
/** Delta: removed nodeIds since last sync. */
|
|
97
|
+
removed?: string[];
|
|
98
|
+
/** Delta: peers with updated capabilities. */
|
|
99
|
+
updated?: PeerInfo[];
|
|
92
100
|
};
|
|
93
101
|
}
|
|
94
102
|
|
|
@@ -173,12 +181,6 @@ export interface ImageContent {
|
|
|
173
181
|
// ── Handoff ────────────────────────────────────────────────────────
|
|
174
182
|
export type HandoffStatus = "working" | "input_required" | "completed" | "failed" | "canceled";
|
|
175
183
|
|
|
176
|
-
export interface Artifact {
|
|
177
|
-
name: string;
|
|
178
|
-
mimeType: string;
|
|
179
|
-
data: string; // text content or base64-encoded binary
|
|
180
|
-
}
|
|
181
|
-
|
|
182
184
|
export interface HandoffRequest extends ClusterFrame {
|
|
183
185
|
type: "handoff_req";
|
|
184
186
|
id: string;
|
|
@@ -197,7 +199,6 @@ export interface HandoffStreamChunk extends ClusterFrame {
|
|
|
197
199
|
payload: {
|
|
198
200
|
delta: string;
|
|
199
201
|
done: boolean;
|
|
200
|
-
artifacts?: Artifact[];
|
|
201
202
|
sessionId?: string; // included for session watchers (multi-device sync)
|
|
202
203
|
};
|
|
203
204
|
}
|
|
@@ -211,7 +212,6 @@ export interface HandoffResponse extends ClusterFrame {
|
|
|
211
212
|
agent?: string;
|
|
212
213
|
result?: string;
|
|
213
214
|
error?: string;
|
|
214
|
-
artifacts?: Artifact[];
|
|
215
215
|
inputRequired?: boolean;
|
|
216
216
|
handoffId?: string;
|
|
217
217
|
sessionId?: string; // For multi-turn conversation reuse
|
|
@@ -388,6 +388,8 @@ export interface DeviceInfo {
|
|
|
388
388
|
totalMemoryMB: number; // total system memory in MB
|
|
389
389
|
hostname: string; // machine hostname
|
|
390
390
|
openclawVersion?: string; // e.g. "2026.3.7"
|
|
391
|
+
cwd?: string; // process.cwd() at gateway startup
|
|
392
|
+
workspace?: string; // OpenClaw workspace dir (agents.defaults.workspace)
|
|
391
393
|
}
|
|
392
394
|
|
|
393
395
|
// ── Shared info types ──────────────────────────────────────────────
|
|
@@ -955,6 +957,87 @@ export interface TerminalCloseResponse extends ClusterFrame {
|
|
|
955
957
|
};
|
|
956
958
|
}
|
|
957
959
|
|
|
960
|
+
// ── Kanban board ─────────────────────────────────────────────────
|
|
961
|
+
|
|
962
|
+
export type CardStage = "backlog" | "claimed" | "in_progress" | "review" | "done" | "archived";
|
|
963
|
+
export type CardPriority = "low" | "medium" | "high" | "urgent";
|
|
964
|
+
|
|
965
|
+
export interface CardAnnotation {
|
|
966
|
+
id: string;
|
|
967
|
+
nodeId: string;
|
|
968
|
+
agent: string;
|
|
969
|
+
type: "note" | "session_link" | "artifact" | "progress" | "error";
|
|
970
|
+
content: string;
|
|
971
|
+
ts: number;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
export interface KanbanCard {
|
|
975
|
+
id: string;
|
|
976
|
+
title: string;
|
|
977
|
+
description: string;
|
|
978
|
+
stage: CardStage;
|
|
979
|
+
priority: CardPriority;
|
|
980
|
+
targetNode?: string;
|
|
981
|
+
targetAgent?: string;
|
|
982
|
+
cwd?: string;
|
|
983
|
+
assignedNode?: string;
|
|
984
|
+
assignedAgent?: string;
|
|
985
|
+
claimedAt?: number;
|
|
986
|
+
handoffId?: string;
|
|
987
|
+
acpSessionId?: string;
|
|
988
|
+
annotations: CardAnnotation[];
|
|
989
|
+
createdBy: string;
|
|
990
|
+
createdAt: number;
|
|
991
|
+
updatedAt: number;
|
|
992
|
+
completedAt?: number;
|
|
993
|
+
labels: string[];
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
export interface KanbanBoardDoc {
|
|
997
|
+
cards: Record<string, KanbanCard>;
|
|
998
|
+
nextSeq: number;
|
|
999
|
+
config: {
|
|
1000
|
+
prefix: string;
|
|
1001
|
+
autoAssign: boolean;
|
|
1002
|
+
stages: CardStage[];
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
export interface KanbanSyncFrame extends ClusterFrame {
|
|
1007
|
+
type: "kanban_sync";
|
|
1008
|
+
payload: { data: string };
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
export interface KanbanNotifyFrame extends ClusterFrame {
|
|
1012
|
+
type: "kanban_notify";
|
|
1013
|
+
payload: {
|
|
1014
|
+
event: "card_created" | "card_claimed" | "card_stage_changed" | "card_completed" | "card_annotated";
|
|
1015
|
+
cardId: string;
|
|
1016
|
+
cardTitle: string;
|
|
1017
|
+
stage?: CardStage;
|
|
1018
|
+
nodeId?: string;
|
|
1019
|
+
agent?: string;
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
// ── Log replication ──────────────────────────────────────────────
|
|
1024
|
+
|
|
1025
|
+
export interface LogSyncFrame extends ClusterFrame {
|
|
1026
|
+
type: "log_sync";
|
|
1027
|
+
payload: {
|
|
1028
|
+
/** Which replicated table this sync concerns. */
|
|
1029
|
+
table: "audit_log" | "health_events" | "handoff_history";
|
|
1030
|
+
/** Sender's vector clock for this table: { nodeId: maxSourceSeq }. */
|
|
1031
|
+
vector?: Record<string, number>;
|
|
1032
|
+
/** Delta rows for the receiver to apply. */
|
|
1033
|
+
rows?: Array<Record<string, unknown>>;
|
|
1034
|
+
/** True when this is a sync request (receiver should compute and send delta). */
|
|
1035
|
+
request?: boolean;
|
|
1036
|
+
/** True when more batches follow (receiver should expect continuation). */
|
|
1037
|
+
hasMore?: boolean;
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
|
|
958
1041
|
// ── Union of all frame types ───────────────────────────────────────
|
|
959
1042
|
export type AnyClusterFrame =
|
|
960
1043
|
| AuthChallenge
|
|
@@ -1029,4 +1112,7 @@ export type AnyClusterFrame =
|
|
|
1029
1112
|
| FileTransferAck
|
|
1030
1113
|
| FileTransferChunk
|
|
1031
1114
|
| FileTransferChunkAck
|
|
1032
|
-
| FileTransferComplete
|
|
1115
|
+
| FileTransferComplete
|
|
1116
|
+
| KanbanSyncFrame
|
|
1117
|
+
| KanbanNotifyFrame
|
|
1118
|
+
| LogSyncFrame;
|