clawnexus 0.2.8 → 0.3.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/dist/a2a/card.d.ts +29 -0
- package/dist/a2a/card.js +30 -0
- package/dist/adapter/index.js +4 -0
- package/dist/adapter/nanobot.d.ts +1 -0
- package/dist/adapter/nanobot.js +17 -2
- package/dist/adapter/nanoclaw.d.ts +21 -4
- package/dist/adapter/nanoclaw.js +229 -44
- package/dist/adapter/openclaw.d.ts +9 -0
- package/dist/adapter/openclaw.js +62 -0
- package/dist/adapter/openfang.d.ts +11 -0
- package/dist/adapter/openfang.js +88 -0
- package/dist/adapter/types.d.ts +3 -0
- package/dist/agent/engine.js +9 -2
- package/dist/agent/executor.d.ts +48 -0
- package/dist/agent/executor.js +374 -0
- package/dist/agent/gateway.d.ts +26 -0
- package/dist/agent/gateway.js +298 -0
- package/dist/agent/protocol.js +2 -0
- package/dist/agent/router.d.ts +10 -1
- package/dist/agent/router.js +31 -2
- package/dist/agent/services.d.ts +36 -0
- package/dist/agent/services.js +153 -0
- package/dist/agent/tasks.js +4 -2
- package/dist/api/server.d.ts +7 -1
- package/dist/api/server.js +134 -10
- package/dist/discovery/broadcast.js +5 -4
- package/dist/health/checker.d.ts +4 -0
- package/dist/health/checker.js +84 -20
- package/dist/local/probe.d.ts +4 -0
- package/dist/local/probe.js +126 -12
- package/dist/registry/auto-register.js +32 -20
- package/dist/relay/connector.d.ts +5 -1
- package/dist/relay/connector.js +13 -2
- package/dist/types.d.ts +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import type { TaskManager } from "./tasks.js";
|
|
3
|
+
import type { AgentRouter } from "./router.js";
|
|
4
|
+
type GwState = "disconnected" | "connecting" | "ready" | "error";
|
|
5
|
+
export interface TaskExecutorOptions {
|
|
6
|
+
tasks: TaskManager;
|
|
7
|
+
gatewayUrl?: string;
|
|
8
|
+
maxConcurrent?: number;
|
|
9
|
+
}
|
|
10
|
+
export declare class TaskExecutor extends EventEmitter {
|
|
11
|
+
private readonly tasks;
|
|
12
|
+
private readonly gatewayUrl;
|
|
13
|
+
private readonly maxConcurrent;
|
|
14
|
+
private router;
|
|
15
|
+
private gwConn;
|
|
16
|
+
private gwState;
|
|
17
|
+
private queue;
|
|
18
|
+
private executing;
|
|
19
|
+
private stateChangeHandler;
|
|
20
|
+
private reconnectTimer;
|
|
21
|
+
private draining;
|
|
22
|
+
private closed;
|
|
23
|
+
constructor(opts: TaskExecutorOptions);
|
|
24
|
+
setRouter(router: AgentRouter): void;
|
|
25
|
+
start(): void;
|
|
26
|
+
enqueue(taskId: string): void;
|
|
27
|
+
getStatus(): {
|
|
28
|
+
gw_state: GwState;
|
|
29
|
+
queue_length: number;
|
|
30
|
+
executing: Array<{
|
|
31
|
+
task_id: string;
|
|
32
|
+
session_key: string;
|
|
33
|
+
}>;
|
|
34
|
+
max_concurrent: number;
|
|
35
|
+
};
|
|
36
|
+
close(): Promise<void>;
|
|
37
|
+
private ensureConnection;
|
|
38
|
+
private connectGatewayV3;
|
|
39
|
+
private handleGwMessage;
|
|
40
|
+
private handleGwEvent;
|
|
41
|
+
private handleTaskFinal;
|
|
42
|
+
private handleTaskError;
|
|
43
|
+
private drainQueue;
|
|
44
|
+
private executeTask;
|
|
45
|
+
private clearTaskTimers;
|
|
46
|
+
private scheduleReconnect;
|
|
47
|
+
}
|
|
48
|
+
export {};
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Layer B — Task Executor
|
|
3
|
+
// Connects to local OpenClaw Gateway via WebSocket, executes accepted inbound tasks,
|
|
4
|
+
// reports results back to the proposer via AgentRouter.
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.TaskExecutor = void 0;
|
|
7
|
+
const node_events_1 = require("node:events");
|
|
8
|
+
const ws_1 = require("ws");
|
|
9
|
+
const node_crypto_1 = require("node:crypto");
|
|
10
|
+
const gateway_js_1 = require("./gateway.js");
|
|
11
|
+
const DEFAULT_GW_URL = "ws://127.0.0.1:18789";
|
|
12
|
+
const HEARTBEAT_INTERVAL_MS = 15_000;
|
|
13
|
+
const DEFAULT_MAX_CONCURRENT = 3;
|
|
14
|
+
class TaskExecutor extends node_events_1.EventEmitter {
|
|
15
|
+
tasks;
|
|
16
|
+
gatewayUrl;
|
|
17
|
+
maxConcurrent;
|
|
18
|
+
router = null;
|
|
19
|
+
gwConn = null;
|
|
20
|
+
gwState = "disconnected";
|
|
21
|
+
// Queue of task IDs waiting to execute
|
|
22
|
+
queue = [];
|
|
23
|
+
// Currently executing tasks
|
|
24
|
+
executing = new Map();
|
|
25
|
+
stateChangeHandler = null;
|
|
26
|
+
reconnectTimer = null;
|
|
27
|
+
draining = false;
|
|
28
|
+
closed = false;
|
|
29
|
+
constructor(opts) {
|
|
30
|
+
super();
|
|
31
|
+
this.tasks = opts.tasks;
|
|
32
|
+
this.gatewayUrl = opts.gatewayUrl ?? DEFAULT_GW_URL;
|
|
33
|
+
this.maxConcurrent = opts.maxConcurrent ?? DEFAULT_MAX_CONCURRENT;
|
|
34
|
+
}
|
|
35
|
+
setRouter(router) {
|
|
36
|
+
this.router = router;
|
|
37
|
+
}
|
|
38
|
+
start() {
|
|
39
|
+
// Listen for accepted inbound tasks
|
|
40
|
+
this.stateChangeHandler = (task, newState) => {
|
|
41
|
+
if (newState === "accepted" && task.direction === "inbound") {
|
|
42
|
+
this.enqueue(task.task_id);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
this.tasks.on("stateChange", this.stateChangeHandler);
|
|
46
|
+
// Also pick up any already-accepted inbound tasks (e.g. after restart)
|
|
47
|
+
for (const task of this.tasks.getActive()) {
|
|
48
|
+
if (task.state === "accepted" && task.direction === "inbound") {
|
|
49
|
+
this.enqueue(task.task_id);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
enqueue(taskId) {
|
|
54
|
+
if (this.executing.has(taskId) || this.queue.includes(taskId))
|
|
55
|
+
return;
|
|
56
|
+
this.queue.push(taskId);
|
|
57
|
+
this.drainQueue();
|
|
58
|
+
}
|
|
59
|
+
getStatus() {
|
|
60
|
+
return {
|
|
61
|
+
gw_state: this.gwState,
|
|
62
|
+
queue_length: this.queue.length,
|
|
63
|
+
executing: Array.from(this.executing.values()).map((e) => ({
|
|
64
|
+
task_id: e.taskId,
|
|
65
|
+
session_key: e.sessionKey,
|
|
66
|
+
})),
|
|
67
|
+
max_concurrent: this.maxConcurrent,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
async close() {
|
|
71
|
+
this.closed = true;
|
|
72
|
+
if (this.stateChangeHandler) {
|
|
73
|
+
this.tasks.off("stateChange", this.stateChangeHandler);
|
|
74
|
+
this.stateChangeHandler = null;
|
|
75
|
+
}
|
|
76
|
+
if (this.reconnectTimer) {
|
|
77
|
+
clearTimeout(this.reconnectTimer);
|
|
78
|
+
this.reconnectTimer = null;
|
|
79
|
+
}
|
|
80
|
+
// Abort all executing tasks
|
|
81
|
+
for (const [taskId, exec] of this.executing) {
|
|
82
|
+
this.clearTaskTimers(exec);
|
|
83
|
+
this.tasks.updateState(taskId, "failed", { error: "Executor shutting down" });
|
|
84
|
+
}
|
|
85
|
+
this.executing.clear();
|
|
86
|
+
this.queue = [];
|
|
87
|
+
if (this.gwConn) {
|
|
88
|
+
this.gwConn.close();
|
|
89
|
+
this.gwConn = null;
|
|
90
|
+
}
|
|
91
|
+
this.gwState = "disconnected";
|
|
92
|
+
}
|
|
93
|
+
// --- Gateway Connection ---
|
|
94
|
+
async ensureConnection() {
|
|
95
|
+
if (this.gwState === "ready" && this.gwConn?.ws?.readyState === ws_1.WebSocket.OPEN) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
if (this.gwState === "connecting") {
|
|
99
|
+
// Already in progress — wait
|
|
100
|
+
return new Promise((resolve) => {
|
|
101
|
+
const onReady = () => { cleanup(); resolve(true); };
|
|
102
|
+
const onError = () => { cleanup(); resolve(false); };
|
|
103
|
+
const cleanup = () => {
|
|
104
|
+
this.off("gw:ready", onReady);
|
|
105
|
+
this.off("gw:error", onError);
|
|
106
|
+
};
|
|
107
|
+
this.once("gw:ready", onReady);
|
|
108
|
+
this.once("gw:error", onError);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return this.connectGatewayV3();
|
|
112
|
+
}
|
|
113
|
+
async connectGatewayV3() {
|
|
114
|
+
if (this.closed)
|
|
115
|
+
return false;
|
|
116
|
+
this.gwState = "connecting";
|
|
117
|
+
try {
|
|
118
|
+
const conn = await (0, gateway_js_1.connectGateway)({
|
|
119
|
+
gatewayUrl: this.gatewayUrl,
|
|
120
|
+
scopes: ["operator.read", "operator.write"],
|
|
121
|
+
});
|
|
122
|
+
this.gwConn = conn;
|
|
123
|
+
this.gwState = "ready";
|
|
124
|
+
// Set up event listener for runtime messages
|
|
125
|
+
conn.ws.on("message", (data) => {
|
|
126
|
+
let msg;
|
|
127
|
+
try {
|
|
128
|
+
msg = JSON.parse(data.toString());
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
this.handleGwMessage(msg);
|
|
134
|
+
});
|
|
135
|
+
conn.ws.on("close", () => {
|
|
136
|
+
const wasReady = this.gwState === "ready";
|
|
137
|
+
this.gwState = "disconnected";
|
|
138
|
+
this.gwConn = null;
|
|
139
|
+
if (wasReady) {
|
|
140
|
+
console.log("[clawnexus] [Executor] Gateway connection closed");
|
|
141
|
+
this.scheduleReconnect();
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
conn.ws.on("error", (err) => {
|
|
145
|
+
console.log(`[clawnexus] [Executor] Gateway error: ${err.message}`);
|
|
146
|
+
});
|
|
147
|
+
console.log("[clawnexus] [Executor] Gateway connection ready");
|
|
148
|
+
this.emit("gw:ready");
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
console.log(`[clawnexus] [Executor] Gateway connection failed: ${err.message}`);
|
|
153
|
+
this.gwState = "error";
|
|
154
|
+
this.emit("gw:error", err.message);
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
handleGwMessage(msg) {
|
|
159
|
+
const type = msg.type;
|
|
160
|
+
// Event frame
|
|
161
|
+
if (type === "event") {
|
|
162
|
+
this.handleGwEvent(msg);
|
|
163
|
+
}
|
|
164
|
+
// Error response
|
|
165
|
+
else if (type === "res" && msg.ok === false) {
|
|
166
|
+
const error = msg.error;
|
|
167
|
+
const id = msg.id;
|
|
168
|
+
if (id) {
|
|
169
|
+
// Find which task sent this request
|
|
170
|
+
for (const [, exec] of this.executing) {
|
|
171
|
+
if (exec.requestId === id) {
|
|
172
|
+
this.handleTaskError(exec.sessionKey, error?.message ?? "Gateway request error");
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
handleGwEvent(msg) {
|
|
180
|
+
const event = msg.event;
|
|
181
|
+
const payload = msg.payload;
|
|
182
|
+
// Chat events use the session key from payload or top-level
|
|
183
|
+
const sessionKey = payload?.sessionKey ?? msg.sessionKey;
|
|
184
|
+
if (!sessionKey)
|
|
185
|
+
return;
|
|
186
|
+
// Find the executing task for this sessionKey
|
|
187
|
+
let execEntry;
|
|
188
|
+
for (const exec of this.executing.values()) {
|
|
189
|
+
if (exec.sessionKey === sessionKey) {
|
|
190
|
+
execEntry = exec;
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (!execEntry)
|
|
195
|
+
return;
|
|
196
|
+
if (event === "chat" || event === "chat.update") {
|
|
197
|
+
const state = payload?.state ?? msg.data?.state;
|
|
198
|
+
if (state === "final") {
|
|
199
|
+
this.handleTaskFinal(execEntry, msg);
|
|
200
|
+
}
|
|
201
|
+
else if (state === "error") {
|
|
202
|
+
const errorMessage = payload?.errorMessage ?? "OpenClaw chat error";
|
|
203
|
+
this.handleTaskError(execEntry.sessionKey, errorMessage);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
handleTaskFinal(exec, msg) {
|
|
208
|
+
this.clearTaskTimers(exec);
|
|
209
|
+
const payload = msg.payload;
|
|
210
|
+
const data = payload ?? msg.data;
|
|
211
|
+
// Extract the assistant's reply from the final message
|
|
212
|
+
const messages = data?.messages;
|
|
213
|
+
let result = "";
|
|
214
|
+
if (messages && messages.length > 0) {
|
|
215
|
+
// Last assistant message
|
|
216
|
+
const lastAssistant = [...messages].reverse().find((m) => m.role === "assistant");
|
|
217
|
+
if (lastAssistant) {
|
|
218
|
+
const content = lastAssistant.content;
|
|
219
|
+
if (typeof content === "string") {
|
|
220
|
+
result = content;
|
|
221
|
+
}
|
|
222
|
+
else if (Array.isArray(content)) {
|
|
223
|
+
// Content blocks — extract text
|
|
224
|
+
result = content
|
|
225
|
+
.filter((b) => b.type === "text")
|
|
226
|
+
.map((b) => b.text)
|
|
227
|
+
.join("\n");
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// If no structured messages, try extracting from accumulated content
|
|
232
|
+
if (!result) {
|
|
233
|
+
result = data?.content ?? "Task completed (no output)";
|
|
234
|
+
}
|
|
235
|
+
const task = this.tasks.getById(exec.taskId);
|
|
236
|
+
this.tasks.updateState(exec.taskId, "completed", { result });
|
|
237
|
+
// Send report to proposer
|
|
238
|
+
if (task?.room_id && task.peer_claw_id && this.router) {
|
|
239
|
+
this.router.sendReport(task.room_id, task.peer_claw_id, exec.taskId, "completed", result);
|
|
240
|
+
}
|
|
241
|
+
this.executing.delete(exec.taskId);
|
|
242
|
+
this.emit("task:completed", exec.taskId);
|
|
243
|
+
this.drainQueue();
|
|
244
|
+
}
|
|
245
|
+
handleTaskError(sessionKey, errorMsg) {
|
|
246
|
+
for (const [taskId, exec] of this.executing) {
|
|
247
|
+
if (exec.sessionKey === sessionKey) {
|
|
248
|
+
this.clearTaskTimers(exec);
|
|
249
|
+
const task = this.tasks.getById(taskId);
|
|
250
|
+
this.tasks.updateState(taskId, "failed", { error: errorMsg });
|
|
251
|
+
if (task?.room_id && task.peer_claw_id && this.router) {
|
|
252
|
+
this.router.sendReport(task.room_id, task.peer_claw_id, taskId, "failed", undefined, errorMsg);
|
|
253
|
+
}
|
|
254
|
+
this.executing.delete(taskId);
|
|
255
|
+
this.emit("task:failed", taskId, errorMsg);
|
|
256
|
+
this.drainQueue();
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// --- Task Execution ---
|
|
262
|
+
async drainQueue() {
|
|
263
|
+
if (this.draining)
|
|
264
|
+
return;
|
|
265
|
+
this.draining = true;
|
|
266
|
+
try {
|
|
267
|
+
while (this.queue.length > 0 && this.executing.size < this.maxConcurrent && !this.closed) {
|
|
268
|
+
const taskId = this.queue.shift();
|
|
269
|
+
const task = this.tasks.getById(taskId);
|
|
270
|
+
if (!task || task.state !== "accepted" || task.direction !== "inbound") {
|
|
271
|
+
continue; // Skip stale entries
|
|
272
|
+
}
|
|
273
|
+
await this.executeTask(task);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
finally {
|
|
277
|
+
this.draining = false;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
async executeTask(task) {
|
|
281
|
+
const connected = await this.ensureConnection();
|
|
282
|
+
if (!connected || !this.gwConn || this.gwConn.ws.readyState !== ws_1.WebSocket.OPEN) {
|
|
283
|
+
console.log(`[clawnexus] [Executor] Cannot execute task ${task.task_id}: gateway not available`);
|
|
284
|
+
this.tasks.updateState(task.task_id, "failed", { error: "OpenClaw Gateway not available" });
|
|
285
|
+
if (task.room_id && task.peer_claw_id && this.router) {
|
|
286
|
+
this.router.sendReport(task.room_id, task.peer_claw_id, task.task_id, "failed", undefined, "OpenClaw Gateway not available");
|
|
287
|
+
}
|
|
288
|
+
this.emit("task:failed", task.task_id, "Gateway not available");
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
const sessionKey = `agent:main:main:dm:clawnexus-task-${task.task_id}`;
|
|
292
|
+
const message = task.task.description + (task.task.input ? "\n\n" + JSON.stringify(task.task.input) : "");
|
|
293
|
+
// Transition to executing
|
|
294
|
+
this.tasks.updateState(task.task_id, "executing");
|
|
295
|
+
const requestId = (0, node_crypto_1.randomUUID)();
|
|
296
|
+
const exec = {
|
|
297
|
+
taskId: task.task_id,
|
|
298
|
+
sessionKey,
|
|
299
|
+
requestId,
|
|
300
|
+
heartbeatTimer: null,
|
|
301
|
+
timeoutTimer: null,
|
|
302
|
+
aborted: false,
|
|
303
|
+
};
|
|
304
|
+
this.executing.set(task.task_id, exec);
|
|
305
|
+
// Send chat message to OpenClaw (v3 protocol frame format)
|
|
306
|
+
const chatMsg = {
|
|
307
|
+
type: "req",
|
|
308
|
+
id: requestId,
|
|
309
|
+
method: "chat.send",
|
|
310
|
+
params: {
|
|
311
|
+
sessionKey,
|
|
312
|
+
message,
|
|
313
|
+
idempotencyKey: requestId,
|
|
314
|
+
},
|
|
315
|
+
};
|
|
316
|
+
this.gwConn.ws.send(JSON.stringify(chatMsg));
|
|
317
|
+
// Start heartbeat (sends Layer B heartbeat to proposer every 15s)
|
|
318
|
+
if (task.room_id && task.peer_claw_id && this.router) {
|
|
319
|
+
exec.heartbeatTimer = setInterval(() => {
|
|
320
|
+
if (this.router && task.room_id) {
|
|
321
|
+
this.router.sendHeartbeat(task.room_id, task.peer_claw_id, task.task_id);
|
|
322
|
+
}
|
|
323
|
+
}, HEARTBEAT_INTERVAL_MS);
|
|
324
|
+
}
|
|
325
|
+
// Set up timeout
|
|
326
|
+
const maxDurationS = task.task.constraints?.max_duration_s ?? 600;
|
|
327
|
+
exec.timeoutTimer = setTimeout(() => {
|
|
328
|
+
if (exec.aborted)
|
|
329
|
+
return;
|
|
330
|
+
exec.aborted = true;
|
|
331
|
+
// Abort the chat
|
|
332
|
+
if (this.gwConn?.ws?.readyState === ws_1.WebSocket.OPEN) {
|
|
333
|
+
this.gwConn.ws.send(JSON.stringify({
|
|
334
|
+
type: "req",
|
|
335
|
+
id: (0, node_crypto_1.randomUUID)(),
|
|
336
|
+
method: "chat.abort",
|
|
337
|
+
params: { sessionKey },
|
|
338
|
+
}));
|
|
339
|
+
}
|
|
340
|
+
this.clearTaskTimers(exec);
|
|
341
|
+
this.tasks.updateState(task.task_id, "timeout");
|
|
342
|
+
if (task.room_id && task.peer_claw_id && this.router) {
|
|
343
|
+
this.router.sendReport(task.room_id, task.peer_claw_id, task.task_id, "failed", undefined, "Task execution timed out");
|
|
344
|
+
}
|
|
345
|
+
this.executing.delete(task.task_id);
|
|
346
|
+
this.emit("task:timeout", task.task_id);
|
|
347
|
+
this.drainQueue();
|
|
348
|
+
}, maxDurationS * 1000);
|
|
349
|
+
this.emit("task:executing", task.task_id);
|
|
350
|
+
console.log(`[clawnexus] [Executor] Executing task ${task.task_id} (session: ${sessionKey})`);
|
|
351
|
+
}
|
|
352
|
+
// --- Helpers ---
|
|
353
|
+
clearTaskTimers(exec) {
|
|
354
|
+
if (exec.heartbeatTimer) {
|
|
355
|
+
clearInterval(exec.heartbeatTimer);
|
|
356
|
+
exec.heartbeatTimer = null;
|
|
357
|
+
}
|
|
358
|
+
if (exec.timeoutTimer) {
|
|
359
|
+
clearTimeout(exec.timeoutTimer);
|
|
360
|
+
exec.timeoutTimer = null;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
scheduleReconnect() {
|
|
364
|
+
if (this.closed || this.reconnectTimer)
|
|
365
|
+
return;
|
|
366
|
+
this.reconnectTimer = setTimeout(() => {
|
|
367
|
+
this.reconnectTimer = null;
|
|
368
|
+
if (!this.closed && (this.executing.size > 0 || this.queue.length > 0)) {
|
|
369
|
+
this.ensureConnection().catch(() => { });
|
|
370
|
+
}
|
|
371
|
+
}, 5000);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
exports.TaskExecutor = TaskExecutor;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { WebSocket } from "ws";
|
|
2
|
+
interface DeviceIdentity {
|
|
3
|
+
deviceId: string;
|
|
4
|
+
publicKeyPem: string;
|
|
5
|
+
privateKeyPem: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function loadOrCreateDeviceIdentity(): DeviceIdentity;
|
|
8
|
+
export interface GatewayConnectionOptions {
|
|
9
|
+
gatewayUrl?: string;
|
|
10
|
+
connectTimeoutMs?: number;
|
|
11
|
+
requestTimeoutMs?: number;
|
|
12
|
+
role?: string;
|
|
13
|
+
scopes?: string[];
|
|
14
|
+
}
|
|
15
|
+
export interface GatewayConnection {
|
|
16
|
+
ws: WebSocket;
|
|
17
|
+
deviceId: string;
|
|
18
|
+
request(method: string, params?: unknown): Promise<unknown>;
|
|
19
|
+
close(): void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Connect to the OpenClaw Gateway using Protocol v3 handshake.
|
|
23
|
+
* Handles device identity, Ed25519 signing, and device token storage.
|
|
24
|
+
*/
|
|
25
|
+
export declare function connectGateway(opts?: GatewayConnectionOptions): Promise<GatewayConnection>;
|
|
26
|
+
export {};
|