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