@chucky.cloud/sdk 0.2.7 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.cjs +1361 -0
- package/dist/browser.cjs.map +1 -0
- package/dist/browser.d.cts +1 -0
- package/dist/browser.d.ts +1 -8
- package/dist/browser.js +1278 -11
- package/dist/browser.js.map +1 -1
- package/dist/index.cjs +1361 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1973 -0
- package/dist/index.d.ts +1953 -48
- package/dist/index.js +1278 -72
- package/dist/index.js.map +1 -1
- package/dist/node.cjs +1361 -0
- package/dist/node.cjs.map +1 -0
- package/dist/node.d.cts +1 -0
- package/dist/node.d.ts +1 -8
- package/dist/node.js +1278 -10
- package/dist/node.js.map +1 -1
- package/package.json +13 -7
- package/dist/browser.d.ts.map +0 -1
- package/dist/client/ChuckyClient.d.ts +0 -141
- package/dist/client/ChuckyClient.d.ts.map +0 -1
- package/dist/client/ChuckyClient.js +0 -209
- package/dist/client/ChuckyClient.js.map +0 -1
- package/dist/client/Session.d.ts +0 -167
- package/dist/client/Session.d.ts.map +0 -1
- package/dist/client/Session.js +0 -392
- package/dist/client/Session.js.map +0 -1
- package/dist/client/index.d.ts +0 -10
- package/dist/client/index.d.ts.map +0 -1
- package/dist/client/index.js +0 -9
- package/dist/client/index.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/node.d.ts.map +0 -1
- package/dist/tools/McpServer.d.ts +0 -117
- package/dist/tools/McpServer.d.ts.map +0 -1
- package/dist/tools/McpServer.js +0 -142
- package/dist/tools/McpServer.js.map +0 -1
- package/dist/tools/index.d.ts +0 -9
- package/dist/tools/index.d.ts.map +0 -1
- package/dist/tools/index.js +0 -8
- package/dist/tools/index.js.map +0 -1
- package/dist/tools/tool.d.ts +0 -146
- package/dist/tools/tool.d.ts.map +0 -1
- package/dist/tools/tool.js +0 -232
- package/dist/tools/tool.js.map +0 -1
- package/dist/transport/Transport.d.ts +0 -82
- package/dist/transport/Transport.d.ts.map +0 -1
- package/dist/transport/Transport.js +0 -47
- package/dist/transport/Transport.js.map +0 -1
- package/dist/transport/WebSocketTransport.d.ts +0 -78
- package/dist/transport/WebSocketTransport.d.ts.map +0 -1
- package/dist/transport/WebSocketTransport.js +0 -258
- package/dist/transport/WebSocketTransport.js.map +0 -1
- package/dist/transport/index.d.ts +0 -10
- package/dist/transport/index.d.ts.map +0 -1
- package/dist/transport/index.js +0 -8
- package/dist/transport/index.js.map +0 -1
- package/dist/types/index.d.ts +0 -12
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -8
- package/dist/types/index.js.map +0 -1
- package/dist/types/messages.d.ts +0 -327
- package/dist/types/messages.d.ts.map +0 -1
- package/dist/types/messages.js +0 -133
- package/dist/types/messages.js.map +0 -1
- package/dist/types/options.d.ts +0 -212
- package/dist/types/options.d.ts.map +0 -1
- package/dist/types/options.js +0 -8
- package/dist/types/options.js.map +0 -1
- package/dist/types/results.d.ts +0 -186
- package/dist/types/results.d.ts.map +0 -1
- package/dist/types/results.js +0 -7
- package/dist/types/results.js.map +0 -1
- package/dist/types/token.d.ts +0 -124
- package/dist/types/token.d.ts.map +0 -1
- package/dist/types/token.js +0 -7
- package/dist/types/token.js.map +0 -1
- package/dist/types/tools.d.ts +0 -234
- package/dist/types/tools.d.ts.map +0 -1
- package/dist/types/tools.js +0 -31
- package/dist/types/tools.js.map +0 -1
- package/dist/utils/errors.d.ts +0 -80
- package/dist/utils/errors.d.ts.map +0 -1
- package/dist/utils/errors.js +0 -158
- package/dist/utils/errors.js.map +0 -1
- package/dist/utils/index.d.ts +0 -8
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -8
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/token.d.ts +0 -104
- package/dist/utils/token.d.ts.map +0 -1
- package/dist/utils/token.js +0 -209
- package/dist/utils/token.js.map +0 -1
package/dist/browser.cjs
ADDED
|
@@ -0,0 +1,1361 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
31
|
+
|
|
32
|
+
// src/browser.ts
|
|
33
|
+
var browser_exports = {};
|
|
34
|
+
__export(browser_exports, {
|
|
35
|
+
AuthenticationError: () => AuthenticationError,
|
|
36
|
+
BudgetExceededError: () => BudgetExceededError,
|
|
37
|
+
ChuckyClient: () => ChuckyClient,
|
|
38
|
+
ChuckyError: () => ChuckyError,
|
|
39
|
+
ConcurrencyLimitError: () => ConcurrencyLimitError,
|
|
40
|
+
ConnectionError: () => ConnectionError,
|
|
41
|
+
McpServerBuilder: () => McpServerBuilder,
|
|
42
|
+
RateLimitError: () => RateLimitError,
|
|
43
|
+
Session: () => Session,
|
|
44
|
+
SessionError: () => SessionError,
|
|
45
|
+
TimeoutError: () => TimeoutError,
|
|
46
|
+
ToolExecutionError: () => ToolExecutionError,
|
|
47
|
+
ValidationError: () => ValidationError,
|
|
48
|
+
WebSocketTransport: () => WebSocketTransport,
|
|
49
|
+
browserTool: () => browserTool,
|
|
50
|
+
createBudget: () => createBudget,
|
|
51
|
+
createClient: () => createClient,
|
|
52
|
+
createControlMessage: () => createControlMessage,
|
|
53
|
+
createError: () => createError,
|
|
54
|
+
createInitMessage: () => createInitMessage,
|
|
55
|
+
createMcpServer: () => createMcpServer,
|
|
56
|
+
createPingMessage: () => createPingMessage,
|
|
57
|
+
createToken: () => createToken,
|
|
58
|
+
createTool: () => createTool,
|
|
59
|
+
createToolResultMessage: () => createToolResultMessage,
|
|
60
|
+
createUserMessage: () => createUserMessage,
|
|
61
|
+
decodeToken: () => decodeToken,
|
|
62
|
+
errorResult: () => errorResult,
|
|
63
|
+
extractProjectId: () => extractProjectId,
|
|
64
|
+
getAssistantText: () => getAssistantText,
|
|
65
|
+
getResultText: () => getResultText,
|
|
66
|
+
imageResult: () => imageResult,
|
|
67
|
+
isAssistantMessage: () => isAssistantMessage,
|
|
68
|
+
isControlMessage: () => isControlMessage,
|
|
69
|
+
isErrorMessage: () => isErrorMessage,
|
|
70
|
+
isErrorResult: () => isErrorResult,
|
|
71
|
+
isResultMessage: () => isResultMessage,
|
|
72
|
+
isStreamEvent: () => isStreamEvent,
|
|
73
|
+
isSuccessResult: () => isSuccessResult,
|
|
74
|
+
isSystemMessage: () => isSystemMessage,
|
|
75
|
+
isTokenExpired: () => isTokenExpired,
|
|
76
|
+
isToolCallMessage: () => isToolCallMessage,
|
|
77
|
+
isUserMessage: () => isUserMessage,
|
|
78
|
+
mcpServer: () => mcpServer,
|
|
79
|
+
serverTool: () => serverTool,
|
|
80
|
+
textResult: () => textResult,
|
|
81
|
+
tool: () => tool,
|
|
82
|
+
verifyToken: () => verifyToken
|
|
83
|
+
});
|
|
84
|
+
module.exports = __toCommonJS(browser_exports);
|
|
85
|
+
|
|
86
|
+
// src/types/messages.ts
|
|
87
|
+
function createInitMessage(payload) {
|
|
88
|
+
return { type: "init", payload };
|
|
89
|
+
}
|
|
90
|
+
function createUserMessage(content, sessionId, options = {}) {
|
|
91
|
+
const messageContent = typeof content === "string" ? content : content;
|
|
92
|
+
return {
|
|
93
|
+
type: "user",
|
|
94
|
+
uuid: options.uuid,
|
|
95
|
+
session_id: sessionId,
|
|
96
|
+
message: {
|
|
97
|
+
role: "user",
|
|
98
|
+
content: messageContent
|
|
99
|
+
},
|
|
100
|
+
parent_tool_use_id: options.parentToolUseId ?? null
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function createControlMessage(action, data) {
|
|
104
|
+
return { type: "control", payload: { action, data } };
|
|
105
|
+
}
|
|
106
|
+
function createPingMessage() {
|
|
107
|
+
return { type: "ping", payload: { timestamp: Date.now() } };
|
|
108
|
+
}
|
|
109
|
+
function createToolResultMessage(callId, result) {
|
|
110
|
+
return { type: "tool_result", payload: { callId, result } };
|
|
111
|
+
}
|
|
112
|
+
function isUserMessage(message) {
|
|
113
|
+
return message.type === "user";
|
|
114
|
+
}
|
|
115
|
+
function isAssistantMessage(message) {
|
|
116
|
+
return message.type === "assistant";
|
|
117
|
+
}
|
|
118
|
+
function isResultMessage(message) {
|
|
119
|
+
return message.type === "result";
|
|
120
|
+
}
|
|
121
|
+
function isSuccessResult(message) {
|
|
122
|
+
return message.type === "result" && message.subtype === "success";
|
|
123
|
+
}
|
|
124
|
+
function isErrorResult(message) {
|
|
125
|
+
return message.type === "result" && message.subtype !== "success";
|
|
126
|
+
}
|
|
127
|
+
function isSystemMessage(message) {
|
|
128
|
+
return message.type === "system";
|
|
129
|
+
}
|
|
130
|
+
function isStreamEvent(message) {
|
|
131
|
+
return message.type === "stream_event";
|
|
132
|
+
}
|
|
133
|
+
function isToolCallMessage(message) {
|
|
134
|
+
return message.type === "tool_call";
|
|
135
|
+
}
|
|
136
|
+
function isControlMessage(message) {
|
|
137
|
+
return message.type === "control";
|
|
138
|
+
}
|
|
139
|
+
function isErrorMessage(message) {
|
|
140
|
+
return message.type === "error";
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/transport/Transport.ts
|
|
144
|
+
var BaseTransport = class {
|
|
145
|
+
constructor(config) {
|
|
146
|
+
__publicField(this, "_status", "disconnected");
|
|
147
|
+
__publicField(this, "handlers", {});
|
|
148
|
+
__publicField(this, "config");
|
|
149
|
+
const cleanConfig = Object.fromEntries(
|
|
150
|
+
Object.entries(config).filter(([_, v]) => v !== void 0)
|
|
151
|
+
);
|
|
152
|
+
this.config = {
|
|
153
|
+
timeout: 6e4,
|
|
154
|
+
// 60s - container startup can take time
|
|
155
|
+
keepAliveInterval: 3e5,
|
|
156
|
+
// 5 minutes
|
|
157
|
+
autoReconnect: false,
|
|
158
|
+
// Disabled - server doesn't support reconnect
|
|
159
|
+
maxReconnectAttempts: 0,
|
|
160
|
+
debug: false,
|
|
161
|
+
...cleanConfig
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
get status() {
|
|
165
|
+
return this._status;
|
|
166
|
+
}
|
|
167
|
+
setStatus(status) {
|
|
168
|
+
if (this._status !== status) {
|
|
169
|
+
this._status = status;
|
|
170
|
+
this.handlers.onStatusChange?.(status);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
log(...args) {
|
|
174
|
+
if (this.config.debug) {
|
|
175
|
+
console.log("[ChuckySDK]", ...args);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
logError(...args) {
|
|
179
|
+
console.error("[ChuckySDK]", ...args);
|
|
180
|
+
}
|
|
181
|
+
setEventHandlers(handlers) {
|
|
182
|
+
this.handlers = { ...this.handlers, ...handlers };
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// src/transport/WebSocketTransport.ts
|
|
187
|
+
async function getWebSocket() {
|
|
188
|
+
if (typeof WebSocket !== "undefined") {
|
|
189
|
+
return WebSocket;
|
|
190
|
+
}
|
|
191
|
+
const ws = await import("ws");
|
|
192
|
+
return ws.default;
|
|
193
|
+
}
|
|
194
|
+
var WebSocketTransport = class extends BaseTransport {
|
|
195
|
+
constructor(config) {
|
|
196
|
+
super(config);
|
|
197
|
+
__publicField(this, "ws", null);
|
|
198
|
+
__publicField(this, "keepAliveTimer", null);
|
|
199
|
+
__publicField(this, "reconnectAttempts", 0);
|
|
200
|
+
__publicField(this, "reconnectTimer", null);
|
|
201
|
+
__publicField(this, "readyPromise", null);
|
|
202
|
+
__publicField(this, "readyResolve", null);
|
|
203
|
+
__publicField(this, "readyReject", null);
|
|
204
|
+
__publicField(this, "messageQueue", []);
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Build the WebSocket URL with token
|
|
208
|
+
*/
|
|
209
|
+
buildUrl() {
|
|
210
|
+
const url = new URL(this.config.url);
|
|
211
|
+
url.searchParams.set("token", this.config.token);
|
|
212
|
+
return url.toString();
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Connect to the WebSocket server
|
|
216
|
+
*/
|
|
217
|
+
async connect() {
|
|
218
|
+
if (this._status === "connected" || this._status === "connecting") {
|
|
219
|
+
return this.waitForReady();
|
|
220
|
+
}
|
|
221
|
+
this.setStatus("connecting");
|
|
222
|
+
this.log("Connecting to", this.config.url);
|
|
223
|
+
this.readyPromise = new Promise((resolve, reject) => {
|
|
224
|
+
this.readyResolve = resolve;
|
|
225
|
+
this.readyReject = reject;
|
|
226
|
+
});
|
|
227
|
+
try {
|
|
228
|
+
const WS = await getWebSocket();
|
|
229
|
+
const wsUrl = this.buildUrl();
|
|
230
|
+
this.ws = new WS(wsUrl);
|
|
231
|
+
const timeoutId = setTimeout(() => {
|
|
232
|
+
if (this._status === "connecting") {
|
|
233
|
+
this.ws?.close();
|
|
234
|
+
const error = new Error("Connection timeout");
|
|
235
|
+
this.readyReject?.(error);
|
|
236
|
+
this.handlers.onError?.(error);
|
|
237
|
+
this.setStatus("error");
|
|
238
|
+
}
|
|
239
|
+
}, this.config.timeout);
|
|
240
|
+
this.ws.onopen = () => {
|
|
241
|
+
clearTimeout(timeoutId);
|
|
242
|
+
this.log("Connected");
|
|
243
|
+
this.setStatus("connected");
|
|
244
|
+
this.reconnectAttempts = 0;
|
|
245
|
+
this.startKeepAlive();
|
|
246
|
+
this.readyResolve?.();
|
|
247
|
+
this.flushMessageQueue();
|
|
248
|
+
};
|
|
249
|
+
this.ws.onclose = (event) => {
|
|
250
|
+
clearTimeout(timeoutId);
|
|
251
|
+
this.log("Disconnected:", event.code, event.reason);
|
|
252
|
+
this.stopKeepAlive();
|
|
253
|
+
if (this._status === "connecting") {
|
|
254
|
+
const error = new Error(`Connection failed: ${event.reason || "Unknown"}`);
|
|
255
|
+
this.readyReject?.(error);
|
|
256
|
+
}
|
|
257
|
+
this.setStatus("disconnected");
|
|
258
|
+
this.handlers.onClose?.(event.code, event.reason);
|
|
259
|
+
if (this.config.autoReconnect && this.reconnectAttempts < (this.config.maxReconnectAttempts || 5)) {
|
|
260
|
+
this.scheduleReconnect();
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
this.ws.onerror = (event) => {
|
|
264
|
+
clearTimeout(timeoutId);
|
|
265
|
+
const error = new Error("WebSocket error");
|
|
266
|
+
this.logError("WebSocket error:", event);
|
|
267
|
+
this.handlers.onError?.(error);
|
|
268
|
+
if (this._status === "connecting") {
|
|
269
|
+
this.readyReject?.(error);
|
|
270
|
+
}
|
|
271
|
+
this.setStatus("error");
|
|
272
|
+
};
|
|
273
|
+
this.ws.onmessage = (event) => {
|
|
274
|
+
this.handleMessage(event.data);
|
|
275
|
+
};
|
|
276
|
+
return this.readyPromise;
|
|
277
|
+
} catch (error) {
|
|
278
|
+
this.setStatus("error");
|
|
279
|
+
this.readyReject?.(error);
|
|
280
|
+
throw error;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Disconnect from the server
|
|
285
|
+
*/
|
|
286
|
+
async disconnect() {
|
|
287
|
+
this.log("Disconnecting");
|
|
288
|
+
this.stopKeepAlive();
|
|
289
|
+
this.clearReconnectTimer();
|
|
290
|
+
if (this.ws) {
|
|
291
|
+
this.ws.close(1e3, "Client disconnect");
|
|
292
|
+
this.ws = null;
|
|
293
|
+
}
|
|
294
|
+
this.setStatus("disconnected");
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Send a message to the server
|
|
298
|
+
*/
|
|
299
|
+
async send(message) {
|
|
300
|
+
if (this._status === "disconnected" && !this.config.autoReconnect) {
|
|
301
|
+
this.log("Message dropped (disconnected):", message.type);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
if (this._status !== "connected") {
|
|
305
|
+
this.messageQueue.push(message);
|
|
306
|
+
this.log("Message queued (not connected):", message.type);
|
|
307
|
+
if (this._status === "disconnected" && this.config.autoReconnect) {
|
|
308
|
+
await this.connect();
|
|
309
|
+
}
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
this.sendImmediate(message);
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Send a message immediately
|
|
316
|
+
*/
|
|
317
|
+
sendImmediate(message) {
|
|
318
|
+
if (!this.ws || this.ws.readyState !== 1) {
|
|
319
|
+
this.logError("Cannot send: WebSocket not ready");
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
const data = JSON.stringify(message);
|
|
323
|
+
this.handlers.onRawMessage?.("out", message);
|
|
324
|
+
this.ws.send(data);
|
|
325
|
+
this.log("Sent:", message.type);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Wait for the connection to be ready
|
|
329
|
+
*/
|
|
330
|
+
async waitForReady() {
|
|
331
|
+
if (this._status === "connected") {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
if (this.readyPromise) {
|
|
335
|
+
return this.readyPromise;
|
|
336
|
+
}
|
|
337
|
+
return this.connect();
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Handle incoming message
|
|
341
|
+
*/
|
|
342
|
+
handleMessage(data) {
|
|
343
|
+
try {
|
|
344
|
+
const text = typeof data === "string" ? data : new TextDecoder().decode(data);
|
|
345
|
+
const message = JSON.parse(text);
|
|
346
|
+
this.handlers.onRawMessage?.("in", message);
|
|
347
|
+
this.log("Received:", message.type);
|
|
348
|
+
if (message.type === "pong") {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
if (isErrorMessage(message)) {
|
|
352
|
+
const error = new Error(message.payload.message);
|
|
353
|
+
this.handlers.onError?.(error);
|
|
354
|
+
}
|
|
355
|
+
this.handlers.onMessage?.(message);
|
|
356
|
+
} catch (error) {
|
|
357
|
+
this.logError("Failed to parse message:", error);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Start keep-alive ping
|
|
362
|
+
*/
|
|
363
|
+
startKeepAlive() {
|
|
364
|
+
this.stopKeepAlive();
|
|
365
|
+
if (this.config.keepAliveInterval && this.config.keepAliveInterval > 0) {
|
|
366
|
+
this.keepAliveTimer = setInterval(() => {
|
|
367
|
+
if (this._status === "connected" && this.ws?.readyState === 1) {
|
|
368
|
+
this.sendImmediate(createPingMessage());
|
|
369
|
+
}
|
|
370
|
+
}, this.config.keepAliveInterval);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Stop keep-alive ping
|
|
375
|
+
*/
|
|
376
|
+
stopKeepAlive() {
|
|
377
|
+
if (this.keepAliveTimer) {
|
|
378
|
+
clearInterval(this.keepAliveTimer);
|
|
379
|
+
this.keepAliveTimer = null;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Schedule a reconnection attempt
|
|
384
|
+
*/
|
|
385
|
+
scheduleReconnect() {
|
|
386
|
+
this.clearReconnectTimer();
|
|
387
|
+
this.reconnectAttempts++;
|
|
388
|
+
const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts - 1), 16e3);
|
|
389
|
+
this.log(`Scheduling reconnect in ${delay}ms (attempt ${this.reconnectAttempts})`);
|
|
390
|
+
this.setStatus("reconnecting");
|
|
391
|
+
this.reconnectTimer = setTimeout(() => {
|
|
392
|
+
this.connect().catch((error) => {
|
|
393
|
+
this.logError("Reconnect failed:", error);
|
|
394
|
+
});
|
|
395
|
+
}, delay);
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Clear reconnect timer
|
|
399
|
+
*/
|
|
400
|
+
clearReconnectTimer() {
|
|
401
|
+
if (this.reconnectTimer) {
|
|
402
|
+
clearTimeout(this.reconnectTimer);
|
|
403
|
+
this.reconnectTimer = null;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Flush queued messages
|
|
408
|
+
*/
|
|
409
|
+
flushMessageQueue() {
|
|
410
|
+
while (this.messageQueue.length > 0) {
|
|
411
|
+
const message = this.messageQueue.shift();
|
|
412
|
+
this.sendImmediate(message);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
// src/client/Session.ts
|
|
418
|
+
function generateUUID() {
|
|
419
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
420
|
+
const r = Math.random() * 16 | 0;
|
|
421
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
422
|
+
return v.toString(16);
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
var Session = class {
|
|
426
|
+
constructor(transport, options, config = {}) {
|
|
427
|
+
__publicField(this, "transport");
|
|
428
|
+
__publicField(this, "options");
|
|
429
|
+
__publicField(this, "config");
|
|
430
|
+
__publicField(this, "eventHandlers", {});
|
|
431
|
+
__publicField(this, "toolHandlers", /* @__PURE__ */ new Map());
|
|
432
|
+
__publicField(this, "messageBuffer", []);
|
|
433
|
+
__publicField(this, "_state", "idle");
|
|
434
|
+
__publicField(this, "_sessionId");
|
|
435
|
+
__publicField(this, "messageResolvers", []);
|
|
436
|
+
__publicField(this, "connected", false);
|
|
437
|
+
__publicField(this, "connectPromise", null);
|
|
438
|
+
this.transport = transport;
|
|
439
|
+
this.options = options;
|
|
440
|
+
this.config = config;
|
|
441
|
+
this._sessionId = options.sessionId || generateUUID();
|
|
442
|
+
if (options.mcpServers) {
|
|
443
|
+
for (const server of options.mcpServers) {
|
|
444
|
+
if ("tools" in server) {
|
|
445
|
+
for (const tool2 of server.tools) {
|
|
446
|
+
if (tool2.handler) {
|
|
447
|
+
this.toolHandlers.set(tool2.name, tool2.handler);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
this.transport.setEventHandlers({
|
|
454
|
+
onMessage: (message) => this.handleMessage(message),
|
|
455
|
+
onError: (error) => this.eventHandlers.onError?.(error),
|
|
456
|
+
onStatusChange: (status) => {
|
|
457
|
+
if (status === "disconnected" && this._state !== "completed") {
|
|
458
|
+
this._state = "error";
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Get the session ID
|
|
465
|
+
*/
|
|
466
|
+
get sessionId() {
|
|
467
|
+
return this._sessionId;
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Set event handlers
|
|
471
|
+
*/
|
|
472
|
+
on(handlers) {
|
|
473
|
+
this.eventHandlers = { ...this.eventHandlers, ...handlers };
|
|
474
|
+
return this;
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Connect and initialize the session (called automatically on first send)
|
|
478
|
+
*/
|
|
479
|
+
async ensureConnected() {
|
|
480
|
+
if (this.connected) return;
|
|
481
|
+
if (this.connectPromise) {
|
|
482
|
+
return this.connectPromise;
|
|
483
|
+
}
|
|
484
|
+
this.connectPromise = this.connect();
|
|
485
|
+
await this.connectPromise;
|
|
486
|
+
}
|
|
487
|
+
async connect() {
|
|
488
|
+
this._state = "initializing";
|
|
489
|
+
await this.transport.connect();
|
|
490
|
+
const initPayload = this.buildInitPayload();
|
|
491
|
+
await this.transport.send(createInitMessage(initPayload));
|
|
492
|
+
await this.waitForReady();
|
|
493
|
+
this._state = "ready";
|
|
494
|
+
this.connected = true;
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Send a message to the session
|
|
498
|
+
*
|
|
499
|
+
* Matches V2 SDK: send() returns Promise<void>
|
|
500
|
+
* Use stream() to get the response.
|
|
501
|
+
*
|
|
502
|
+
* @example
|
|
503
|
+
* ```typescript
|
|
504
|
+
* await session.send('Hello!');
|
|
505
|
+
* for await (const msg of session.stream()) {
|
|
506
|
+
* // Handle messages
|
|
507
|
+
* }
|
|
508
|
+
* ```
|
|
509
|
+
*/
|
|
510
|
+
async send(message) {
|
|
511
|
+
await this.ensureConnected();
|
|
512
|
+
if (this._state !== "ready") {
|
|
513
|
+
throw new Error(`Cannot send: session state is ${this._state}`);
|
|
514
|
+
}
|
|
515
|
+
this._state = "processing";
|
|
516
|
+
const userMessage = createUserMessage(message, this._sessionId);
|
|
517
|
+
await this.transport.send(userMessage);
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Stream the response after sending a message
|
|
521
|
+
*
|
|
522
|
+
* Matches V2 SDK: Returns AsyncGenerator<SDKMessage>
|
|
523
|
+
*
|
|
524
|
+
* @example
|
|
525
|
+
* ```typescript
|
|
526
|
+
* await session.send('Hello!');
|
|
527
|
+
* for await (const msg of session.stream()) {
|
|
528
|
+
* if (msg.type === 'assistant') {
|
|
529
|
+
* const text = msg.message.content
|
|
530
|
+
* .filter(b => b.type === 'text')
|
|
531
|
+
* .map(b => b.text)
|
|
532
|
+
* .join('');
|
|
533
|
+
* console.log(text);
|
|
534
|
+
* }
|
|
535
|
+
* if (msg.type === 'result') {
|
|
536
|
+
* console.log('Done:', msg.result);
|
|
537
|
+
* }
|
|
538
|
+
* }
|
|
539
|
+
* ```
|
|
540
|
+
*/
|
|
541
|
+
async *stream() {
|
|
542
|
+
while (this._state === "processing" || this._state === "waiting_tool") {
|
|
543
|
+
const msg = await this.waitForNextMessage();
|
|
544
|
+
if (isToolCallMessage(msg)) {
|
|
545
|
+
this._state = "waiting_tool";
|
|
546
|
+
await this.handleToolCall(msg.payload);
|
|
547
|
+
this._state = "processing";
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
if (isAssistantMessage(msg) || isResultMessage(msg) || isSystemMessage(msg) || isStreamEvent(msg)) {
|
|
551
|
+
yield msg;
|
|
552
|
+
}
|
|
553
|
+
if (isResultMessage(msg)) {
|
|
554
|
+
this._state = "ready";
|
|
555
|
+
break;
|
|
556
|
+
}
|
|
557
|
+
if (isErrorMessage(msg)) {
|
|
558
|
+
this._state = "ready";
|
|
559
|
+
throw new Error(msg.payload.message);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Receive messages (alias for stream for V2 compatibility)
|
|
565
|
+
*/
|
|
566
|
+
receive() {
|
|
567
|
+
return this.stream();
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Close the session
|
|
571
|
+
*/
|
|
572
|
+
close() {
|
|
573
|
+
this.transport.send(createControlMessage("close")).catch(() => {
|
|
574
|
+
});
|
|
575
|
+
this.transport.disconnect().catch(() => {
|
|
576
|
+
});
|
|
577
|
+
this._state = "completed";
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Support for `await using` (TypeScript 5.2+)
|
|
581
|
+
*/
|
|
582
|
+
async [Symbol.asyncDispose]() {
|
|
583
|
+
this.close();
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Build init payload from options
|
|
587
|
+
*/
|
|
588
|
+
buildInitPayload() {
|
|
589
|
+
const { mcpServers, ...rest } = this.options;
|
|
590
|
+
const serializedMcpServers = mcpServers?.map((server) => {
|
|
591
|
+
if ("tools" in server) {
|
|
592
|
+
return {
|
|
593
|
+
name: server.name,
|
|
594
|
+
version: server.version,
|
|
595
|
+
tools: server.tools.map((tool2) => ({
|
|
596
|
+
name: tool2.name,
|
|
597
|
+
description: tool2.description,
|
|
598
|
+
inputSchema: tool2.inputSchema,
|
|
599
|
+
// If tool has a handler, it executes on the client (SDK) side
|
|
600
|
+
executeIn: tool2.handler ? "client" : tool2.executeIn
|
|
601
|
+
}))
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
return server;
|
|
605
|
+
});
|
|
606
|
+
return {
|
|
607
|
+
...rest,
|
|
608
|
+
// tools is passed through as-is (string[] or preset for allowlisting)
|
|
609
|
+
mcpServers: serializedMcpServers
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Handle incoming message
|
|
614
|
+
*/
|
|
615
|
+
handleMessage(message) {
|
|
616
|
+
this.log("Received:", message.type);
|
|
617
|
+
if (isControlMessage(message)) {
|
|
618
|
+
if (message.payload.action === "session_info") {
|
|
619
|
+
const info = message.payload.data;
|
|
620
|
+
this._sessionId = info.sessionId || this._sessionId;
|
|
621
|
+
this.eventHandlers.onSessionInfo?.(info);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
if (isSystemMessage(message) && message.subtype === "init") {
|
|
625
|
+
this._sessionId = message.session_id || this._sessionId;
|
|
626
|
+
this.eventHandlers.onSessionInfo?.({
|
|
627
|
+
sessionId: message.session_id,
|
|
628
|
+
model: message.model,
|
|
629
|
+
tools: message.tools
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
if (isErrorMessage(message)) {
|
|
633
|
+
const error = new Error(message.payload.message);
|
|
634
|
+
this.eventHandlers.onError?.(error);
|
|
635
|
+
}
|
|
636
|
+
const resolver = this.messageResolvers.shift();
|
|
637
|
+
if (resolver) {
|
|
638
|
+
resolver(message);
|
|
639
|
+
} else {
|
|
640
|
+
this.messageBuffer.push(message);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Wait for session to be ready
|
|
645
|
+
*/
|
|
646
|
+
async waitForReady() {
|
|
647
|
+
return new Promise((resolve, reject) => {
|
|
648
|
+
const timeout = setTimeout(() => {
|
|
649
|
+
reject(new Error("Session initialization timeout"));
|
|
650
|
+
}, 3e4);
|
|
651
|
+
const checkReady = (message) => {
|
|
652
|
+
if (isControlMessage(message)) {
|
|
653
|
+
if (message.payload.action === "ready" || message.payload.action === "session_info") {
|
|
654
|
+
clearTimeout(timeout);
|
|
655
|
+
resolve();
|
|
656
|
+
return true;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
if (isSystemMessage(message) && message.subtype === "init") {
|
|
660
|
+
clearTimeout(timeout);
|
|
661
|
+
resolve();
|
|
662
|
+
return true;
|
|
663
|
+
}
|
|
664
|
+
if (isErrorMessage(message)) {
|
|
665
|
+
clearTimeout(timeout);
|
|
666
|
+
reject(new Error(message.payload.message));
|
|
667
|
+
return true;
|
|
668
|
+
}
|
|
669
|
+
return false;
|
|
670
|
+
};
|
|
671
|
+
for (let i = 0; i < this.messageBuffer.length; i++) {
|
|
672
|
+
if (checkReady(this.messageBuffer[i])) {
|
|
673
|
+
this.messageBuffer.splice(i, 1);
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
this.messageResolvers.push((msg) => {
|
|
678
|
+
checkReady(msg);
|
|
679
|
+
});
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Wait for next message
|
|
684
|
+
*/
|
|
685
|
+
async waitForNextMessage() {
|
|
686
|
+
if (this.messageBuffer.length > 0) {
|
|
687
|
+
return this.messageBuffer.shift();
|
|
688
|
+
}
|
|
689
|
+
return new Promise((resolve) => {
|
|
690
|
+
this.messageResolvers.push(resolve);
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Handle a tool call
|
|
695
|
+
*/
|
|
696
|
+
async handleToolCall(toolCall) {
|
|
697
|
+
const handler = this.toolHandlers.get(toolCall.toolName);
|
|
698
|
+
if (!handler) {
|
|
699
|
+
this.log("No local handler for tool:", toolCall.toolName);
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
this.log("Executing tool:", toolCall.toolName);
|
|
703
|
+
try {
|
|
704
|
+
const result = await handler(toolCall.input);
|
|
705
|
+
await this.transport.send(createToolResultMessage(toolCall.callId, result));
|
|
706
|
+
} catch (error) {
|
|
707
|
+
const errorResult2 = {
|
|
708
|
+
content: [{ type: "text", text: error.message }],
|
|
709
|
+
isError: true
|
|
710
|
+
};
|
|
711
|
+
await this.transport.send(createToolResultMessage(toolCall.callId, errorResult2));
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Log debug messages
|
|
716
|
+
*/
|
|
717
|
+
log(...args) {
|
|
718
|
+
if (this.config.debug) {
|
|
719
|
+
console.log("[Session]", ...args);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
function getAssistantText(msg) {
|
|
724
|
+
if (msg.type !== "assistant") return null;
|
|
725
|
+
return msg.message.content.filter((block) => block.type === "text").map((block) => block.text).join("");
|
|
726
|
+
}
|
|
727
|
+
function getResultText(msg) {
|
|
728
|
+
if (msg.type !== "result") return null;
|
|
729
|
+
const resultMsg = msg;
|
|
730
|
+
if (resultMsg.subtype === "success") {
|
|
731
|
+
return resultMsg.result;
|
|
732
|
+
}
|
|
733
|
+
return null;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// src/client/ChuckyClient.ts
|
|
737
|
+
var DEFAULT_BASE_URL = "wss://conjure.chucky.cloud/ws";
|
|
738
|
+
var ChuckyClient = class {
|
|
739
|
+
/**
|
|
740
|
+
* Create a new Chucky client
|
|
741
|
+
*/
|
|
742
|
+
constructor(options) {
|
|
743
|
+
__publicField(this, "options");
|
|
744
|
+
__publicField(this, "eventHandlers", {});
|
|
745
|
+
__publicField(this, "activeSessions", /* @__PURE__ */ new Map());
|
|
746
|
+
this.options = {
|
|
747
|
+
baseUrl: DEFAULT_BASE_URL,
|
|
748
|
+
debug: false,
|
|
749
|
+
...options
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* Set event handlers
|
|
754
|
+
*/
|
|
755
|
+
on(handlers) {
|
|
756
|
+
this.eventHandlers = { ...this.eventHandlers, ...handlers };
|
|
757
|
+
return this;
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Create a new session
|
|
761
|
+
*
|
|
762
|
+
* Matches V2 SDK: createSession() returns a Session immediately.
|
|
763
|
+
* Connection happens automatically on first send().
|
|
764
|
+
*
|
|
765
|
+
* @param options - Session configuration options
|
|
766
|
+
* @returns A new session instance
|
|
767
|
+
*
|
|
768
|
+
* @example
|
|
769
|
+
* ```typescript
|
|
770
|
+
* const session = client.createSession({
|
|
771
|
+
* model: 'claude-sonnet-4-5-20250929',
|
|
772
|
+
* systemPrompt: 'You are a helpful coding assistant.',
|
|
773
|
+
* });
|
|
774
|
+
*
|
|
775
|
+
* await session.send('Hello!');
|
|
776
|
+
* for await (const msg of session.stream()) {
|
|
777
|
+
* // Handle messages
|
|
778
|
+
* }
|
|
779
|
+
* ```
|
|
780
|
+
*/
|
|
781
|
+
createSession(options = {}) {
|
|
782
|
+
const transport = this.createTransport();
|
|
783
|
+
const session = new Session(transport, options, {
|
|
784
|
+
debug: this.options.debug
|
|
785
|
+
});
|
|
786
|
+
session.on({
|
|
787
|
+
onSessionInfo: (info) => {
|
|
788
|
+
if (info.sessionId) {
|
|
789
|
+
this.activeSessions.set(info.sessionId, session);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
return session;
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* Resume an existing session
|
|
797
|
+
*
|
|
798
|
+
* @param sessionId - The session ID to resume
|
|
799
|
+
* @param options - Additional session options
|
|
800
|
+
* @returns The resumed session
|
|
801
|
+
*
|
|
802
|
+
* @example
|
|
803
|
+
* ```typescript
|
|
804
|
+
* const session = client.resumeSession('session-123');
|
|
805
|
+
* await session.send('Continue our conversation');
|
|
806
|
+
* ```
|
|
807
|
+
*/
|
|
808
|
+
resumeSession(sessionId, options = {}) {
|
|
809
|
+
return this.createSession({
|
|
810
|
+
...options,
|
|
811
|
+
sessionId
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
/**
|
|
815
|
+
* Execute a one-shot prompt (stateless)
|
|
816
|
+
*
|
|
817
|
+
* Supports two call signatures:
|
|
818
|
+
* - `prompt('message', { model: '...' })` - positional style
|
|
819
|
+
* - `prompt({ message: '...', model: '...' })` - object style
|
|
820
|
+
*
|
|
821
|
+
* @param messageOrOptions - The message string OR an options object with message
|
|
822
|
+
* @param options - Prompt configuration (only used with positional style)
|
|
823
|
+
* @returns The result message
|
|
824
|
+
*
|
|
825
|
+
* @example
|
|
826
|
+
* ```typescript
|
|
827
|
+
* // Positional style
|
|
828
|
+
* const result = await client.prompt(
|
|
829
|
+
* 'Explain quantum computing in simple terms',
|
|
830
|
+
* { model: 'claude-sonnet-4-5-20250929' }
|
|
831
|
+
* );
|
|
832
|
+
*
|
|
833
|
+
* // Object style
|
|
834
|
+
* const result = await client.prompt({
|
|
835
|
+
* message: 'Explain quantum computing in simple terms',
|
|
836
|
+
* model: 'claude-sonnet-4-5-20250929',
|
|
837
|
+
* });
|
|
838
|
+
*
|
|
839
|
+
* if (result.subtype === 'success') {
|
|
840
|
+
* console.log(result.result);
|
|
841
|
+
* }
|
|
842
|
+
* ```
|
|
843
|
+
*/
|
|
844
|
+
async prompt(messageOrOptions, options = {}) {
|
|
845
|
+
let message;
|
|
846
|
+
let sessionOptions;
|
|
847
|
+
if (typeof messageOrOptions === "object") {
|
|
848
|
+
const { message: msg, ...rest } = messageOrOptions;
|
|
849
|
+
message = msg;
|
|
850
|
+
sessionOptions = rest;
|
|
851
|
+
} else {
|
|
852
|
+
message = messageOrOptions;
|
|
853
|
+
sessionOptions = options;
|
|
854
|
+
}
|
|
855
|
+
const session = this.createSession(sessionOptions);
|
|
856
|
+
try {
|
|
857
|
+
await session.send(message);
|
|
858
|
+
let result = null;
|
|
859
|
+
for await (const msg of session.stream()) {
|
|
860
|
+
if (msg.type === "result") {
|
|
861
|
+
result = msg;
|
|
862
|
+
break;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
if (!result) {
|
|
866
|
+
throw new Error("No result message received");
|
|
867
|
+
}
|
|
868
|
+
return result;
|
|
869
|
+
} finally {
|
|
870
|
+
session.close();
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Close all active sessions and disconnect
|
|
875
|
+
*/
|
|
876
|
+
close() {
|
|
877
|
+
for (const session of this.activeSessions.values()) {
|
|
878
|
+
session.close();
|
|
879
|
+
}
|
|
880
|
+
this.activeSessions.clear();
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Create a new transport instance
|
|
884
|
+
*/
|
|
885
|
+
createTransport() {
|
|
886
|
+
return new WebSocketTransport({
|
|
887
|
+
url: this.options.baseUrl,
|
|
888
|
+
token: this.options.token,
|
|
889
|
+
timeout: this.options.timeout,
|
|
890
|
+
keepAliveInterval: this.options.keepAliveInterval,
|
|
891
|
+
autoReconnect: this.options.autoReconnect,
|
|
892
|
+
maxReconnectAttempts: this.options.maxReconnectAttempts,
|
|
893
|
+
debug: this.options.debug
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
};
|
|
897
|
+
function createClient(options) {
|
|
898
|
+
return new ChuckyClient(options);
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
// src/tools/tool.ts
|
|
902
|
+
function isZodSchema(schema) {
|
|
903
|
+
return typeof schema === "object" && schema !== null && ("_def" in schema || "shape" in schema);
|
|
904
|
+
}
|
|
905
|
+
function zodToJsonSchema(zodSchema) {
|
|
906
|
+
const def = zodSchema._def;
|
|
907
|
+
if (!def) {
|
|
908
|
+
return {
|
|
909
|
+
type: "object",
|
|
910
|
+
properties: {}
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
const typeName = def.typeName;
|
|
914
|
+
if (typeName === "ZodObject") {
|
|
915
|
+
const shape = def.shape?.() || {};
|
|
916
|
+
const properties = {};
|
|
917
|
+
const required = [];
|
|
918
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
919
|
+
const valueDef = value._def;
|
|
920
|
+
if (valueDef) {
|
|
921
|
+
const prop = zodDefToJsonSchema(valueDef);
|
|
922
|
+
if (prop) {
|
|
923
|
+
properties[key] = prop;
|
|
924
|
+
if (valueDef.typeName !== "ZodOptional") {
|
|
925
|
+
required.push(key);
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
return {
|
|
931
|
+
type: "object",
|
|
932
|
+
properties,
|
|
933
|
+
required: required.length > 0 ? required : void 0
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
return {
|
|
937
|
+
type: "object",
|
|
938
|
+
properties: {}
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
function zodDefToJsonSchema(def) {
|
|
942
|
+
const typeName = def.typeName;
|
|
943
|
+
switch (typeName) {
|
|
944
|
+
case "ZodString":
|
|
945
|
+
return { type: "string", description: def.description };
|
|
946
|
+
case "ZodNumber":
|
|
947
|
+
return { type: "number", description: def.description };
|
|
948
|
+
case "ZodBoolean":
|
|
949
|
+
return { type: "boolean", description: def.description };
|
|
950
|
+
case "ZodArray":
|
|
951
|
+
const innerDef = def.innerType?._def;
|
|
952
|
+
return {
|
|
953
|
+
type: "array",
|
|
954
|
+
description: def.description,
|
|
955
|
+
items: innerDef ? zodDefToJsonSchema(innerDef) || { type: "string" } : { type: "string" }
|
|
956
|
+
};
|
|
957
|
+
case "ZodEnum":
|
|
958
|
+
return {
|
|
959
|
+
type: "string",
|
|
960
|
+
description: def.description,
|
|
961
|
+
enum: def.values
|
|
962
|
+
};
|
|
963
|
+
case "ZodOptional":
|
|
964
|
+
const innerOptDef = def.innerType?._def;
|
|
965
|
+
return innerOptDef ? zodDefToJsonSchema(innerOptDef) : null;
|
|
966
|
+
default:
|
|
967
|
+
return { type: "string" };
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
function createTool(options) {
|
|
971
|
+
let inputSchema;
|
|
972
|
+
if (isZodSchema(options.inputSchema)) {
|
|
973
|
+
inputSchema = zodToJsonSchema(options.inputSchema);
|
|
974
|
+
} else {
|
|
975
|
+
inputSchema = options.inputSchema;
|
|
976
|
+
}
|
|
977
|
+
return {
|
|
978
|
+
name: options.name,
|
|
979
|
+
description: options.description,
|
|
980
|
+
inputSchema,
|
|
981
|
+
executeIn: options.executeIn ?? "server",
|
|
982
|
+
handler: options.handler
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
function tool(name, description, inputSchema, handler) {
|
|
986
|
+
return createTool({ name, description, inputSchema, handler });
|
|
987
|
+
}
|
|
988
|
+
function browserTool(options) {
|
|
989
|
+
return createTool({ ...options, executeIn: "browser" });
|
|
990
|
+
}
|
|
991
|
+
function serverTool(options) {
|
|
992
|
+
return createTool({ ...options, executeIn: "server" });
|
|
993
|
+
}
|
|
994
|
+
function textResult(text) {
|
|
995
|
+
return {
|
|
996
|
+
content: [{ type: "text", text }]
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
function errorResult(message) {
|
|
1000
|
+
return {
|
|
1001
|
+
content: [{ type: "text", text: message }],
|
|
1002
|
+
isError: true
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
function imageResult(data, mimeType) {
|
|
1006
|
+
return {
|
|
1007
|
+
content: [{ type: "image", data, mimeType }]
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
// src/tools/McpServer.ts
|
|
1012
|
+
var McpServerBuilder = class {
|
|
1013
|
+
/**
|
|
1014
|
+
* Create a new MCP server builder
|
|
1015
|
+
*
|
|
1016
|
+
* @param name - Server name
|
|
1017
|
+
* @param version - Server version (default: '1.0.0')
|
|
1018
|
+
*/
|
|
1019
|
+
constructor(name, version = "1.0.0") {
|
|
1020
|
+
__publicField(this, "name");
|
|
1021
|
+
__publicField(this, "version");
|
|
1022
|
+
__publicField(this, "tools", []);
|
|
1023
|
+
this.name = name;
|
|
1024
|
+
this.version = version;
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* Add a tool to the server
|
|
1028
|
+
*
|
|
1029
|
+
* @param options - Tool configuration
|
|
1030
|
+
* @returns This builder for chaining
|
|
1031
|
+
*/
|
|
1032
|
+
addTool(options) {
|
|
1033
|
+
this.tools.push(createTool(options));
|
|
1034
|
+
return this;
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* Add an existing tool definition
|
|
1038
|
+
*
|
|
1039
|
+
* @param tool - Tool definition
|
|
1040
|
+
* @returns This builder for chaining
|
|
1041
|
+
*/
|
|
1042
|
+
add(tool2) {
|
|
1043
|
+
this.tools.push(tool2);
|
|
1044
|
+
return this;
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* Add multiple tools at once
|
|
1048
|
+
*
|
|
1049
|
+
* @param tools - Array of tool definitions
|
|
1050
|
+
* @returns This builder for chaining
|
|
1051
|
+
*/
|
|
1052
|
+
addTools(tools) {
|
|
1053
|
+
this.tools.push(...tools);
|
|
1054
|
+
return this;
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Build the MCP server definition
|
|
1058
|
+
*
|
|
1059
|
+
* @returns Complete MCP server definition
|
|
1060
|
+
*/
|
|
1061
|
+
build() {
|
|
1062
|
+
return {
|
|
1063
|
+
name: this.name,
|
|
1064
|
+
version: this.version,
|
|
1065
|
+
tools: this.tools
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
};
|
|
1069
|
+
function createMcpServer(name, tools, version = "1.0.0") {
|
|
1070
|
+
return {
|
|
1071
|
+
name,
|
|
1072
|
+
version,
|
|
1073
|
+
tools
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
function mcpServer(name, version) {
|
|
1077
|
+
return new McpServerBuilder(name, version);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
// src/utils/token.ts
|
|
1081
|
+
function base64UrlEncode(data) {
|
|
1082
|
+
let base64;
|
|
1083
|
+
if (typeof data === "string") {
|
|
1084
|
+
const bytes = new TextEncoder().encode(data);
|
|
1085
|
+
base64 = btoa(String.fromCharCode(...bytes));
|
|
1086
|
+
} else {
|
|
1087
|
+
base64 = btoa(String.fromCharCode(...data));
|
|
1088
|
+
}
|
|
1089
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
1090
|
+
}
|
|
1091
|
+
function base64UrlDecode(str) {
|
|
1092
|
+
const base64 = str.replace(/-/g, "+").replace(/_/g, "/");
|
|
1093
|
+
const padding = "=".repeat((4 - base64.length % 4) % 4);
|
|
1094
|
+
const bytes = atob(base64 + padding);
|
|
1095
|
+
return bytes;
|
|
1096
|
+
}
|
|
1097
|
+
async function getCrypto() {
|
|
1098
|
+
if (typeof crypto !== "undefined" && crypto.subtle) {
|
|
1099
|
+
return crypto;
|
|
1100
|
+
}
|
|
1101
|
+
const nodeCrypto = await import("crypto");
|
|
1102
|
+
return nodeCrypto.webcrypto;
|
|
1103
|
+
}
|
|
1104
|
+
async function createHmacSignature(secret, data) {
|
|
1105
|
+
const { subtle } = await getCrypto();
|
|
1106
|
+
const keyData = new TextEncoder().encode(secret);
|
|
1107
|
+
const key = await subtle.importKey(
|
|
1108
|
+
"raw",
|
|
1109
|
+
keyData,
|
|
1110
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
1111
|
+
false,
|
|
1112
|
+
["sign"]
|
|
1113
|
+
);
|
|
1114
|
+
const signature = await subtle.sign("HMAC", key, new TextEncoder().encode(data));
|
|
1115
|
+
return base64UrlEncode(new Uint8Array(signature));
|
|
1116
|
+
}
|
|
1117
|
+
async function verifyHmacSignature(secret, data, signature) {
|
|
1118
|
+
const expectedSignature = await createHmacSignature(secret, data);
|
|
1119
|
+
return signature === expectedSignature;
|
|
1120
|
+
}
|
|
1121
|
+
async function createToken(options) {
|
|
1122
|
+
const {
|
|
1123
|
+
userId,
|
|
1124
|
+
projectId,
|
|
1125
|
+
secret,
|
|
1126
|
+
expiresIn = 3600,
|
|
1127
|
+
budget,
|
|
1128
|
+
permissions,
|
|
1129
|
+
sdkConfig
|
|
1130
|
+
} = options;
|
|
1131
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1132
|
+
const payload = {
|
|
1133
|
+
sub: userId,
|
|
1134
|
+
iss: projectId,
|
|
1135
|
+
iat: now,
|
|
1136
|
+
exp: now + expiresIn,
|
|
1137
|
+
budget,
|
|
1138
|
+
...permissions && { permissions },
|
|
1139
|
+
...sdkConfig && { sdkConfig }
|
|
1140
|
+
};
|
|
1141
|
+
const header = {
|
|
1142
|
+
alg: "HS256",
|
|
1143
|
+
typ: "JWT"
|
|
1144
|
+
};
|
|
1145
|
+
const encodedHeader = base64UrlEncode(JSON.stringify(header));
|
|
1146
|
+
const encodedPayload = base64UrlEncode(JSON.stringify(payload));
|
|
1147
|
+
const signatureInput = `${encodedHeader}.${encodedPayload}`;
|
|
1148
|
+
const signature = await createHmacSignature(secret, signatureInput);
|
|
1149
|
+
return `${encodedHeader}.${encodedPayload}.${signature}`;
|
|
1150
|
+
}
|
|
1151
|
+
function decodeToken(token) {
|
|
1152
|
+
const parts = token.split(".");
|
|
1153
|
+
if (parts.length !== 3) {
|
|
1154
|
+
throw new Error("Invalid token format");
|
|
1155
|
+
}
|
|
1156
|
+
const [encodedHeader, encodedPayload, signature] = parts;
|
|
1157
|
+
const header = JSON.parse(base64UrlDecode(encodedHeader));
|
|
1158
|
+
const payload = JSON.parse(base64UrlDecode(encodedPayload));
|
|
1159
|
+
return { header, payload, signature };
|
|
1160
|
+
}
|
|
1161
|
+
async function verifyToken(token, secret) {
|
|
1162
|
+
const parts = token.split(".");
|
|
1163
|
+
if (parts.length !== 3) {
|
|
1164
|
+
return false;
|
|
1165
|
+
}
|
|
1166
|
+
const [encodedHeader, encodedPayload, signature] = parts;
|
|
1167
|
+
const signatureInput = `${encodedHeader}.${encodedPayload}`;
|
|
1168
|
+
return verifyHmacSignature(secret, signatureInput, signature);
|
|
1169
|
+
}
|
|
1170
|
+
function isTokenExpired(token) {
|
|
1171
|
+
try {
|
|
1172
|
+
const decoded = decodeToken(token);
|
|
1173
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1174
|
+
return decoded.payload.exp < now;
|
|
1175
|
+
} catch {
|
|
1176
|
+
return true;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
function extractProjectId(_hmacKey) {
|
|
1180
|
+
throw new Error(
|
|
1181
|
+
"extractProjectId() is deprecated. The project ID is now separate from the HMAC key for security. Get your project ID from the Chucky portal (app.chucky.cloud) in your project settings."
|
|
1182
|
+
);
|
|
1183
|
+
}
|
|
1184
|
+
function createBudget(options) {
|
|
1185
|
+
return {
|
|
1186
|
+
ai: Math.floor(options.aiDollars * 1e6),
|
|
1187
|
+
// Convert to microdollars
|
|
1188
|
+
compute: Math.floor(options.computeHours * 3600),
|
|
1189
|
+
// Convert to seconds
|
|
1190
|
+
window: options.window,
|
|
1191
|
+
windowStart: (options.windowStart || /* @__PURE__ */ new Date()).toISOString()
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
// src/utils/errors.ts
|
|
1196
|
+
var ChuckyError = class _ChuckyError extends Error {
|
|
1197
|
+
constructor(message, code, details) {
|
|
1198
|
+
super(message);
|
|
1199
|
+
/** Error code */
|
|
1200
|
+
__publicField(this, "code");
|
|
1201
|
+
/** Additional error details */
|
|
1202
|
+
__publicField(this, "details");
|
|
1203
|
+
this.name = "ChuckyError";
|
|
1204
|
+
this.code = code;
|
|
1205
|
+
this.details = details;
|
|
1206
|
+
if (Error.captureStackTrace) {
|
|
1207
|
+
Error.captureStackTrace(this, _ChuckyError);
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
};
|
|
1211
|
+
var ConnectionError = class extends ChuckyError {
|
|
1212
|
+
constructor(message, details) {
|
|
1213
|
+
super(message, "CONNECTION_ERROR", details);
|
|
1214
|
+
this.name = "ConnectionError";
|
|
1215
|
+
}
|
|
1216
|
+
};
|
|
1217
|
+
var AuthenticationError = class extends ChuckyError {
|
|
1218
|
+
constructor(message, details) {
|
|
1219
|
+
super(message, "AUTHENTICATION_ERROR", details);
|
|
1220
|
+
this.name = "AuthenticationError";
|
|
1221
|
+
}
|
|
1222
|
+
};
|
|
1223
|
+
var BudgetExceededError = class extends ChuckyError {
|
|
1224
|
+
constructor(message, details) {
|
|
1225
|
+
super(message, "BUDGET_EXCEEDED", details);
|
|
1226
|
+
this.name = "BudgetExceededError";
|
|
1227
|
+
}
|
|
1228
|
+
};
|
|
1229
|
+
var ConcurrencyLimitError = class extends ChuckyError {
|
|
1230
|
+
constructor(message, details) {
|
|
1231
|
+
super(message, "CONCURRENCY_LIMIT", details);
|
|
1232
|
+
this.name = "ConcurrencyLimitError";
|
|
1233
|
+
}
|
|
1234
|
+
};
|
|
1235
|
+
var RateLimitError = class extends ChuckyError {
|
|
1236
|
+
constructor(message, details) {
|
|
1237
|
+
super(message, "RATE_LIMIT", details);
|
|
1238
|
+
this.name = "RateLimitError";
|
|
1239
|
+
}
|
|
1240
|
+
};
|
|
1241
|
+
var SessionError = class extends ChuckyError {
|
|
1242
|
+
constructor(message, details) {
|
|
1243
|
+
super(message, "SESSION_ERROR", details);
|
|
1244
|
+
this.name = "SessionError";
|
|
1245
|
+
}
|
|
1246
|
+
};
|
|
1247
|
+
var ToolExecutionError = class extends ChuckyError {
|
|
1248
|
+
constructor(message, toolName, details) {
|
|
1249
|
+
super(message, "TOOL_EXECUTION_ERROR", { ...details, toolName });
|
|
1250
|
+
/** Tool name */
|
|
1251
|
+
__publicField(this, "toolName");
|
|
1252
|
+
this.name = "ToolExecutionError";
|
|
1253
|
+
this.toolName = toolName;
|
|
1254
|
+
}
|
|
1255
|
+
};
|
|
1256
|
+
var TimeoutError = class extends ChuckyError {
|
|
1257
|
+
constructor(message, details) {
|
|
1258
|
+
super(message, "TIMEOUT", details);
|
|
1259
|
+
this.name = "TimeoutError";
|
|
1260
|
+
}
|
|
1261
|
+
};
|
|
1262
|
+
var ValidationError = class extends ChuckyError {
|
|
1263
|
+
constructor(message, details) {
|
|
1264
|
+
super(message, "VALIDATION_ERROR", details);
|
|
1265
|
+
this.name = "ValidationError";
|
|
1266
|
+
}
|
|
1267
|
+
};
|
|
1268
|
+
function parseErrorCode(message) {
|
|
1269
|
+
const patterns = [
|
|
1270
|
+
{ pattern: /budget.*exceed/i, code: "BUDGET_EXCEEDED" },
|
|
1271
|
+
{ pattern: /concurrency.*limit/i, code: "CONCURRENCY_LIMIT" },
|
|
1272
|
+
{ pattern: /rate.*limit/i, code: "RATE_LIMIT" },
|
|
1273
|
+
{ pattern: /auth|unauthorized|forbidden/i, code: "AUTHENTICATION_ERROR" },
|
|
1274
|
+
{ pattern: /timeout/i, code: "TIMEOUT" },
|
|
1275
|
+
{ pattern: /connect|disconnect|websocket/i, code: "CONNECTION_ERROR" },
|
|
1276
|
+
{ pattern: /session/i, code: "SESSION_ERROR" },
|
|
1277
|
+
{ pattern: /tool/i, code: "TOOL_EXECUTION_ERROR" },
|
|
1278
|
+
{ pattern: /invalid|validation/i, code: "VALIDATION_ERROR" }
|
|
1279
|
+
];
|
|
1280
|
+
for (const { pattern, code } of patterns) {
|
|
1281
|
+
if (pattern.test(message)) {
|
|
1282
|
+
return code;
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
return null;
|
|
1286
|
+
}
|
|
1287
|
+
function createError(message, code) {
|
|
1288
|
+
const errorCode = code || parseErrorCode(message) || "UNKNOWN_ERROR";
|
|
1289
|
+
switch (errorCode) {
|
|
1290
|
+
case "CONNECTION_ERROR":
|
|
1291
|
+
return new ConnectionError(message);
|
|
1292
|
+
case "AUTHENTICATION_ERROR":
|
|
1293
|
+
return new AuthenticationError(message);
|
|
1294
|
+
case "BUDGET_EXCEEDED":
|
|
1295
|
+
return new BudgetExceededError(message);
|
|
1296
|
+
case "CONCURRENCY_LIMIT":
|
|
1297
|
+
return new ConcurrencyLimitError(message);
|
|
1298
|
+
case "RATE_LIMIT":
|
|
1299
|
+
return new RateLimitError(message);
|
|
1300
|
+
case "SESSION_ERROR":
|
|
1301
|
+
return new SessionError(message);
|
|
1302
|
+
case "TIMEOUT":
|
|
1303
|
+
return new TimeoutError(message);
|
|
1304
|
+
case "VALIDATION_ERROR":
|
|
1305
|
+
return new ValidationError(message);
|
|
1306
|
+
default:
|
|
1307
|
+
return new ChuckyError(message, errorCode);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1311
|
+
0 && (module.exports = {
|
|
1312
|
+
AuthenticationError,
|
|
1313
|
+
BudgetExceededError,
|
|
1314
|
+
ChuckyClient,
|
|
1315
|
+
ChuckyError,
|
|
1316
|
+
ConcurrencyLimitError,
|
|
1317
|
+
ConnectionError,
|
|
1318
|
+
McpServerBuilder,
|
|
1319
|
+
RateLimitError,
|
|
1320
|
+
Session,
|
|
1321
|
+
SessionError,
|
|
1322
|
+
TimeoutError,
|
|
1323
|
+
ToolExecutionError,
|
|
1324
|
+
ValidationError,
|
|
1325
|
+
WebSocketTransport,
|
|
1326
|
+
browserTool,
|
|
1327
|
+
createBudget,
|
|
1328
|
+
createClient,
|
|
1329
|
+
createControlMessage,
|
|
1330
|
+
createError,
|
|
1331
|
+
createInitMessage,
|
|
1332
|
+
createMcpServer,
|
|
1333
|
+
createPingMessage,
|
|
1334
|
+
createToken,
|
|
1335
|
+
createTool,
|
|
1336
|
+
createToolResultMessage,
|
|
1337
|
+
createUserMessage,
|
|
1338
|
+
decodeToken,
|
|
1339
|
+
errorResult,
|
|
1340
|
+
extractProjectId,
|
|
1341
|
+
getAssistantText,
|
|
1342
|
+
getResultText,
|
|
1343
|
+
imageResult,
|
|
1344
|
+
isAssistantMessage,
|
|
1345
|
+
isControlMessage,
|
|
1346
|
+
isErrorMessage,
|
|
1347
|
+
isErrorResult,
|
|
1348
|
+
isResultMessage,
|
|
1349
|
+
isStreamEvent,
|
|
1350
|
+
isSuccessResult,
|
|
1351
|
+
isSystemMessage,
|
|
1352
|
+
isTokenExpired,
|
|
1353
|
+
isToolCallMessage,
|
|
1354
|
+
isUserMessage,
|
|
1355
|
+
mcpServer,
|
|
1356
|
+
serverTool,
|
|
1357
|
+
textResult,
|
|
1358
|
+
tool,
|
|
1359
|
+
verifyToken
|
|
1360
|
+
});
|
|
1361
|
+
//# sourceMappingURL=browser.cjs.map
|