@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.
@@ -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
+ };