@dexto/client-sdk 1.2.4 → 1.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -4
- package/dist/client.d.ts +3022 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/index.cjs +115 -799
- package/dist/index.d.ts +8 -230
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +114 -800
- package/dist/streaming.d.ts +57 -0
- package/dist/streaming.d.ts.map +1 -0
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +12 -4
- package/dist/index.d.cts +0 -232
package/dist/index.cjs
CHANGED
|
@@ -3,7 +3,6 @@ var __defProp = Object.defineProperty;
|
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
7
6
|
var __export = (target, all) => {
|
|
8
7
|
for (var name in all)
|
|
9
8
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -17,833 +16,150 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
16
|
return to;
|
|
18
17
|
};
|
|
19
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
-
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
21
19
|
|
|
22
20
|
// src/index.ts
|
|
23
21
|
var index_exports = {};
|
|
24
22
|
__export(index_exports, {
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
SSEError: () => SSEError,
|
|
24
|
+
createDextoClient: () => createDextoClient,
|
|
25
|
+
createMessageStream: () => createMessageStream,
|
|
26
|
+
createStream: () => createStream,
|
|
27
|
+
stream: () => stream
|
|
27
28
|
});
|
|
28
29
|
module.exports = __toCommonJS(index_exports);
|
|
29
30
|
|
|
30
|
-
// src/
|
|
31
|
-
var
|
|
32
|
-
/**
|
|
33
|
-
* Connection and Network Errors
|
|
34
|
-
*/
|
|
35
|
-
static connectionFailed(baseUrl, originalError) {
|
|
36
|
-
const error = new Error(`Failed to connect to Dexto server at ${baseUrl}`);
|
|
37
|
-
error.name = "ConnectionError";
|
|
38
|
-
error.baseUrl = baseUrl;
|
|
39
|
-
error.originalError = originalError == null ? void 0 : originalError.message;
|
|
40
|
-
return error;
|
|
41
|
-
}
|
|
42
|
-
static networkError(message, originalError) {
|
|
43
|
-
const error = new Error(`Network error: ${message}`);
|
|
44
|
-
error.name = "NetworkError";
|
|
45
|
-
error.originalError = originalError == null ? void 0 : originalError.message;
|
|
46
|
-
return error;
|
|
47
|
-
}
|
|
48
|
-
static httpError(status, statusText, endpoint, details) {
|
|
49
|
-
const error = new Error(`HTTP ${status}: ${statusText}${endpoint ? ` (${endpoint})` : ""}`);
|
|
50
|
-
error.name = "HttpError";
|
|
51
|
-
error.status = status;
|
|
52
|
-
error.statusText = statusText;
|
|
53
|
-
error.endpoint = endpoint;
|
|
54
|
-
error.details = details;
|
|
55
|
-
return error;
|
|
56
|
-
}
|
|
57
|
-
static timeoutError(operation, timeout) {
|
|
58
|
-
const error = new Error(`Operation '${operation}' timed out after ${timeout}ms`);
|
|
59
|
-
error.name = "TimeoutError";
|
|
60
|
-
error.operation = operation;
|
|
61
|
-
error.timeout = timeout;
|
|
62
|
-
return error;
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* WebSocket Errors
|
|
66
|
-
*/
|
|
67
|
-
static websocketConnectionFailed(url, originalError) {
|
|
68
|
-
const error = new Error(`Failed to connect WebSocket to ${url}`);
|
|
69
|
-
error.name = "WebSocketConnectionError";
|
|
70
|
-
error.url = url;
|
|
71
|
-
error.originalError = originalError == null ? void 0 : originalError.message;
|
|
72
|
-
return error;
|
|
73
|
-
}
|
|
74
|
-
static websocketSendFailed(originalError) {
|
|
75
|
-
const error = new Error("Failed to send WebSocket message");
|
|
76
|
-
error.name = "WebSocketSendError";
|
|
77
|
-
error.originalError = originalError == null ? void 0 : originalError.message;
|
|
78
|
-
return error;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Configuration Errors
|
|
82
|
-
*/
|
|
83
|
-
static invalidConfig(field, value, reason) {
|
|
84
|
-
const error = new Error(`Invalid configuration for ${field}: ${reason}`);
|
|
85
|
-
error.name = "InvalidConfigError";
|
|
86
|
-
error.field = field;
|
|
87
|
-
error.value = value;
|
|
88
|
-
error.reason = reason;
|
|
89
|
-
return error;
|
|
90
|
-
}
|
|
91
|
-
static responseParseError(originalError) {
|
|
92
|
-
const error = new Error("Failed to parse server response");
|
|
93
|
-
error.name = "ResponseParseError";
|
|
94
|
-
error.originalError = originalError == null ? void 0 : originalError.message;
|
|
95
|
-
return error;
|
|
96
|
-
}
|
|
97
|
-
};
|
|
31
|
+
// src/client.ts
|
|
32
|
+
var import_client = require("hono/client");
|
|
98
33
|
|
|
99
|
-
// src/
|
|
100
|
-
var
|
|
101
|
-
constructor(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
this.
|
|
105
|
-
|
|
106
|
-
this.fetchFn = window.fetch.bind(window);
|
|
107
|
-
} else if (typeof globalThis !== "undefined" && globalThis.fetch) {
|
|
108
|
-
this.fetchFn = globalThis.fetch;
|
|
109
|
-
} else {
|
|
110
|
-
throw ClientError.invalidConfig(
|
|
111
|
-
"fetch",
|
|
112
|
-
"unavailable",
|
|
113
|
-
"No fetch implementation available. Use Node.js 18+ or a browser."
|
|
114
|
-
);
|
|
115
|
-
}
|
|
34
|
+
// src/streaming.ts
|
|
35
|
+
var SSEError = class extends Error {
|
|
36
|
+
constructor(status, body) {
|
|
37
|
+
super(`SSE Error: ${status}`);
|
|
38
|
+
this.status = status;
|
|
39
|
+
this.body = body;
|
|
40
|
+
this.name = "SSEError";
|
|
116
41
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
(k) => k.toLowerCase() === "content-type"
|
|
124
|
-
);
|
|
125
|
-
let payload = void 0;
|
|
126
|
-
const isBodyInit = (b) => {
|
|
127
|
-
if (b == null) return false;
|
|
128
|
-
return typeof b === "string" || typeof globalThis.Blob !== "undefined" && b instanceof globalThis.Blob || typeof globalThis.FormData !== "undefined" && b instanceof globalThis.FormData || typeof globalThis.URLSearchParams !== "undefined" && b instanceof globalThis.URLSearchParams || typeof globalThis.ReadableStream !== "undefined" && b instanceof globalThis.ReadableStream || typeof ArrayBuffer !== "undefined" && b instanceof ArrayBuffer;
|
|
129
|
-
};
|
|
130
|
-
if (body !== void 0) {
|
|
131
|
-
if (isBodyInit(body)) {
|
|
132
|
-
payload = body;
|
|
133
|
-
} else {
|
|
134
|
-
payload = JSON.stringify(body);
|
|
135
|
-
if (!hasContentType) {
|
|
136
|
-
requestHeaders["Content-Type"] = "application/json";
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
if (this.config.apiKey) {
|
|
141
|
-
requestHeaders["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
142
|
-
}
|
|
143
|
-
const requestInit = {
|
|
144
|
-
method,
|
|
145
|
-
headers: requestHeaders,
|
|
146
|
-
...payload !== void 0 && { body: payload }
|
|
147
|
-
};
|
|
148
|
-
const controller = new AbortController();
|
|
149
|
-
const resolvedTimeout = (_a = timeout != null ? timeout : this.config.timeout) != null ? _a : 3e4;
|
|
150
|
-
const timeoutId = setTimeout(() => controller.abort(), resolvedTimeout);
|
|
151
|
-
requestInit.signal = controller.signal;
|
|
42
|
+
};
|
|
43
|
+
async function* stream(response, options) {
|
|
44
|
+
var _a;
|
|
45
|
+
if (!response.ok) {
|
|
46
|
+
const contentType = response.headers.get("content-type");
|
|
47
|
+
let errorBody;
|
|
152
48
|
try {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
throw ClientError.httpError(
|
|
158
|
-
response.status,
|
|
159
|
-
response.statusText,
|
|
160
|
-
endpoint,
|
|
161
|
-
errorData
|
|
162
|
-
);
|
|
163
|
-
}
|
|
164
|
-
if (response.status === 204 || response.headers.get("content-length") === "0") {
|
|
165
|
-
return {};
|
|
166
|
-
}
|
|
167
|
-
const contentType = response.headers.get("content-type") || "";
|
|
168
|
-
if (contentType.includes("application/json")) {
|
|
169
|
-
const data = await response.json();
|
|
170
|
-
return data;
|
|
171
|
-
}
|
|
172
|
-
const text = await response.text();
|
|
173
|
-
return text ? text : {};
|
|
174
|
-
} catch (error) {
|
|
175
|
-
clearTimeout(timeoutId);
|
|
176
|
-
if (error instanceof Error && error.name.startsWith("ClientError")) {
|
|
177
|
-
throw error;
|
|
178
|
-
}
|
|
179
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
180
|
-
throw ClientError.timeoutError(endpoint, resolvedTimeout);
|
|
181
|
-
}
|
|
182
|
-
throw ClientError.networkError(
|
|
183
|
-
error instanceof Error ? error.message : "Unknown network error",
|
|
184
|
-
error instanceof Error ? error : void 0
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
async fetchWithRetry(url, init) {
|
|
189
|
-
var _a;
|
|
190
|
-
let lastError = null;
|
|
191
|
-
const method = (init.method || "GET").toUpperCase();
|
|
192
|
-
const canRetry = ["GET", "HEAD", "PUT", "DELETE"].includes(method);
|
|
193
|
-
const maxRetries = (_a = this.config.retries) != null ? _a : 3;
|
|
194
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
195
|
-
try {
|
|
196
|
-
const response = await this.fetchFn(url, init);
|
|
197
|
-
const transientStatus = [429, 502, 503, 504];
|
|
198
|
-
if (canRetry && transientStatus.includes(response.status) && attempt < maxRetries) {
|
|
199
|
-
const retryAfter = response.headers.get("retry-after");
|
|
200
|
-
let delay = Math.min(1e3 * Math.pow(2, attempt), 1e4);
|
|
201
|
-
if (retryAfter) {
|
|
202
|
-
const secs = Number(retryAfter);
|
|
203
|
-
if (Number.isFinite(secs) && secs >= 0) {
|
|
204
|
-
delay = secs * 1e3;
|
|
205
|
-
} else {
|
|
206
|
-
const retryTime = Date.parse(retryAfter);
|
|
207
|
-
if (retryTime > Date.now()) {
|
|
208
|
-
delay = Math.max(0, retryTime - Date.now());
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
delay = Math.min(delay, 3e4);
|
|
213
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
return response;
|
|
217
|
-
} catch (error) {
|
|
218
|
-
lastError = error instanceof Error ? error : ClientError.networkError(
|
|
219
|
-
error instanceof Error ? error.message : String(error),
|
|
220
|
-
error instanceof Error ? error : void 0
|
|
221
|
-
);
|
|
222
|
-
if (attempt === maxRetries || !canRetry || error instanceof Error && error.name === "AbortError") {
|
|
223
|
-
break;
|
|
224
|
-
}
|
|
225
|
-
const delay = Math.min(1e3 * Math.pow(2, attempt), 1e4);
|
|
226
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
49
|
+
if (contentType && contentType.includes("application/json")) {
|
|
50
|
+
errorBody = await response.json();
|
|
51
|
+
} else {
|
|
52
|
+
errorBody = await response.text();
|
|
227
53
|
}
|
|
228
|
-
}
|
|
229
|
-
throw lastError;
|
|
230
|
-
}
|
|
231
|
-
async safeParseJson(response) {
|
|
232
|
-
try {
|
|
233
|
-
return await response.json();
|
|
234
54
|
} catch (e) {
|
|
235
|
-
|
|
55
|
+
errorBody = "Unknown error";
|
|
236
56
|
}
|
|
57
|
+
throw new SSEError(response.status, errorBody);
|
|
237
58
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
if (headers) options.headers = headers;
|
|
242
|
-
return this.request(endpoint, options);
|
|
243
|
-
}
|
|
244
|
-
async post(endpoint, body, headers) {
|
|
245
|
-
const options = { method: "POST" };
|
|
246
|
-
if (body !== void 0) options.body = body;
|
|
247
|
-
if (headers) options.headers = headers;
|
|
248
|
-
return this.request(endpoint, options);
|
|
249
|
-
}
|
|
250
|
-
async put(endpoint, body, headers) {
|
|
251
|
-
const options = { method: "PUT" };
|
|
252
|
-
if (body !== void 0) options.body = body;
|
|
253
|
-
if (headers) options.headers = headers;
|
|
254
|
-
return this.request(endpoint, options);
|
|
59
|
+
const reader = (_a = response.body) == null ? void 0 : _a.getReader();
|
|
60
|
+
if (!reader) {
|
|
61
|
+
throw new Error("Response body is null");
|
|
255
62
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
// src/websocket-client.ts
|
|
264
|
-
var WebSocketClient = class {
|
|
265
|
-
constructor(url, options = {}) {
|
|
266
|
-
__publicField(this, "ws", null);
|
|
267
|
-
__publicField(this, "url");
|
|
268
|
-
__publicField(this, "eventHandlers", /* @__PURE__ */ new Map());
|
|
269
|
-
__publicField(this, "stateHandlers", /* @__PURE__ */ new Set());
|
|
270
|
-
__publicField(this, "reconnectEnabled", true);
|
|
271
|
-
__publicField(this, "reconnectInterval", 5e3);
|
|
272
|
-
__publicField(this, "maxReconnectAttempts", 10);
|
|
273
|
-
__publicField(this, "reconnectAttempts", 0);
|
|
274
|
-
__publicField(this, "isIntentionallyClosed", false);
|
|
275
|
-
var _a, _b, _c;
|
|
276
|
-
this.url = url;
|
|
277
|
-
this.reconnectEnabled = (_a = options.reconnect) != null ? _a : true;
|
|
278
|
-
this.reconnectInterval = (_b = options.reconnectInterval) != null ? _b : 5e3;
|
|
279
|
-
this.maxReconnectAttempts = (_c = options.maxReconnectAttempts) != null ? _c : 10;
|
|
280
|
-
}
|
|
281
|
-
connect() {
|
|
282
|
-
return new Promise((resolve, reject) => {
|
|
283
|
-
try {
|
|
284
|
-
this.isIntentionallyClosed = false;
|
|
285
|
-
this.emitState("connecting");
|
|
286
|
-
if (typeof WebSocket !== "undefined") {
|
|
287
|
-
this.ws = new WebSocket(this.url);
|
|
288
|
-
} else {
|
|
289
|
-
reject(
|
|
290
|
-
ClientError.websocketConnectionFailed(
|
|
291
|
-
this.url,
|
|
292
|
-
new Error(
|
|
293
|
-
"WebSocket not available in this environment. Use HTTP methods or run in a browser."
|
|
294
|
-
)
|
|
295
|
-
)
|
|
296
|
-
);
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
299
|
-
this.setupEventHandlers(resolve, reject);
|
|
300
|
-
} catch (error) {
|
|
301
|
-
reject(
|
|
302
|
-
ClientError.websocketConnectionFailed(
|
|
303
|
-
this.url,
|
|
304
|
-
error instanceof Error ? error : new Error("Failed to create WebSocket connection")
|
|
305
|
-
)
|
|
306
|
-
);
|
|
307
|
-
}
|
|
63
|
+
const decoder = new globalThis.TextDecoder();
|
|
64
|
+
let buffer = "";
|
|
65
|
+
const signal = options == null ? void 0 : options.signal;
|
|
66
|
+
let aborted = false;
|
|
67
|
+
const abortHandler = () => {
|
|
68
|
+
aborted = true;
|
|
69
|
+
reader.cancel().catch(() => {
|
|
308
70
|
});
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
if (
|
|
312
|
-
|
|
313
|
-
this.reconnectAttempts = 0;
|
|
314
|
-
this.emitState("open");
|
|
315
|
-
resolve();
|
|
316
|
-
};
|
|
317
|
-
this.ws.onmessage = (event) => {
|
|
318
|
-
const raw = event.data;
|
|
319
|
-
const tryDispatch = (text) => {
|
|
320
|
-
try {
|
|
321
|
-
this.handleIncomingMessage(JSON.parse(text));
|
|
322
|
-
} catch (err) {
|
|
323
|
-
console.warn(
|
|
324
|
-
`[WebSocketClient] Failed to parse WebSocket message: ${err instanceof Error ? err.message : String(err)}`
|
|
325
|
-
);
|
|
326
|
-
}
|
|
327
|
-
};
|
|
328
|
-
if (typeof raw === "string") {
|
|
329
|
-
tryDispatch(raw);
|
|
330
|
-
} else if (typeof globalThis.Blob !== "undefined" && raw instanceof globalThis.Blob) {
|
|
331
|
-
raw.text().then(tryDispatch).catch((err) => {
|
|
332
|
-
console.warn(
|
|
333
|
-
`[WebSocketClient] Failed to read Blob message: ${err instanceof Error ? err.message : String(err)}`
|
|
334
|
-
);
|
|
335
|
-
});
|
|
336
|
-
} else if (typeof ArrayBuffer !== "undefined" && raw instanceof ArrayBuffer) {
|
|
337
|
-
try {
|
|
338
|
-
const text = new globalThis.TextDecoder().decode(raw);
|
|
339
|
-
tryDispatch(text);
|
|
340
|
-
} catch (err) {
|
|
341
|
-
console.warn(
|
|
342
|
-
`[WebSocketClient] Failed to decode ArrayBuffer message: ${err instanceof Error ? err.message : String(err)}`
|
|
343
|
-
);
|
|
344
|
-
}
|
|
345
|
-
} else {
|
|
346
|
-
console.warn(
|
|
347
|
-
`[WebSocketClient] Ignoring non-text WebSocket message of type: ${Object.prototype.toString.call(raw)}`
|
|
348
|
-
);
|
|
349
|
-
}
|
|
350
|
-
};
|
|
351
|
-
this.ws.onclose = (_event) => {
|
|
352
|
-
this.emitState("closed");
|
|
353
|
-
const shouldReconnect = !this.isIntentionallyClosed && this.reconnectEnabled && this.reconnectAttempts < this.maxReconnectAttempts;
|
|
354
|
-
if (shouldReconnect) {
|
|
355
|
-
this.scheduleReconnect(resolve, reject);
|
|
356
|
-
} else if (!this.isIntentionallyClosed && this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
357
|
-
reject(
|
|
358
|
-
ClientError.websocketConnectionFailed(
|
|
359
|
-
this.url,
|
|
360
|
-
new Error(`Max reconnect attempts reached (${this.maxReconnectAttempts})`)
|
|
361
|
-
)
|
|
362
|
-
);
|
|
363
|
-
}
|
|
364
|
-
};
|
|
365
|
-
this.ws.onerror = (_error) => {
|
|
366
|
-
this.emitState("error");
|
|
367
|
-
if (this.reconnectAttempts === 0) {
|
|
368
|
-
reject(ClientError.websocketConnectionFailed(this.url));
|
|
369
|
-
}
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
scheduleReconnect(resolve, reject) {
|
|
373
|
-
this.reconnectAttempts++;
|
|
374
|
-
setTimeout(() => {
|
|
375
|
-
if (!this.isIntentionallyClosed) {
|
|
376
|
-
this.emitState("connecting");
|
|
377
|
-
if (resolve && reject) {
|
|
378
|
-
this.connect().then(resolve).catch(reject);
|
|
379
|
-
} else {
|
|
380
|
-
this.connect().catch(() => {
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}, this.reconnectInterval);
|
|
385
|
-
}
|
|
386
|
-
handleIncomingMessage(data) {
|
|
387
|
-
var _a;
|
|
388
|
-
const msgData = data;
|
|
389
|
-
const eventType = msgData.type || msgData.event;
|
|
390
|
-
if (!eventType) {
|
|
391
|
-
console.warn("Received WebSocket message without event type:", msgData);
|
|
392
|
-
return;
|
|
393
|
-
}
|
|
394
|
-
const event = {
|
|
395
|
-
type: eventType,
|
|
396
|
-
data: msgData.data || msgData,
|
|
397
|
-
sessionId: msgData.sessionId || ((_a = msgData.data) == null ? void 0 : _a.sessionId)
|
|
398
|
-
};
|
|
399
|
-
const handlers = this.eventHandlers.get(event.type);
|
|
400
|
-
if (handlers) {
|
|
401
|
-
handlers.forEach((handler) => {
|
|
402
|
-
try {
|
|
403
|
-
handler(event);
|
|
404
|
-
} catch (error) {
|
|
405
|
-
console.error(
|
|
406
|
-
`Error in event handler for ${event.type}: ${error instanceof Error ? error.message : String(error)}`
|
|
407
|
-
);
|
|
408
|
-
}
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
|
-
const wildcardHandlers = this.eventHandlers.get("*");
|
|
412
|
-
if (wildcardHandlers) {
|
|
413
|
-
wildcardHandlers.forEach((handler) => {
|
|
414
|
-
try {
|
|
415
|
-
handler(event);
|
|
416
|
-
} catch (error) {
|
|
417
|
-
console.error(
|
|
418
|
-
`Error in wildcard event handler: ${error instanceof Error ? error.message : String(error)}`
|
|
419
|
-
);
|
|
420
|
-
}
|
|
71
|
+
};
|
|
72
|
+
if (signal) {
|
|
73
|
+
if (signal.aborted) {
|
|
74
|
+
reader.cancel().catch(() => {
|
|
421
75
|
});
|
|
76
|
+
return;
|
|
422
77
|
}
|
|
78
|
+
signal.addEventListener("abort", abortHandler);
|
|
423
79
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
return false;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
// Subscribe to specific event types
|
|
440
|
-
on(eventType, handler) {
|
|
441
|
-
if (!this.eventHandlers.has(eventType)) {
|
|
442
|
-
this.eventHandlers.set(eventType, /* @__PURE__ */ new Set());
|
|
443
|
-
}
|
|
444
|
-
this.eventHandlers.get(eventType).add(handler);
|
|
445
|
-
return () => {
|
|
446
|
-
const handlers = this.eventHandlers.get(eventType);
|
|
447
|
-
if (handlers) {
|
|
448
|
-
handlers.delete(handler);
|
|
449
|
-
if (handlers.size === 0) {
|
|
450
|
-
this.eventHandlers.delete(eventType);
|
|
80
|
+
try {
|
|
81
|
+
while (true) {
|
|
82
|
+
if (aborted || (signal == null ? void 0 : signal.aborted)) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const parts = buffer.split("\n\n");
|
|
86
|
+
if (parts.length > 1) {
|
|
87
|
+
const eventString = parts.shift();
|
|
88
|
+
buffer = parts.join("\n\n");
|
|
89
|
+
const event = parseSSE(eventString);
|
|
90
|
+
if (event) {
|
|
91
|
+
yield event;
|
|
451
92
|
}
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const { done, value } = await reader.read();
|
|
96
|
+
if (done) {
|
|
97
|
+
if (buffer.trim()) {
|
|
98
|
+
const event = parseSSE(buffer);
|
|
99
|
+
if (event) {
|
|
100
|
+
yield event;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
452
104
|
}
|
|
453
|
-
|
|
454
|
-
}
|
|
455
|
-
// Subscribe to connection state changes
|
|
456
|
-
onConnectionState(handler) {
|
|
457
|
-
this.stateHandlers.add(handler);
|
|
458
|
-
return () => {
|
|
459
|
-
this.stateHandlers.delete(handler);
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
emitState(state) {
|
|
463
|
-
this.stateHandlers.forEach((handler) => {
|
|
464
|
-
try {
|
|
465
|
-
handler(state);
|
|
466
|
-
} catch (error) {
|
|
467
|
-
console.error(
|
|
468
|
-
`Error in connection state handler: ${error instanceof Error ? error.message : String(error)}`
|
|
469
|
-
);
|
|
470
|
-
}
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
|
-
// Get current connection state
|
|
474
|
-
get state() {
|
|
475
|
-
if (!this.ws) return "closed";
|
|
476
|
-
switch (this.ws.readyState) {
|
|
477
|
-
case WebSocket.CONNECTING:
|
|
478
|
-
return "connecting";
|
|
479
|
-
case WebSocket.OPEN:
|
|
480
|
-
return "open";
|
|
481
|
-
case WebSocket.CLOSING:
|
|
482
|
-
case WebSocket.CLOSED:
|
|
483
|
-
return "closed";
|
|
484
|
-
default:
|
|
485
|
-
return "closed";
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
// Close the connection
|
|
489
|
-
close() {
|
|
490
|
-
this.isIntentionallyClosed = true;
|
|
491
|
-
this.reconnectEnabled = false;
|
|
492
|
-
if (this.ws) {
|
|
493
|
-
this.ws.close();
|
|
494
|
-
this.ws = null;
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
// Enable/disable automatic reconnection
|
|
498
|
-
setReconnectEnabled(enabled) {
|
|
499
|
-
this.reconnectEnabled = enabled;
|
|
500
|
-
}
|
|
501
|
-
};
|
|
502
|
-
|
|
503
|
-
// src/schemas.ts
|
|
504
|
-
function isValidUrl(url) {
|
|
505
|
-
try {
|
|
506
|
-
new URL(url);
|
|
507
|
-
return /^https?:\/\//i.test(url);
|
|
508
|
-
} catch (e) {
|
|
509
|
-
return false;
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
// src/client.ts
|
|
514
|
-
var DextoClient = class {
|
|
515
|
-
constructor(config, options = {}) {
|
|
516
|
-
__publicField(this, "http");
|
|
517
|
-
__publicField(this, "ws", null);
|
|
518
|
-
__publicField(this, "config");
|
|
519
|
-
__publicField(this, "options");
|
|
520
|
-
var _a, _b, _c, _d, _e, _f;
|
|
521
|
-
if (!isValidUrl(config.baseUrl)) {
|
|
522
|
-
throw ClientError.invalidConfig(
|
|
523
|
-
"baseUrl",
|
|
524
|
-
config.baseUrl,
|
|
525
|
-
"Must be a valid HTTP/HTTPS URL"
|
|
526
|
-
);
|
|
105
|
+
buffer += decoder.decode(value, { stream: true }).replace(/\r\n/g, "\n");
|
|
527
106
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
timeout: (_a = config.timeout) != null ? _a : 3e4,
|
|
532
|
-
retries: (_b = config.retries) != null ? _b : 3
|
|
533
|
-
};
|
|
534
|
-
this.options = {
|
|
535
|
-
enableWebSocket: (_c = options.enableWebSocket) != null ? _c : true,
|
|
536
|
-
reconnect: (_d = options.reconnect) != null ? _d : true,
|
|
537
|
-
reconnectInterval: (_e = options.reconnectInterval) != null ? _e : 5e3,
|
|
538
|
-
debug: (_f = options.debug) != null ? _f : false
|
|
539
|
-
};
|
|
540
|
-
this.http = new HttpClient(this.config);
|
|
541
|
-
if (this.options.enableWebSocket) {
|
|
542
|
-
this.initializeWebSocket();
|
|
107
|
+
} finally {
|
|
108
|
+
if (signal) {
|
|
109
|
+
signal.removeEventListener("abort", abortHandler);
|
|
543
110
|
}
|
|
544
|
-
|
|
545
|
-
initializeWebSocket() {
|
|
546
|
-
var _a, _b;
|
|
547
|
-
const wsUrl = this.config.baseUrl.replace(/^https/, "wss").replace(/^http/, "ws");
|
|
548
|
-
this.ws = new WebSocketClient(wsUrl, {
|
|
549
|
-
reconnect: (_a = this.options.reconnect) != null ? _a : true,
|
|
550
|
-
reconnectInterval: (_b = this.options.reconnectInterval) != null ? _b : 5e3
|
|
111
|
+
reader.cancel().catch(() => {
|
|
551
112
|
});
|
|
552
113
|
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
this.config.baseUrl,
|
|
563
|
-
error instanceof Error ? error : void 0
|
|
564
|
-
);
|
|
565
|
-
}
|
|
566
|
-
if (this.options.enableWebSocket && this.ws) {
|
|
114
|
+
}
|
|
115
|
+
async function* createStream(responsePromise, options) {
|
|
116
|
+
const response = await responsePromise;
|
|
117
|
+
yield* stream(response, options);
|
|
118
|
+
}
|
|
119
|
+
async function* createMessageStream(responsePromise, options) {
|
|
120
|
+
const sseStream = createStream(responsePromise, options);
|
|
121
|
+
for await (const event of sseStream) {
|
|
122
|
+
if (event.data) {
|
|
567
123
|
try {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
console.warn(
|
|
572
|
-
`WebSocket connection failed, continuing with HTTP-only mode: ${error instanceof Error ? error.message : String(error)}`
|
|
573
|
-
);
|
|
124
|
+
const parsed = JSON.parse(event.data);
|
|
125
|
+
if (event.event && !parsed.type) {
|
|
126
|
+
parsed.type = event.event;
|
|
574
127
|
}
|
|
575
|
-
|
|
128
|
+
yield parsed;
|
|
129
|
+
} catch (e) {
|
|
576
130
|
}
|
|
577
131
|
}
|
|
578
132
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
}
|
|
609
|
-
/**
|
|
610
|
-
* Send a message via WebSocket for streaming responses
|
|
611
|
-
*/
|
|
612
|
-
sendMessageStream(input) {
|
|
613
|
-
if (!this.ws || this.ws.state !== "open") {
|
|
614
|
-
throw ClientError.connectionFailed(
|
|
615
|
-
"WebSocket endpoint",
|
|
616
|
-
new Error("WebSocket connection not available for streaming")
|
|
617
|
-
);
|
|
618
|
-
}
|
|
619
|
-
return this.ws.send({
|
|
620
|
-
type: "message",
|
|
621
|
-
content: input.content,
|
|
622
|
-
...input.sessionId && { sessionId: input.sessionId },
|
|
623
|
-
...input.imageData && { imageData: input.imageData },
|
|
624
|
-
...input.fileData && { fileData: input.fileData },
|
|
625
|
-
stream: true
|
|
626
|
-
});
|
|
627
|
-
}
|
|
628
|
-
// ============= SESSION MANAGEMENT =============
|
|
629
|
-
/**
|
|
630
|
-
* List all sessions
|
|
631
|
-
*/
|
|
632
|
-
async listSessions() {
|
|
633
|
-
const response = await this.http.get("/api/sessions");
|
|
634
|
-
return response.sessions;
|
|
635
|
-
}
|
|
636
|
-
/**
|
|
637
|
-
* Create a new session
|
|
638
|
-
*/
|
|
639
|
-
async createSession(sessionId) {
|
|
640
|
-
const response = await this.http.post("/api/sessions", {
|
|
641
|
-
...sessionId && { sessionId }
|
|
642
|
-
});
|
|
643
|
-
return response.session;
|
|
644
|
-
}
|
|
645
|
-
/**
|
|
646
|
-
* Get session details
|
|
647
|
-
*/
|
|
648
|
-
async getSession(sessionId) {
|
|
649
|
-
const response = await this.http.get(
|
|
650
|
-
`/api/sessions/${encodeURIComponent(sessionId)}`
|
|
651
|
-
);
|
|
652
|
-
return response.session;
|
|
653
|
-
}
|
|
654
|
-
/**
|
|
655
|
-
* Get session conversation history
|
|
656
|
-
*/
|
|
657
|
-
async getSessionHistory(sessionId) {
|
|
658
|
-
const response = await this.http.get(
|
|
659
|
-
`/api/sessions/${encodeURIComponent(sessionId)}/history`
|
|
660
|
-
);
|
|
661
|
-
return response.history;
|
|
662
|
-
}
|
|
663
|
-
/**
|
|
664
|
-
* Delete a session permanently
|
|
665
|
-
*/
|
|
666
|
-
async deleteSession(sessionId) {
|
|
667
|
-
await this.http.delete(`/api/sessions/${encodeURIComponent(sessionId)}`);
|
|
668
|
-
}
|
|
669
|
-
/**
|
|
670
|
-
* Load a session as the current working session
|
|
671
|
-
*/
|
|
672
|
-
async loadSession(sessionId) {
|
|
673
|
-
const id = sessionId === null ? "null" : sessionId;
|
|
674
|
-
await this.http.post(`/api/sessions/${encodeURIComponent(id)}/load`);
|
|
675
|
-
}
|
|
676
|
-
/**
|
|
677
|
-
* Get the current working session
|
|
678
|
-
*/
|
|
679
|
-
async getCurrentSession() {
|
|
680
|
-
const response = await this.http.get("/api/sessions/current");
|
|
681
|
-
return response.currentSessionId;
|
|
682
|
-
}
|
|
683
|
-
/**
|
|
684
|
-
* Reset conversation (clear history while keeping session alive)
|
|
685
|
-
*/
|
|
686
|
-
async resetConversation(sessionId) {
|
|
687
|
-
await this.http.post("/api/reset", {
|
|
688
|
-
...sessionId && { sessionId }
|
|
689
|
-
});
|
|
690
|
-
}
|
|
691
|
-
// ============= LLM MANAGEMENT =============
|
|
692
|
-
/**
|
|
693
|
-
* Get current LLM configuration
|
|
694
|
-
*/
|
|
695
|
-
async getCurrentLLMConfig(sessionId) {
|
|
696
|
-
const params = sessionId ? `?sessionId=${encodeURIComponent(sessionId)}` : "";
|
|
697
|
-
const response = await this.http.get(`/api/llm/current${params}`);
|
|
698
|
-
return response.config;
|
|
699
|
-
}
|
|
700
|
-
/**
|
|
701
|
-
* Switch LLM configuration
|
|
702
|
-
*/
|
|
703
|
-
async switchLLM(config, sessionId) {
|
|
704
|
-
const requestBody = {
|
|
705
|
-
...config,
|
|
706
|
-
...sessionId && { sessionId }
|
|
133
|
+
}
|
|
134
|
+
function parseSSE(raw) {
|
|
135
|
+
const lines = raw.split("\n").map((line) => line.replace(/\r$/, ""));
|
|
136
|
+
const event = {};
|
|
137
|
+
let hasData = false;
|
|
138
|
+
for (const line of lines) {
|
|
139
|
+
if (line.startsWith(":")) continue;
|
|
140
|
+
if (line.startsWith("data: ")) {
|
|
141
|
+
const data = line.slice(6);
|
|
142
|
+
event.data = event.data ? event.data + "\n" + data : data;
|
|
143
|
+
hasData = true;
|
|
144
|
+
} else if (line.startsWith("event: ")) {
|
|
145
|
+
event.event = line.slice(7);
|
|
146
|
+
} else if (line.startsWith("id: ")) {
|
|
147
|
+
event.id = line.slice(4);
|
|
148
|
+
} else if (line.startsWith("retry: ")) {
|
|
149
|
+
event.retry = parseInt(line.slice(7), 10);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (!hasData && !event.event && !event.id) return null;
|
|
153
|
+
return event;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/client.ts
|
|
157
|
+
function createDextoClient(config) {
|
|
158
|
+
const options = {};
|
|
159
|
+
if (config.apiKey) {
|
|
160
|
+
options.headers = {
|
|
161
|
+
Authorization: `Bearer ${config.apiKey}`
|
|
707
162
|
};
|
|
708
|
-
const response = await this.http.post(
|
|
709
|
-
"/api/llm/switch",
|
|
710
|
-
requestBody
|
|
711
|
-
);
|
|
712
|
-
return response.config;
|
|
713
|
-
}
|
|
714
|
-
/**
|
|
715
|
-
* Get available LLM providers and models
|
|
716
|
-
*/
|
|
717
|
-
async getLLMProviders() {
|
|
718
|
-
const response = await this.http.get(
|
|
719
|
-
"/api/llm/providers"
|
|
720
|
-
);
|
|
721
|
-
return response.providers;
|
|
722
|
-
}
|
|
723
|
-
/**
|
|
724
|
-
* Get LLM catalog with filtering options
|
|
725
|
-
*/
|
|
726
|
-
async getLLMCatalog(options = {}) {
|
|
727
|
-
const params = new globalThis.URLSearchParams();
|
|
728
|
-
if (options.provider) params.set("provider", options.provider);
|
|
729
|
-
if (options.hasKey !== void 0) params.set("hasKey", options.hasKey.toString());
|
|
730
|
-
if (options.router) params.set("router", options.router);
|
|
731
|
-
if (options.fileType) params.set("fileType", options.fileType);
|
|
732
|
-
if (options.defaultOnly) params.set("defaultOnly", "true");
|
|
733
|
-
if (options.mode) params.set("mode", options.mode);
|
|
734
|
-
const queryString = params.toString();
|
|
735
|
-
const endpoint = queryString ? `/api/llm/catalog?${queryString}` : "/api/llm/catalog";
|
|
736
|
-
return this.http.get(endpoint);
|
|
737
163
|
}
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
* List connected MCP servers
|
|
741
|
-
*/
|
|
742
|
-
async listMCPServers() {
|
|
743
|
-
const response = await this.http.get("/api/mcp/servers");
|
|
744
|
-
return response.servers;
|
|
745
|
-
}
|
|
746
|
-
/**
|
|
747
|
-
* Connect to a new MCP server
|
|
748
|
-
*/
|
|
749
|
-
async connectMCPServer(name, config) {
|
|
750
|
-
await this.http.post("/api/mcp/servers", { name, config });
|
|
751
|
-
}
|
|
752
|
-
/**
|
|
753
|
-
* Disconnect from an MCP server
|
|
754
|
-
*/
|
|
755
|
-
async disconnectMCPServer(serverId) {
|
|
756
|
-
await this.http.delete(`/api/mcp/servers/${encodeURIComponent(serverId)}`);
|
|
757
|
-
}
|
|
758
|
-
/**
|
|
759
|
-
* Get tools from a specific MCP server
|
|
760
|
-
*/
|
|
761
|
-
async getMCPServerTools(serverId) {
|
|
762
|
-
const response = await this.http.get(
|
|
763
|
-
`/api/mcp/servers/${encodeURIComponent(serverId)}/tools`
|
|
764
|
-
);
|
|
765
|
-
return response.tools;
|
|
766
|
-
}
|
|
767
|
-
/**
|
|
768
|
-
* Execute a tool from an MCP server
|
|
769
|
-
*/
|
|
770
|
-
async executeMCPTool(serverId, toolName, args) {
|
|
771
|
-
const response = await this.http.post(
|
|
772
|
-
`/api/mcp/servers/${encodeURIComponent(serverId)}/tools/${encodeURIComponent(toolName)}/execute`,
|
|
773
|
-
args
|
|
774
|
-
);
|
|
775
|
-
return response.data;
|
|
776
|
-
}
|
|
777
|
-
// ============= SEARCH =============
|
|
778
|
-
/**
|
|
779
|
-
* Search messages across sessions
|
|
780
|
-
*/
|
|
781
|
-
async searchMessages(query, options = {}) {
|
|
782
|
-
const params = new globalThis.URLSearchParams();
|
|
783
|
-
params.append("q", query);
|
|
784
|
-
if (options.limit !== void 0) params.append("limit", String(options.limit));
|
|
785
|
-
if (options.offset !== void 0) params.append("offset", String(options.offset));
|
|
786
|
-
if (options.sessionId) params.append("sessionId", options.sessionId);
|
|
787
|
-
if (options.role) params.append("role", options.role);
|
|
788
|
-
return this.http.get(`/api/search/messages?${params}`);
|
|
789
|
-
}
|
|
790
|
-
/**
|
|
791
|
-
* Search sessions that contain the query
|
|
792
|
-
*/
|
|
793
|
-
async searchSessions(query) {
|
|
794
|
-
const params = new globalThis.URLSearchParams({ q: query });
|
|
795
|
-
return this.http.get(`/api/search/sessions?${params}`);
|
|
796
|
-
}
|
|
797
|
-
// ============= EVENT HANDLING =============
|
|
798
|
-
/**
|
|
799
|
-
* Subscribe to real-time events
|
|
800
|
-
*/
|
|
801
|
-
on(eventType, handler) {
|
|
802
|
-
if (!this.ws) {
|
|
803
|
-
if (this.options.debug) {
|
|
804
|
-
console.warn("WebSocket not available, events will not be received");
|
|
805
|
-
}
|
|
806
|
-
return () => {
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
|
-
return this.ws.on(eventType, handler);
|
|
810
|
-
}
|
|
811
|
-
/**
|
|
812
|
-
* Subscribe to connection state changes
|
|
813
|
-
*/
|
|
814
|
-
onConnectionState(handler) {
|
|
815
|
-
if (!this.ws) {
|
|
816
|
-
return () => {
|
|
817
|
-
};
|
|
818
|
-
}
|
|
819
|
-
return this.ws.onConnectionState(handler);
|
|
820
|
-
}
|
|
821
|
-
// ============= GREETING =============
|
|
822
|
-
/**
|
|
823
|
-
* Get agent greeting message
|
|
824
|
-
*/
|
|
825
|
-
async getGreeting(sessionId) {
|
|
826
|
-
const params = sessionId ? `?sessionId=${encodeURIComponent(sessionId)}` : "";
|
|
827
|
-
const response = await this.http.get(`/api/greeting${params}`);
|
|
828
|
-
return response.greeting;
|
|
829
|
-
}
|
|
830
|
-
// ============= UTILITY METHODS =============
|
|
831
|
-
/**
|
|
832
|
-
* Get connection status
|
|
833
|
-
*/
|
|
834
|
-
get connectionState() {
|
|
835
|
-
return this.ws ? this.ws.state : "closed";
|
|
836
|
-
}
|
|
837
|
-
/**
|
|
838
|
-
* Check if client is connected
|
|
839
|
-
*/
|
|
840
|
-
get isConnected() {
|
|
841
|
-
return this.connectionState === "open";
|
|
842
|
-
}
|
|
843
|
-
/**
|
|
844
|
-
* Get client configuration
|
|
845
|
-
*/
|
|
846
|
-
get clientConfig() {
|
|
847
|
-
return { ...this.config };
|
|
848
|
-
}
|
|
849
|
-
};
|
|
164
|
+
return (0, import_client.hc)(config.baseUrl, options);
|
|
165
|
+
}
|