@moxxy/cli 0.0.2 → 0.0.3
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/chunk-6DZX6EAA.mjs +37 -0
- package/dist/chunk-C46NSEKG.mjs +211 -0
- package/dist/chunk-NGVL4Q5C.mjs +1102 -0
- package/dist/cli-7MK4YGOP.mjs +7 -0
- package/dist/dist-7LTHRYKA.mjs +11569 -0
- package/dist/dist-FAXRJMEN.mjs +6812 -0
- package/dist/dist-LKIOZQ42.mjs +17 -0
- package/dist/dist-UYA4RJUH.mjs +2792 -0
- package/dist/index.js +23868 -111
- package/dist/index.mjs +3 -2
- package/dist/src-D5HMDDVE.mjs +1324 -0
- package/package.json +11 -11
|
@@ -0,0 +1,2792 @@
|
|
|
1
|
+
import "./chunk-6DZX6EAA.mjs";
|
|
2
|
+
|
|
3
|
+
// ../molt/dist/index.mjs
|
|
4
|
+
import { randomUUID } from "crypto";
|
|
5
|
+
import {
|
|
6
|
+
createHash,
|
|
7
|
+
createPrivateKey,
|
|
8
|
+
createPublicKey,
|
|
9
|
+
sign as cryptoSign,
|
|
10
|
+
verify as cryptoVerify,
|
|
11
|
+
generateKeyPairSync
|
|
12
|
+
} from "crypto";
|
|
13
|
+
import { exec } from "child_process";
|
|
14
|
+
import { promisify } from "util";
|
|
15
|
+
var ED25519_SPKI_PREFIX = Buffer.from("302a300506032b6570032100", "hex");
|
|
16
|
+
var DeviceIdentityManager = class {
|
|
17
|
+
identity = null;
|
|
18
|
+
/**
|
|
19
|
+
* Base64URL encode (Clawdbot format)
|
|
20
|
+
*/
|
|
21
|
+
base64UrlEncode(buf) {
|
|
22
|
+
return buf.toString("base64").replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/g, "");
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Base64URL decode
|
|
26
|
+
*/
|
|
27
|
+
base64UrlDecode(input) {
|
|
28
|
+
const normalized = input.replaceAll("-", "+").replaceAll("_", "/");
|
|
29
|
+
const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
|
|
30
|
+
return Buffer.from(padded, "base64");
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Extract raw 32-byte public key from PEM
|
|
34
|
+
*/
|
|
35
|
+
derivePublicKeyRaw(publicKeyPem) {
|
|
36
|
+
const key = createPublicKey(publicKeyPem);
|
|
37
|
+
const spki = key.export({ type: "spki", format: "der" });
|
|
38
|
+
if (spki.length === ED25519_SPKI_PREFIX.length + 32 && spki.subarray(0, ED25519_SPKI_PREFIX.length).equals(ED25519_SPKI_PREFIX)) {
|
|
39
|
+
return spki.subarray(ED25519_SPKI_PREFIX.length);
|
|
40
|
+
}
|
|
41
|
+
return spki;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Generate device ID fingerprint (SHA-256 of raw public key)
|
|
45
|
+
*/
|
|
46
|
+
fingerprintPublicKey(publicKeyPem) {
|
|
47
|
+
const raw = this.derivePublicKeyRaw(publicKeyPem);
|
|
48
|
+
return createHash("sha256").update(raw).digest("hex");
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Generate a new device identity
|
|
52
|
+
*/
|
|
53
|
+
async generateIdentity(deviceId) {
|
|
54
|
+
const { publicKey, privateKey } = generateKeyPairSync("ed25519");
|
|
55
|
+
const publicKeyPem = publicKey.export({ type: "spki", format: "pem" }).toString();
|
|
56
|
+
const privateKeyPem = privateKey.export({ type: "pkcs8", format: "pem" }).toString();
|
|
57
|
+
const generatedDeviceId = deviceId || this.fingerprintPublicKey(publicKeyPem);
|
|
58
|
+
const identity = {
|
|
59
|
+
id: generatedDeviceId,
|
|
60
|
+
publicKeyPem,
|
|
61
|
+
privateKeyPem
|
|
62
|
+
};
|
|
63
|
+
this.identity = identity;
|
|
64
|
+
return identity;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Load identity from storage
|
|
68
|
+
*/
|
|
69
|
+
setIdentity(identity) {
|
|
70
|
+
this.identity = identity;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Load identity from a private key PEM string
|
|
74
|
+
* The public key and device ID are derived from the private key
|
|
75
|
+
*/
|
|
76
|
+
loadFromPrivateKey(privateKeyPem) {
|
|
77
|
+
const normalizedPem = privateKeyPem.replace(/\\n/g, "\n");
|
|
78
|
+
const privateKey = createPrivateKey(normalizedPem);
|
|
79
|
+
const publicKey = createPublicKey(privateKey);
|
|
80
|
+
const publicKeyPem = publicKey.export({ type: "spki", format: "pem" }).toString();
|
|
81
|
+
const privateKeyPemNormalized = privateKey.export({ type: "pkcs8", format: "pem" }).toString();
|
|
82
|
+
const deviceId = this.fingerprintPublicKey(publicKeyPem);
|
|
83
|
+
const identity = {
|
|
84
|
+
id: deviceId,
|
|
85
|
+
publicKeyPem,
|
|
86
|
+
privateKeyPem: privateKeyPemNormalized
|
|
87
|
+
};
|
|
88
|
+
this.identity = identity;
|
|
89
|
+
return identity;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get current identity
|
|
93
|
+
*/
|
|
94
|
+
getIdentity() {
|
|
95
|
+
return this.identity;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Sign device auth payload (Clawdbot format)
|
|
99
|
+
* Payload format: v2|deviceId|clientId|clientMode|role|scopes|signedAtMs|token|nonce
|
|
100
|
+
*/
|
|
101
|
+
async signDeviceAuthPayload(params) {
|
|
102
|
+
if (!this.identity) {
|
|
103
|
+
throw new Error("Device identity not initialized");
|
|
104
|
+
}
|
|
105
|
+
const version = "v2";
|
|
106
|
+
const scopes = params.scopes.join(",");
|
|
107
|
+
const token = params.token ?? "";
|
|
108
|
+
const payloadParts = [
|
|
109
|
+
version,
|
|
110
|
+
params.deviceId,
|
|
111
|
+
params.clientId,
|
|
112
|
+
params.clientMode,
|
|
113
|
+
params.role,
|
|
114
|
+
scopes,
|
|
115
|
+
String(params.signedAtMs),
|
|
116
|
+
token,
|
|
117
|
+
params.nonce
|
|
118
|
+
];
|
|
119
|
+
const payload = payloadParts.join("|");
|
|
120
|
+
const key = createPrivateKey(this.identity.privateKeyPem);
|
|
121
|
+
const sig = cryptoSign(null, Buffer.from(payload, "utf8"), key);
|
|
122
|
+
const signature = this.base64UrlEncode(sig);
|
|
123
|
+
if (process.env.DEBUG_MOLT_AUTH) {
|
|
124
|
+
console.log("[Auth Debug] Payload:", payload);
|
|
125
|
+
console.log("[Auth Debug] Signature (base64url):", signature);
|
|
126
|
+
console.log("[Auth Debug] Public key (raw, base64url):", this.getPublicKeyBase64Url());
|
|
127
|
+
console.log("[Auth Debug] Device ID:", this.identity.id);
|
|
128
|
+
}
|
|
129
|
+
return signature;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Get public key in base64url format (raw 32 bytes)
|
|
133
|
+
*/
|
|
134
|
+
getPublicKeyBase64Url() {
|
|
135
|
+
if (!this.identity) {
|
|
136
|
+
throw new Error("Device identity not initialized");
|
|
137
|
+
}
|
|
138
|
+
const raw = this.derivePublicKeyRaw(this.identity.publicKeyPem);
|
|
139
|
+
return this.base64UrlEncode(raw);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Verify a signature (for testing)
|
|
143
|
+
*/
|
|
144
|
+
async verifySignature(nonce, signature, publicKey) {
|
|
145
|
+
const pubKey = publicKey || this.identity?.publicKeyPem;
|
|
146
|
+
if (!pubKey) {
|
|
147
|
+
throw new Error("Public key not available");
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
const key = pubKey.includes("BEGIN") ? createPublicKey(pubKey) : createPublicKey({
|
|
151
|
+
key: Buffer.concat([ED25519_SPKI_PREFIX, this.base64UrlDecode(publicKey)]),
|
|
152
|
+
type: "spki",
|
|
153
|
+
format: "der"
|
|
154
|
+
});
|
|
155
|
+
const sig = this.base64UrlDecode(signature);
|
|
156
|
+
return cryptoVerify(null, Buffer.from(nonce, "utf8"), key, sig);
|
|
157
|
+
} catch (_error) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Export identity for storage
|
|
163
|
+
*/
|
|
164
|
+
exportIdentity() {
|
|
165
|
+
return this.identity;
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
var ClawdbotClient = class {
|
|
169
|
+
ws = null;
|
|
170
|
+
config;
|
|
171
|
+
deviceManager;
|
|
172
|
+
connectionState = "disconnected";
|
|
173
|
+
connectRequestId = null;
|
|
174
|
+
pendingRequests = /* @__PURE__ */ new Map();
|
|
175
|
+
eventHandlers = /* @__PURE__ */ new Map();
|
|
176
|
+
reconnectAttempts = 0;
|
|
177
|
+
reconnectTimer = null;
|
|
178
|
+
/** Cached snapshot from connection (contains health, agents, etc.) */
|
|
179
|
+
connectionSnapshot = null;
|
|
180
|
+
constructor(config) {
|
|
181
|
+
this.config = {
|
|
182
|
+
gatewayUrl: config.gatewayUrl,
|
|
183
|
+
authToken: config.authToken,
|
|
184
|
+
clientName: config.clientName || "ava-orchestrator",
|
|
185
|
+
clientVersion: config.clientVersion || "1.0.0",
|
|
186
|
+
deviceId: config.deviceId,
|
|
187
|
+
devicePrivateKey: config.devicePrivateKey,
|
|
188
|
+
autoReconnect: config.autoReconnect ?? true,
|
|
189
|
+
reconnectInterval: config.reconnectInterval ?? 5e3,
|
|
190
|
+
maxReconnectAttempts: config.maxReconnectAttempts ?? 10,
|
|
191
|
+
requestTimeout: config.requestTimeout ?? 3e4,
|
|
192
|
+
// Use 'node' role which is the only valid role for gateway connections
|
|
193
|
+
role: config.role || "node",
|
|
194
|
+
scopes: config.scopes || ["node.read", "node.write"]
|
|
195
|
+
};
|
|
196
|
+
this.deviceManager = new DeviceIdentityManager();
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Connect to Clawdbot Gateway
|
|
200
|
+
*/
|
|
201
|
+
async connect() {
|
|
202
|
+
if (this.connectionState === "connected" || this.connectionState === "connecting") {
|
|
203
|
+
throw new Error(`Already ${this.connectionState}`);
|
|
204
|
+
}
|
|
205
|
+
this.setConnectionState("connecting");
|
|
206
|
+
let identity = this.deviceManager.getIdentity();
|
|
207
|
+
if (!identity) {
|
|
208
|
+
if (this.config.devicePrivateKey) {
|
|
209
|
+
identity = this.deviceManager.loadFromPrivateKey(this.config.devicePrivateKey);
|
|
210
|
+
} else {
|
|
211
|
+
await this.deviceManager.generateIdentity(this.config.deviceId);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return new Promise((resolve, reject) => {
|
|
215
|
+
try {
|
|
216
|
+
this.ws = new WebSocket(this.config.gatewayUrl);
|
|
217
|
+
this.ws.onopen = () => {
|
|
218
|
+
this.emit("connection:connected");
|
|
219
|
+
};
|
|
220
|
+
this.ws.onmessage = async (event) => {
|
|
221
|
+
try {
|
|
222
|
+
const message = JSON.parse(event.data);
|
|
223
|
+
this.emit("message", message);
|
|
224
|
+
await this.handleMessage(message, resolve, reject);
|
|
225
|
+
} catch (error) {
|
|
226
|
+
console.error("Failed to parse message:", error);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
this.ws.onerror = (_error) => {
|
|
230
|
+
console.error("[Molt SDK] WebSocket error (molt may reconnect)");
|
|
231
|
+
this.emit("connection:error", new Error("WebSocket error"));
|
|
232
|
+
};
|
|
233
|
+
this.ws.onclose = (event) => {
|
|
234
|
+
this.handleDisconnect(event.reason);
|
|
235
|
+
if (this.connectionState === "connecting" || this.connectionState === "authenticating") {
|
|
236
|
+
reject(new Error(`Connection closed: ${event.reason || "Unknown reason"}`));
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
} catch (error) {
|
|
240
|
+
reject(error);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Handle incoming messages
|
|
246
|
+
*/
|
|
247
|
+
async handleMessage(message, connectResolve, connectReject) {
|
|
248
|
+
if (message.type === "event" && message.event === "connect.challenge") {
|
|
249
|
+
await this.handleChallenge(message.payload);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (message.type === "event" && message.event === "hello-ok") {
|
|
253
|
+
this.setConnectionState("connected");
|
|
254
|
+
this.reconnectAttempts = 0;
|
|
255
|
+
if (connectResolve) {
|
|
256
|
+
connectResolve(void 0);
|
|
257
|
+
}
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (message.type === "res") {
|
|
261
|
+
const response = message;
|
|
262
|
+
const isConnectResponse = this.connectRequestId && response.id === this.connectRequestId || response.ok && response.payload?.type === "hello-ok";
|
|
263
|
+
if (isConnectResponse) {
|
|
264
|
+
this.connectRequestId = null;
|
|
265
|
+
if (response.ok) {
|
|
266
|
+
const payload = response.payload;
|
|
267
|
+
if (payload?.snapshot) {
|
|
268
|
+
this.connectionSnapshot = payload.snapshot;
|
|
269
|
+
}
|
|
270
|
+
this.setConnectionState("connected");
|
|
271
|
+
this.reconnectAttempts = 0;
|
|
272
|
+
if (connectResolve) {
|
|
273
|
+
connectResolve(void 0);
|
|
274
|
+
}
|
|
275
|
+
} else {
|
|
276
|
+
const error = new Error(response.error?.message || "Connection failed");
|
|
277
|
+
error.code = response.error?.code;
|
|
278
|
+
if (connectReject) {
|
|
279
|
+
connectReject(error);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
this.handleResponse(response);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
if (message.type === "event") {
|
|
288
|
+
const eventMsg = message;
|
|
289
|
+
this.emit("event", eventMsg.event, eventMsg.payload);
|
|
290
|
+
if (eventMsg.event === "health" && eventMsg.payload) {
|
|
291
|
+
if (this.connectionSnapshot) {
|
|
292
|
+
this.connectionSnapshot.health = eventMsg.payload;
|
|
293
|
+
} else {
|
|
294
|
+
this.connectionSnapshot = { health: eventMsg.payload };
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (eventMsg.event.startsWith("agent:")) {
|
|
298
|
+
const agentId = eventMsg.payload?.agentId;
|
|
299
|
+
if (agentId) {
|
|
300
|
+
if (eventMsg.event === "agent:status") {
|
|
301
|
+
this.emit("agent:status", agentId, eventMsg.payload.status);
|
|
302
|
+
} else if (eventMsg.event === "agent:log") {
|
|
303
|
+
this.emit("agent:log", agentId, eventMsg.payload.message, eventMsg.payload.level);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (message.type === "err") {
|
|
310
|
+
console.error("Gateway error:", message);
|
|
311
|
+
if (connectReject && (this.connectionState === "connecting" || this.connectionState === "authenticating")) {
|
|
312
|
+
connectReject(new Error(message.error?.message || "Unknown error"));
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Detect platform
|
|
318
|
+
*/
|
|
319
|
+
getPlatform() {
|
|
320
|
+
if (typeof process !== "undefined" && process.platform) {
|
|
321
|
+
const platformMap = {
|
|
322
|
+
darwin: "macos",
|
|
323
|
+
linux: "linux",
|
|
324
|
+
win32: "windows",
|
|
325
|
+
freebsd: "freebsd",
|
|
326
|
+
openbsd: "openbsd"
|
|
327
|
+
};
|
|
328
|
+
return platformMap[process.platform] || process.platform;
|
|
329
|
+
}
|
|
330
|
+
return "unknown";
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Handle connect challenge from gateway
|
|
334
|
+
*/
|
|
335
|
+
async handleChallenge(challenge) {
|
|
336
|
+
this.setConnectionState("authenticating");
|
|
337
|
+
const identity = this.deviceManager.getIdentity();
|
|
338
|
+
if (!identity) {
|
|
339
|
+
throw new Error("Device identity not initialized");
|
|
340
|
+
}
|
|
341
|
+
const clientId = "cli";
|
|
342
|
+
const clientMode = "node";
|
|
343
|
+
const role = this.config.role;
|
|
344
|
+
const scopes = this.config.scopes;
|
|
345
|
+
const token = this.config.authToken;
|
|
346
|
+
const signature = await this.deviceManager.signDeviceAuthPayload({
|
|
347
|
+
deviceId: identity.id,
|
|
348
|
+
clientId,
|
|
349
|
+
clientMode,
|
|
350
|
+
role,
|
|
351
|
+
scopes,
|
|
352
|
+
signedAtMs: challenge.ts,
|
|
353
|
+
token,
|
|
354
|
+
nonce: challenge.nonce
|
|
355
|
+
});
|
|
356
|
+
const connectParams = {
|
|
357
|
+
minProtocol: 3,
|
|
358
|
+
maxProtocol: 3,
|
|
359
|
+
client: {
|
|
360
|
+
id: clientId,
|
|
361
|
+
platform: this.getPlatform(),
|
|
362
|
+
mode: clientMode,
|
|
363
|
+
version: this.config.clientVersion
|
|
364
|
+
},
|
|
365
|
+
role,
|
|
366
|
+
scopes,
|
|
367
|
+
caps: [],
|
|
368
|
+
commands: [],
|
|
369
|
+
permissions: {},
|
|
370
|
+
locale: "en-US",
|
|
371
|
+
userAgent: `${this.config.clientName}/${this.config.clientVersion}`,
|
|
372
|
+
device: {
|
|
373
|
+
id: identity.id,
|
|
374
|
+
publicKey: this.deviceManager.getPublicKeyBase64Url(),
|
|
375
|
+
// Use base64url raw format
|
|
376
|
+
signature,
|
|
377
|
+
signedAt: challenge.ts,
|
|
378
|
+
nonce: challenge.nonce
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
if (this.config.authToken) {
|
|
382
|
+
connectParams.auth = {
|
|
383
|
+
token: this.config.authToken
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
const connectRequest = {
|
|
387
|
+
type: "req",
|
|
388
|
+
id: randomUUID(),
|
|
389
|
+
method: "connect",
|
|
390
|
+
params: connectParams
|
|
391
|
+
};
|
|
392
|
+
this.connectRequestId = connectRequest.id;
|
|
393
|
+
this.send(connectRequest);
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Handle RPC response
|
|
397
|
+
*/
|
|
398
|
+
handleResponse(message) {
|
|
399
|
+
const pending = this.pendingRequests.get(message.id);
|
|
400
|
+
if (!pending) {
|
|
401
|
+
console.warn("Received response for unknown request:", message.id);
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
clearTimeout(pending.timeout);
|
|
405
|
+
this.pendingRequests.delete(message.id);
|
|
406
|
+
if (message.ok) {
|
|
407
|
+
pending.resolve(message.result ?? message.payload);
|
|
408
|
+
} else {
|
|
409
|
+
const error = new Error(message.error?.message || "Request failed");
|
|
410
|
+
error.code = message.error?.code;
|
|
411
|
+
error.details = message.error?.details;
|
|
412
|
+
pending.reject(error);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Handle disconnection
|
|
417
|
+
*/
|
|
418
|
+
handleDisconnect(reason) {
|
|
419
|
+
const previousState = this.connectionState;
|
|
420
|
+
this.setConnectionState("disconnected");
|
|
421
|
+
this.emit("connection:disconnected", reason);
|
|
422
|
+
for (const [_id, pending] of this.pendingRequests.entries()) {
|
|
423
|
+
clearTimeout(pending.timeout);
|
|
424
|
+
pending.reject(new Error("Connection closed"));
|
|
425
|
+
}
|
|
426
|
+
this.pendingRequests.clear();
|
|
427
|
+
if (this.config.autoReconnect && previousState === "connected" && this.reconnectAttempts < this.config.maxReconnectAttempts) {
|
|
428
|
+
this.scheduleReconnect();
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Schedule reconnection attempt
|
|
433
|
+
*/
|
|
434
|
+
scheduleReconnect() {
|
|
435
|
+
if (this.reconnectTimer) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
this.reconnectAttempts++;
|
|
439
|
+
this.setConnectionState("reconnecting");
|
|
440
|
+
const delay = this.config.reconnectInterval * Math.min(this.reconnectAttempts, 5);
|
|
441
|
+
this.reconnectTimer = setTimeout(async () => {
|
|
442
|
+
this.reconnectTimer = null;
|
|
443
|
+
try {
|
|
444
|
+
await this.connect();
|
|
445
|
+
} catch (error) {
|
|
446
|
+
console.error("Reconnection failed:", error);
|
|
447
|
+
if (this.reconnectAttempts < this.config.maxReconnectAttempts) {
|
|
448
|
+
this.scheduleReconnect();
|
|
449
|
+
} else {
|
|
450
|
+
this.setConnectionState("error");
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}, delay);
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Send a message to the gateway
|
|
457
|
+
*/
|
|
458
|
+
send(message) {
|
|
459
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
460
|
+
throw new Error("WebSocket is not connected");
|
|
461
|
+
}
|
|
462
|
+
this.ws.send(JSON.stringify(message));
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Make an RPC request
|
|
466
|
+
*/
|
|
467
|
+
async request(method, params = {}) {
|
|
468
|
+
if (this.connectionState !== "connected") {
|
|
469
|
+
throw new Error(`Cannot send request: connection state is ${this.connectionState}`);
|
|
470
|
+
}
|
|
471
|
+
const id = randomUUID();
|
|
472
|
+
return new Promise((resolve, reject) => {
|
|
473
|
+
const timeout = setTimeout(() => {
|
|
474
|
+
this.pendingRequests.delete(id);
|
|
475
|
+
reject(new Error(`Request timeout: ${method}`));
|
|
476
|
+
}, this.config.requestTimeout);
|
|
477
|
+
this.pendingRequests.set(id, { resolve, reject, timeout });
|
|
478
|
+
const request = {
|
|
479
|
+
type: "req",
|
|
480
|
+
id,
|
|
481
|
+
method,
|
|
482
|
+
params
|
|
483
|
+
};
|
|
484
|
+
this.send(request);
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Get gateway status
|
|
489
|
+
*/
|
|
490
|
+
async getStatus() {
|
|
491
|
+
return await this.request("status", {});
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* List agents
|
|
495
|
+
*/
|
|
496
|
+
async listAgents() {
|
|
497
|
+
const result = await this.request("agents.list", {});
|
|
498
|
+
return result?.agents || [];
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Execute a task with an agent
|
|
502
|
+
*/
|
|
503
|
+
async executeTask(params) {
|
|
504
|
+
const result = await this.request("agent.execute", {
|
|
505
|
+
sessionId: params.sessionId,
|
|
506
|
+
agentId: params.agentId,
|
|
507
|
+
message: params.message,
|
|
508
|
+
timeout: params.timeout,
|
|
509
|
+
deliver: params.deliver
|
|
510
|
+
});
|
|
511
|
+
return result;
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Event emitter
|
|
515
|
+
*/
|
|
516
|
+
on(event, handler) {
|
|
517
|
+
if (!this.eventHandlers.has(event)) {
|
|
518
|
+
this.eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
519
|
+
}
|
|
520
|
+
this.eventHandlers.get(event).add(handler);
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Remove event handler
|
|
524
|
+
*/
|
|
525
|
+
off(event, handler) {
|
|
526
|
+
const handlers = this.eventHandlers.get(event);
|
|
527
|
+
if (handlers) {
|
|
528
|
+
handlers.delete(handler);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Emit event
|
|
533
|
+
*/
|
|
534
|
+
emit(event, ...args) {
|
|
535
|
+
const handlers = this.eventHandlers.get(event);
|
|
536
|
+
if (handlers) {
|
|
537
|
+
handlers.forEach((handler) => {
|
|
538
|
+
try {
|
|
539
|
+
handler(...args);
|
|
540
|
+
} catch (error) {
|
|
541
|
+
console.error(`Error in event handler for ${event}:`, error);
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Set connection state and emit event
|
|
548
|
+
*/
|
|
549
|
+
setConnectionState(state) {
|
|
550
|
+
this.connectionState = state;
|
|
551
|
+
this.emit("connection:state", state);
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Get current connection state
|
|
555
|
+
*/
|
|
556
|
+
getConnectionState() {
|
|
557
|
+
return this.connectionState;
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Disconnect from gateway
|
|
561
|
+
*/
|
|
562
|
+
disconnect() {
|
|
563
|
+
if (this.reconnectTimer) {
|
|
564
|
+
clearTimeout(this.reconnectTimer);
|
|
565
|
+
this.reconnectTimer = null;
|
|
566
|
+
}
|
|
567
|
+
if (this.ws) {
|
|
568
|
+
this.ws.close();
|
|
569
|
+
this.ws = null;
|
|
570
|
+
}
|
|
571
|
+
this.setConnectionState("disconnected");
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Check if connected
|
|
575
|
+
*/
|
|
576
|
+
isConnected() {
|
|
577
|
+
return this.connectionState === "connected";
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Get cached connection snapshot (contains health, agents, etc.)
|
|
581
|
+
* This is populated from the initial connection and updated by events
|
|
582
|
+
*/
|
|
583
|
+
getSnapshot() {
|
|
584
|
+
return this.connectionSnapshot;
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Get cached health from connection snapshot
|
|
588
|
+
* Returns null if not connected or no health data available
|
|
589
|
+
*/
|
|
590
|
+
getCachedHealth() {
|
|
591
|
+
return this.connectionSnapshot?.health || null;
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Invalidate cached health data
|
|
595
|
+
* Call this after external operations (like CLI) that modify state
|
|
596
|
+
*/
|
|
597
|
+
invalidateHealthCache() {
|
|
598
|
+
if (this.connectionSnapshot) {
|
|
599
|
+
delete this.connectionSnapshot.health?.agents;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
var MoltError = class extends Error {
|
|
604
|
+
/** Error code */
|
|
605
|
+
code;
|
|
606
|
+
/** Additional error details */
|
|
607
|
+
details;
|
|
608
|
+
/** Original error if wrapped */
|
|
609
|
+
cause;
|
|
610
|
+
constructor(message, code, details, cause) {
|
|
611
|
+
super(message);
|
|
612
|
+
this.name = "MoltError";
|
|
613
|
+
this.code = code;
|
|
614
|
+
this.details = details;
|
|
615
|
+
this.cause = cause;
|
|
616
|
+
if (Error.captureStackTrace) {
|
|
617
|
+
Error.captureStackTrace(this, this.constructor);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Convert error to JSON for logging/serialization
|
|
622
|
+
*/
|
|
623
|
+
toJSON() {
|
|
624
|
+
return {
|
|
625
|
+
name: this.name,
|
|
626
|
+
message: this.message,
|
|
627
|
+
code: this.code,
|
|
628
|
+
details: this.details,
|
|
629
|
+
stack: this.stack
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
var ConnectionError = class extends MoltError {
|
|
634
|
+
constructor(message, details, cause) {
|
|
635
|
+
super(message, "CONNECTION_ERROR", details, cause);
|
|
636
|
+
this.name = "ConnectionError";
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
var AuthenticationError = class extends MoltError {
|
|
640
|
+
constructor(message, details, cause) {
|
|
641
|
+
super(message, "AUTHENTICATION_ERROR", details, cause);
|
|
642
|
+
this.name = "AuthenticationError";
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
var TimeoutError = class extends MoltError {
|
|
646
|
+
/** The method that timed out */
|
|
647
|
+
method;
|
|
648
|
+
/** Timeout duration in ms */
|
|
649
|
+
timeoutMs;
|
|
650
|
+
constructor(message, method, timeoutMs, details) {
|
|
651
|
+
super(message, "TIMEOUT_ERROR", { ...details, method, timeoutMs });
|
|
652
|
+
this.name = "TimeoutError";
|
|
653
|
+
this.method = method;
|
|
654
|
+
this.timeoutMs = timeoutMs;
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
var RPCError = class extends MoltError {
|
|
658
|
+
/** The RPC method that failed */
|
|
659
|
+
method;
|
|
660
|
+
/** Gateway error code */
|
|
661
|
+
gatewayCode;
|
|
662
|
+
constructor(message, method, gatewayCode, details) {
|
|
663
|
+
super(message, "RPC_ERROR", { ...details, method, gatewayCode });
|
|
664
|
+
this.name = "RPCError";
|
|
665
|
+
this.method = method;
|
|
666
|
+
this.gatewayCode = gatewayCode;
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
var ValidationError = class extends MoltError {
|
|
670
|
+
/** Field that failed validation */
|
|
671
|
+
field;
|
|
672
|
+
constructor(message, field, details) {
|
|
673
|
+
super(message, "VALIDATION_ERROR", { ...details, field });
|
|
674
|
+
this.name = "ValidationError";
|
|
675
|
+
this.field = field;
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
var NotFoundError = class extends MoltError {
|
|
679
|
+
/** Type of resource not found */
|
|
680
|
+
resourceType;
|
|
681
|
+
/** ID of the resource */
|
|
682
|
+
resourceId;
|
|
683
|
+
constructor(resourceType, resourceId, details) {
|
|
684
|
+
super(`${resourceType} '${resourceId}' not found`, "NOT_FOUND", {
|
|
685
|
+
...details,
|
|
686
|
+
resourceType,
|
|
687
|
+
resourceId
|
|
688
|
+
});
|
|
689
|
+
this.name = "NotFoundError";
|
|
690
|
+
this.resourceType = resourceType;
|
|
691
|
+
this.resourceId = resourceId;
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
var NotConnectedError = class extends MoltError {
|
|
695
|
+
constructor(message = "Transport is not connected") {
|
|
696
|
+
super(message, "NOT_CONNECTED");
|
|
697
|
+
this.name = "NotConnectedError";
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
var CLIError = class extends MoltError {
|
|
701
|
+
/** Exit code from CLI */
|
|
702
|
+
exitCode;
|
|
703
|
+
/** stdout from CLI */
|
|
704
|
+
stdout;
|
|
705
|
+
/** stderr from CLI */
|
|
706
|
+
stderr;
|
|
707
|
+
constructor(message, exitCode, stdout, stderr, details) {
|
|
708
|
+
super(message, "CLI_ERROR", { ...details, exitCode, stdout, stderr });
|
|
709
|
+
this.name = "CLIError";
|
|
710
|
+
this.exitCode = exitCode;
|
|
711
|
+
this.stdout = stdout;
|
|
712
|
+
this.stderr = stderr;
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
var ErrorCodes = {
|
|
716
|
+
CONNECTION_ERROR: "CONNECTION_ERROR",
|
|
717
|
+
AUTHENTICATION_ERROR: "AUTHENTICATION_ERROR",
|
|
718
|
+
TIMEOUT_ERROR: "TIMEOUT_ERROR",
|
|
719
|
+
RPC_ERROR: "RPC_ERROR",
|
|
720
|
+
VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
721
|
+
NOT_FOUND: "NOT_FOUND",
|
|
722
|
+
NOT_CONNECTED: "NOT_CONNECTED",
|
|
723
|
+
CLI_ERROR: "CLI_ERROR"
|
|
724
|
+
};
|
|
725
|
+
var TypedEventEmitter = class {
|
|
726
|
+
handlers = /* @__PURE__ */ new Map();
|
|
727
|
+
/**
|
|
728
|
+
* Subscribe to an event with type-safe handler
|
|
729
|
+
*/
|
|
730
|
+
on(event, handler) {
|
|
731
|
+
if (!this.handlers.has(event)) {
|
|
732
|
+
this.handlers.set(event, /* @__PURE__ */ new Set());
|
|
733
|
+
}
|
|
734
|
+
this.handlers.get(event).add(handler);
|
|
735
|
+
return this;
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Subscribe to an event once
|
|
739
|
+
*/
|
|
740
|
+
once(event, handler) {
|
|
741
|
+
const onceHandler = ((...args) => {
|
|
742
|
+
this.off(event, onceHandler);
|
|
743
|
+
handler(...args);
|
|
744
|
+
});
|
|
745
|
+
return this.on(event, onceHandler);
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Unsubscribe from an event
|
|
749
|
+
*/
|
|
750
|
+
off(event, handler) {
|
|
751
|
+
const eventHandlers = this.handlers.get(event);
|
|
752
|
+
if (eventHandlers) {
|
|
753
|
+
eventHandlers.delete(handler);
|
|
754
|
+
if (eventHandlers.size === 0) {
|
|
755
|
+
this.handlers.delete(event);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
return this;
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Emit an event with type-safe arguments
|
|
762
|
+
*/
|
|
763
|
+
emit(event, ...args) {
|
|
764
|
+
const eventHandlers = this.handlers.get(event);
|
|
765
|
+
if (!eventHandlers || eventHandlers.size === 0) {
|
|
766
|
+
return false;
|
|
767
|
+
}
|
|
768
|
+
eventHandlers.forEach((handler) => {
|
|
769
|
+
try {
|
|
770
|
+
handler(...args);
|
|
771
|
+
} catch (error) {
|
|
772
|
+
console.error(`Error in event handler for '${event}':`, error);
|
|
773
|
+
}
|
|
774
|
+
});
|
|
775
|
+
return true;
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Remove all handlers for an event or all events
|
|
779
|
+
*/
|
|
780
|
+
removeAllListeners(event) {
|
|
781
|
+
if (event) {
|
|
782
|
+
this.handlers.delete(event);
|
|
783
|
+
} else {
|
|
784
|
+
this.handlers.clear();
|
|
785
|
+
}
|
|
786
|
+
return this;
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* Get the number of listeners for an event
|
|
790
|
+
*/
|
|
791
|
+
listenerCount(event) {
|
|
792
|
+
return this.handlers.get(event)?.size ?? 0;
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Get all registered event names
|
|
796
|
+
*/
|
|
797
|
+
eventNames() {
|
|
798
|
+
return Array.from(this.handlers.keys());
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* Wait for an event to occur (promisified)
|
|
802
|
+
*/
|
|
803
|
+
waitFor(event, timeout) {
|
|
804
|
+
return new Promise((resolve, reject) => {
|
|
805
|
+
let timeoutId;
|
|
806
|
+
const handler = ((...args) => {
|
|
807
|
+
if (timeoutId) {
|
|
808
|
+
clearTimeout(timeoutId);
|
|
809
|
+
}
|
|
810
|
+
resolve(args);
|
|
811
|
+
});
|
|
812
|
+
this.once(event, handler);
|
|
813
|
+
if (timeout) {
|
|
814
|
+
timeoutId = setTimeout(() => {
|
|
815
|
+
this.off(event, handler);
|
|
816
|
+
reject(new Error(`Timeout waiting for event '${event}'`));
|
|
817
|
+
}, timeout);
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
};
|
|
822
|
+
function createEventEmitter() {
|
|
823
|
+
return new TypedEventEmitter();
|
|
824
|
+
}
|
|
825
|
+
var BaseModule = class {
|
|
826
|
+
transport;
|
|
827
|
+
events;
|
|
828
|
+
constructor(config) {
|
|
829
|
+
this.transport = config.transport;
|
|
830
|
+
this.events = config.events;
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Make an RPC request with error handling
|
|
834
|
+
*/
|
|
835
|
+
async request(method, params = {}) {
|
|
836
|
+
if (!this.transport.isConnected()) {
|
|
837
|
+
throw new NotConnectedError();
|
|
838
|
+
}
|
|
839
|
+
try {
|
|
840
|
+
return await this.transport.request(method, params);
|
|
841
|
+
} catch (error) {
|
|
842
|
+
if (error instanceof Error) {
|
|
843
|
+
if (error.message.includes("timeout") || error.message.includes("Timeout")) {
|
|
844
|
+
throw new TimeoutError(error.message, method);
|
|
845
|
+
}
|
|
846
|
+
const anyError = error;
|
|
847
|
+
if (anyError.code || anyError.gatewayCode) {
|
|
848
|
+
throw new RPCError(
|
|
849
|
+
error.message,
|
|
850
|
+
method,
|
|
851
|
+
anyError.code || anyError.gatewayCode,
|
|
852
|
+
anyError.details
|
|
853
|
+
);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
throw error;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Subscribe to transport events
|
|
861
|
+
*/
|
|
862
|
+
onTransportEvent(event, handler) {
|
|
863
|
+
this.transport.on(event, handler);
|
|
864
|
+
}
|
|
865
|
+
/**
|
|
866
|
+
* Unsubscribe from transport events
|
|
867
|
+
*/
|
|
868
|
+
offTransportEvent(event, handler) {
|
|
869
|
+
this.transport.off(event, handler);
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* Check if transport is connected
|
|
873
|
+
*/
|
|
874
|
+
isConnected() {
|
|
875
|
+
return this.transport.isConnected();
|
|
876
|
+
}
|
|
877
|
+
};
|
|
878
|
+
var AgentsModule = class extends BaseModule {
|
|
879
|
+
/**
|
|
880
|
+
* List all agents
|
|
881
|
+
*/
|
|
882
|
+
async list() {
|
|
883
|
+
const result = await this.request("agents.list", {});
|
|
884
|
+
if (!result) {
|
|
885
|
+
console.warn("[Molt SDK] agents.list returned null/undefined");
|
|
886
|
+
return {
|
|
887
|
+
agents: [],
|
|
888
|
+
defaultAgentId: void 0
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
if (typeof result !== "object") {
|
|
892
|
+
console.warn("[Molt SDK] agents.list returned non-object:", typeof result);
|
|
893
|
+
return {
|
|
894
|
+
agents: [],
|
|
895
|
+
defaultAgentId: void 0
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
const agents = (result.agents || []).map((a) => ({
|
|
899
|
+
...a,
|
|
900
|
+
agentId: a.agentId || a.id
|
|
901
|
+
// Normalize to agentId
|
|
902
|
+
}));
|
|
903
|
+
const anyResult = result;
|
|
904
|
+
return {
|
|
905
|
+
agents,
|
|
906
|
+
defaultAgentId: result.defaultAgentId || anyResult.defaultId
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* Get a specific agent by ID
|
|
911
|
+
*/
|
|
912
|
+
async get(agentId) {
|
|
913
|
+
const result = await this.list();
|
|
914
|
+
const agent = result.agents.find((a) => a.agentId === agentId || a.id === agentId);
|
|
915
|
+
if (!agent) {
|
|
916
|
+
throw new NotFoundError("Agent", agentId);
|
|
917
|
+
}
|
|
918
|
+
return agent;
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* Create a new agent with comprehensive configuration
|
|
922
|
+
*/
|
|
923
|
+
async create(options) {
|
|
924
|
+
return this.request("agents.create", {
|
|
925
|
+
agentId: options.agentId,
|
|
926
|
+
workspace: options.workspace,
|
|
927
|
+
isDefault: options.isDefault,
|
|
928
|
+
identity: options.identity,
|
|
929
|
+
model: options.model,
|
|
930
|
+
sandbox: options.sandbox,
|
|
931
|
+
heartbeat: options.heartbeat,
|
|
932
|
+
systemPrompt: options.systemPrompt,
|
|
933
|
+
tools: options.tools,
|
|
934
|
+
groupChat: options.groupChat
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Update an agent's configuration
|
|
939
|
+
*/
|
|
940
|
+
async update(agentId, options) {
|
|
941
|
+
return this.request("agents.update", {
|
|
942
|
+
agentId,
|
|
943
|
+
...options
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* Delete an agent
|
|
948
|
+
*/
|
|
949
|
+
async delete(agentId) {
|
|
950
|
+
await this.request("agents.delete", { agentId });
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Clone an existing agent with new settings
|
|
954
|
+
*/
|
|
955
|
+
async clone(options) {
|
|
956
|
+
const sourceConfig = await this.getConfig(options.sourceAgentId);
|
|
957
|
+
return this.create({
|
|
958
|
+
agentId: options.newAgentId,
|
|
959
|
+
workspace: options.workspace,
|
|
960
|
+
isDefault: false,
|
|
961
|
+
identity: options.identity || sourceConfig.identity,
|
|
962
|
+
model: options.model || sourceConfig.model,
|
|
963
|
+
sandbox: sourceConfig.sandbox,
|
|
964
|
+
heartbeat: sourceConfig.heartbeat,
|
|
965
|
+
systemPrompt: sourceConfig.systemPrompt,
|
|
966
|
+
tools: sourceConfig.tools,
|
|
967
|
+
groupChat: sourceConfig.groupChat
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
// Identity Management
|
|
971
|
+
/**
|
|
972
|
+
* Get agent identity
|
|
973
|
+
*/
|
|
974
|
+
async getIdentity(agentId) {
|
|
975
|
+
return this.request("agents.identity", { agentId });
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* Set agent identity
|
|
979
|
+
* @deprecated Gateway doesn't support agents.setIdentity
|
|
980
|
+
*/
|
|
981
|
+
async setIdentity(agentId, _identity) {
|
|
982
|
+
return {
|
|
983
|
+
agentId
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
/**
|
|
987
|
+
* Load identity from IDENTITY.md file in workspace
|
|
988
|
+
*/
|
|
989
|
+
async loadIdentityFromFile(agentId, identityFile) {
|
|
990
|
+
return this.request("agents.loadIdentity", {
|
|
991
|
+
agentId,
|
|
992
|
+
identityFile,
|
|
993
|
+
fromIdentity: true
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
// Default Agent
|
|
997
|
+
/**
|
|
998
|
+
* Get the default agent
|
|
999
|
+
*/
|
|
1000
|
+
async getDefault() {
|
|
1001
|
+
const result = await this.list();
|
|
1002
|
+
if (!result.defaultAgentId) {
|
|
1003
|
+
return null;
|
|
1004
|
+
}
|
|
1005
|
+
return result.agents.find((a) => a.agentId === result.defaultAgentId) || null;
|
|
1006
|
+
}
|
|
1007
|
+
/**
|
|
1008
|
+
* Set the default agent
|
|
1009
|
+
*/
|
|
1010
|
+
async setDefault(agentId) {
|
|
1011
|
+
await this.request("agents.setDefault", { agentId });
|
|
1012
|
+
}
|
|
1013
|
+
// Configuration
|
|
1014
|
+
/**
|
|
1015
|
+
* Get full agent configuration
|
|
1016
|
+
*/
|
|
1017
|
+
async getConfig(agentId) {
|
|
1018
|
+
return this.request("agents.getConfig", { agentId });
|
|
1019
|
+
}
|
|
1020
|
+
/**
|
|
1021
|
+
* Export agent configuration (for backup/transfer)
|
|
1022
|
+
*/
|
|
1023
|
+
async exportConfig(agentId) {
|
|
1024
|
+
return this.getConfig(agentId);
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* Import agent configuration
|
|
1028
|
+
*/
|
|
1029
|
+
async importConfig(config) {
|
|
1030
|
+
return this.create(config);
|
|
1031
|
+
}
|
|
1032
|
+
// Model Configuration
|
|
1033
|
+
/**
|
|
1034
|
+
* Set agent's primary model
|
|
1035
|
+
*/
|
|
1036
|
+
async setModel(agentId, model) {
|
|
1037
|
+
await this.update(agentId, {
|
|
1038
|
+
model: { primary: model }
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
/**
|
|
1042
|
+
* Set agent's model fallbacks
|
|
1043
|
+
*/
|
|
1044
|
+
async setModelFallbacks(agentId, fallbacks) {
|
|
1045
|
+
await this.update(agentId, {
|
|
1046
|
+
model: { fallbacks }
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
// Workspace
|
|
1050
|
+
/**
|
|
1051
|
+
* Set agent's workspace
|
|
1052
|
+
*/
|
|
1053
|
+
async setWorkspace(agentId, workspace) {
|
|
1054
|
+
await this.update(agentId, { workspace });
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Get agent's workspace path
|
|
1058
|
+
*/
|
|
1059
|
+
async getWorkspace(agentId) {
|
|
1060
|
+
try {
|
|
1061
|
+
const config = await this.getConfig(agentId);
|
|
1062
|
+
return config.workspace;
|
|
1063
|
+
} catch (error) {
|
|
1064
|
+
console.warn(`[Molt SDK] Could not get workspace for ${agentId}: ${error?.message || error}`);
|
|
1065
|
+
return void 0;
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
// System Prompt
|
|
1069
|
+
// NOTE: These methods are disabled because the Molt Gateway doesn't support agents.update
|
|
1070
|
+
/**
|
|
1071
|
+
* Set agent's system prompt
|
|
1072
|
+
* @deprecated Gateway doesn't support agents.update
|
|
1073
|
+
*/
|
|
1074
|
+
async setSystemPrompt(_agentId, _systemPrompt) {
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* Get agent's system prompt
|
|
1078
|
+
* @deprecated Gateway doesn't support agents.getConfig
|
|
1079
|
+
*/
|
|
1080
|
+
async getSystemPrompt(_agentId) {
|
|
1081
|
+
return void 0;
|
|
1082
|
+
}
|
|
1083
|
+
// Sandbox
|
|
1084
|
+
/**
|
|
1085
|
+
* Configure agent sandbox settings
|
|
1086
|
+
*/
|
|
1087
|
+
async configureSandbox(agentId, sandbox) {
|
|
1088
|
+
await this.update(agentId, { sandbox });
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Enable sandbox for agent
|
|
1092
|
+
*/
|
|
1093
|
+
async enableSandbox(agentId, mode = "non-main") {
|
|
1094
|
+
await this.configureSandbox(agentId, { mode });
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* Disable sandbox for agent
|
|
1098
|
+
*/
|
|
1099
|
+
async disableSandbox(agentId) {
|
|
1100
|
+
await this.configureSandbox(agentId, { mode: "off" });
|
|
1101
|
+
}
|
|
1102
|
+
// Heartbeat
|
|
1103
|
+
/**
|
|
1104
|
+
* Configure agent heartbeat
|
|
1105
|
+
*/
|
|
1106
|
+
async configureHeartbeat(agentId, heartbeat) {
|
|
1107
|
+
await this.update(agentId, { heartbeat });
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Enable heartbeat for agent
|
|
1111
|
+
*/
|
|
1112
|
+
async enableHeartbeat(agentId, options = {}) {
|
|
1113
|
+
await this.configureHeartbeat(agentId, {
|
|
1114
|
+
enabled: true,
|
|
1115
|
+
every: options.every || "1h",
|
|
1116
|
+
prompt: options.prompt,
|
|
1117
|
+
target: options.target
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
/**
|
|
1121
|
+
* Disable heartbeat for agent
|
|
1122
|
+
*/
|
|
1123
|
+
async disableHeartbeat(agentId) {
|
|
1124
|
+
await this.configureHeartbeat(agentId, { enabled: false });
|
|
1125
|
+
}
|
|
1126
|
+
// Tools
|
|
1127
|
+
// NOTE: These methods are disabled because the Molt Gateway doesn't support
|
|
1128
|
+
// agents.getConfig, agents.update, or agents.setIdentity methods.
|
|
1129
|
+
/**
|
|
1130
|
+
* Configure agent tools
|
|
1131
|
+
* @deprecated Gateway doesn't support agents.update
|
|
1132
|
+
*/
|
|
1133
|
+
async configureTools(_agentId, _tools) {
|
|
1134
|
+
}
|
|
1135
|
+
/**
|
|
1136
|
+
* Enable specific tools for agent
|
|
1137
|
+
* @deprecated Gateway doesn't support agents.getConfig/agents.update
|
|
1138
|
+
*/
|
|
1139
|
+
async enableTools(_agentId, _tools) {
|
|
1140
|
+
}
|
|
1141
|
+
/**
|
|
1142
|
+
* Disable specific tools for agent
|
|
1143
|
+
* @deprecated Gateway doesn't support agents.getConfig/agents.update
|
|
1144
|
+
*/
|
|
1145
|
+
async disableTools(_agentId, _tools) {
|
|
1146
|
+
}
|
|
1147
|
+
// Group Chat
|
|
1148
|
+
/**
|
|
1149
|
+
* Configure group chat settings
|
|
1150
|
+
*/
|
|
1151
|
+
async configureGroupChat(agentId, groupChat) {
|
|
1152
|
+
await this.update(agentId, { groupChat });
|
|
1153
|
+
}
|
|
1154
|
+
/**
|
|
1155
|
+
* Enable group chat for agent
|
|
1156
|
+
*/
|
|
1157
|
+
async enableGroupChat(agentId, options = {}) {
|
|
1158
|
+
await this.configureGroupChat(agentId, {
|
|
1159
|
+
enabled: true,
|
|
1160
|
+
mentionPatterns: options.mentionPatterns,
|
|
1161
|
+
replyToAll: options.replyToAll
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* Disable group chat for agent
|
|
1166
|
+
*/
|
|
1167
|
+
async disableGroupChat(agentId) {
|
|
1168
|
+
await this.configureGroupChat(agentId, { enabled: false });
|
|
1169
|
+
}
|
|
1170
|
+
// Utility Methods
|
|
1171
|
+
/**
|
|
1172
|
+
* Check if an agent exists
|
|
1173
|
+
*/
|
|
1174
|
+
async exists(agentId) {
|
|
1175
|
+
try {
|
|
1176
|
+
await this.get(agentId);
|
|
1177
|
+
return true;
|
|
1178
|
+
} catch (error) {
|
|
1179
|
+
if (error instanceof NotFoundError) {
|
|
1180
|
+
return false;
|
|
1181
|
+
}
|
|
1182
|
+
throw error;
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
/**
|
|
1186
|
+
* List all agent IDs
|
|
1187
|
+
*/
|
|
1188
|
+
async listIds() {
|
|
1189
|
+
const result = await this.list();
|
|
1190
|
+
return result.agents.map((a) => a.agentId);
|
|
1191
|
+
}
|
|
1192
|
+
/**
|
|
1193
|
+
* Get agent count
|
|
1194
|
+
*/
|
|
1195
|
+
async count() {
|
|
1196
|
+
const result = await this.list();
|
|
1197
|
+
return result.agents.length;
|
|
1198
|
+
}
|
|
1199
|
+
};
|
|
1200
|
+
var ChatModule = class extends BaseModule {
|
|
1201
|
+
/**
|
|
1202
|
+
* Generate an idempotency key for RPC requests
|
|
1203
|
+
*/
|
|
1204
|
+
generateIdempotencyKey() {
|
|
1205
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
1206
|
+
}
|
|
1207
|
+
/**
|
|
1208
|
+
* Send a message and wait for response
|
|
1209
|
+
* Gateway is async - returns runId and actual response comes via events
|
|
1210
|
+
*/
|
|
1211
|
+
async send(options) {
|
|
1212
|
+
let sessionKey = options.sessionKey;
|
|
1213
|
+
if (!sessionKey && options.agentId) {
|
|
1214
|
+
sessionKey = `agent:${options.agentId}:${Date.now()}`;
|
|
1215
|
+
} else if (options.agentId && sessionKey && !sessionKey.startsWith("agent:")) {
|
|
1216
|
+
sessionKey = `agent:${options.agentId}:${sessionKey}`;
|
|
1217
|
+
}
|
|
1218
|
+
const timeout = options.timeout || 36e5;
|
|
1219
|
+
let fullResponse = "";
|
|
1220
|
+
let responseReceived = false;
|
|
1221
|
+
let runId = null;
|
|
1222
|
+
let resolved = false;
|
|
1223
|
+
const responsePromise = new Promise((resolve, reject) => {
|
|
1224
|
+
const timeoutId = setTimeout(() => {
|
|
1225
|
+
if (!resolved) {
|
|
1226
|
+
cleanup();
|
|
1227
|
+
reject(new Error("Chat response timeout"));
|
|
1228
|
+
}
|
|
1229
|
+
}, timeout);
|
|
1230
|
+
const handleAgentEvent = (event) => {
|
|
1231
|
+
if (!runId) return;
|
|
1232
|
+
if (event.runId !== runId) return;
|
|
1233
|
+
if (event.stream === "lifecycle") {
|
|
1234
|
+
if (event.data?.phase === "end" || event.data?.phase === "complete") {
|
|
1235
|
+
if (!resolved) {
|
|
1236
|
+
resolved = true;
|
|
1237
|
+
cleanup();
|
|
1238
|
+
resolve(fullResponse);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1243
|
+
if (event.stream === "assistant" && event.data?.text) {
|
|
1244
|
+
fullResponse = event.data.text;
|
|
1245
|
+
}
|
|
1246
|
+
};
|
|
1247
|
+
const handleChatEvent = (event) => {
|
|
1248
|
+
if (!runId) return;
|
|
1249
|
+
if (event.runId !== runId) return;
|
|
1250
|
+
if (event.state === "final" || event.state === "complete" || event.state === "done") {
|
|
1251
|
+
if (event.message?.content) {
|
|
1252
|
+
const content = event.message.content;
|
|
1253
|
+
if (Array.isArray(content)) {
|
|
1254
|
+
fullResponse = content.map((c) => c.text || "").join("");
|
|
1255
|
+
} else if (typeof content === "string") {
|
|
1256
|
+
fullResponse = content;
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
if (!resolved) {
|
|
1260
|
+
resolved = true;
|
|
1261
|
+
cleanup();
|
|
1262
|
+
resolve(fullResponse);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
};
|
|
1266
|
+
const cleanup = () => {
|
|
1267
|
+
clearTimeout(timeoutId);
|
|
1268
|
+
this.offTransportEvent("agent", handleAgentEvent);
|
|
1269
|
+
this.offTransportEvent("chat", handleChatEvent);
|
|
1270
|
+
};
|
|
1271
|
+
this.onTransportEvent("agent", handleAgentEvent);
|
|
1272
|
+
this.onTransportEvent("chat", handleChatEvent);
|
|
1273
|
+
});
|
|
1274
|
+
const result = await this.request("chat.send", {
|
|
1275
|
+
idempotencyKey: this.generateIdempotencyKey(),
|
|
1276
|
+
message: options.message,
|
|
1277
|
+
sessionKey,
|
|
1278
|
+
target: options.target,
|
|
1279
|
+
deliver: options.deliver,
|
|
1280
|
+
replyChannel: options.replyChannel,
|
|
1281
|
+
replyTo: options.replyTo,
|
|
1282
|
+
thinking: options.thinking
|
|
1283
|
+
});
|
|
1284
|
+
if (result?.runId) {
|
|
1285
|
+
runId = result.runId;
|
|
1286
|
+
} else {
|
|
1287
|
+
console.warn("[Molt SDK] chat.send: No runId in result, events may not be matched");
|
|
1288
|
+
}
|
|
1289
|
+
if (result?.response || result?.content || result?.message) {
|
|
1290
|
+
fullResponse = result.response || result.content || result.message;
|
|
1291
|
+
responseReceived = true;
|
|
1292
|
+
}
|
|
1293
|
+
if (!responseReceived && result?.status === "started") {
|
|
1294
|
+
try {
|
|
1295
|
+
fullResponse = await responsePromise;
|
|
1296
|
+
} catch (err) {
|
|
1297
|
+
console.warn("[Molt SDK] chat.send: Failed to get async response:", err?.message);
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
const normalizedResult = {
|
|
1301
|
+
sessionKey: result?.sessionKey || sessionKey || "",
|
|
1302
|
+
agentId: result?.agentId || options.agentId || "",
|
|
1303
|
+
response: fullResponse,
|
|
1304
|
+
timestamp: result?.timestamp || Date.now()
|
|
1305
|
+
};
|
|
1306
|
+
if (normalizedResult.response) {
|
|
1307
|
+
this.events.emit("chat:message", {
|
|
1308
|
+
role: "assistant",
|
|
1309
|
+
content: normalizedResult.response,
|
|
1310
|
+
timestamp: normalizedResult.timestamp
|
|
1311
|
+
});
|
|
1312
|
+
}
|
|
1313
|
+
return normalizedResult;
|
|
1314
|
+
}
|
|
1315
|
+
/**
|
|
1316
|
+
* Send a message with streaming response
|
|
1317
|
+
* Returns an async iterator for streaming chunks
|
|
1318
|
+
*/
|
|
1319
|
+
async *sendStream(options) {
|
|
1320
|
+
const agentId = options.agentId;
|
|
1321
|
+
let sessionKey;
|
|
1322
|
+
if (options.sessionKey) {
|
|
1323
|
+
if (agentId && !options.sessionKey.startsWith("agent:")) {
|
|
1324
|
+
sessionKey = `agent:${agentId}:${options.sessionKey}`;
|
|
1325
|
+
} else {
|
|
1326
|
+
sessionKey = options.sessionKey;
|
|
1327
|
+
}
|
|
1328
|
+
} else if (agentId) {
|
|
1329
|
+
sessionKey = `agent:${agentId}:stream-${Date.now()}`;
|
|
1330
|
+
} else {
|
|
1331
|
+
sessionKey = `stream-${Date.now()}`;
|
|
1332
|
+
}
|
|
1333
|
+
const chunks = [];
|
|
1334
|
+
const _resolveStream = null;
|
|
1335
|
+
const _rejectStream = null;
|
|
1336
|
+
let streamDone = false;
|
|
1337
|
+
let fullResponse = "";
|
|
1338
|
+
const chunkQueue = [];
|
|
1339
|
+
let resolveNextChunk = null;
|
|
1340
|
+
const handleStreamChunk = (chunk) => {
|
|
1341
|
+
if (chunk.sessionKey !== sessionKey) return;
|
|
1342
|
+
chunks.push(chunk);
|
|
1343
|
+
fullResponse += chunk.content;
|
|
1344
|
+
if (options.onChunk) {
|
|
1345
|
+
options.onChunk(chunk);
|
|
1346
|
+
}
|
|
1347
|
+
this.events.emit("chat:stream", chunk);
|
|
1348
|
+
if (chunk.done) {
|
|
1349
|
+
streamDone = true;
|
|
1350
|
+
this.events.emit("chat:stream:end", sessionKey, agentId || "");
|
|
1351
|
+
if (options.onEnd) {
|
|
1352
|
+
options.onEnd(sessionKey, agentId || "");
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
if (resolveNextChunk) {
|
|
1356
|
+
const resolve = resolveNextChunk;
|
|
1357
|
+
resolveNextChunk = null;
|
|
1358
|
+
if (chunk.done) {
|
|
1359
|
+
resolve({
|
|
1360
|
+
done: true,
|
|
1361
|
+
value: {
|
|
1362
|
+
sessionKey,
|
|
1363
|
+
agentId: agentId || "",
|
|
1364
|
+
response: fullResponse,
|
|
1365
|
+
timestamp: Date.now()
|
|
1366
|
+
}
|
|
1367
|
+
});
|
|
1368
|
+
} else {
|
|
1369
|
+
resolve({ done: false, value: chunk });
|
|
1370
|
+
}
|
|
1371
|
+
} else {
|
|
1372
|
+
chunkQueue.push(chunk);
|
|
1373
|
+
}
|
|
1374
|
+
};
|
|
1375
|
+
this.onTransportEvent("chat.stream", handleStreamChunk);
|
|
1376
|
+
try {
|
|
1377
|
+
this.events.emit("chat:stream:start", sessionKey, agentId || "");
|
|
1378
|
+
if (options.onStart) {
|
|
1379
|
+
options.onStart(sessionKey, agentId || "");
|
|
1380
|
+
}
|
|
1381
|
+
const requestPromise = this.request("chat.stream", {
|
|
1382
|
+
idempotencyKey: this.generateIdempotencyKey(),
|
|
1383
|
+
message: options.message,
|
|
1384
|
+
sessionKey,
|
|
1385
|
+
target: options.target,
|
|
1386
|
+
deliver: options.deliver,
|
|
1387
|
+
replyChannel: options.replyChannel,
|
|
1388
|
+
replyTo: options.replyTo,
|
|
1389
|
+
thinking: options.thinking,
|
|
1390
|
+
stream: true
|
|
1391
|
+
});
|
|
1392
|
+
while (!streamDone) {
|
|
1393
|
+
if (chunkQueue.length > 0) {
|
|
1394
|
+
const chunk = chunkQueue.shift();
|
|
1395
|
+
if (chunk.done) {
|
|
1396
|
+
return {
|
|
1397
|
+
sessionKey,
|
|
1398
|
+
agentId: agentId || "",
|
|
1399
|
+
response: fullResponse,
|
|
1400
|
+
timestamp: Date.now()
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
yield chunk;
|
|
1404
|
+
} else {
|
|
1405
|
+
const result = await new Promise(
|
|
1406
|
+
(resolve) => {
|
|
1407
|
+
resolveNextChunk = resolve;
|
|
1408
|
+
}
|
|
1409
|
+
);
|
|
1410
|
+
if (result.done) {
|
|
1411
|
+
return result.value;
|
|
1412
|
+
}
|
|
1413
|
+
yield result.value;
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
await requestPromise;
|
|
1417
|
+
return {
|
|
1418
|
+
sessionKey,
|
|
1419
|
+
agentId: agentId || "",
|
|
1420
|
+
response: fullResponse,
|
|
1421
|
+
timestamp: Date.now()
|
|
1422
|
+
};
|
|
1423
|
+
} finally {
|
|
1424
|
+
this.offTransportEvent("chat.stream", handleStreamChunk);
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
/**
|
|
1428
|
+
* Get chat history for a session
|
|
1429
|
+
*/
|
|
1430
|
+
async history(sessionKey, options = {}) {
|
|
1431
|
+
return this.request("chat.history", {
|
|
1432
|
+
sessionKey,
|
|
1433
|
+
limit: options.limit ?? 100,
|
|
1434
|
+
offset: options.offset ?? 0,
|
|
1435
|
+
since: options.since,
|
|
1436
|
+
until: options.until
|
|
1437
|
+
});
|
|
1438
|
+
}
|
|
1439
|
+
/**
|
|
1440
|
+
* Get recent messages across all sessions
|
|
1441
|
+
*/
|
|
1442
|
+
async recentMessages(options = {}) {
|
|
1443
|
+
const result = await this.request("chat.recent", {
|
|
1444
|
+
limit: options.limit ?? 50,
|
|
1445
|
+
agentId: options.agentId
|
|
1446
|
+
});
|
|
1447
|
+
return result.messages || [];
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* Simple message send (convenience method)
|
|
1451
|
+
*/
|
|
1452
|
+
async message(message, agentId, sessionKey) {
|
|
1453
|
+
const result = await this.send({ message, agentId, sessionKey });
|
|
1454
|
+
return result.response;
|
|
1455
|
+
}
|
|
1456
|
+
/**
|
|
1457
|
+
* Deliver a message to a target channel
|
|
1458
|
+
*/
|
|
1459
|
+
async deliver(message, target, options = {}) {
|
|
1460
|
+
return this.send({
|
|
1461
|
+
message,
|
|
1462
|
+
target,
|
|
1463
|
+
deliver: true,
|
|
1464
|
+
replyChannel: options.channel,
|
|
1465
|
+
agentId: options.agentId,
|
|
1466
|
+
sessionKey: options.sessionKey
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1469
|
+
/**
|
|
1470
|
+
* Cancel an ongoing chat request by runId
|
|
1471
|
+
* Note: This attempts to cancel but the gateway may not support it yet
|
|
1472
|
+
* In that case, the execution will complete but results should be ignored
|
|
1473
|
+
*/
|
|
1474
|
+
async cancel(runId) {
|
|
1475
|
+
try {
|
|
1476
|
+
const result = await this.request("chat.cancel", {
|
|
1477
|
+
runId
|
|
1478
|
+
});
|
|
1479
|
+
return result;
|
|
1480
|
+
} catch (error) {
|
|
1481
|
+
const errorMsg = error?.message || String(error);
|
|
1482
|
+
console.warn("[Molt SDK] chat.cancel: Failed or not supported:", errorMsg);
|
|
1483
|
+
return {
|
|
1484
|
+
success: false,
|
|
1485
|
+
message: `Cancel not supported or failed: ${errorMsg}`
|
|
1486
|
+
};
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
};
|
|
1490
|
+
var ConfigModule = class extends BaseModule {
|
|
1491
|
+
/**
|
|
1492
|
+
* Get a configuration value
|
|
1493
|
+
*/
|
|
1494
|
+
async get(key) {
|
|
1495
|
+
const result = await this.request("config.get", { key });
|
|
1496
|
+
return result.value;
|
|
1497
|
+
}
|
|
1498
|
+
/**
|
|
1499
|
+
* Set a configuration value
|
|
1500
|
+
*/
|
|
1501
|
+
async set(key, value) {
|
|
1502
|
+
await this.request("config.set", { key, value });
|
|
1503
|
+
}
|
|
1504
|
+
/**
|
|
1505
|
+
* Get multiple configuration values
|
|
1506
|
+
*/
|
|
1507
|
+
async getMany(keys) {
|
|
1508
|
+
const result = await this.request("config.getMany", {
|
|
1509
|
+
keys
|
|
1510
|
+
});
|
|
1511
|
+
return result.values || {};
|
|
1512
|
+
}
|
|
1513
|
+
/**
|
|
1514
|
+
* Set multiple configuration values
|
|
1515
|
+
*/
|
|
1516
|
+
async setMany(values) {
|
|
1517
|
+
await this.request("config.setMany", { values });
|
|
1518
|
+
}
|
|
1519
|
+
/**
|
|
1520
|
+
* Delete a configuration value
|
|
1521
|
+
*/
|
|
1522
|
+
async delete(key) {
|
|
1523
|
+
await this.request("config.delete", { key });
|
|
1524
|
+
}
|
|
1525
|
+
/**
|
|
1526
|
+
* Get configuration schema
|
|
1527
|
+
*/
|
|
1528
|
+
async getSchema(section) {
|
|
1529
|
+
return this.request("config.schema", { section });
|
|
1530
|
+
}
|
|
1531
|
+
/**
|
|
1532
|
+
* Get all configuration for a section
|
|
1533
|
+
*/
|
|
1534
|
+
async getSection(section) {
|
|
1535
|
+
return this.request("config.getSection", { section });
|
|
1536
|
+
}
|
|
1537
|
+
/**
|
|
1538
|
+
* Reset a configuration key to default
|
|
1539
|
+
*/
|
|
1540
|
+
async reset(key) {
|
|
1541
|
+
await this.request("config.reset", { key });
|
|
1542
|
+
}
|
|
1543
|
+
/**
|
|
1544
|
+
* Reset a section to defaults
|
|
1545
|
+
*/
|
|
1546
|
+
async resetSection(section) {
|
|
1547
|
+
await this.request("config.resetSection", { section });
|
|
1548
|
+
}
|
|
1549
|
+
/**
|
|
1550
|
+
* Export all configuration
|
|
1551
|
+
*/
|
|
1552
|
+
async export() {
|
|
1553
|
+
return this.request("config.export", {});
|
|
1554
|
+
}
|
|
1555
|
+
/**
|
|
1556
|
+
* Import configuration
|
|
1557
|
+
*/
|
|
1558
|
+
async import(config, merge = true) {
|
|
1559
|
+
await this.request("config.import", { config, merge });
|
|
1560
|
+
}
|
|
1561
|
+
/**
|
|
1562
|
+
* Validate configuration
|
|
1563
|
+
*/
|
|
1564
|
+
async validate(config) {
|
|
1565
|
+
return this.request("config.validate", { config });
|
|
1566
|
+
}
|
|
1567
|
+
// Convenience methods for common config operations
|
|
1568
|
+
/**
|
|
1569
|
+
* Get gateway configuration
|
|
1570
|
+
*/
|
|
1571
|
+
async getGateway() {
|
|
1572
|
+
return this.getSection("gateway");
|
|
1573
|
+
}
|
|
1574
|
+
/**
|
|
1575
|
+
* Get channels configuration
|
|
1576
|
+
*/
|
|
1577
|
+
async getChannels() {
|
|
1578
|
+
return this.getSection("channels");
|
|
1579
|
+
}
|
|
1580
|
+
/**
|
|
1581
|
+
* Get agents configuration
|
|
1582
|
+
*/
|
|
1583
|
+
async getAgents() {
|
|
1584
|
+
return this.getSection("agents");
|
|
1585
|
+
}
|
|
1586
|
+
/**
|
|
1587
|
+
* Get models configuration
|
|
1588
|
+
*/
|
|
1589
|
+
async getModels() {
|
|
1590
|
+
return this.getSection("models");
|
|
1591
|
+
}
|
|
1592
|
+
/**
|
|
1593
|
+
* Set gateway port
|
|
1594
|
+
*/
|
|
1595
|
+
async setGatewayPort(port) {
|
|
1596
|
+
await this.set("gateway.port", port);
|
|
1597
|
+
}
|
|
1598
|
+
/**
|
|
1599
|
+
* Set gateway token
|
|
1600
|
+
*/
|
|
1601
|
+
async setGatewayToken(token) {
|
|
1602
|
+
await this.set("gateway.auth.token", token);
|
|
1603
|
+
}
|
|
1604
|
+
};
|
|
1605
|
+
var ModelsModule = class extends BaseModule {
|
|
1606
|
+
/**
|
|
1607
|
+
* List all available models
|
|
1608
|
+
*/
|
|
1609
|
+
async list() {
|
|
1610
|
+
const result = await this.request("models.list", {});
|
|
1611
|
+
return {
|
|
1612
|
+
models: result.models || [],
|
|
1613
|
+
providers: result.providers || []
|
|
1614
|
+
};
|
|
1615
|
+
}
|
|
1616
|
+
/**
|
|
1617
|
+
* Get model status (default model, fallbacks, auth overview)
|
|
1618
|
+
*/
|
|
1619
|
+
async status() {
|
|
1620
|
+
return this.request("models.status", {});
|
|
1621
|
+
}
|
|
1622
|
+
/**
|
|
1623
|
+
* Get a specific model by ID
|
|
1624
|
+
*/
|
|
1625
|
+
async get(modelId) {
|
|
1626
|
+
const result = await this.list();
|
|
1627
|
+
const model = result.models.find((m) => m.id === modelId);
|
|
1628
|
+
if (!model) {
|
|
1629
|
+
throw new NotFoundError("Model", modelId);
|
|
1630
|
+
}
|
|
1631
|
+
return model;
|
|
1632
|
+
}
|
|
1633
|
+
/**
|
|
1634
|
+
* Set the default model
|
|
1635
|
+
*/
|
|
1636
|
+
async setDefault(model, agentId) {
|
|
1637
|
+
await this.request("models.set", {
|
|
1638
|
+
model,
|
|
1639
|
+
agentId
|
|
1640
|
+
});
|
|
1641
|
+
}
|
|
1642
|
+
/**
|
|
1643
|
+
* Get the current default model
|
|
1644
|
+
*/
|
|
1645
|
+
async getDefault(_agentId) {
|
|
1646
|
+
const status = await this.status();
|
|
1647
|
+
return status.defaultModel;
|
|
1648
|
+
}
|
|
1649
|
+
/**
|
|
1650
|
+
* Scan for available models from all providers
|
|
1651
|
+
*/
|
|
1652
|
+
async scan() {
|
|
1653
|
+
return this.request("models.scan", {});
|
|
1654
|
+
}
|
|
1655
|
+
/**
|
|
1656
|
+
* List configured providers
|
|
1657
|
+
*/
|
|
1658
|
+
async listProviders() {
|
|
1659
|
+
const status = await this.status();
|
|
1660
|
+
return status.providers;
|
|
1661
|
+
}
|
|
1662
|
+
/**
|
|
1663
|
+
* Get models from a specific provider
|
|
1664
|
+
*/
|
|
1665
|
+
async getProviderModels(providerId) {
|
|
1666
|
+
const result = await this.list();
|
|
1667
|
+
return result.models.filter((m) => m.provider === providerId);
|
|
1668
|
+
}
|
|
1669
|
+
// Aliases
|
|
1670
|
+
/**
|
|
1671
|
+
* List model aliases
|
|
1672
|
+
*/
|
|
1673
|
+
async listAliases() {
|
|
1674
|
+
const result = await this.request("models.aliases.list", {});
|
|
1675
|
+
return result.aliases || [];
|
|
1676
|
+
}
|
|
1677
|
+
/**
|
|
1678
|
+
* Add a model alias
|
|
1679
|
+
*/
|
|
1680
|
+
async addAlias(alias, model) {
|
|
1681
|
+
await this.request("models.aliases.add", { alias, model });
|
|
1682
|
+
}
|
|
1683
|
+
/**
|
|
1684
|
+
* Remove a model alias
|
|
1685
|
+
*/
|
|
1686
|
+
async removeAlias(alias) {
|
|
1687
|
+
await this.request("models.aliases.remove", { alias });
|
|
1688
|
+
}
|
|
1689
|
+
/**
|
|
1690
|
+
* Resolve an alias to full model reference
|
|
1691
|
+
*/
|
|
1692
|
+
async resolveAlias(alias) {
|
|
1693
|
+
const aliases = await this.listAliases();
|
|
1694
|
+
const found = aliases.find((a) => a.alias === alias);
|
|
1695
|
+
return found?.model || null;
|
|
1696
|
+
}
|
|
1697
|
+
// Fallbacks
|
|
1698
|
+
/**
|
|
1699
|
+
* List fallback configurations
|
|
1700
|
+
*/
|
|
1701
|
+
async listFallbacks() {
|
|
1702
|
+
const result = await this.request("models.fallbacks.list", {});
|
|
1703
|
+
return result.fallbacks || [];
|
|
1704
|
+
}
|
|
1705
|
+
/**
|
|
1706
|
+
* Set fallback models for a primary model
|
|
1707
|
+
*/
|
|
1708
|
+
async setFallbacks(primary, fallbacks) {
|
|
1709
|
+
await this.request("models.fallbacks.set", { primary, fallbacks });
|
|
1710
|
+
}
|
|
1711
|
+
/**
|
|
1712
|
+
* Add a fallback model
|
|
1713
|
+
*/
|
|
1714
|
+
async addFallback(primary, fallback) {
|
|
1715
|
+
const current = await this.listFallbacks();
|
|
1716
|
+
const existing = current.find((f) => f.primary === primary);
|
|
1717
|
+
const fallbacks = existing ? [...existing.fallbacks, fallback] : [fallback];
|
|
1718
|
+
await this.setFallbacks(primary, fallbacks);
|
|
1719
|
+
}
|
|
1720
|
+
/**
|
|
1721
|
+
* Remove a fallback model
|
|
1722
|
+
*/
|
|
1723
|
+
async removeFallback(primary, fallback) {
|
|
1724
|
+
const current = await this.listFallbacks();
|
|
1725
|
+
const existing = current.find((f) => f.primary === primary);
|
|
1726
|
+
if (existing) {
|
|
1727
|
+
const fallbacks = existing.fallbacks.filter((f) => f !== fallback);
|
|
1728
|
+
await this.setFallbacks(primary, fallbacks);
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
// Auth Profiles
|
|
1732
|
+
/**
|
|
1733
|
+
* List auth profiles
|
|
1734
|
+
*/
|
|
1735
|
+
async listAuthProfiles() {
|
|
1736
|
+
const status = await this.status();
|
|
1737
|
+
return status.authProfiles;
|
|
1738
|
+
}
|
|
1739
|
+
/**
|
|
1740
|
+
* Add an auth profile (API key or token)
|
|
1741
|
+
*/
|
|
1742
|
+
async addAuth(options) {
|
|
1743
|
+
return this.request("models.auth.add", {
|
|
1744
|
+
provider: options.provider,
|
|
1745
|
+
type: options.type,
|
|
1746
|
+
value: options.value,
|
|
1747
|
+
profileId: options.profileId
|
|
1748
|
+
});
|
|
1749
|
+
}
|
|
1750
|
+
/**
|
|
1751
|
+
* Remove an auth profile
|
|
1752
|
+
*/
|
|
1753
|
+
async removeAuth(profileId) {
|
|
1754
|
+
await this.request("models.auth.remove", { profileId });
|
|
1755
|
+
}
|
|
1756
|
+
/**
|
|
1757
|
+
* Probe auth profiles to verify they work
|
|
1758
|
+
*/
|
|
1759
|
+
async probeAuth(options = {}) {
|
|
1760
|
+
const result = await this.request("models.auth.probe", {
|
|
1761
|
+
provider: options.provider,
|
|
1762
|
+
profileId: options.profileId,
|
|
1763
|
+
timeout: options.timeout,
|
|
1764
|
+
maxTokens: options.maxTokens
|
|
1765
|
+
});
|
|
1766
|
+
return result.results || [];
|
|
1767
|
+
}
|
|
1768
|
+
/**
|
|
1769
|
+
* Check if a provider is configured and authenticated
|
|
1770
|
+
*/
|
|
1771
|
+
async isProviderConfigured(providerId) {
|
|
1772
|
+
const providers = await this.listProviders();
|
|
1773
|
+
const provider = providers.find((p) => p.id === providerId);
|
|
1774
|
+
return Boolean(provider?.configured && provider?.authStatus === "valid");
|
|
1775
|
+
}
|
|
1776
|
+
// Convenience methods
|
|
1777
|
+
/**
|
|
1778
|
+
* Get recommended models for a provider
|
|
1779
|
+
*/
|
|
1780
|
+
async getRecommended(providerId) {
|
|
1781
|
+
const result = await this.list();
|
|
1782
|
+
let models = result.models;
|
|
1783
|
+
if (providerId) {
|
|
1784
|
+
models = models.filter((m) => m.provider === providerId);
|
|
1785
|
+
}
|
|
1786
|
+
return models.filter((m) => m.contextWindow >= 1e5).sort((a, b) => b.contextWindow - a.contextWindow);
|
|
1787
|
+
}
|
|
1788
|
+
/**
|
|
1789
|
+
* Get the best available model based on current configuration
|
|
1790
|
+
*/
|
|
1791
|
+
async getBestAvailable() {
|
|
1792
|
+
const status = await this.status();
|
|
1793
|
+
const models = await this.list();
|
|
1794
|
+
const defaultModel = models.models.find((m) => m.id === status.defaultModel);
|
|
1795
|
+
if (defaultModel) {
|
|
1796
|
+
return defaultModel;
|
|
1797
|
+
}
|
|
1798
|
+
for (const fallback of status.fallbacks) {
|
|
1799
|
+
const model = models.models.find((m) => m.id === fallback);
|
|
1800
|
+
if (model) {
|
|
1801
|
+
return model;
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
return null;
|
|
1805
|
+
}
|
|
1806
|
+
};
|
|
1807
|
+
var NodesModule = class extends BaseModule {
|
|
1808
|
+
/**
|
|
1809
|
+
* List all nodes
|
|
1810
|
+
*/
|
|
1811
|
+
async list(options = {}) {
|
|
1812
|
+
const result = await this.request("nodes.list", {
|
|
1813
|
+
status: options.status ?? "all",
|
|
1814
|
+
capability: options.capability
|
|
1815
|
+
});
|
|
1816
|
+
return result.nodes || [];
|
|
1817
|
+
}
|
|
1818
|
+
/**
|
|
1819
|
+
* Get a specific node
|
|
1820
|
+
*/
|
|
1821
|
+
async get(nodeId) {
|
|
1822
|
+
const nodes = await this.list();
|
|
1823
|
+
const node = nodes.find((n) => n.nodeId === nodeId);
|
|
1824
|
+
if (!node) {
|
|
1825
|
+
throw new NotFoundError("Node", nodeId);
|
|
1826
|
+
}
|
|
1827
|
+
return node;
|
|
1828
|
+
}
|
|
1829
|
+
/**
|
|
1830
|
+
* Invoke a method on a node
|
|
1831
|
+
*/
|
|
1832
|
+
async invoke(options) {
|
|
1833
|
+
return this.request("nodes.invoke", {
|
|
1834
|
+
nodeId: options.nodeId,
|
|
1835
|
+
method: options.method,
|
|
1836
|
+
params: options.params || {},
|
|
1837
|
+
timeout: options.timeout
|
|
1838
|
+
});
|
|
1839
|
+
}
|
|
1840
|
+
/**
|
|
1841
|
+
* Get capabilities of a node
|
|
1842
|
+
*/
|
|
1843
|
+
async getCapabilities(nodeId) {
|
|
1844
|
+
const result = await this.request("nodes.capabilities", {
|
|
1845
|
+
nodeId
|
|
1846
|
+
});
|
|
1847
|
+
return result.capabilities || [];
|
|
1848
|
+
}
|
|
1849
|
+
/**
|
|
1850
|
+
* Get all online nodes
|
|
1851
|
+
*/
|
|
1852
|
+
async online() {
|
|
1853
|
+
return this.list({ status: "online" });
|
|
1854
|
+
}
|
|
1855
|
+
/**
|
|
1856
|
+
* Get nodes with a specific capability
|
|
1857
|
+
*/
|
|
1858
|
+
async withCapability(capability) {
|
|
1859
|
+
return this.list({ capability });
|
|
1860
|
+
}
|
|
1861
|
+
/**
|
|
1862
|
+
* Check if a node is online
|
|
1863
|
+
*/
|
|
1864
|
+
async isOnline(nodeId) {
|
|
1865
|
+
try {
|
|
1866
|
+
const node = await this.get(nodeId);
|
|
1867
|
+
return node.status === "online";
|
|
1868
|
+
} catch (error) {
|
|
1869
|
+
if (error instanceof NotFoundError) {
|
|
1870
|
+
return false;
|
|
1871
|
+
}
|
|
1872
|
+
throw error;
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
/**
|
|
1876
|
+
* Ping a node
|
|
1877
|
+
*/
|
|
1878
|
+
async ping(nodeId) {
|
|
1879
|
+
const start = Date.now();
|
|
1880
|
+
const _result = await this.invoke({
|
|
1881
|
+
nodeId,
|
|
1882
|
+
method: "ping",
|
|
1883
|
+
timeout: 5e3
|
|
1884
|
+
});
|
|
1885
|
+
return {
|
|
1886
|
+
pong: true,
|
|
1887
|
+
latencyMs: Date.now() - start
|
|
1888
|
+
};
|
|
1889
|
+
}
|
|
1890
|
+
// Convenience methods for common node operations
|
|
1891
|
+
/**
|
|
1892
|
+
* Take a camera snapshot (camera capability)
|
|
1893
|
+
*/
|
|
1894
|
+
async cameraSnap(nodeId) {
|
|
1895
|
+
const result = await this.invoke({
|
|
1896
|
+
nodeId,
|
|
1897
|
+
method: "camera.snap"
|
|
1898
|
+
});
|
|
1899
|
+
return result.result;
|
|
1900
|
+
}
|
|
1901
|
+
/**
|
|
1902
|
+
* Get device location (location capability)
|
|
1903
|
+
*/
|
|
1904
|
+
async getLocation(nodeId) {
|
|
1905
|
+
const result = await this.invoke({
|
|
1906
|
+
nodeId,
|
|
1907
|
+
method: "location.get"
|
|
1908
|
+
});
|
|
1909
|
+
return result.result;
|
|
1910
|
+
}
|
|
1911
|
+
/**
|
|
1912
|
+
* Execute a system command (system.run capability)
|
|
1913
|
+
*/
|
|
1914
|
+
async exec(nodeId, command, options = {}) {
|
|
1915
|
+
const result = await this.invoke({
|
|
1916
|
+
nodeId,
|
|
1917
|
+
method: "system.run",
|
|
1918
|
+
params: { command, cwd: options.cwd },
|
|
1919
|
+
timeout: options.timeout
|
|
1920
|
+
});
|
|
1921
|
+
return result.result;
|
|
1922
|
+
}
|
|
1923
|
+
/**
|
|
1924
|
+
* Navigate browser canvas (canvas capability)
|
|
1925
|
+
*/
|
|
1926
|
+
async canvasNavigate(nodeId, url) {
|
|
1927
|
+
const result = await this.invoke({
|
|
1928
|
+
nodeId,
|
|
1929
|
+
method: "canvas.navigate",
|
|
1930
|
+
params: { url }
|
|
1931
|
+
});
|
|
1932
|
+
return result.result;
|
|
1933
|
+
}
|
|
1934
|
+
};
|
|
1935
|
+
var SessionsModule = class extends BaseModule {
|
|
1936
|
+
/**
|
|
1937
|
+
* List sessions
|
|
1938
|
+
*/
|
|
1939
|
+
async list(options = {}) {
|
|
1940
|
+
const result = await this.request("sessions.list", {
|
|
1941
|
+
agentId: options.agentId,
|
|
1942
|
+
limit: options.limit ?? 50,
|
|
1943
|
+
activeWithinMinutes: options.activeWithinMinutes,
|
|
1944
|
+
includeArchived: options.includeArchived
|
|
1945
|
+
});
|
|
1946
|
+
return {
|
|
1947
|
+
sessions: result.sessions || [],
|
|
1948
|
+
total: result.total ?? result.sessions?.length ?? 0
|
|
1949
|
+
};
|
|
1950
|
+
}
|
|
1951
|
+
/**
|
|
1952
|
+
* Get a specific session by key
|
|
1953
|
+
*/
|
|
1954
|
+
async get(sessionKey) {
|
|
1955
|
+
const result = await this.request("sessions.get", {
|
|
1956
|
+
key: sessionKey
|
|
1957
|
+
});
|
|
1958
|
+
if (!result.session) {
|
|
1959
|
+
throw new NotFoundError("Session", sessionKey);
|
|
1960
|
+
}
|
|
1961
|
+
return result.session;
|
|
1962
|
+
}
|
|
1963
|
+
/**
|
|
1964
|
+
* Create a new session
|
|
1965
|
+
*/
|
|
1966
|
+
async create(options = {}) {
|
|
1967
|
+
return this.request("sessions.create", {
|
|
1968
|
+
key: options.key,
|
|
1969
|
+
agentId: options.agentId,
|
|
1970
|
+
initialMessage: options.initialMessage,
|
|
1971
|
+
name: options.name,
|
|
1972
|
+
tags: options.tags
|
|
1973
|
+
});
|
|
1974
|
+
}
|
|
1975
|
+
/**
|
|
1976
|
+
* Update a session (patch)
|
|
1977
|
+
*/
|
|
1978
|
+
async update(sessionKey, updates) {
|
|
1979
|
+
return this.request("sessions.patch", {
|
|
1980
|
+
key: sessionKey,
|
|
1981
|
+
updates
|
|
1982
|
+
});
|
|
1983
|
+
}
|
|
1984
|
+
/**
|
|
1985
|
+
* Delete a session
|
|
1986
|
+
*/
|
|
1987
|
+
async delete(sessionKey) {
|
|
1988
|
+
await this.request("sessions.delete", { key: sessionKey });
|
|
1989
|
+
}
|
|
1990
|
+
/**
|
|
1991
|
+
* Get session preview (summary)
|
|
1992
|
+
*/
|
|
1993
|
+
async preview(sessionKey) {
|
|
1994
|
+
return this.request("sessions.preview", {
|
|
1995
|
+
key: sessionKey
|
|
1996
|
+
});
|
|
1997
|
+
}
|
|
1998
|
+
/**
|
|
1999
|
+
* Get session chat history
|
|
2000
|
+
*/
|
|
2001
|
+
async history(sessionKey, options = {}) {
|
|
2002
|
+
return this.request("sessions.history", {
|
|
2003
|
+
key: sessionKey,
|
|
2004
|
+
limit: options.limit ?? 100,
|
|
2005
|
+
offset: options.offset ?? 0
|
|
2006
|
+
});
|
|
2007
|
+
}
|
|
2008
|
+
/**
|
|
2009
|
+
* Archive a session
|
|
2010
|
+
*/
|
|
2011
|
+
async archive(sessionKey) {
|
|
2012
|
+
return this.update(sessionKey, { archived: true });
|
|
2013
|
+
}
|
|
2014
|
+
/**
|
|
2015
|
+
* Unarchive a session
|
|
2016
|
+
*/
|
|
2017
|
+
async unarchive(sessionKey) {
|
|
2018
|
+
return this.update(sessionKey, { archived: false });
|
|
2019
|
+
}
|
|
2020
|
+
/**
|
|
2021
|
+
* Add tags to a session
|
|
2022
|
+
*/
|
|
2023
|
+
async addTags(sessionKey, tags) {
|
|
2024
|
+
const session = await this.get(sessionKey);
|
|
2025
|
+
const existingTags = session.tags || [];
|
|
2026
|
+
const newTags = [.../* @__PURE__ */ new Set([...existingTags, ...tags])];
|
|
2027
|
+
return this.update(sessionKey, { tags: newTags });
|
|
2028
|
+
}
|
|
2029
|
+
/**
|
|
2030
|
+
* Remove tags from a session
|
|
2031
|
+
*/
|
|
2032
|
+
async removeTags(sessionKey, tags) {
|
|
2033
|
+
const session = await this.get(sessionKey);
|
|
2034
|
+
const existingTags = session.tags || [];
|
|
2035
|
+
const newTags = existingTags.filter((t) => !tags.includes(t));
|
|
2036
|
+
return this.update(sessionKey, { tags: newTags });
|
|
2037
|
+
}
|
|
2038
|
+
/**
|
|
2039
|
+
* Check if a session exists
|
|
2040
|
+
*/
|
|
2041
|
+
async exists(sessionKey) {
|
|
2042
|
+
try {
|
|
2043
|
+
await this.get(sessionKey);
|
|
2044
|
+
return true;
|
|
2045
|
+
} catch (error) {
|
|
2046
|
+
if (error instanceof NotFoundError) {
|
|
2047
|
+
return false;
|
|
2048
|
+
}
|
|
2049
|
+
throw error;
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
/**
|
|
2053
|
+
* Get recent sessions
|
|
2054
|
+
*/
|
|
2055
|
+
async recent(limit = 10) {
|
|
2056
|
+
const result = await this.list({ limit, activeWithinMinutes: 60 * 24 });
|
|
2057
|
+
return result.sessions;
|
|
2058
|
+
}
|
|
2059
|
+
};
|
|
2060
|
+
var SystemModule = class extends BaseModule {
|
|
2061
|
+
/**
|
|
2062
|
+
* Get detailed gateway health status
|
|
2063
|
+
*/
|
|
2064
|
+
async health() {
|
|
2065
|
+
return this.request("health", {});
|
|
2066
|
+
}
|
|
2067
|
+
/**
|
|
2068
|
+
* Get gateway status
|
|
2069
|
+
*/
|
|
2070
|
+
async status() {
|
|
2071
|
+
return this.request("status", {});
|
|
2072
|
+
}
|
|
2073
|
+
/**
|
|
2074
|
+
* Get system presence (connected devices/clients)
|
|
2075
|
+
*/
|
|
2076
|
+
async presence() {
|
|
2077
|
+
const result = await this.request("system-presence", {});
|
|
2078
|
+
return result.presence || [];
|
|
2079
|
+
}
|
|
2080
|
+
/**
|
|
2081
|
+
* Query logs
|
|
2082
|
+
*/
|
|
2083
|
+
async logs(options = {}) {
|
|
2084
|
+
const result = await this.request("logs.query", {
|
|
2085
|
+
since: options.since,
|
|
2086
|
+
until: options.until,
|
|
2087
|
+
level: options.level,
|
|
2088
|
+
limit: options.limit ?? 100,
|
|
2089
|
+
agentId: options.agentId
|
|
2090
|
+
});
|
|
2091
|
+
return result.logs || [];
|
|
2092
|
+
}
|
|
2093
|
+
/**
|
|
2094
|
+
* Get usage statistics
|
|
2095
|
+
*/
|
|
2096
|
+
async usage(options = {}) {
|
|
2097
|
+
return this.request("usage.status", {
|
|
2098
|
+
since: options.since,
|
|
2099
|
+
until: options.until,
|
|
2100
|
+
agentId: options.agentId,
|
|
2101
|
+
model: options.model
|
|
2102
|
+
});
|
|
2103
|
+
}
|
|
2104
|
+
/**
|
|
2105
|
+
* Get usage cost breakdown
|
|
2106
|
+
*/
|
|
2107
|
+
async usageCost(options = {}) {
|
|
2108
|
+
return this.request("usage.cost", {
|
|
2109
|
+
since: options.since,
|
|
2110
|
+
until: options.until,
|
|
2111
|
+
agentId: options.agentId,
|
|
2112
|
+
model: options.model
|
|
2113
|
+
});
|
|
2114
|
+
}
|
|
2115
|
+
/**
|
|
2116
|
+
* Ping the gateway (simple health check)
|
|
2117
|
+
*/
|
|
2118
|
+
async ping() {
|
|
2119
|
+
return this.request("ping", {});
|
|
2120
|
+
}
|
|
2121
|
+
/**
|
|
2122
|
+
* Get gateway version information
|
|
2123
|
+
*/
|
|
2124
|
+
async version() {
|
|
2125
|
+
return this.request("version", {});
|
|
2126
|
+
}
|
|
2127
|
+
};
|
|
2128
|
+
var execAsync = promisify(exec);
|
|
2129
|
+
var DEFAULT_CLI_CONFIG = {
|
|
2130
|
+
cliPath: "moltbot",
|
|
2131
|
+
timeout: 6e5,
|
|
2132
|
+
// 10 minutes
|
|
2133
|
+
maxBuffer: 50 * 1024 * 1024
|
|
2134
|
+
// 50MB
|
|
2135
|
+
};
|
|
2136
|
+
var CLITransport = class {
|
|
2137
|
+
config;
|
|
2138
|
+
eventHandlers = /* @__PURE__ */ new Map();
|
|
2139
|
+
connected = false;
|
|
2140
|
+
constructor(config = {}) {
|
|
2141
|
+
this.config = {
|
|
2142
|
+
...DEFAULT_CLI_CONFIG,
|
|
2143
|
+
...config
|
|
2144
|
+
};
|
|
2145
|
+
}
|
|
2146
|
+
/**
|
|
2147
|
+
* "Connect" - validates CLI is available
|
|
2148
|
+
*/
|
|
2149
|
+
async connect() {
|
|
2150
|
+
try {
|
|
2151
|
+
await execAsync(`${this.config.cliPath} --version`, {
|
|
2152
|
+
timeout: 5e3
|
|
2153
|
+
});
|
|
2154
|
+
this.connected = true;
|
|
2155
|
+
this.emit("connection:connected");
|
|
2156
|
+
this.emit("connection:state", "connected");
|
|
2157
|
+
} catch (error) {
|
|
2158
|
+
this.connected = false;
|
|
2159
|
+
const err = new CLIError(
|
|
2160
|
+
`CLI not available at '${this.config.cliPath}'`,
|
|
2161
|
+
error.code,
|
|
2162
|
+
error.stdout,
|
|
2163
|
+
error.stderr
|
|
2164
|
+
);
|
|
2165
|
+
this.emit("connection:error", err);
|
|
2166
|
+
throw err;
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
/**
|
|
2170
|
+
* "Disconnect" - no-op for CLI
|
|
2171
|
+
*/
|
|
2172
|
+
disconnect() {
|
|
2173
|
+
this.connected = false;
|
|
2174
|
+
this.emit("connection:disconnected");
|
|
2175
|
+
this.emit("connection:state", "disconnected");
|
|
2176
|
+
}
|
|
2177
|
+
/**
|
|
2178
|
+
* Check if "connected" (CLI available)
|
|
2179
|
+
*/
|
|
2180
|
+
isConnected() {
|
|
2181
|
+
return this.connected;
|
|
2182
|
+
}
|
|
2183
|
+
/**
|
|
2184
|
+
* Get connection state
|
|
2185
|
+
*/
|
|
2186
|
+
getConnectionState() {
|
|
2187
|
+
return this.connected ? "connected" : "disconnected";
|
|
2188
|
+
}
|
|
2189
|
+
/**
|
|
2190
|
+
* Execute a CLI command and parse JSON response
|
|
2191
|
+
*/
|
|
2192
|
+
async execCommand(command) {
|
|
2193
|
+
try {
|
|
2194
|
+
const { stdout } = await execAsync(command, {
|
|
2195
|
+
timeout: this.config.timeout,
|
|
2196
|
+
maxBuffer: this.config.maxBuffer
|
|
2197
|
+
});
|
|
2198
|
+
if (stdout.trim()) {
|
|
2199
|
+
try {
|
|
2200
|
+
return JSON.parse(stdout.trim());
|
|
2201
|
+
} catch {
|
|
2202
|
+
return { raw: stdout.trim() };
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
return { success: true };
|
|
2206
|
+
} catch (error) {
|
|
2207
|
+
const execError = error;
|
|
2208
|
+
throw new CLIError(
|
|
2209
|
+
execError.message || "CLI command failed",
|
|
2210
|
+
execError.code,
|
|
2211
|
+
execError.stdout,
|
|
2212
|
+
execError.stderr
|
|
2213
|
+
);
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
/**
|
|
2217
|
+
* Make an RPC-like request via CLI
|
|
2218
|
+
*/
|
|
2219
|
+
async request(method, params = {}) {
|
|
2220
|
+
if (!this.connected) {
|
|
2221
|
+
throw new NotConnectedError("CLI transport not initialized. Call connect() first.");
|
|
2222
|
+
}
|
|
2223
|
+
const command = this.buildCommand(method, params);
|
|
2224
|
+
const result = await this.execCommand(command);
|
|
2225
|
+
return result;
|
|
2226
|
+
}
|
|
2227
|
+
/**
|
|
2228
|
+
* Build CLI command from RPC method and params
|
|
2229
|
+
*/
|
|
2230
|
+
buildCommand(method, params) {
|
|
2231
|
+
const cli = this.config.cliPath;
|
|
2232
|
+
switch (method) {
|
|
2233
|
+
// Health/Status
|
|
2234
|
+
case "health":
|
|
2235
|
+
return `${cli} health --json`;
|
|
2236
|
+
case "status":
|
|
2237
|
+
return `${cli} status --json`;
|
|
2238
|
+
case "ping":
|
|
2239
|
+
return `${cli} health --json`;
|
|
2240
|
+
// Agents
|
|
2241
|
+
case "agents.list":
|
|
2242
|
+
return `${cli} agents list --json`;
|
|
2243
|
+
case "agents.create":
|
|
2244
|
+
return this.buildAgentCreateCommand(cli, params);
|
|
2245
|
+
case "agents.delete":
|
|
2246
|
+
return `${cli} agents delete ${params.agentId} --json`;
|
|
2247
|
+
// Sessions
|
|
2248
|
+
case "sessions.list":
|
|
2249
|
+
return this.buildSessionsListCommand(cli, params);
|
|
2250
|
+
case "sessions.delete":
|
|
2251
|
+
return `${cli} sessions delete ${params.key} --json`;
|
|
2252
|
+
// Chat/Agent execution
|
|
2253
|
+
case "chat.send":
|
|
2254
|
+
case "agent.execute":
|
|
2255
|
+
return this.buildAgentCommand(cli, params);
|
|
2256
|
+
// Models
|
|
2257
|
+
case "models.list":
|
|
2258
|
+
return `${cli} models list --json`;
|
|
2259
|
+
// Config
|
|
2260
|
+
case "config.get":
|
|
2261
|
+
return `${cli} config get ${params.key} --json`;
|
|
2262
|
+
case "config.set":
|
|
2263
|
+
return `${cli} config set ${params.key} ${JSON.stringify(params.value)} --json`;
|
|
2264
|
+
// Logs
|
|
2265
|
+
case "logs.query":
|
|
2266
|
+
return this.buildLogsCommand(cli, params);
|
|
2267
|
+
// Default: try direct CLI command
|
|
2268
|
+
default:
|
|
2269
|
+
return this.buildGenericCommand(cli, method, params);
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
buildAgentCreateCommand(cli, params) {
|
|
2273
|
+
const parts = [cli, "agents", "add", params.agentId];
|
|
2274
|
+
if (params.workspace) {
|
|
2275
|
+
parts.push("--workspace", params.workspace);
|
|
2276
|
+
}
|
|
2277
|
+
parts.push("--json");
|
|
2278
|
+
return parts.join(" ");
|
|
2279
|
+
}
|
|
2280
|
+
buildSessionsListCommand(cli, params) {
|
|
2281
|
+
const parts = [cli, "sessions"];
|
|
2282
|
+
if (params.activeWithinMinutes) {
|
|
2283
|
+
parts.push("--active", String(params.activeWithinMinutes));
|
|
2284
|
+
}
|
|
2285
|
+
parts.push("--json");
|
|
2286
|
+
return parts.join(" ");
|
|
2287
|
+
}
|
|
2288
|
+
buildAgentCommand(cli, params) {
|
|
2289
|
+
const parts = [cli, "agent"];
|
|
2290
|
+
if (params.sessionKey || params.sessionId) {
|
|
2291
|
+
parts.push("--session-id", params.sessionKey || params.sessionId);
|
|
2292
|
+
}
|
|
2293
|
+
if (params.agentId) {
|
|
2294
|
+
parts.push("--agent", params.agentId);
|
|
2295
|
+
}
|
|
2296
|
+
if (params.message) {
|
|
2297
|
+
const escapedMessage = params.message.replace(/"/g, '\\"');
|
|
2298
|
+
parts.push("--message", `"${escapedMessage}"`);
|
|
2299
|
+
}
|
|
2300
|
+
if (params.target) {
|
|
2301
|
+
parts.push("--to", params.target);
|
|
2302
|
+
}
|
|
2303
|
+
if (params.deliver) {
|
|
2304
|
+
parts.push("--deliver");
|
|
2305
|
+
}
|
|
2306
|
+
if (params.timeout) {
|
|
2307
|
+
parts.push("--timeout", String(Math.floor(params.timeout / 1e3)));
|
|
2308
|
+
}
|
|
2309
|
+
if (params.thinking) {
|
|
2310
|
+
parts.push("--thinking", params.thinking);
|
|
2311
|
+
}
|
|
2312
|
+
parts.push("--json");
|
|
2313
|
+
return parts.join(" ");
|
|
2314
|
+
}
|
|
2315
|
+
buildLogsCommand(cli, params) {
|
|
2316
|
+
const parts = [cli, "logs"];
|
|
2317
|
+
if (params.limit) {
|
|
2318
|
+
parts.push("--limit", String(params.limit));
|
|
2319
|
+
}
|
|
2320
|
+
if (params.level) {
|
|
2321
|
+
parts.push("--level", params.level);
|
|
2322
|
+
}
|
|
2323
|
+
parts.push("--json");
|
|
2324
|
+
return parts.join(" ");
|
|
2325
|
+
}
|
|
2326
|
+
buildGenericCommand(cli, method, params) {
|
|
2327
|
+
const command = method.replace(/\./g, " ");
|
|
2328
|
+
const parts = [cli, command];
|
|
2329
|
+
for (const [key, value] of Object.entries(params)) {
|
|
2330
|
+
if (value !== void 0 && value !== null) {
|
|
2331
|
+
const flag = `--${key.replace(/([A-Z])/g, "-$1").toLowerCase()}`;
|
|
2332
|
+
if (typeof value === "boolean") {
|
|
2333
|
+
if (value) parts.push(flag);
|
|
2334
|
+
} else {
|
|
2335
|
+
parts.push(flag, String(value));
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
parts.push("--json");
|
|
2340
|
+
return parts.join(" ");
|
|
2341
|
+
}
|
|
2342
|
+
/**
|
|
2343
|
+
* Subscribe to an event (limited support for CLI)
|
|
2344
|
+
*/
|
|
2345
|
+
on(event, handler) {
|
|
2346
|
+
if (!this.eventHandlers.has(event)) {
|
|
2347
|
+
this.eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
2348
|
+
}
|
|
2349
|
+
this.eventHandlers.get(event).add(handler);
|
|
2350
|
+
}
|
|
2351
|
+
/**
|
|
2352
|
+
* Unsubscribe from an event
|
|
2353
|
+
*/
|
|
2354
|
+
off(event, handler) {
|
|
2355
|
+
const handlers = this.eventHandlers.get(event);
|
|
2356
|
+
if (handlers) {
|
|
2357
|
+
handlers.delete(handler);
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
/**
|
|
2361
|
+
* Emit an event
|
|
2362
|
+
*/
|
|
2363
|
+
emit(event, ...args) {
|
|
2364
|
+
const handlers = this.eventHandlers.get(event);
|
|
2365
|
+
if (handlers) {
|
|
2366
|
+
handlers.forEach((handler) => {
|
|
2367
|
+
try {
|
|
2368
|
+
handler(...args);
|
|
2369
|
+
} catch (error) {
|
|
2370
|
+
console.error(`Error in event handler for ${event}:`, error);
|
|
2371
|
+
}
|
|
2372
|
+
});
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
};
|
|
2376
|
+
function createCLITransport(config = {}) {
|
|
2377
|
+
return new CLITransport(config);
|
|
2378
|
+
}
|
|
2379
|
+
var WebSocketTransport = class {
|
|
2380
|
+
client;
|
|
2381
|
+
eventHandlers = /* @__PURE__ */ new Map();
|
|
2382
|
+
constructor(config) {
|
|
2383
|
+
this.client = new ClawdbotClient({
|
|
2384
|
+
gatewayUrl: config.gatewayUrl,
|
|
2385
|
+
authToken: config.authToken,
|
|
2386
|
+
clientName: config.clientName || "molt-sdk",
|
|
2387
|
+
clientVersion: config.clientVersion || "1.0.0",
|
|
2388
|
+
deviceId: config.deviceId,
|
|
2389
|
+
devicePrivateKey: config.devicePrivateKey,
|
|
2390
|
+
autoReconnect: config.autoReconnect ?? true,
|
|
2391
|
+
reconnectInterval: config.reconnectInterval ?? 5e3,
|
|
2392
|
+
maxReconnectAttempts: config.maxReconnectAttempts ?? 10,
|
|
2393
|
+
requestTimeout: config.requestTimeout ?? 3e4,
|
|
2394
|
+
role: config.role,
|
|
2395
|
+
scopes: config.scopes
|
|
2396
|
+
});
|
|
2397
|
+
this.setupEventForwarding();
|
|
2398
|
+
}
|
|
2399
|
+
/**
|
|
2400
|
+
* Setup event forwarding from internal client
|
|
2401
|
+
*/
|
|
2402
|
+
setupEventForwarding() {
|
|
2403
|
+
this.client.on("connection:state", (state) => {
|
|
2404
|
+
this.emit("connection:state", state);
|
|
2405
|
+
});
|
|
2406
|
+
this.client.on("connection:connected", () => {
|
|
2407
|
+
this.emit("connection:connected");
|
|
2408
|
+
});
|
|
2409
|
+
this.client.on("connection:disconnected", (reason) => {
|
|
2410
|
+
this.emit("connection:disconnected", reason);
|
|
2411
|
+
});
|
|
2412
|
+
this.client.on("connection:error", (error) => {
|
|
2413
|
+
this.emit("connection:error", error);
|
|
2414
|
+
});
|
|
2415
|
+
this.client.on("message", (message) => {
|
|
2416
|
+
this.emit("message", message);
|
|
2417
|
+
});
|
|
2418
|
+
this.client.on("event", (event, payload) => {
|
|
2419
|
+
this.emit("event", event, payload);
|
|
2420
|
+
this.emit(event, payload);
|
|
2421
|
+
});
|
|
2422
|
+
this.client.on("agent:status", (agentId, status) => {
|
|
2423
|
+
this.emit("agent:status", agentId, status);
|
|
2424
|
+
});
|
|
2425
|
+
this.client.on("agent:log", (agentId, message, level) => {
|
|
2426
|
+
this.emit("agent:log", agentId, message, level);
|
|
2427
|
+
});
|
|
2428
|
+
}
|
|
2429
|
+
/**
|
|
2430
|
+
* Connect to the gateway
|
|
2431
|
+
*/
|
|
2432
|
+
async connect() {
|
|
2433
|
+
await this.client.connect();
|
|
2434
|
+
}
|
|
2435
|
+
/**
|
|
2436
|
+
* Disconnect from the gateway
|
|
2437
|
+
*/
|
|
2438
|
+
disconnect() {
|
|
2439
|
+
this.client.disconnect();
|
|
2440
|
+
}
|
|
2441
|
+
/**
|
|
2442
|
+
* Check if connected
|
|
2443
|
+
*/
|
|
2444
|
+
isConnected() {
|
|
2445
|
+
return this.client.isConnected();
|
|
2446
|
+
}
|
|
2447
|
+
/**
|
|
2448
|
+
* Get current connection state
|
|
2449
|
+
*/
|
|
2450
|
+
getConnectionState() {
|
|
2451
|
+
return this.client.getConnectionState();
|
|
2452
|
+
}
|
|
2453
|
+
/**
|
|
2454
|
+
* Make an RPC request
|
|
2455
|
+
*/
|
|
2456
|
+
async request(method, params = {}) {
|
|
2457
|
+
return this.client.request(method, params);
|
|
2458
|
+
}
|
|
2459
|
+
/**
|
|
2460
|
+
* Subscribe to an event
|
|
2461
|
+
*/
|
|
2462
|
+
on(event, handler) {
|
|
2463
|
+
if (!this.eventHandlers.has(event)) {
|
|
2464
|
+
this.eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
2465
|
+
}
|
|
2466
|
+
this.eventHandlers.get(event).add(handler);
|
|
2467
|
+
}
|
|
2468
|
+
/**
|
|
2469
|
+
* Unsubscribe from an event
|
|
2470
|
+
*/
|
|
2471
|
+
off(event, handler) {
|
|
2472
|
+
const handlers = this.eventHandlers.get(event);
|
|
2473
|
+
if (handlers) {
|
|
2474
|
+
handlers.delete(handler);
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
/**
|
|
2478
|
+
* Emit an event
|
|
2479
|
+
*/
|
|
2480
|
+
emit(event, ...args) {
|
|
2481
|
+
const handlers = this.eventHandlers.get(event);
|
|
2482
|
+
if (handlers) {
|
|
2483
|
+
handlers.forEach((handler) => {
|
|
2484
|
+
try {
|
|
2485
|
+
handler(...args);
|
|
2486
|
+
} catch (error) {
|
|
2487
|
+
console.error(`Error in event handler for ${event}:`, error);
|
|
2488
|
+
}
|
|
2489
|
+
});
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
/**
|
|
2493
|
+
* Get the underlying client (for advanced use cases)
|
|
2494
|
+
*/
|
|
2495
|
+
getClient() {
|
|
2496
|
+
return this.client;
|
|
2497
|
+
}
|
|
2498
|
+
/**
|
|
2499
|
+
* Get cached connection snapshot
|
|
2500
|
+
*/
|
|
2501
|
+
getSnapshot() {
|
|
2502
|
+
return this.client.getSnapshot();
|
|
2503
|
+
}
|
|
2504
|
+
/**
|
|
2505
|
+
* Get cached health from connection snapshot
|
|
2506
|
+
*/
|
|
2507
|
+
getCachedHealth() {
|
|
2508
|
+
return this.client.getCachedHealth();
|
|
2509
|
+
}
|
|
2510
|
+
/**
|
|
2511
|
+
* Invalidate cached health data
|
|
2512
|
+
*/
|
|
2513
|
+
invalidateHealthCache() {
|
|
2514
|
+
this.client.invalidateHealthCache();
|
|
2515
|
+
}
|
|
2516
|
+
};
|
|
2517
|
+
var DEFAULT_CONFIG = {
|
|
2518
|
+
gatewayUrl: "ws://127.0.0.1:18789",
|
|
2519
|
+
clientName: "molt-sdk",
|
|
2520
|
+
clientVersion: "1.0.0",
|
|
2521
|
+
autoReconnect: true,
|
|
2522
|
+
reconnectInterval: 5e3,
|
|
2523
|
+
maxReconnectAttempts: 10,
|
|
2524
|
+
requestTimeout: 3e4,
|
|
2525
|
+
autoConnect: true
|
|
2526
|
+
};
|
|
2527
|
+
var MoltSDK = class _MoltSDK {
|
|
2528
|
+
transport;
|
|
2529
|
+
events;
|
|
2530
|
+
config;
|
|
2531
|
+
// Domain modules
|
|
2532
|
+
system;
|
|
2533
|
+
agents;
|
|
2534
|
+
sessions;
|
|
2535
|
+
chat;
|
|
2536
|
+
configuration;
|
|
2537
|
+
nodes;
|
|
2538
|
+
models;
|
|
2539
|
+
/**
|
|
2540
|
+
* Private constructor - use static factory methods
|
|
2541
|
+
*/
|
|
2542
|
+
constructor(config, transport, events) {
|
|
2543
|
+
this.config = config;
|
|
2544
|
+
this.transport = transport;
|
|
2545
|
+
this.events = events;
|
|
2546
|
+
const moduleConfig = { transport, events };
|
|
2547
|
+
this.system = new SystemModule(moduleConfig);
|
|
2548
|
+
this.agents = new AgentsModule(moduleConfig);
|
|
2549
|
+
this.sessions = new SessionsModule(moduleConfig);
|
|
2550
|
+
this.chat = new ChatModule(moduleConfig);
|
|
2551
|
+
this.configuration = new ConfigModule(moduleConfig);
|
|
2552
|
+
this.nodes = new NodesModule(moduleConfig);
|
|
2553
|
+
this.models = new ModelsModule(moduleConfig);
|
|
2554
|
+
this.setupEventForwarding();
|
|
2555
|
+
}
|
|
2556
|
+
/**
|
|
2557
|
+
* Setup event forwarding from transport
|
|
2558
|
+
*/
|
|
2559
|
+
setupEventForwarding() {
|
|
2560
|
+
this.transport.on("connection:state", (state) => {
|
|
2561
|
+
this.events.emit("connection:state", state);
|
|
2562
|
+
});
|
|
2563
|
+
this.transport.on("connection:connected", () => {
|
|
2564
|
+
this.events.emit("connection:connected");
|
|
2565
|
+
});
|
|
2566
|
+
this.transport.on("connection:disconnected", (reason) => {
|
|
2567
|
+
this.events.emit("connection:disconnected", reason);
|
|
2568
|
+
});
|
|
2569
|
+
this.transport.on("connection:error", (error) => {
|
|
2570
|
+
this.events.emit("connection:error", error);
|
|
2571
|
+
});
|
|
2572
|
+
this.transport.on("message", (message) => {
|
|
2573
|
+
this.events.emit("message", message);
|
|
2574
|
+
});
|
|
2575
|
+
this.transport.on("event", (eventName, payload) => {
|
|
2576
|
+
this.events.emit("event", eventName, payload);
|
|
2577
|
+
});
|
|
2578
|
+
}
|
|
2579
|
+
/**
|
|
2580
|
+
* Create a new MoltSDK instance with WebSocket transport
|
|
2581
|
+
* Automatically connects to the gateway
|
|
2582
|
+
*/
|
|
2583
|
+
static async create(config = {}) {
|
|
2584
|
+
const fullConfig = {
|
|
2585
|
+
...DEFAULT_CONFIG,
|
|
2586
|
+
...config
|
|
2587
|
+
};
|
|
2588
|
+
const events = new TypedEventEmitter();
|
|
2589
|
+
const transport = new WebSocketTransport(fullConfig);
|
|
2590
|
+
const sdk = new _MoltSDK(fullConfig, transport, events);
|
|
2591
|
+
if (fullConfig.autoConnect !== false) {
|
|
2592
|
+
try {
|
|
2593
|
+
await sdk.connect();
|
|
2594
|
+
} catch (error) {
|
|
2595
|
+
throw new ConnectionError(
|
|
2596
|
+
`Failed to connect to gateway: ${error instanceof Error ? error.message : String(error)}`,
|
|
2597
|
+
{ gatewayUrl: fullConfig.gatewayUrl },
|
|
2598
|
+
error instanceof Error ? error : void 0
|
|
2599
|
+
);
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
return sdk;
|
|
2603
|
+
}
|
|
2604
|
+
/**
|
|
2605
|
+
* Create a new MoltSDK instance without auto-connecting
|
|
2606
|
+
* Call connect() manually when ready
|
|
2607
|
+
*/
|
|
2608
|
+
static createDisconnected(config = {}) {
|
|
2609
|
+
const fullConfig = {
|
|
2610
|
+
...DEFAULT_CONFIG,
|
|
2611
|
+
...config,
|
|
2612
|
+
autoConnect: false
|
|
2613
|
+
};
|
|
2614
|
+
const events = new TypedEventEmitter();
|
|
2615
|
+
const transport = new WebSocketTransport(fullConfig);
|
|
2616
|
+
return new _MoltSDK(fullConfig, transport, events);
|
|
2617
|
+
}
|
|
2618
|
+
/**
|
|
2619
|
+
* Connect to the gateway
|
|
2620
|
+
*/
|
|
2621
|
+
async connect() {
|
|
2622
|
+
await this.transport.connect();
|
|
2623
|
+
}
|
|
2624
|
+
/**
|
|
2625
|
+
* Disconnect from the gateway
|
|
2626
|
+
*/
|
|
2627
|
+
disconnect() {
|
|
2628
|
+
this.transport.disconnect();
|
|
2629
|
+
}
|
|
2630
|
+
/**
|
|
2631
|
+
* Check if connected
|
|
2632
|
+
*/
|
|
2633
|
+
isConnected() {
|
|
2634
|
+
return this.transport.isConnected();
|
|
2635
|
+
}
|
|
2636
|
+
/**
|
|
2637
|
+
* Get current connection state
|
|
2638
|
+
*/
|
|
2639
|
+
getConnectionState() {
|
|
2640
|
+
return this.transport.getConnectionState();
|
|
2641
|
+
}
|
|
2642
|
+
/**
|
|
2643
|
+
* Subscribe to an event
|
|
2644
|
+
*/
|
|
2645
|
+
on(event, handler) {
|
|
2646
|
+
this.events.on(event, handler);
|
|
2647
|
+
return this;
|
|
2648
|
+
}
|
|
2649
|
+
/**
|
|
2650
|
+
* Subscribe to an event once
|
|
2651
|
+
*/
|
|
2652
|
+
once(event, handler) {
|
|
2653
|
+
this.events.once(event, handler);
|
|
2654
|
+
return this;
|
|
2655
|
+
}
|
|
2656
|
+
/**
|
|
2657
|
+
* Unsubscribe from an event
|
|
2658
|
+
*/
|
|
2659
|
+
off(event, handler) {
|
|
2660
|
+
this.events.off(event, handler);
|
|
2661
|
+
return this;
|
|
2662
|
+
}
|
|
2663
|
+
/**
|
|
2664
|
+
* Wait for an event (promisified)
|
|
2665
|
+
*/
|
|
2666
|
+
waitFor(event, timeout) {
|
|
2667
|
+
return this.events.waitFor(event, timeout);
|
|
2668
|
+
}
|
|
2669
|
+
/**
|
|
2670
|
+
* Get the event emitter (for advanced use)
|
|
2671
|
+
*/
|
|
2672
|
+
getEventEmitter() {
|
|
2673
|
+
return this.events;
|
|
2674
|
+
}
|
|
2675
|
+
/**
|
|
2676
|
+
* Get the transport (for advanced use)
|
|
2677
|
+
*/
|
|
2678
|
+
getTransport() {
|
|
2679
|
+
return this.transport;
|
|
2680
|
+
}
|
|
2681
|
+
/**
|
|
2682
|
+
* Get SDK configuration
|
|
2683
|
+
*/
|
|
2684
|
+
getConfig() {
|
|
2685
|
+
return { ...this.config };
|
|
2686
|
+
}
|
|
2687
|
+
/**
|
|
2688
|
+
* Make a raw RPC request (for advanced use)
|
|
2689
|
+
*/
|
|
2690
|
+
async request(method, params = {}) {
|
|
2691
|
+
return this.transport.request(method, params);
|
|
2692
|
+
}
|
|
2693
|
+
/**
|
|
2694
|
+
* Get cached connection snapshot (contains health, agents, etc.)
|
|
2695
|
+
* This data is populated from the initial connection and updated by events
|
|
2696
|
+
*/
|
|
2697
|
+
getSnapshot() {
|
|
2698
|
+
return this.transport.getSnapshot?.() || null;
|
|
2699
|
+
}
|
|
2700
|
+
/**
|
|
2701
|
+
* Get cached health from connection snapshot
|
|
2702
|
+
* This avoids the "unauthorized role" error when calling health RPC
|
|
2703
|
+
*/
|
|
2704
|
+
getCachedHealth() {
|
|
2705
|
+
return this.transport.getCachedHealth?.() || null;
|
|
2706
|
+
}
|
|
2707
|
+
/**
|
|
2708
|
+
* Invalidate cached health data
|
|
2709
|
+
* Call this after external operations (like CLI) that modify agent state
|
|
2710
|
+
*/
|
|
2711
|
+
invalidateHealthCache() {
|
|
2712
|
+
if (typeof this.transport.invalidateHealthCache === "function") {
|
|
2713
|
+
this.transport.invalidateHealthCache();
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
/**
|
|
2717
|
+
* Create a MoltSDK instance with CLI transport
|
|
2718
|
+
* Uses CLI commands instead of WebSocket for communication
|
|
2719
|
+
*
|
|
2720
|
+
* @example
|
|
2721
|
+
* ```typescript
|
|
2722
|
+
* const sdk = await MoltSDK.createWithCLI();
|
|
2723
|
+
* const agents = await sdk.agents.list();
|
|
2724
|
+
* const response = await sdk.chat.send({ message: 'Hello!' });
|
|
2725
|
+
* ```
|
|
2726
|
+
*/
|
|
2727
|
+
static async createWithCLI(config = {}) {
|
|
2728
|
+
const events = new TypedEventEmitter();
|
|
2729
|
+
const transport = new CLITransport(config);
|
|
2730
|
+
const sdkConfig = {
|
|
2731
|
+
gatewayUrl: "cli://",
|
|
2732
|
+
clientName: "molt-sdk-cli",
|
|
2733
|
+
clientVersion: "1.0.0",
|
|
2734
|
+
autoReconnect: false,
|
|
2735
|
+
autoConnect: false
|
|
2736
|
+
};
|
|
2737
|
+
const sdk = new _MoltSDK(sdkConfig, transport, events);
|
|
2738
|
+
await sdk.connect();
|
|
2739
|
+
return sdk;
|
|
2740
|
+
}
|
|
2741
|
+
/**
|
|
2742
|
+
* Create a disconnected CLI SDK instance
|
|
2743
|
+
*/
|
|
2744
|
+
static createCLIDisconnected(config = {}) {
|
|
2745
|
+
const events = new TypedEventEmitter();
|
|
2746
|
+
const transport = new CLITransport(config);
|
|
2747
|
+
const sdkConfig = {
|
|
2748
|
+
gatewayUrl: "cli://",
|
|
2749
|
+
clientName: "molt-sdk-cli",
|
|
2750
|
+
clientVersion: "1.0.0",
|
|
2751
|
+
autoReconnect: false,
|
|
2752
|
+
autoConnect: false
|
|
2753
|
+
};
|
|
2754
|
+
return new _MoltSDK(sdkConfig, transport, events);
|
|
2755
|
+
}
|
|
2756
|
+
};
|
|
2757
|
+
async function createMoltSDK(config = {}) {
|
|
2758
|
+
return MoltSDK.create(config);
|
|
2759
|
+
}
|
|
2760
|
+
function createDisconnectedMoltSDK(config = {}) {
|
|
2761
|
+
return MoltSDK.createDisconnected(config);
|
|
2762
|
+
}
|
|
2763
|
+
export {
|
|
2764
|
+
AgentsModule,
|
|
2765
|
+
AuthenticationError,
|
|
2766
|
+
BaseModule,
|
|
2767
|
+
CLIError,
|
|
2768
|
+
CLITransport,
|
|
2769
|
+
ChatModule,
|
|
2770
|
+
ClawdbotClient,
|
|
2771
|
+
ConfigModule,
|
|
2772
|
+
ConnectionError,
|
|
2773
|
+
DeviceIdentityManager,
|
|
2774
|
+
ErrorCodes,
|
|
2775
|
+
ModelsModule,
|
|
2776
|
+
MoltError,
|
|
2777
|
+
MoltSDK,
|
|
2778
|
+
NodesModule,
|
|
2779
|
+
NotConnectedError,
|
|
2780
|
+
NotFoundError,
|
|
2781
|
+
RPCError,
|
|
2782
|
+
SessionsModule,
|
|
2783
|
+
SystemModule,
|
|
2784
|
+
TimeoutError,
|
|
2785
|
+
TypedEventEmitter,
|
|
2786
|
+
ValidationError,
|
|
2787
|
+
WebSocketTransport,
|
|
2788
|
+
createCLITransport,
|
|
2789
|
+
createDisconnectedMoltSDK,
|
|
2790
|
+
createEventEmitter,
|
|
2791
|
+
createMoltSDK
|
|
2792
|
+
};
|