@olbrain/js-sdk 1.0.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/README.md +488 -0
- package/dist/chunk-QEZYOGUI.mjs +29 -0
- package/dist/chunk-QEZYOGUI.mjs.map +1 -0
- package/dist/eventsource-TFSNWQMS.mjs +382 -0
- package/dist/eventsource-TFSNWQMS.mjs.map +1 -0
- package/dist/index.d.mts +250 -0
- package/dist/index.d.ts +250 -0
- package/dist/index.global.js +979 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +1001 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +573 -0
- package/dist/index.mjs.map +1 -0
- package/dist/widget.d.ts +345 -0
- package/dist/widget.widget.global.js +1747 -0
- package/dist/widget.widget.global.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,1747 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var OlbrainWidget = (() => {
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
10
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
11
|
+
}) : x)(function(x) {
|
|
12
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
13
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
14
|
+
});
|
|
15
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
16
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
17
|
+
};
|
|
18
|
+
var __export = (target, all) => {
|
|
19
|
+
for (var name in all)
|
|
20
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
21
|
+
};
|
|
22
|
+
var __copyProps = (to, from, except, desc) => {
|
|
23
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
24
|
+
for (let key of __getOwnPropNames(from))
|
|
25
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
26
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
27
|
+
}
|
|
28
|
+
return to;
|
|
29
|
+
};
|
|
30
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
31
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
32
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
33
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
34
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
35
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
36
|
+
mod
|
|
37
|
+
));
|
|
38
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
39
|
+
|
|
40
|
+
// node_modules/eventsource/lib/eventsource.js
|
|
41
|
+
var require_eventsource = __commonJS({
|
|
42
|
+
"node_modules/eventsource/lib/eventsource.js"(exports, module) {
|
|
43
|
+
"use strict";
|
|
44
|
+
var parse = __require("url").parse;
|
|
45
|
+
var events = __require("events");
|
|
46
|
+
var https = __require("https");
|
|
47
|
+
var http = __require("http");
|
|
48
|
+
var util = __require("util");
|
|
49
|
+
var httpsOptions = [
|
|
50
|
+
"pfx",
|
|
51
|
+
"key",
|
|
52
|
+
"passphrase",
|
|
53
|
+
"cert",
|
|
54
|
+
"ca",
|
|
55
|
+
"ciphers",
|
|
56
|
+
"rejectUnauthorized",
|
|
57
|
+
"secureProtocol",
|
|
58
|
+
"servername",
|
|
59
|
+
"checkServerIdentity"
|
|
60
|
+
];
|
|
61
|
+
var bom = [239, 187, 191];
|
|
62
|
+
var colon = 58;
|
|
63
|
+
var space = 32;
|
|
64
|
+
var lineFeed = 10;
|
|
65
|
+
var carriageReturn = 13;
|
|
66
|
+
var maxBufferAheadAllocation = 1024 * 256;
|
|
67
|
+
var reUnsafeHeader = /^(cookie|authorization)$/i;
|
|
68
|
+
function hasBom(buf) {
|
|
69
|
+
return bom.every(function(charCode, index) {
|
|
70
|
+
return buf[index] === charCode;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function EventSource2(url, eventSourceInitDict) {
|
|
74
|
+
var readyState = EventSource2.CONNECTING;
|
|
75
|
+
var headers = eventSourceInitDict && eventSourceInitDict.headers;
|
|
76
|
+
var hasNewOrigin = false;
|
|
77
|
+
Object.defineProperty(this, "readyState", {
|
|
78
|
+
get: function() {
|
|
79
|
+
return readyState;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
Object.defineProperty(this, "url", {
|
|
83
|
+
get: function() {
|
|
84
|
+
return url;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
var self = this;
|
|
88
|
+
self.reconnectInterval = 1e3;
|
|
89
|
+
self.connectionInProgress = false;
|
|
90
|
+
function onConnectionClosed(message) {
|
|
91
|
+
if (readyState === EventSource2.CLOSED) return;
|
|
92
|
+
readyState = EventSource2.CONNECTING;
|
|
93
|
+
_emit("error", new Event("error", { message }));
|
|
94
|
+
if (reconnectUrl) {
|
|
95
|
+
url = reconnectUrl;
|
|
96
|
+
reconnectUrl = null;
|
|
97
|
+
hasNewOrigin = false;
|
|
98
|
+
}
|
|
99
|
+
setTimeout(function() {
|
|
100
|
+
if (readyState !== EventSource2.CONNECTING || self.connectionInProgress) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
self.connectionInProgress = true;
|
|
104
|
+
connect();
|
|
105
|
+
}, self.reconnectInterval);
|
|
106
|
+
}
|
|
107
|
+
var req;
|
|
108
|
+
var lastEventId = "";
|
|
109
|
+
if (headers && headers["Last-Event-ID"]) {
|
|
110
|
+
lastEventId = headers["Last-Event-ID"];
|
|
111
|
+
delete headers["Last-Event-ID"];
|
|
112
|
+
}
|
|
113
|
+
var discardTrailingNewline = false;
|
|
114
|
+
var data = "";
|
|
115
|
+
var eventName = "";
|
|
116
|
+
var reconnectUrl = null;
|
|
117
|
+
function connect() {
|
|
118
|
+
var options = parse(url);
|
|
119
|
+
var isSecure = options.protocol === "https:";
|
|
120
|
+
options.headers = { "Cache-Control": "no-cache", "Accept": "text/event-stream" };
|
|
121
|
+
if (lastEventId) options.headers["Last-Event-ID"] = lastEventId;
|
|
122
|
+
if (headers) {
|
|
123
|
+
var reqHeaders = hasNewOrigin ? removeUnsafeHeaders(headers) : headers;
|
|
124
|
+
for (var i in reqHeaders) {
|
|
125
|
+
var header = reqHeaders[i];
|
|
126
|
+
if (header) {
|
|
127
|
+
options.headers[i] = header;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
options.rejectUnauthorized = !(eventSourceInitDict && !eventSourceInitDict.rejectUnauthorized);
|
|
132
|
+
if (eventSourceInitDict && eventSourceInitDict.createConnection !== void 0) {
|
|
133
|
+
options.createConnection = eventSourceInitDict.createConnection;
|
|
134
|
+
}
|
|
135
|
+
var useProxy = eventSourceInitDict && eventSourceInitDict.proxy;
|
|
136
|
+
if (useProxy) {
|
|
137
|
+
var proxy = parse(eventSourceInitDict.proxy);
|
|
138
|
+
isSecure = proxy.protocol === "https:";
|
|
139
|
+
options.protocol = isSecure ? "https:" : "http:";
|
|
140
|
+
options.path = url;
|
|
141
|
+
options.headers.Host = options.host;
|
|
142
|
+
options.hostname = proxy.hostname;
|
|
143
|
+
options.host = proxy.host;
|
|
144
|
+
options.port = proxy.port;
|
|
145
|
+
}
|
|
146
|
+
if (eventSourceInitDict && eventSourceInitDict.https) {
|
|
147
|
+
for (var optName in eventSourceInitDict.https) {
|
|
148
|
+
if (httpsOptions.indexOf(optName) === -1) {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
var option = eventSourceInitDict.https[optName];
|
|
152
|
+
if (option !== void 0) {
|
|
153
|
+
options[optName] = option;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (eventSourceInitDict && eventSourceInitDict.withCredentials !== void 0) {
|
|
158
|
+
options.withCredentials = eventSourceInitDict.withCredentials;
|
|
159
|
+
}
|
|
160
|
+
req = (isSecure ? https : http).request(options, function(res) {
|
|
161
|
+
self.connectionInProgress = false;
|
|
162
|
+
if (res.statusCode === 500 || res.statusCode === 502 || res.statusCode === 503 || res.statusCode === 504) {
|
|
163
|
+
_emit("error", new Event("error", { status: res.statusCode, message: res.statusMessage }));
|
|
164
|
+
onConnectionClosed();
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (res.statusCode === 301 || res.statusCode === 302 || res.statusCode === 307) {
|
|
168
|
+
var location = res.headers.location;
|
|
169
|
+
if (!location) {
|
|
170
|
+
_emit("error", new Event("error", { status: res.statusCode, message: res.statusMessage }));
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
var prevOrigin = new URL(url).origin;
|
|
174
|
+
var nextOrigin = new URL(location).origin;
|
|
175
|
+
hasNewOrigin = prevOrigin !== nextOrigin;
|
|
176
|
+
if (res.statusCode === 307) reconnectUrl = url;
|
|
177
|
+
url = location;
|
|
178
|
+
process.nextTick(connect);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (res.statusCode !== 200) {
|
|
182
|
+
_emit("error", new Event("error", { status: res.statusCode, message: res.statusMessage }));
|
|
183
|
+
return self.close();
|
|
184
|
+
}
|
|
185
|
+
readyState = EventSource2.OPEN;
|
|
186
|
+
res.on("close", function() {
|
|
187
|
+
res.removeAllListeners("close");
|
|
188
|
+
res.removeAllListeners("end");
|
|
189
|
+
onConnectionClosed();
|
|
190
|
+
});
|
|
191
|
+
res.on("end", function() {
|
|
192
|
+
res.removeAllListeners("close");
|
|
193
|
+
res.removeAllListeners("end");
|
|
194
|
+
onConnectionClosed();
|
|
195
|
+
});
|
|
196
|
+
_emit("open", new Event("open"));
|
|
197
|
+
var buf;
|
|
198
|
+
var newBuffer;
|
|
199
|
+
var startingPos = 0;
|
|
200
|
+
var startingFieldLength = -1;
|
|
201
|
+
var newBufferSize = 0;
|
|
202
|
+
var bytesUsed = 0;
|
|
203
|
+
res.on("data", function(chunk) {
|
|
204
|
+
if (!buf) {
|
|
205
|
+
buf = chunk;
|
|
206
|
+
if (hasBom(buf)) {
|
|
207
|
+
buf = buf.slice(bom.length);
|
|
208
|
+
}
|
|
209
|
+
bytesUsed = buf.length;
|
|
210
|
+
} else {
|
|
211
|
+
if (chunk.length > buf.length - bytesUsed) {
|
|
212
|
+
newBufferSize = buf.length * 2 + chunk.length;
|
|
213
|
+
if (newBufferSize > maxBufferAheadAllocation) {
|
|
214
|
+
newBufferSize = buf.length + chunk.length + maxBufferAheadAllocation;
|
|
215
|
+
}
|
|
216
|
+
newBuffer = Buffer.alloc(newBufferSize);
|
|
217
|
+
buf.copy(newBuffer, 0, 0, bytesUsed);
|
|
218
|
+
buf = newBuffer;
|
|
219
|
+
}
|
|
220
|
+
chunk.copy(buf, bytesUsed);
|
|
221
|
+
bytesUsed += chunk.length;
|
|
222
|
+
}
|
|
223
|
+
var pos = 0;
|
|
224
|
+
var length = bytesUsed;
|
|
225
|
+
while (pos < length) {
|
|
226
|
+
if (discardTrailingNewline) {
|
|
227
|
+
if (buf[pos] === lineFeed) {
|
|
228
|
+
++pos;
|
|
229
|
+
}
|
|
230
|
+
discardTrailingNewline = false;
|
|
231
|
+
}
|
|
232
|
+
var lineLength = -1;
|
|
233
|
+
var fieldLength = startingFieldLength;
|
|
234
|
+
var c;
|
|
235
|
+
for (var i2 = startingPos; lineLength < 0 && i2 < length; ++i2) {
|
|
236
|
+
c = buf[i2];
|
|
237
|
+
if (c === colon) {
|
|
238
|
+
if (fieldLength < 0) {
|
|
239
|
+
fieldLength = i2 - pos;
|
|
240
|
+
}
|
|
241
|
+
} else if (c === carriageReturn) {
|
|
242
|
+
discardTrailingNewline = true;
|
|
243
|
+
lineLength = i2 - pos;
|
|
244
|
+
} else if (c === lineFeed) {
|
|
245
|
+
lineLength = i2 - pos;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (lineLength < 0) {
|
|
249
|
+
startingPos = length - pos;
|
|
250
|
+
startingFieldLength = fieldLength;
|
|
251
|
+
break;
|
|
252
|
+
} else {
|
|
253
|
+
startingPos = 0;
|
|
254
|
+
startingFieldLength = -1;
|
|
255
|
+
}
|
|
256
|
+
parseEventStreamLine(buf, pos, fieldLength, lineLength);
|
|
257
|
+
pos += lineLength + 1;
|
|
258
|
+
}
|
|
259
|
+
if (pos === length) {
|
|
260
|
+
buf = void 0;
|
|
261
|
+
bytesUsed = 0;
|
|
262
|
+
} else if (pos > 0) {
|
|
263
|
+
buf = buf.slice(pos, bytesUsed);
|
|
264
|
+
bytesUsed = buf.length;
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
req.on("error", function(err) {
|
|
269
|
+
self.connectionInProgress = false;
|
|
270
|
+
onConnectionClosed(err.message);
|
|
271
|
+
});
|
|
272
|
+
if (req.setNoDelay) req.setNoDelay(true);
|
|
273
|
+
req.end();
|
|
274
|
+
}
|
|
275
|
+
connect();
|
|
276
|
+
function _emit() {
|
|
277
|
+
if (self.listeners(arguments[0]).length > 0) {
|
|
278
|
+
self.emit.apply(self, arguments);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
this._close = function() {
|
|
282
|
+
if (readyState === EventSource2.CLOSED) return;
|
|
283
|
+
readyState = EventSource2.CLOSED;
|
|
284
|
+
if (req.abort) req.abort();
|
|
285
|
+
if (req.xhr && req.xhr.abort) req.xhr.abort();
|
|
286
|
+
};
|
|
287
|
+
function parseEventStreamLine(buf, pos, fieldLength, lineLength) {
|
|
288
|
+
if (lineLength === 0) {
|
|
289
|
+
if (data.length > 0) {
|
|
290
|
+
var type = eventName || "message";
|
|
291
|
+
_emit(type, new MessageEvent(type, {
|
|
292
|
+
data: data.slice(0, -1),
|
|
293
|
+
// remove trailing newline
|
|
294
|
+
lastEventId,
|
|
295
|
+
origin: new URL(url).origin
|
|
296
|
+
}));
|
|
297
|
+
data = "";
|
|
298
|
+
}
|
|
299
|
+
eventName = void 0;
|
|
300
|
+
} else if (fieldLength > 0) {
|
|
301
|
+
var noValue = fieldLength < 0;
|
|
302
|
+
var step = 0;
|
|
303
|
+
var field = buf.slice(pos, pos + (noValue ? lineLength : fieldLength)).toString();
|
|
304
|
+
if (noValue) {
|
|
305
|
+
step = lineLength;
|
|
306
|
+
} else if (buf[pos + fieldLength + 1] !== space) {
|
|
307
|
+
step = fieldLength + 1;
|
|
308
|
+
} else {
|
|
309
|
+
step = fieldLength + 2;
|
|
310
|
+
}
|
|
311
|
+
pos += step;
|
|
312
|
+
var valueLength = lineLength - step;
|
|
313
|
+
var value = buf.slice(pos, pos + valueLength).toString();
|
|
314
|
+
if (field === "data") {
|
|
315
|
+
data += value + "\n";
|
|
316
|
+
} else if (field === "event") {
|
|
317
|
+
eventName = value;
|
|
318
|
+
} else if (field === "id") {
|
|
319
|
+
lastEventId = value;
|
|
320
|
+
} else if (field === "retry") {
|
|
321
|
+
var retry = parseInt(value, 10);
|
|
322
|
+
if (!Number.isNaN(retry)) {
|
|
323
|
+
self.reconnectInterval = retry;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
module.exports = EventSource2;
|
|
330
|
+
util.inherits(EventSource2, events.EventEmitter);
|
|
331
|
+
EventSource2.prototype.constructor = EventSource2;
|
|
332
|
+
["open", "error", "message"].forEach(function(method) {
|
|
333
|
+
Object.defineProperty(EventSource2.prototype, "on" + method, {
|
|
334
|
+
/**
|
|
335
|
+
* Returns the current listener
|
|
336
|
+
*
|
|
337
|
+
* @return {Mixed} the set function or undefined
|
|
338
|
+
* @api private
|
|
339
|
+
*/
|
|
340
|
+
get: function get() {
|
|
341
|
+
var listener = this.listeners(method)[0];
|
|
342
|
+
return listener ? listener._listener ? listener._listener : listener : void 0;
|
|
343
|
+
},
|
|
344
|
+
/**
|
|
345
|
+
* Start listening for events
|
|
346
|
+
*
|
|
347
|
+
* @param {Function} listener the listener
|
|
348
|
+
* @return {Mixed} the set function or undefined
|
|
349
|
+
* @api private
|
|
350
|
+
*/
|
|
351
|
+
set: function set(listener) {
|
|
352
|
+
this.removeAllListeners(method);
|
|
353
|
+
this.addEventListener(method, listener);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
Object.defineProperty(EventSource2, "CONNECTING", { enumerable: true, value: 0 });
|
|
358
|
+
Object.defineProperty(EventSource2, "OPEN", { enumerable: true, value: 1 });
|
|
359
|
+
Object.defineProperty(EventSource2, "CLOSED", { enumerable: true, value: 2 });
|
|
360
|
+
EventSource2.prototype.CONNECTING = 0;
|
|
361
|
+
EventSource2.prototype.OPEN = 1;
|
|
362
|
+
EventSource2.prototype.CLOSED = 2;
|
|
363
|
+
EventSource2.prototype.close = function() {
|
|
364
|
+
this._close();
|
|
365
|
+
};
|
|
366
|
+
EventSource2.prototype.addEventListener = function addEventListener(type, listener) {
|
|
367
|
+
if (typeof listener === "function") {
|
|
368
|
+
listener._listener = listener;
|
|
369
|
+
this.on(type, listener);
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
EventSource2.prototype.dispatchEvent = function dispatchEvent(event) {
|
|
373
|
+
if (!event.type) {
|
|
374
|
+
throw new Error("UNSPECIFIED_EVENT_TYPE_ERR");
|
|
375
|
+
}
|
|
376
|
+
this.emit(event.type, event.detail);
|
|
377
|
+
};
|
|
378
|
+
EventSource2.prototype.removeEventListener = function removeEventListener(type, listener) {
|
|
379
|
+
if (typeof listener === "function") {
|
|
380
|
+
listener._listener = void 0;
|
|
381
|
+
this.removeListener(type, listener);
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
function Event(type, optionalProperties) {
|
|
385
|
+
Object.defineProperty(this, "type", { writable: false, value: type, enumerable: true });
|
|
386
|
+
if (optionalProperties) {
|
|
387
|
+
for (var f in optionalProperties) {
|
|
388
|
+
if (optionalProperties.hasOwnProperty(f)) {
|
|
389
|
+
Object.defineProperty(this, f, { writable: false, value: optionalProperties[f], enumerable: true });
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
function MessageEvent(type, eventInitDict) {
|
|
395
|
+
Object.defineProperty(this, "type", { writable: false, value: type, enumerable: true });
|
|
396
|
+
for (var f in eventInitDict) {
|
|
397
|
+
if (eventInitDict.hasOwnProperty(f)) {
|
|
398
|
+
Object.defineProperty(this, f, { writable: false, value: eventInitDict[f], enumerable: true });
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
function removeUnsafeHeaders(headers) {
|
|
403
|
+
var safe = {};
|
|
404
|
+
for (var key in headers) {
|
|
405
|
+
if (reUnsafeHeader.test(key)) {
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
safe[key] = headers[key];
|
|
409
|
+
}
|
|
410
|
+
return safe;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
// src/widget.ts
|
|
416
|
+
var widget_exports = {};
|
|
417
|
+
__export(widget_exports, {
|
|
418
|
+
AgentClient: () => AgentClient,
|
|
419
|
+
AuthenticationError: () => AuthenticationError,
|
|
420
|
+
ChatWidget: () => ChatWidget,
|
|
421
|
+
NetworkError: () => NetworkError,
|
|
422
|
+
OlbrainError: () => OlbrainError,
|
|
423
|
+
RateLimitError: () => RateLimitError,
|
|
424
|
+
SessionNotFoundError: () => SessionNotFoundError,
|
|
425
|
+
StreamingError: () => StreamingError,
|
|
426
|
+
VERSION: () => VERSION,
|
|
427
|
+
ValidationError: () => ValidationError
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
// src/core/exceptions.ts
|
|
431
|
+
var OlbrainError = class _OlbrainError extends Error {
|
|
432
|
+
constructor(message) {
|
|
433
|
+
super(message);
|
|
434
|
+
this.name = "OlbrainError";
|
|
435
|
+
Object.setPrototypeOf(this, _OlbrainError.prototype);
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
var AuthenticationError = class _AuthenticationError extends OlbrainError {
|
|
439
|
+
constructor(message = "Authentication failed") {
|
|
440
|
+
super(message);
|
|
441
|
+
this.name = "AuthenticationError";
|
|
442
|
+
Object.setPrototypeOf(this, _AuthenticationError.prototype);
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
var SessionNotFoundError = class _SessionNotFoundError extends OlbrainError {
|
|
446
|
+
constructor(sessionId) {
|
|
447
|
+
super(`Session not found: ${sessionId}`);
|
|
448
|
+
this.name = "SessionNotFoundError";
|
|
449
|
+
this.sessionId = sessionId;
|
|
450
|
+
Object.setPrototypeOf(this, _SessionNotFoundError.prototype);
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
var RateLimitError = class _RateLimitError extends OlbrainError {
|
|
454
|
+
constructor(message = "Rate limit exceeded", retryAfter) {
|
|
455
|
+
super(message);
|
|
456
|
+
this.name = "RateLimitError";
|
|
457
|
+
this.retryAfter = retryAfter;
|
|
458
|
+
Object.setPrototypeOf(this, _RateLimitError.prototype);
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
var NetworkError = class _NetworkError extends OlbrainError {
|
|
462
|
+
constructor(message = "Network error", statusCode) {
|
|
463
|
+
super(message);
|
|
464
|
+
this.name = "NetworkError";
|
|
465
|
+
this.statusCode = statusCode;
|
|
466
|
+
Object.setPrototypeOf(this, _NetworkError.prototype);
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
var ValidationError = class _ValidationError extends OlbrainError {
|
|
470
|
+
constructor(message = "Validation error") {
|
|
471
|
+
super(message);
|
|
472
|
+
this.name = "ValidationError";
|
|
473
|
+
Object.setPrototypeOf(this, _ValidationError.prototype);
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
var StreamingError = class _StreamingError extends OlbrainError {
|
|
477
|
+
constructor(message = "Streaming error") {
|
|
478
|
+
super(message);
|
|
479
|
+
this.name = "StreamingError";
|
|
480
|
+
Object.setPrototypeOf(this, _StreamingError.prototype);
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
// src/core/utils.ts
|
|
485
|
+
function validateApiKey(apiKey) {
|
|
486
|
+
if (!apiKey) {
|
|
487
|
+
throw new ValidationError("API key is required");
|
|
488
|
+
}
|
|
489
|
+
const validPrefixes = ["sk_live_", "org_live_", "sk_", "org_"];
|
|
490
|
+
if (!validPrefixes.some((prefix) => apiKey.startsWith(prefix))) {
|
|
491
|
+
throw new ValidationError(
|
|
492
|
+
"Invalid API key format. Must start with sk_, org_, sk_live_, or org_live_"
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
function validateAgentId(agentId) {
|
|
497
|
+
if (!agentId) {
|
|
498
|
+
throw new ValidationError("Agent ID is required");
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
function formatAuthHeader(apiKey) {
|
|
502
|
+
return `Bearer ${apiKey}`;
|
|
503
|
+
}
|
|
504
|
+
function getExponentialBackoffDelay(attempt, baseDelay = 5e3, maxDelay = 6e4) {
|
|
505
|
+
const delay2 = baseDelay * Math.pow(2, attempt);
|
|
506
|
+
return Math.min(delay2, maxDelay);
|
|
507
|
+
}
|
|
508
|
+
function isBrowser() {
|
|
509
|
+
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
510
|
+
}
|
|
511
|
+
function isNode() {
|
|
512
|
+
return typeof process !== "undefined" && process.versions && process.versions.node;
|
|
513
|
+
}
|
|
514
|
+
async function getEventSourceImpl() {
|
|
515
|
+
if (isBrowser()) {
|
|
516
|
+
return EventSource;
|
|
517
|
+
}
|
|
518
|
+
if (isNode()) {
|
|
519
|
+
try {
|
|
520
|
+
const { EventSource: NodeEventSource } = await Promise.resolve().then(() => __toESM(require_eventsource()));
|
|
521
|
+
return NodeEventSource;
|
|
522
|
+
} catch {
|
|
523
|
+
throw new Error(
|
|
524
|
+
'EventSource not available in Node.js. Install "eventsource" package: npm install eventsource'
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
throw new Error("EventSource not available in this environment");
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// src/core/streaming.ts
|
|
532
|
+
var MessageStream = class {
|
|
533
|
+
constructor(config, onMessage, onError) {
|
|
534
|
+
this.isRunning = false;
|
|
535
|
+
this.reconnectAttempt = 0;
|
|
536
|
+
this.config = config;
|
|
537
|
+
this.onMessage = onMessage;
|
|
538
|
+
this.onError = onError;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Start the streaming connection
|
|
542
|
+
*/
|
|
543
|
+
async start() {
|
|
544
|
+
if (this.isRunning) {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
this.isRunning = true;
|
|
548
|
+
this.reconnectAttempt = 0;
|
|
549
|
+
await this._connect();
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Stop the streaming connection
|
|
553
|
+
*/
|
|
554
|
+
stop() {
|
|
555
|
+
this.isRunning = false;
|
|
556
|
+
this._cleanup();
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Establish SSE connection
|
|
560
|
+
*/
|
|
561
|
+
async _connect() {
|
|
562
|
+
try {
|
|
563
|
+
const EventSourceImpl = await getEventSourceImpl();
|
|
564
|
+
const url = `${this.config.baseUrl}/sessions/${this.config.sessionId}/stream`;
|
|
565
|
+
const headers = {
|
|
566
|
+
"Authorization": formatAuthHeader(this.config.apiKey),
|
|
567
|
+
"X-Agent-ID": this.config.agentId
|
|
568
|
+
};
|
|
569
|
+
this.eventSource = new EventSourceImpl(url, { headers });
|
|
570
|
+
this.eventSource.addEventListener("message", (event) => {
|
|
571
|
+
this._handleMessage(event);
|
|
572
|
+
});
|
|
573
|
+
this.eventSource.addEventListener("ping", () => {
|
|
574
|
+
});
|
|
575
|
+
this.eventSource.addEventListener("error", () => {
|
|
576
|
+
this._handleConnectionError();
|
|
577
|
+
});
|
|
578
|
+
this.reconnectAttempt = 0;
|
|
579
|
+
} catch (error) {
|
|
580
|
+
this._handleConnectionError();
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Handle incoming message event
|
|
585
|
+
*/
|
|
586
|
+
_handleMessage(event) {
|
|
587
|
+
if (!event.data) {
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
try {
|
|
591
|
+
const data = JSON.parse(event.data);
|
|
592
|
+
if (data.type === "ping" || data.type === "keepalive") {
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
if (data.role && data.content) {
|
|
596
|
+
const message = {
|
|
597
|
+
role: data.role,
|
|
598
|
+
content: data.content,
|
|
599
|
+
timestamp: data.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
600
|
+
metadata: data.metadata
|
|
601
|
+
};
|
|
602
|
+
this.onMessage(message);
|
|
603
|
+
}
|
|
604
|
+
} catch (error) {
|
|
605
|
+
if (this.onError) {
|
|
606
|
+
this.onError(
|
|
607
|
+
new StreamingError(`Failed to parse message: ${error.message}`)
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Handle connection errors and attempt reconnection
|
|
614
|
+
*/
|
|
615
|
+
_handleConnectionError() {
|
|
616
|
+
if (!this.isRunning) {
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
this._cleanup();
|
|
620
|
+
const delay2 = getExponentialBackoffDelay(this.reconnectAttempt);
|
|
621
|
+
if (this.onError) {
|
|
622
|
+
this.onError(
|
|
623
|
+
new StreamingError(
|
|
624
|
+
`Connection lost. Reconnecting in ${delay2}ms (attempt ${this.reconnectAttempt + 1})`
|
|
625
|
+
)
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
this.reconnectAttempt++;
|
|
629
|
+
this.reconnectTimeout = setTimeout(async () => {
|
|
630
|
+
if (this.isRunning) {
|
|
631
|
+
await this._connect();
|
|
632
|
+
}
|
|
633
|
+
}, delay2);
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Clean up resources
|
|
637
|
+
*/
|
|
638
|
+
_cleanup() {
|
|
639
|
+
if (this.eventSource) {
|
|
640
|
+
this.eventSource.close();
|
|
641
|
+
this.eventSource = void 0;
|
|
642
|
+
}
|
|
643
|
+
if (this.reconnectTimeout) {
|
|
644
|
+
clearTimeout(this.reconnectTimeout);
|
|
645
|
+
this.reconnectTimeout = void 0;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
var StreamManager = class {
|
|
650
|
+
constructor() {
|
|
651
|
+
this.streams = /* @__PURE__ */ new Map();
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Start streaming for a session
|
|
655
|
+
*/
|
|
656
|
+
async startStream(sessionId, config, onMessage, onError) {
|
|
657
|
+
this.stopStream(sessionId);
|
|
658
|
+
const stream = new MessageStream(config, onMessage, onError);
|
|
659
|
+
this.streams.set(sessionId, stream);
|
|
660
|
+
await stream.start();
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Stop streaming for a session
|
|
664
|
+
*/
|
|
665
|
+
stopStream(sessionId) {
|
|
666
|
+
const stream = this.streams.get(sessionId);
|
|
667
|
+
if (stream) {
|
|
668
|
+
stream.stop();
|
|
669
|
+
this.streams.delete(sessionId);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Stop all streams
|
|
674
|
+
*/
|
|
675
|
+
stopAllStreams() {
|
|
676
|
+
for (const stream of this.streams.values()) {
|
|
677
|
+
stream.stop();
|
|
678
|
+
}
|
|
679
|
+
this.streams.clear();
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Check if a stream is running
|
|
683
|
+
*/
|
|
684
|
+
isStreamRunning(sessionId) {
|
|
685
|
+
return this.streams.has(sessionId);
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
|
|
689
|
+
// src/core/client.ts
|
|
690
|
+
var DEFAULT_BASE_URL = "https://olbrain-agent-cloud-768934887465.us-central1.run.app";
|
|
691
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
692
|
+
var AgentClient = class {
|
|
693
|
+
constructor(config) {
|
|
694
|
+
validateApiKey(config.apiKey);
|
|
695
|
+
validateAgentId(config.agentId);
|
|
696
|
+
this.config = {
|
|
697
|
+
agentId: config.agentId,
|
|
698
|
+
apiKey: config.apiKey,
|
|
699
|
+
baseUrl: config.baseUrl || DEFAULT_BASE_URL
|
|
700
|
+
};
|
|
701
|
+
this.streamManager = new StreamManager();
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Create a new session
|
|
705
|
+
*/
|
|
706
|
+
async createSession(options) {
|
|
707
|
+
const payload = {
|
|
708
|
+
message: options?.title || "Chat Session",
|
|
709
|
+
response_mode: "sync",
|
|
710
|
+
mode: "production"
|
|
711
|
+
};
|
|
712
|
+
if (options?.userId) {
|
|
713
|
+
payload.user_id = options.userId;
|
|
714
|
+
}
|
|
715
|
+
if (options?.metadata) {
|
|
716
|
+
payload.metadata = options.metadata;
|
|
717
|
+
}
|
|
718
|
+
const response = await this._request("POST", "/api/agent/webhook", payload);
|
|
719
|
+
if (!response.session_id) {
|
|
720
|
+
throw new OlbrainError("Failed to create session: no session_id returned");
|
|
721
|
+
}
|
|
722
|
+
return response.session_id;
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* Get session information
|
|
726
|
+
* @note May not be implemented on all backends
|
|
727
|
+
*/
|
|
728
|
+
async getSession(sessionId) {
|
|
729
|
+
try {
|
|
730
|
+
const response = await this._request("POST", "/api/agent/webhook", {
|
|
731
|
+
session_id: sessionId,
|
|
732
|
+
action: "get_session_info"
|
|
733
|
+
});
|
|
734
|
+
return this._parseSessionInfo(response);
|
|
735
|
+
} catch (error) {
|
|
736
|
+
console.warn("get_session_info not available, returning basic info");
|
|
737
|
+
return {
|
|
738
|
+
sessionId,
|
|
739
|
+
title: "Session",
|
|
740
|
+
status: "active",
|
|
741
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
742
|
+
messageCount: 0,
|
|
743
|
+
metadata: {}
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Update session information
|
|
749
|
+
* @note May not be implemented on all backends
|
|
750
|
+
*/
|
|
751
|
+
async updateSession(sessionId, updates) {
|
|
752
|
+
try {
|
|
753
|
+
const payload = {
|
|
754
|
+
session_id: sessionId,
|
|
755
|
+
action: "update_session"
|
|
756
|
+
};
|
|
757
|
+
if (updates.title !== void 0) payload.title = updates.title;
|
|
758
|
+
if (updates.status !== void 0) payload.status = updates.status;
|
|
759
|
+
if (updates.metadata !== void 0) payload.metadata = updates.metadata;
|
|
760
|
+
const response = await this._request("POST", "/api/agent/webhook", payload);
|
|
761
|
+
return this._parseSessionInfo(response);
|
|
762
|
+
} catch (error) {
|
|
763
|
+
console.warn("update_session not available");
|
|
764
|
+
throw error;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Delete a session
|
|
769
|
+
* @note May not be implemented on all backends
|
|
770
|
+
*/
|
|
771
|
+
async deleteSession(sessionId) {
|
|
772
|
+
try {
|
|
773
|
+
await this._request("POST", "/api/agent/webhook", {
|
|
774
|
+
session_id: sessionId,
|
|
775
|
+
action: "delete_session"
|
|
776
|
+
});
|
|
777
|
+
} catch (error) {
|
|
778
|
+
console.warn("delete_session not available");
|
|
779
|
+
throw error;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Get messages from a session
|
|
784
|
+
* @note May not be implemented on all backends
|
|
785
|
+
*/
|
|
786
|
+
async getMessages(sessionId, limit) {
|
|
787
|
+
try {
|
|
788
|
+
const response = await this._request("POST", "/api/agent/webhook", {
|
|
789
|
+
session_id: sessionId,
|
|
790
|
+
action: "get_messages",
|
|
791
|
+
limit: limit || 100
|
|
792
|
+
});
|
|
793
|
+
return (response.messages || []).map((msg) => ({
|
|
794
|
+
role: msg.role,
|
|
795
|
+
content: msg.content,
|
|
796
|
+
timestamp: msg.timestamp,
|
|
797
|
+
metadata: msg.metadata
|
|
798
|
+
}));
|
|
799
|
+
} catch (error) {
|
|
800
|
+
console.warn("get_messages not available");
|
|
801
|
+
return [];
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Get session statistics
|
|
806
|
+
* @note May not be implemented on all backends
|
|
807
|
+
*/
|
|
808
|
+
async getSessionStats(sessionId) {
|
|
809
|
+
try {
|
|
810
|
+
const response = await this._request("POST", "/api/agent/webhook", {
|
|
811
|
+
session_id: sessionId,
|
|
812
|
+
action: "get_stats"
|
|
813
|
+
});
|
|
814
|
+
return {
|
|
815
|
+
sessionId: response.session_id,
|
|
816
|
+
messageCount: response.message_count,
|
|
817
|
+
totalTokens: response.total_tokens,
|
|
818
|
+
createdAt: response.created_at,
|
|
819
|
+
lastMessageAt: response.last_message_at
|
|
820
|
+
};
|
|
821
|
+
} catch (error) {
|
|
822
|
+
console.warn("get_stats not available");
|
|
823
|
+
return {
|
|
824
|
+
sessionId,
|
|
825
|
+
messageCount: 0,
|
|
826
|
+
totalTokens: 0,
|
|
827
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
/**
|
|
832
|
+
* Send a message and wait for response
|
|
833
|
+
*/
|
|
834
|
+
async sendAndWait(sessionId, message, options) {
|
|
835
|
+
if (!message) {
|
|
836
|
+
throw new ValidationError("Message cannot be empty");
|
|
837
|
+
}
|
|
838
|
+
const payload = {
|
|
839
|
+
session_id: sessionId,
|
|
840
|
+
message: message.trim(),
|
|
841
|
+
response_mode: "sync"
|
|
842
|
+
};
|
|
843
|
+
if (options?.metadata) {
|
|
844
|
+
payload.metadata = options.metadata;
|
|
845
|
+
}
|
|
846
|
+
const timeout = options?.timeout || DEFAULT_TIMEOUT_MS;
|
|
847
|
+
const response = await this._request(
|
|
848
|
+
"POST",
|
|
849
|
+
"/api/agent/webhook",
|
|
850
|
+
payload,
|
|
851
|
+
timeout
|
|
852
|
+
);
|
|
853
|
+
return {
|
|
854
|
+
text: response.response || response.text || "",
|
|
855
|
+
sessionId: response.session_id,
|
|
856
|
+
success: response.success !== false,
|
|
857
|
+
tokenUsage: response.token_usage ? {
|
|
858
|
+
promptTokens: response.token_usage.prompt_tokens,
|
|
859
|
+
completionTokens: response.token_usage.completion_tokens,
|
|
860
|
+
totalTokens: response.token_usage.total_tokens,
|
|
861
|
+
cost: response.token_usage.cost
|
|
862
|
+
} : void 0,
|
|
863
|
+
modelUsed: response.model_used,
|
|
864
|
+
responseTimeMs: response.response_time_ms,
|
|
865
|
+
error: response.error
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Send a message (fire and forget)
|
|
870
|
+
*/
|
|
871
|
+
async send(sessionId, message) {
|
|
872
|
+
if (!message) {
|
|
873
|
+
throw new ValidationError("Message cannot be empty");
|
|
874
|
+
}
|
|
875
|
+
await this._request("POST", "/api/agent/webhook", {
|
|
876
|
+
session_id: sessionId,
|
|
877
|
+
message: message.trim(),
|
|
878
|
+
response_mode: "sync"
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Start listening for messages via SSE streaming
|
|
883
|
+
*/
|
|
884
|
+
async listen(sessionId, onMessage, onError) {
|
|
885
|
+
await this.streamManager.startStream(
|
|
886
|
+
sessionId,
|
|
887
|
+
{
|
|
888
|
+
sessionId,
|
|
889
|
+
apiKey: this.config.apiKey,
|
|
890
|
+
agentId: this.config.agentId,
|
|
891
|
+
baseUrl: this.config.baseUrl
|
|
892
|
+
},
|
|
893
|
+
onMessage,
|
|
894
|
+
onError
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Stop listening for messages
|
|
899
|
+
*/
|
|
900
|
+
stopListening(sessionId) {
|
|
901
|
+
this.streamManager.stopStream(sessionId);
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Close client and clean up resources
|
|
905
|
+
*/
|
|
906
|
+
close() {
|
|
907
|
+
this.streamManager.stopAllStreams();
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* Helper method to make HTTP requests
|
|
911
|
+
*/
|
|
912
|
+
async _request(method, path, payload, timeout = DEFAULT_TIMEOUT_MS) {
|
|
913
|
+
const url = `${this.config.baseUrl}${path}`;
|
|
914
|
+
const headers = {
|
|
915
|
+
"Content-Type": "application/json",
|
|
916
|
+
"Authorization": formatAuthHeader(this.config.apiKey),
|
|
917
|
+
"X-Agent-ID": this.config.agentId
|
|
918
|
+
};
|
|
919
|
+
const controller = new AbortController();
|
|
920
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
921
|
+
try {
|
|
922
|
+
const response = await fetch(url, {
|
|
923
|
+
method,
|
|
924
|
+
headers,
|
|
925
|
+
body: payload ? JSON.stringify(payload) : void 0,
|
|
926
|
+
signal: controller.signal
|
|
927
|
+
});
|
|
928
|
+
clearTimeout(timeoutId);
|
|
929
|
+
if (!response.ok) {
|
|
930
|
+
const errorData = await response.json().catch(() => ({}));
|
|
931
|
+
const errorMessage = errorData.error || errorData.message || response.statusText;
|
|
932
|
+
if (response.status === 401 || response.status === 403) {
|
|
933
|
+
throw new AuthenticationError(errorMessage);
|
|
934
|
+
} else if (response.status === 404) {
|
|
935
|
+
throw new SessionNotFoundError(payload?.session_id || "unknown");
|
|
936
|
+
} else if (response.status === 429) {
|
|
937
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
938
|
+
throw new RateLimitError(errorMessage, retryAfter ? parseInt(retryAfter) : void 0);
|
|
939
|
+
} else if (response.status >= 500) {
|
|
940
|
+
throw new NetworkError(errorMessage, response.status);
|
|
941
|
+
} else {
|
|
942
|
+
throw new NetworkError(errorMessage, response.status);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
return await response.json();
|
|
946
|
+
} catch (error) {
|
|
947
|
+
clearTimeout(timeoutId);
|
|
948
|
+
if (error instanceof OlbrainError) {
|
|
949
|
+
throw error;
|
|
950
|
+
}
|
|
951
|
+
if (error instanceof TypeError) {
|
|
952
|
+
if (error.message.includes("aborted")) {
|
|
953
|
+
throw new OlbrainError(`Request timeout after ${timeout}ms`);
|
|
954
|
+
}
|
|
955
|
+
throw new NetworkError(error.message);
|
|
956
|
+
}
|
|
957
|
+
throw error;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* Helper to parse session info from response
|
|
962
|
+
*/
|
|
963
|
+
_parseSessionInfo(data) {
|
|
964
|
+
return {
|
|
965
|
+
sessionId: data.session_id,
|
|
966
|
+
title: data.title,
|
|
967
|
+
status: data.status || "active",
|
|
968
|
+
createdAt: data.created_at,
|
|
969
|
+
messageCount: data.message_count || 0,
|
|
970
|
+
userId: data.user_id,
|
|
971
|
+
metadata: data.metadata || {}
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
};
|
|
975
|
+
|
|
976
|
+
// src/index.ts
|
|
977
|
+
var VERSION = "1.0.0";
|
|
978
|
+
|
|
979
|
+
// src/widget/templates.ts
|
|
980
|
+
function widgetContainerTemplate(config) {
|
|
981
|
+
const theme = config.theme === "auto" ? "light" : config.theme;
|
|
982
|
+
return `
|
|
983
|
+
<div class="olbrain-widget-container" data-theme="${theme}">
|
|
984
|
+
<div class="olbrain-chat-widget">
|
|
985
|
+
<div class="olbrain-chat-header">
|
|
986
|
+
<div class="olbrain-chat-title">${escapeHtml(config.title)}</div>
|
|
987
|
+
<button class="olbrain-close-btn" aria-label="Close chat">
|
|
988
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
|
|
989
|
+
<path d="M15.898 4.045l-5.307 5.307 5.307 5.307c.39.39.39 1.025 0 1.415-.195.195-.45.292-.707.292-.257 0-.512-.097-.707-.292l-5.307-5.307-5.307 5.307c-.195.195-.45.292-.707.292-.257 0-.512-.097-.707-.292-.39-.39-.39-1.025 0-1.415l5.307-5.307-5.307-5.307c-.39-.39-.39-1.025 0-1.415.39-.39 1.025-.39 1.415 0l5.307 5.307 5.307-5.307c.39-.39 1.025-.39 1.415 0 .39.39.39 1.025 0 1.415z"/>
|
|
990
|
+
</svg>
|
|
991
|
+
</button>
|
|
992
|
+
</div>
|
|
993
|
+
<div class="olbrain-messages-container"></div>
|
|
994
|
+
<div class="olbrain-input-area">
|
|
995
|
+
<input
|
|
996
|
+
type="text"
|
|
997
|
+
class="olbrain-message-input"
|
|
998
|
+
placeholder="${escapeHtml(config.placeholder)}"
|
|
999
|
+
aria-label="Message input"
|
|
1000
|
+
/>
|
|
1001
|
+
<button class="olbrain-send-btn" aria-label="Send message">
|
|
1002
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
|
|
1003
|
+
<path d="M16.6915026,12.4744748 L3.50612381,13.2599618 C3.19218622,13.2599618 3.03521743,13.4170592 3.03521743,13.5741566 L1.15159189,17.8274344 C0.8376543,18.6129214 0.99,19.3984083 1.77946707,20 C2.41,20.52 3.50612381,20.3429026 4.13399899,20.0274254 L21.714504,3.40596059 C22.6563168,2.59128963 22.6563168,0.9563 21.714504,0.1416 L4.13399899,-0.4800539 C3.50612381,-0.4800539 2.40880848,0.0729863 1.77946707,0.7844625 C0.994041701,1.5699495 0.837158534,2.35644751 1.15159189,3.1419345 L3.03521743,7.39521227 C3.03521743,7.55230969 3.34915502,7.70940711 3.50612381,7.70940711 L16.6915026,8.49489406 C16.6915026,8.49489406 17.1272231,8.49489406 17.1272231,8.99410899 L17.1272231,11.8231405 C17.1272231,12.3223555 16.6915026,12.4744748 16.6915026,12.4744748 Z"/>
|
|
1004
|
+
</svg>
|
|
1005
|
+
</button>
|
|
1006
|
+
</div>
|
|
1007
|
+
</div>
|
|
1008
|
+
<button class="olbrain-toggle-btn" aria-label="Toggle chat widget">
|
|
1009
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
|
1010
|
+
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z"/>
|
|
1011
|
+
</svg>
|
|
1012
|
+
</button>
|
|
1013
|
+
</div>
|
|
1014
|
+
`;
|
|
1015
|
+
}
|
|
1016
|
+
function userMessageTemplate(content, timestamp) {
|
|
1017
|
+
return `
|
|
1018
|
+
<div class="olbrain-message olbrain-message-user">
|
|
1019
|
+
<div class="olbrain-message-content">${escapeHtml(content)}</div>
|
|
1020
|
+
<div class="olbrain-message-time">${formatTime(timestamp)}</div>
|
|
1021
|
+
</div>
|
|
1022
|
+
`;
|
|
1023
|
+
}
|
|
1024
|
+
function assistantMessageTemplate(content, timestamp) {
|
|
1025
|
+
return `
|
|
1026
|
+
<div class="olbrain-message olbrain-message-assistant">
|
|
1027
|
+
<div class="olbrain-message-content">${escapeHtml(content)}</div>
|
|
1028
|
+
<div class="olbrain-message-time">${formatTime(timestamp)}</div>
|
|
1029
|
+
</div>
|
|
1030
|
+
`;
|
|
1031
|
+
}
|
|
1032
|
+
function typingIndicatorTemplate() {
|
|
1033
|
+
return `
|
|
1034
|
+
<div class="olbrain-message olbrain-message-assistant">
|
|
1035
|
+
<div class="olbrain-typing-indicator">
|
|
1036
|
+
<span></span>
|
|
1037
|
+
<span></span>
|
|
1038
|
+
<span></span>
|
|
1039
|
+
</div>
|
|
1040
|
+
</div>
|
|
1041
|
+
`;
|
|
1042
|
+
}
|
|
1043
|
+
function errorMessageTemplate(error) {
|
|
1044
|
+
return `
|
|
1045
|
+
<div class="olbrain-message olbrain-message-error">
|
|
1046
|
+
<div class="olbrain-message-content">Error: ${escapeHtml(error)}</div>
|
|
1047
|
+
</div>
|
|
1048
|
+
`;
|
|
1049
|
+
}
|
|
1050
|
+
function greetingTemplate(greeting) {
|
|
1051
|
+
return `
|
|
1052
|
+
<div class="olbrain-message olbrain-message-greeting">
|
|
1053
|
+
<div class="olbrain-message-content">${escapeHtml(greeting)}</div>
|
|
1054
|
+
</div>
|
|
1055
|
+
`;
|
|
1056
|
+
}
|
|
1057
|
+
function escapeHtml(text) {
|
|
1058
|
+
const div = document.createElement("div");
|
|
1059
|
+
div.textContent = text;
|
|
1060
|
+
return div.innerHTML;
|
|
1061
|
+
}
|
|
1062
|
+
function formatTime(timestamp) {
|
|
1063
|
+
try {
|
|
1064
|
+
const date = new Date(timestamp);
|
|
1065
|
+
const hours = date.getHours().toString().padStart(2, "0");
|
|
1066
|
+
const minutes = date.getMinutes().toString().padStart(2, "0");
|
|
1067
|
+
return `${hours}:${minutes}`;
|
|
1068
|
+
} catch {
|
|
1069
|
+
return "";
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
// src/widget/styles.ts
|
|
1074
|
+
function generateStyles(config) {
|
|
1075
|
+
const primary = config.primaryColor || "#4f46e5";
|
|
1076
|
+
const isDark = config.theme === "dark";
|
|
1077
|
+
const bgColor = isDark ? "#1f2937" : "#ffffff";
|
|
1078
|
+
const textColor = isDark ? "#f3f4f6" : "#111827";
|
|
1079
|
+
const borderColor = isDark ? "#374151" : "#e5e7eb";
|
|
1080
|
+
const hoverBg = isDark ? "#111827" : "#f9fafb";
|
|
1081
|
+
return `
|
|
1082
|
+
.olbrain-widget-container {
|
|
1083
|
+
--primary-color: ${primary};
|
|
1084
|
+
--bg-color: ${bgColor};
|
|
1085
|
+
--text-color: ${textColor};
|
|
1086
|
+
--border-color: ${borderColor};
|
|
1087
|
+
--hover-bg: ${hoverBg};
|
|
1088
|
+
|
|
1089
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
1090
|
+
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
1091
|
+
sans-serif;
|
|
1092
|
+
-webkit-font-smoothing: antialiased;
|
|
1093
|
+
-moz-osx-font-smoothing: grayscale;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
.olbrain-widget-container * {
|
|
1097
|
+
box-sizing: border-box;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
.olbrain-widget-container[data-theme="dark"] {
|
|
1101
|
+
color-scheme: dark;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
.olbrain-widget-container[data-theme="light"] {
|
|
1105
|
+
color-scheme: light;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
/* Toggle Button */
|
|
1109
|
+
.olbrain-toggle-btn {
|
|
1110
|
+
position: fixed;
|
|
1111
|
+
bottom: 24px;
|
|
1112
|
+
right: 24px;
|
|
1113
|
+
width: 56px;
|
|
1114
|
+
height: 56px;
|
|
1115
|
+
border-radius: 50%;
|
|
1116
|
+
background-color: var(--primary-color);
|
|
1117
|
+
border: none;
|
|
1118
|
+
color: white;
|
|
1119
|
+
cursor: pointer;
|
|
1120
|
+
display: flex;
|
|
1121
|
+
align-items: center;
|
|
1122
|
+
justify-content: center;
|
|
1123
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
1124
|
+
transition: all 0.3s ease;
|
|
1125
|
+
z-index: 999999;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
.olbrain-toggle-btn:hover {
|
|
1129
|
+
transform: scale(1.1);
|
|
1130
|
+
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
.olbrain-toggle-btn:active {
|
|
1134
|
+
transform: scale(0.95);
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
.olbrain-toggle-btn.hidden {
|
|
1138
|
+
display: none;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
/* Chat Widget Container */
|
|
1142
|
+
.olbrain-chat-widget {
|
|
1143
|
+
position: fixed;
|
|
1144
|
+
bottom: 100px;
|
|
1145
|
+
right: 24px;
|
|
1146
|
+
width: 420px;
|
|
1147
|
+
max-width: calc(100vw - 32px);
|
|
1148
|
+
height: 600px;
|
|
1149
|
+
max-height: calc(100vh - 140px);
|
|
1150
|
+
background-color: var(--bg-color);
|
|
1151
|
+
border-radius: 12px;
|
|
1152
|
+
box-shadow: 0 5px 40px rgba(0, 0, 0, 0.16);
|
|
1153
|
+
display: flex;
|
|
1154
|
+
flex-direction: column;
|
|
1155
|
+
z-index: 999998;
|
|
1156
|
+
animation: slideUp 0.3s ease;
|
|
1157
|
+
opacity: 0;
|
|
1158
|
+
pointer-events: none;
|
|
1159
|
+
transform: translateY(20px);
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
.olbrain-chat-widget.open {
|
|
1163
|
+
opacity: 1;
|
|
1164
|
+
pointer-events: auto;
|
|
1165
|
+
transform: translateY(0);
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
@keyframes slideUp {
|
|
1169
|
+
from {
|
|
1170
|
+
opacity: 0;
|
|
1171
|
+
transform: translateY(20px);
|
|
1172
|
+
}
|
|
1173
|
+
to {
|
|
1174
|
+
opacity: 1;
|
|
1175
|
+
transform: translateY(0);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
/* Chat Header */
|
|
1180
|
+
.olbrain-chat-header {
|
|
1181
|
+
display: flex;
|
|
1182
|
+
justify-content: space-between;
|
|
1183
|
+
align-items: center;
|
|
1184
|
+
padding: 16px;
|
|
1185
|
+
border-bottom: 1px solid var(--border-color);
|
|
1186
|
+
border-radius: 12px 12px 0 0;
|
|
1187
|
+
background: linear-gradient(135deg, var(--primary-color), color-mix(in srgb, var(--primary-color) 80%, black));
|
|
1188
|
+
color: white;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
.olbrain-chat-title {
|
|
1192
|
+
font-size: 16px;
|
|
1193
|
+
font-weight: 600;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
.olbrain-close-btn {
|
|
1197
|
+
background: none;
|
|
1198
|
+
border: none;
|
|
1199
|
+
color: white;
|
|
1200
|
+
cursor: pointer;
|
|
1201
|
+
display: flex;
|
|
1202
|
+
align-items: center;
|
|
1203
|
+
justify-content: center;
|
|
1204
|
+
padding: 4px;
|
|
1205
|
+
border-radius: 4px;
|
|
1206
|
+
transition: background-color 0.2s;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
.olbrain-close-btn:hover {
|
|
1210
|
+
background-color: rgba(255, 255, 255, 0.2);
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
/* Messages Container */
|
|
1214
|
+
.olbrain-messages-container {
|
|
1215
|
+
flex: 1;
|
|
1216
|
+
overflow-y: auto;
|
|
1217
|
+
padding: 16px;
|
|
1218
|
+
display: flex;
|
|
1219
|
+
flex-direction: column;
|
|
1220
|
+
gap: 12px;
|
|
1221
|
+
background-color: var(--bg-color);
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
.olbrain-messages-container::-webkit-scrollbar {
|
|
1225
|
+
width: 6px;
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
.olbrain-messages-container::-webkit-scrollbar-track {
|
|
1229
|
+
background: transparent;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
.olbrain-messages-container::-webkit-scrollbar-thumb {
|
|
1233
|
+
background: var(--border-color);
|
|
1234
|
+
border-radius: 3px;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
.olbrain-messages-container::-webkit-scrollbar-thumb:hover {
|
|
1238
|
+
background: color-mix(in srgb, var(--border-color) 80%, black);
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
/* Message */
|
|
1242
|
+
.olbrain-message {
|
|
1243
|
+
display: flex;
|
|
1244
|
+
flex-direction: column;
|
|
1245
|
+
gap: 4px;
|
|
1246
|
+
animation: fadeIn 0.3s ease;
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
@keyframes fadeIn {
|
|
1250
|
+
from {
|
|
1251
|
+
opacity: 0;
|
|
1252
|
+
transform: translateY(10px);
|
|
1253
|
+
}
|
|
1254
|
+
to {
|
|
1255
|
+
opacity: 1;
|
|
1256
|
+
transform: translateY(0);
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
.olbrain-message-content {
|
|
1261
|
+
max-width: 85%;
|
|
1262
|
+
padding: 12px 16px;
|
|
1263
|
+
border-radius: 8px;
|
|
1264
|
+
word-wrap: break-word;
|
|
1265
|
+
line-height: 1.4;
|
|
1266
|
+
font-size: 14px;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
/* User Message */
|
|
1270
|
+
.olbrain-message-user .olbrain-message-content {
|
|
1271
|
+
background-color: var(--primary-color);
|
|
1272
|
+
color: white;
|
|
1273
|
+
align-self: flex-end;
|
|
1274
|
+
border-radius: 18px 18px 4px 18px;
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
/* Assistant Message */
|
|
1278
|
+
.olbrain-message-assistant .olbrain-message-content {
|
|
1279
|
+
background-color: var(--hover-bg);
|
|
1280
|
+
color: var(--text-color);
|
|
1281
|
+
align-self: flex-start;
|
|
1282
|
+
border-radius: 18px 18px 18px 4px;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
/* Greeting Message */
|
|
1286
|
+
.olbrain-message-greeting .olbrain-message-content {
|
|
1287
|
+
background-color: transparent;
|
|
1288
|
+
color: var(--text-color);
|
|
1289
|
+
align-self: center;
|
|
1290
|
+
font-style: italic;
|
|
1291
|
+
opacity: 0.7;
|
|
1292
|
+
padding: 8px 12px;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
/* Error Message */
|
|
1296
|
+
.olbrain-message-error .olbrain-message-content {
|
|
1297
|
+
background-color: #fee;
|
|
1298
|
+
color: #c33;
|
|
1299
|
+
align-self: flex-start;
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
.olbrain-message-time {
|
|
1303
|
+
font-size: 12px;
|
|
1304
|
+
color: var(--text-color);
|
|
1305
|
+
opacity: 0.6;
|
|
1306
|
+
padding: 0 16px;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
/* Typing Indicator */
|
|
1310
|
+
.olbrain-typing-indicator {
|
|
1311
|
+
display: flex;
|
|
1312
|
+
gap: 4px;
|
|
1313
|
+
padding: 12px 16px;
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
.olbrain-typing-indicator span {
|
|
1317
|
+
width: 8px;
|
|
1318
|
+
height: 8px;
|
|
1319
|
+
border-radius: 50%;
|
|
1320
|
+
background-color: var(--text-color);
|
|
1321
|
+
opacity: 0.4;
|
|
1322
|
+
animation: typing 1.4s infinite;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
.olbrain-typing-indicator span:nth-child(2) {
|
|
1326
|
+
animation-delay: 0.2s;
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
.olbrain-typing-indicator span:nth-child(3) {
|
|
1330
|
+
animation-delay: 0.4s;
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
@keyframes typing {
|
|
1334
|
+
0%, 60%, 100% {
|
|
1335
|
+
opacity: 0.4;
|
|
1336
|
+
transform: translateY(0);
|
|
1337
|
+
}
|
|
1338
|
+
30% {
|
|
1339
|
+
opacity: 1;
|
|
1340
|
+
transform: translateY(-10px);
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
/* Input Area */
|
|
1345
|
+
.olbrain-input-area {
|
|
1346
|
+
display: flex;
|
|
1347
|
+
gap: 8px;
|
|
1348
|
+
padding: 16px;
|
|
1349
|
+
border-top: 1px solid var(--border-color);
|
|
1350
|
+
background-color: var(--bg-color);
|
|
1351
|
+
border-radius: 0 0 12px 12px;
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
.olbrain-message-input {
|
|
1355
|
+
flex: 1;
|
|
1356
|
+
border: 1px solid var(--border-color);
|
|
1357
|
+
border-radius: 8px;
|
|
1358
|
+
padding: 10px 14px;
|
|
1359
|
+
font-size: 14px;
|
|
1360
|
+
font-family: inherit;
|
|
1361
|
+
background-color: var(--hover-bg);
|
|
1362
|
+
color: var(--text-color);
|
|
1363
|
+
transition: border-color 0.2s;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
.olbrain-message-input:focus {
|
|
1367
|
+
outline: none;
|
|
1368
|
+
border-color: var(--primary-color);
|
|
1369
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-color) 20%, transparent);
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
.olbrain-message-input::placeholder {
|
|
1373
|
+
color: var(--text-color);
|
|
1374
|
+
opacity: 0.5;
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
.olbrain-send-btn {
|
|
1378
|
+
background-color: var(--primary-color);
|
|
1379
|
+
border: none;
|
|
1380
|
+
color: white;
|
|
1381
|
+
border-radius: 8px;
|
|
1382
|
+
width: 40px;
|
|
1383
|
+
height: 40px;
|
|
1384
|
+
display: flex;
|
|
1385
|
+
align-items: center;
|
|
1386
|
+
justify-content: center;
|
|
1387
|
+
cursor: pointer;
|
|
1388
|
+
transition: all 0.2s;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
.olbrain-send-btn:hover {
|
|
1392
|
+
transform: scale(1.05);
|
|
1393
|
+
box-shadow: 0 2px 8px color-mix(in srgb, var(--primary-color) 30%, transparent);
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
.olbrain-send-btn:active {
|
|
1397
|
+
transform: scale(0.95);
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
.olbrain-send-btn:disabled {
|
|
1401
|
+
opacity: 0.5;
|
|
1402
|
+
cursor: not-allowed;
|
|
1403
|
+
transform: none;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
/* Mobile Responsive */
|
|
1407
|
+
@media (max-width: 480px) {
|
|
1408
|
+
.olbrain-chat-widget {
|
|
1409
|
+
width: calc(100vw - 32px);
|
|
1410
|
+
height: calc(100vh - 140px);
|
|
1411
|
+
bottom: 100px;
|
|
1412
|
+
right: 16px;
|
|
1413
|
+
border-radius: 8px;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
.olbrain-toggle-btn {
|
|
1417
|
+
width: 48px;
|
|
1418
|
+
height: 48px;
|
|
1419
|
+
bottom: 20px;
|
|
1420
|
+
right: 16px;
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
.olbrain-message-content {
|
|
1424
|
+
max-width: 90%;
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
`;
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
// src/widget/ChatWidget.ts
|
|
1431
|
+
var DEFAULT_GREETING = "Hi! How can I help you today?";
|
|
1432
|
+
var DEFAULT_TITLE = "Chat with us";
|
|
1433
|
+
var DEFAULT_PLACEHOLDER = "Type your message...";
|
|
1434
|
+
var DEFAULT_PRIMARY_COLOR = "#4f46e5";
|
|
1435
|
+
var SESSION_STORAGE_KEY = "olbrain_widget_session_id";
|
|
1436
|
+
var ChatWidget = class {
|
|
1437
|
+
constructor(config) {
|
|
1438
|
+
this.sessionId = null;
|
|
1439
|
+
this.container = null;
|
|
1440
|
+
this.chatWidget = null;
|
|
1441
|
+
this.toggleBtn = null;
|
|
1442
|
+
this.messagesContainer = null;
|
|
1443
|
+
this.messageInput = null;
|
|
1444
|
+
this.sendBtn = null;
|
|
1445
|
+
this.isOpen = false;
|
|
1446
|
+
this.isWaitingForResponse = false;
|
|
1447
|
+
this.config = this._normalizeConfig(config);
|
|
1448
|
+
this.client = new AgentClient({
|
|
1449
|
+
agentId: config.agentId,
|
|
1450
|
+
apiKey: config.apiKey,
|
|
1451
|
+
baseUrl: config.baseUrl
|
|
1452
|
+
});
|
|
1453
|
+
}
|
|
1454
|
+
/**
|
|
1455
|
+
* Mount widget to the page
|
|
1456
|
+
*/
|
|
1457
|
+
async mount(target) {
|
|
1458
|
+
this._injectStyles();
|
|
1459
|
+
if (target) {
|
|
1460
|
+
if (typeof target === "string") {
|
|
1461
|
+
this.container = document.querySelector(target);
|
|
1462
|
+
if (!this.container) {
|
|
1463
|
+
throw new Error(`Element with selector "${target}" not found`);
|
|
1464
|
+
}
|
|
1465
|
+
} else {
|
|
1466
|
+
this.container = target;
|
|
1467
|
+
}
|
|
1468
|
+
} else {
|
|
1469
|
+
this.container = document.body;
|
|
1470
|
+
}
|
|
1471
|
+
this._render();
|
|
1472
|
+
this._attachEventListeners();
|
|
1473
|
+
await this._initializeSession();
|
|
1474
|
+
this._showGreeting();
|
|
1475
|
+
if (this.config.autoOpen) {
|
|
1476
|
+
this.open();
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
/**
|
|
1480
|
+
* Unmount widget from the page
|
|
1481
|
+
*/
|
|
1482
|
+
unmount() {
|
|
1483
|
+
this.client.stopListening(this.sessionId || "");
|
|
1484
|
+
this.client.close();
|
|
1485
|
+
if (this.container) {
|
|
1486
|
+
const wrapper = this.container.querySelector(".olbrain-widget-container");
|
|
1487
|
+
if (wrapper) {
|
|
1488
|
+
wrapper.remove();
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
this.container = null;
|
|
1492
|
+
}
|
|
1493
|
+
/**
|
|
1494
|
+
* Open chat window
|
|
1495
|
+
*/
|
|
1496
|
+
open() {
|
|
1497
|
+
if (this.chatWidget) {
|
|
1498
|
+
this.chatWidget.classList.add("open");
|
|
1499
|
+
this.isOpen = true;
|
|
1500
|
+
if (this.messageInput) {
|
|
1501
|
+
this.messageInput.focus();
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
/**
|
|
1506
|
+
* Close chat window
|
|
1507
|
+
*/
|
|
1508
|
+
close() {
|
|
1509
|
+
if (this.chatWidget) {
|
|
1510
|
+
this.chatWidget.classList.remove("open");
|
|
1511
|
+
this.isOpen = false;
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
/**
|
|
1515
|
+
* Send a message
|
|
1516
|
+
*/
|
|
1517
|
+
async sendMessage(text) {
|
|
1518
|
+
if (!text.trim() || !this.sessionId || this.isWaitingForResponse) {
|
|
1519
|
+
return;
|
|
1520
|
+
}
|
|
1521
|
+
this._addMessageToUI(text, "user");
|
|
1522
|
+
if (this.messageInput) {
|
|
1523
|
+
this.messageInput.value = "";
|
|
1524
|
+
}
|
|
1525
|
+
this.isWaitingForResponse = true;
|
|
1526
|
+
try {
|
|
1527
|
+
this._showTypingIndicator();
|
|
1528
|
+
const response = await this.client.sendAndWait(this.sessionId, text);
|
|
1529
|
+
this._removeTypingIndicator();
|
|
1530
|
+
if (response.success && response.text) {
|
|
1531
|
+
this._addMessageToUI(response.text, "assistant");
|
|
1532
|
+
} else if (response.error) {
|
|
1533
|
+
this._showError(response.error);
|
|
1534
|
+
}
|
|
1535
|
+
} catch (error) {
|
|
1536
|
+
this._removeTypingIndicator();
|
|
1537
|
+
this._showError(error.message);
|
|
1538
|
+
} finally {
|
|
1539
|
+
this.isWaitingForResponse = false;
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
/**
|
|
1543
|
+
* Normalize configuration with defaults
|
|
1544
|
+
*/
|
|
1545
|
+
_normalizeConfig(config) {
|
|
1546
|
+
return {
|
|
1547
|
+
agentId: config.agentId,
|
|
1548
|
+
apiKey: config.apiKey,
|
|
1549
|
+
position: config.position || "bottom-right",
|
|
1550
|
+
theme: config.theme || "auto",
|
|
1551
|
+
primaryColor: config.primaryColor || DEFAULT_PRIMARY_COLOR,
|
|
1552
|
+
title: config.title || DEFAULT_TITLE,
|
|
1553
|
+
greeting: config.greeting || DEFAULT_GREETING,
|
|
1554
|
+
placeholder: config.placeholder || DEFAULT_PLACEHOLDER,
|
|
1555
|
+
autoOpen: config.autoOpen ?? false,
|
|
1556
|
+
persistSession: config.persistSession ?? true,
|
|
1557
|
+
target: config.target || document.body,
|
|
1558
|
+
onMessage: config.onMessage || (() => {
|
|
1559
|
+
}),
|
|
1560
|
+
baseUrl: config.baseUrl || ""
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
/**
|
|
1564
|
+
* Inject widget styles into the page
|
|
1565
|
+
*/
|
|
1566
|
+
_injectStyles() {
|
|
1567
|
+
if (document.getElementById("olbrain-widget-styles")) {
|
|
1568
|
+
return;
|
|
1569
|
+
}
|
|
1570
|
+
const styleEl = document.createElement("style");
|
|
1571
|
+
styleEl.id = "olbrain-widget-styles";
|
|
1572
|
+
styleEl.textContent = generateStyles({
|
|
1573
|
+
primaryColor: this.config.primaryColor,
|
|
1574
|
+
theme: this.config.theme
|
|
1575
|
+
});
|
|
1576
|
+
document.head.appendChild(styleEl);
|
|
1577
|
+
}
|
|
1578
|
+
/**
|
|
1579
|
+
* Render widget HTML
|
|
1580
|
+
*/
|
|
1581
|
+
_render() {
|
|
1582
|
+
if (!this.container) return;
|
|
1583
|
+
const html = widgetContainerTemplate({
|
|
1584
|
+
title: this.config.title,
|
|
1585
|
+
greeting: this.config.greeting,
|
|
1586
|
+
placeholder: this.config.placeholder,
|
|
1587
|
+
theme: this.config.theme
|
|
1588
|
+
});
|
|
1589
|
+
this.container.insertAdjacentHTML("beforeend", html);
|
|
1590
|
+
const wrapper = this.container.querySelector(".olbrain-widget-container");
|
|
1591
|
+
if (wrapper) {
|
|
1592
|
+
this.chatWidget = wrapper.querySelector(".olbrain-chat-widget");
|
|
1593
|
+
this.toggleBtn = wrapper.querySelector(".olbrain-toggle-btn");
|
|
1594
|
+
this.messagesContainer = wrapper.querySelector(".olbrain-messages-container");
|
|
1595
|
+
this.messageInput = wrapper.querySelector(".olbrain-message-input");
|
|
1596
|
+
this.sendBtn = wrapper.querySelector(".olbrain-send-btn");
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
/**
|
|
1600
|
+
* Attach event listeners
|
|
1601
|
+
*/
|
|
1602
|
+
_attachEventListeners() {
|
|
1603
|
+
this.toggleBtn?.addEventListener("click", () => {
|
|
1604
|
+
this.isOpen ? this.close() : this.open();
|
|
1605
|
+
this.toggleBtn?.classList.toggle("hidden", this.isOpen);
|
|
1606
|
+
});
|
|
1607
|
+
this.chatWidget?.querySelector(".olbrain-close-btn")?.addEventListener("click", () => this.close());
|
|
1608
|
+
this.sendBtn?.addEventListener("click", () => {
|
|
1609
|
+
if (this.messageInput) {
|
|
1610
|
+
this.sendMessage(this.messageInput.value);
|
|
1611
|
+
}
|
|
1612
|
+
});
|
|
1613
|
+
this.messageInput?.addEventListener("keypress", (e) => {
|
|
1614
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
1615
|
+
e.preventDefault();
|
|
1616
|
+
if (this.messageInput) {
|
|
1617
|
+
this.sendMessage(this.messageInput.value);
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
});
|
|
1621
|
+
this.messageInput?.addEventListener("input", () => {
|
|
1622
|
+
if (this.sendBtn) {
|
|
1623
|
+
this.sendBtn.disabled = this.isWaitingForResponse;
|
|
1624
|
+
}
|
|
1625
|
+
});
|
|
1626
|
+
}
|
|
1627
|
+
/**
|
|
1628
|
+
* Initialize session (load or create)
|
|
1629
|
+
*/
|
|
1630
|
+
async _initializeSession() {
|
|
1631
|
+
if (this.config.persistSession) {
|
|
1632
|
+
const savedSessionId = this._getStoredSessionId();
|
|
1633
|
+
if (savedSessionId) {
|
|
1634
|
+
try {
|
|
1635
|
+
await this.client.getSession(savedSessionId);
|
|
1636
|
+
this.sessionId = savedSessionId;
|
|
1637
|
+
return;
|
|
1638
|
+
} catch {
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
try {
|
|
1643
|
+
this.sessionId = await this.client.createSession({
|
|
1644
|
+
title: "Widget Chat Session",
|
|
1645
|
+
metadata: {
|
|
1646
|
+
source: "chat_widget"
|
|
1647
|
+
}
|
|
1648
|
+
});
|
|
1649
|
+
if (this.config.persistSession) {
|
|
1650
|
+
this._storeSessionId(this.sessionId);
|
|
1651
|
+
}
|
|
1652
|
+
} catch (error) {
|
|
1653
|
+
this._showError(`Failed to create session: ${error.message}`);
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
/**
|
|
1657
|
+
* Show greeting message
|
|
1658
|
+
*/
|
|
1659
|
+
_showGreeting() {
|
|
1660
|
+
if (this.messagesContainer) {
|
|
1661
|
+
const html = greetingTemplate(this.config.greeting);
|
|
1662
|
+
this.messagesContainer.insertAdjacentHTML("beforeend", html);
|
|
1663
|
+
this._scrollToBottom();
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
/**
|
|
1667
|
+
* Add message to UI
|
|
1668
|
+
*/
|
|
1669
|
+
_addMessageToUI(content, role) {
|
|
1670
|
+
if (!this.messagesContainer) return;
|
|
1671
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1672
|
+
const html = role === "user" ? userMessageTemplate(content, timestamp) : assistantMessageTemplate(content, timestamp);
|
|
1673
|
+
this.messagesContainer.insertAdjacentHTML("beforeend", html);
|
|
1674
|
+
this._scrollToBottom();
|
|
1675
|
+
if (this.config.onMessage) {
|
|
1676
|
+
this.config.onMessage({
|
|
1677
|
+
role,
|
|
1678
|
+
content,
|
|
1679
|
+
timestamp,
|
|
1680
|
+
metadata: {}
|
|
1681
|
+
});
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
/**
|
|
1685
|
+
* Show typing indicator
|
|
1686
|
+
*/
|
|
1687
|
+
_showTypingIndicator() {
|
|
1688
|
+
if (!this.messagesContainer) return;
|
|
1689
|
+
const html = typingIndicatorTemplate();
|
|
1690
|
+
this.messagesContainer.insertAdjacentHTML("beforeend", html);
|
|
1691
|
+
this._scrollToBottom();
|
|
1692
|
+
}
|
|
1693
|
+
/**
|
|
1694
|
+
* Remove typing indicator
|
|
1695
|
+
*/
|
|
1696
|
+
_removeTypingIndicator() {
|
|
1697
|
+
const indicator = this.messagesContainer?.querySelector(".olbrain-typing-indicator");
|
|
1698
|
+
if (indicator) {
|
|
1699
|
+
indicator.closest(".olbrain-message")?.remove();
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
/**
|
|
1703
|
+
* Show error message
|
|
1704
|
+
*/
|
|
1705
|
+
_showError(error) {
|
|
1706
|
+
if (!this.messagesContainer) return;
|
|
1707
|
+
const html = errorMessageTemplate(error);
|
|
1708
|
+
this.messagesContainer.insertAdjacentHTML("beforeend", html);
|
|
1709
|
+
this._scrollToBottom();
|
|
1710
|
+
}
|
|
1711
|
+
/**
|
|
1712
|
+
* Scroll to bottom of messages
|
|
1713
|
+
*/
|
|
1714
|
+
_scrollToBottom() {
|
|
1715
|
+
if (this.messagesContainer) {
|
|
1716
|
+
setTimeout(() => {
|
|
1717
|
+
this.messagesContainer.scrollTop = this.messagesContainer.scrollHeight;
|
|
1718
|
+
}, 0);
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
/**
|
|
1722
|
+
* Store session ID in localStorage
|
|
1723
|
+
*/
|
|
1724
|
+
_storeSessionId(sessionId) {
|
|
1725
|
+
try {
|
|
1726
|
+
if (typeof localStorage !== "undefined") {
|
|
1727
|
+
localStorage.setItem(SESSION_STORAGE_KEY, sessionId);
|
|
1728
|
+
}
|
|
1729
|
+
} catch {
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
/**
|
|
1733
|
+
* Get stored session ID from localStorage
|
|
1734
|
+
*/
|
|
1735
|
+
_getStoredSessionId() {
|
|
1736
|
+
try {
|
|
1737
|
+
if (typeof localStorage !== "undefined") {
|
|
1738
|
+
return localStorage.getItem(SESSION_STORAGE_KEY);
|
|
1739
|
+
}
|
|
1740
|
+
} catch {
|
|
1741
|
+
}
|
|
1742
|
+
return null;
|
|
1743
|
+
}
|
|
1744
|
+
};
|
|
1745
|
+
return __toCommonJS(widget_exports);
|
|
1746
|
+
})();
|
|
1747
|
+
//# sourceMappingURL=widget.widget.global.js.map
|