@dotbots-boutique/auth-sdk 1.0.16 → 1.0.18
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 +91 -1
- package/dist/cjs/index.js +99 -5
- package/dist/cjs/react/index.js +11 -0
- package/dist/esm/index.js +99 -5
- package/dist/esm/react/index.js +11 -0
- package/dist/types/DotBotsAuth.d.ts +4 -2
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.js +99 -5
- package/dist/types/react/DotBotsAuthProvider.d.ts +3 -1
- package/dist/types/react/index.js +11 -0
- package/dist/types/types.d.ts +42 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -190,6 +190,81 @@ Possible `PAYMENT_FAILED` messages: `INSUFFICIENT_BALANCE`, `BUDGET_EXCEEDED`, `
|
|
|
190
190
|
|
|
191
191
|
---
|
|
192
192
|
|
|
193
|
+
## AI Gateway
|
|
194
|
+
|
|
195
|
+
The SDK provides access to the platform AI gateway. The developer never needs to know which provider or model is used — the platform handles routing, billing and rate limiting.
|
|
196
|
+
|
|
197
|
+
### Non-streaming
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
import type { AiResponse } from '@dotbots-boutique/auth-sdk';
|
|
201
|
+
|
|
202
|
+
const response: AiResponse = await auth.ai('generate-text', {
|
|
203
|
+
messages: [{ role: 'user', content: 'Summarise this document' }],
|
|
204
|
+
maxTokens: 1000,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
console.log(response.content);
|
|
208
|
+
console.log(response.usage.totalTokens);
|
|
209
|
+
console.log(response.cost.eur);
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Streaming
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
let fullText = '';
|
|
216
|
+
|
|
217
|
+
await auth.aiStream('generate-text', {
|
|
218
|
+
messages: [{ role: 'user', content: 'Write a poem' }],
|
|
219
|
+
}, (delta) => {
|
|
220
|
+
fullText += delta;
|
|
221
|
+
updateUI(fullText);
|
|
222
|
+
}, (response) => {
|
|
223
|
+
console.log('Done — model:', response.model, 'tokens:', response.usage.totalTokens);
|
|
224
|
+
});
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Tool calling
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
const response = await auth.ai('agent', {
|
|
231
|
+
messages: [{ role: 'user', content: 'What is the weather in Brussels?' }],
|
|
232
|
+
tools: [{
|
|
233
|
+
name: 'get_weather',
|
|
234
|
+
description: 'Get current weather for a city',
|
|
235
|
+
parameters: { type: 'object', properties: { city: { type: 'string' } }, required: ['city'] },
|
|
236
|
+
}],
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
if (response.toolCalls?.length) {
|
|
240
|
+
console.log(response.toolCalls[0].name, response.toolCalls[0].input);
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Error handling
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
try {
|
|
248
|
+
const response = await auth.ai('generate-text', { messages });
|
|
249
|
+
} catch (error) {
|
|
250
|
+
if (error instanceof DotBotsAuthError) {
|
|
251
|
+
switch (error.code) {
|
|
252
|
+
case 'AI_INSUFFICIENT_BALANCE':
|
|
253
|
+
// show top-up prompt
|
|
254
|
+
break;
|
|
255
|
+
case 'AI_FEATURE_NOT_FOUND':
|
|
256
|
+
// feature not configured
|
|
257
|
+
break;
|
|
258
|
+
case 'AI_CALL_FAILED':
|
|
259
|
+
// provider error
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
193
268
|
## Checking permissions
|
|
194
269
|
|
|
195
270
|
```typescript
|
|
@@ -262,6 +337,11 @@ try {
|
|
|
262
337
|
| `NOT_INITIALIZED` | `initialize()` has not been called yet | Yes |
|
|
263
338
|
| `PROXY_UNAVAILABLE` | Proxy config could not be fetched | No — falls back to direct API |
|
|
264
339
|
| `PAYMENT_FAILED` | Payment was rejected (402) — see `message` for reason | No |
|
|
340
|
+
| `AI_FEATURE_NOT_FOUND` | AI feature code not configured in platform | No |
|
|
341
|
+
| `AI_PROVIDER_NOT_CONFIGURED` | API key not set for AI provider | No |
|
|
342
|
+
| `AI_INSUFFICIENT_BALANCE` | Organisation balance is 0 | No |
|
|
343
|
+
| `AI_CALL_FAILED` | AI provider returned an error | No |
|
|
344
|
+
| `AI_STREAM_ERROR` | Streaming connection failed | No |
|
|
265
345
|
|
|
266
346
|
---
|
|
267
347
|
|
|
@@ -342,6 +422,14 @@ Authenticated fetch wrapper. Routes through the proxy when available, falls back
|
|
|
342
422
|
|
|
343
423
|
Charges a feature usage. Calls `POST {proxyUrl}/payments/charge`. On success, sends a `DOTBOTS_CHARGE` postMessage to the parent window (iframe only) and emits the `charged` event. Throws `PAYMENT_FAILED` on 402 with a message indicating the reason (`INSUFFICIENT_BALANCE`, `BUDGET_EXCEEDED`, `FEATURE_NOT_FOUND`, `PAYMENT_REJECTED`).
|
|
344
424
|
|
|
425
|
+
#### `ai(feature: string, request: AiRequest): Promise<AiResponse>`
|
|
426
|
+
|
|
427
|
+
Non-streaming AI call via the platform AI gateway. Calls `POST {proxyUrl}/ai/call`. Returns the full response with content, model, usage and cost.
|
|
428
|
+
|
|
429
|
+
#### `aiStream(feature: string, request: AiRequest, onDelta: (delta: string) => void, onDone?: (response: AiResponse) => void): Promise<void>`
|
|
430
|
+
|
|
431
|
+
Streaming AI call. `onDelta` fires for each text chunk, `onDone` fires with usage/cost metadata when the stream completes.
|
|
432
|
+
|
|
345
433
|
#### `logout(): Promise<void>`
|
|
346
434
|
|
|
347
435
|
Logs out the user. Revokes tokens on `apiUrl`, clears state, and redirects (standalone) or notifies the parent (iframe).
|
|
@@ -367,6 +455,8 @@ const {
|
|
|
367
455
|
hasRole, // (role: string) => boolean
|
|
368
456
|
fetch, // auth.fetch proxied
|
|
369
457
|
charge, // auth.charge proxied
|
|
458
|
+
ai, // auth.ai proxied
|
|
459
|
+
aiStream, // auth.aiStream proxied
|
|
370
460
|
logout, // auth.logout proxied
|
|
371
461
|
} = useDotBotsAuth();
|
|
372
462
|
```
|
|
@@ -462,7 +552,7 @@ To report a security vulnerability, please email [security@dotbots.ai](mailto:se
|
|
|
462
552
|
|
|
463
553
|
```typescript
|
|
464
554
|
import { DotBotsAuth, DotBotsAuthError } from '@dotbots-boutique/auth-sdk';
|
|
465
|
-
import type { DotBotsUser, DotBotsConfig, DotBotsAuthEvent, DotBotsProxyConfig } from '@dotbots-boutique/auth-sdk';
|
|
555
|
+
import type { DotBotsUser, DotBotsConfig, DotBotsAuthEvent, DotBotsProxyConfig, AiRequest, AiResponse, AiMessage, AiTool, AiToolCall } from '@dotbots-boutique/auth-sdk';
|
|
466
556
|
import { DotBotsAuthProvider, useDotBotsAuth } from '@dotbots-boutique/auth-sdk/react';
|
|
467
557
|
```
|
|
468
558
|
|
package/dist/cjs/index.js
CHANGED
|
@@ -297,8 +297,8 @@ class DotBotsAuth {
|
|
|
297
297
|
this.cachedUser = null;
|
|
298
298
|
this.tokenManager.clear();
|
|
299
299
|
await this.tokenManager.exchangeCode(event.data.code);
|
|
300
|
-
await this.getUser();
|
|
301
|
-
this.emit('userChanged');
|
|
300
|
+
const newUser = await this.getUser();
|
|
301
|
+
this.emit('userChanged', newUser);
|
|
302
302
|
});
|
|
303
303
|
}
|
|
304
304
|
async getUser() {
|
|
@@ -382,6 +382,100 @@ class DotBotsAuth {
|
|
|
382
382
|
this.emit('charged');
|
|
383
383
|
return data;
|
|
384
384
|
}
|
|
385
|
+
async ai(feature, request) {
|
|
386
|
+
this.assertInitialized();
|
|
387
|
+
const baseUrl = this.proxyConfigManager.getBaseUrl();
|
|
388
|
+
console.warn(`[DotBotsAuth] ai() — feature: ${feature}, messages: ${request.messages.length}`);
|
|
389
|
+
const response = await this.buildRequest(`${baseUrl}/ai/call`, {
|
|
390
|
+
method: 'POST',
|
|
391
|
+
headers: { 'Content-Type': 'application/json' },
|
|
392
|
+
body: JSON.stringify({
|
|
393
|
+
feature,
|
|
394
|
+
messages: request.messages,
|
|
395
|
+
tools: request.tools,
|
|
396
|
+
stream: false,
|
|
397
|
+
maxTokens: request.maxTokens,
|
|
398
|
+
}),
|
|
399
|
+
});
|
|
400
|
+
if (response.status === 402) {
|
|
401
|
+
console.error('[DotBotsAuth] ai() failed — 402: insufficient balance');
|
|
402
|
+
throw new DotBotsAuthError('AI_INSUFFICIENT_BALANCE', 'Insufficient balance for AI call');
|
|
403
|
+
}
|
|
404
|
+
if (response.status === 400) {
|
|
405
|
+
const body = await response.json();
|
|
406
|
+
console.error(`[DotBotsAuth] ai() failed — 400: ${body.error}`);
|
|
407
|
+
throw new DotBotsAuthError(body.error || 'AI_FEATURE_NOT_FOUND', body.message ?? 'AI request failed');
|
|
408
|
+
}
|
|
409
|
+
if (!response.ok) {
|
|
410
|
+
console.error(`[DotBotsAuth] ai() failed — HTTP ${response.status}`);
|
|
411
|
+
throw new DotBotsAuthError('AI_CALL_FAILED', `AI call failed: ${response.status}`);
|
|
412
|
+
}
|
|
413
|
+
const data = await response.json();
|
|
414
|
+
console.warn(`[DotBotsAuth] ai() success — model: ${data.model}, tokens: ${data.usage.totalTokens}, transactionId: ${data.transactionId}`);
|
|
415
|
+
return data;
|
|
416
|
+
}
|
|
417
|
+
async aiStream(feature, request, onDelta, onDone) {
|
|
418
|
+
this.assertInitialized();
|
|
419
|
+
const baseUrl = this.proxyConfigManager.getBaseUrl();
|
|
420
|
+
console.warn(`[DotBotsAuth] aiStream() — feature: ${feature}, messages: ${request.messages.length}`);
|
|
421
|
+
const response = await this.buildRequest(`${baseUrl}/ai/call`, {
|
|
422
|
+
method: 'POST',
|
|
423
|
+
headers: { 'Content-Type': 'application/json' },
|
|
424
|
+
body: JSON.stringify({
|
|
425
|
+
feature,
|
|
426
|
+
messages: request.messages,
|
|
427
|
+
tools: request.tools,
|
|
428
|
+
stream: true,
|
|
429
|
+
maxTokens: request.maxTokens,
|
|
430
|
+
}),
|
|
431
|
+
});
|
|
432
|
+
if (response.status === 402) {
|
|
433
|
+
console.error('[DotBotsAuth] aiStream() failed — 402: insufficient balance');
|
|
434
|
+
throw new DotBotsAuthError('AI_INSUFFICIENT_BALANCE', 'Insufficient balance for AI call');
|
|
435
|
+
}
|
|
436
|
+
if (!response.ok) {
|
|
437
|
+
console.error(`[DotBotsAuth] aiStream() failed — HTTP ${response.status}`);
|
|
438
|
+
throw new DotBotsAuthError('AI_CALL_FAILED', `AI stream failed: ${response.status}`);
|
|
439
|
+
}
|
|
440
|
+
const reader = response.body.getReader();
|
|
441
|
+
const decoder = new TextDecoder();
|
|
442
|
+
let buffer = '';
|
|
443
|
+
try {
|
|
444
|
+
while (true) {
|
|
445
|
+
const { done, value } = await reader.read();
|
|
446
|
+
if (done)
|
|
447
|
+
break;
|
|
448
|
+
buffer += decoder.decode(value, { stream: true });
|
|
449
|
+
const lines = buffer.split('\n');
|
|
450
|
+
buffer = lines.pop() ?? '';
|
|
451
|
+
for (const line of lines) {
|
|
452
|
+
if (!line.startsWith('data: '))
|
|
453
|
+
continue;
|
|
454
|
+
const data = JSON.parse(line.slice(6));
|
|
455
|
+
if (data.type === 'text') {
|
|
456
|
+
onDelta(data.delta);
|
|
457
|
+
}
|
|
458
|
+
else if (data.type === 'done') {
|
|
459
|
+
const finalResponse = {
|
|
460
|
+
content: '',
|
|
461
|
+
model: data.model,
|
|
462
|
+
provider: data.provider,
|
|
463
|
+
usage: data.usage,
|
|
464
|
+
cost: data.cost,
|
|
465
|
+
transactionId: data.transactionId,
|
|
466
|
+
toolCalls: data.toolCalls,
|
|
467
|
+
};
|
|
468
|
+
console.warn(`[DotBotsAuth] aiStream() done — model: ${data.model}, tokens: ${data.usage.totalTokens}, transactionId: ${data.transactionId}`);
|
|
469
|
+
onDone?.(finalResponse);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
catch (err) {
|
|
475
|
+
console.error('[DotBotsAuth] aiStream() error:', err instanceof Error ? err.message : err);
|
|
476
|
+
throw new DotBotsAuthError('AI_STREAM_ERROR', 'Streaming connection failed', err instanceof Error ? err : undefined);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
385
479
|
async logout() {
|
|
386
480
|
this.assertInitialized();
|
|
387
481
|
await this.tokenManager.revoke();
|
|
@@ -458,12 +552,12 @@ class DotBotsAuth {
|
|
|
458
552
|
throw new DotBotsAuthError('NOT_INITIALIZED', 'Call initialize() before using the SDK');
|
|
459
553
|
}
|
|
460
554
|
}
|
|
461
|
-
emit(event) {
|
|
555
|
+
emit(event, data) {
|
|
462
556
|
const handlers = this.listeners.get(event);
|
|
463
557
|
if (handlers) {
|
|
464
558
|
for (const handler of handlers) {
|
|
465
559
|
try {
|
|
466
|
-
handler();
|
|
560
|
+
handler(data);
|
|
467
561
|
}
|
|
468
562
|
catch {
|
|
469
563
|
// Don't let listener errors break the SDK
|
|
@@ -472,7 +566,7 @@ class DotBotsAuth {
|
|
|
472
566
|
}
|
|
473
567
|
}
|
|
474
568
|
}
|
|
475
|
-
DotBotsAuth.SDK_VERSION = '1.0.
|
|
569
|
+
DotBotsAuth.SDK_VERSION = '1.0.18';
|
|
476
570
|
|
|
477
571
|
exports.DotBotsAuth = DotBotsAuth;
|
|
478
572
|
exports.DotBotsAuthError = DotBotsAuthError;
|
package/dist/cjs/react/index.js
CHANGED
|
@@ -31,6 +31,15 @@ function DotBotsAuthProvider({ auth, children, loadingComponent, errorComponent,
|
|
|
31
31
|
cancelled = true;
|
|
32
32
|
};
|
|
33
33
|
}, [auth]);
|
|
34
|
+
react.useEffect(() => {
|
|
35
|
+
const handler = (newUser) => {
|
|
36
|
+
setUser(newUser);
|
|
37
|
+
};
|
|
38
|
+
auth.on('userChanged', handler);
|
|
39
|
+
return () => {
|
|
40
|
+
auth.off('userChanged', handler);
|
|
41
|
+
};
|
|
42
|
+
}, [auth]);
|
|
34
43
|
if (isLoading) {
|
|
35
44
|
return jsxRuntime.jsx(jsxRuntime.Fragment, { children: loadingComponent ?? jsxRuntime.jsx("div", { children: "Loading..." }) });
|
|
36
45
|
}
|
|
@@ -50,6 +59,8 @@ function DotBotsAuthProvider({ auth, children, loadingComponent, errorComponent,
|
|
|
50
59
|
hasRole: (role) => auth.hasRole(role),
|
|
51
60
|
fetch: (url, options) => auth.fetch(url, options),
|
|
52
61
|
charge: (featureCode, paidBy, quantity) => auth.charge(featureCode, paidBy, quantity),
|
|
62
|
+
ai: (feature, request) => auth.ai(feature, request),
|
|
63
|
+
aiStream: (feature, request, onDelta, onDone) => auth.aiStream(feature, request, onDelta, onDone),
|
|
53
64
|
logout: () => auth.logout(),
|
|
54
65
|
};
|
|
55
66
|
return (jsxRuntime.jsx(DotBotsAuthContext.Provider, { value: value, children: children }));
|
package/dist/esm/index.js
CHANGED
|
@@ -295,8 +295,8 @@ class DotBotsAuth {
|
|
|
295
295
|
this.cachedUser = null;
|
|
296
296
|
this.tokenManager.clear();
|
|
297
297
|
await this.tokenManager.exchangeCode(event.data.code);
|
|
298
|
-
await this.getUser();
|
|
299
|
-
this.emit('userChanged');
|
|
298
|
+
const newUser = await this.getUser();
|
|
299
|
+
this.emit('userChanged', newUser);
|
|
300
300
|
});
|
|
301
301
|
}
|
|
302
302
|
async getUser() {
|
|
@@ -380,6 +380,100 @@ class DotBotsAuth {
|
|
|
380
380
|
this.emit('charged');
|
|
381
381
|
return data;
|
|
382
382
|
}
|
|
383
|
+
async ai(feature, request) {
|
|
384
|
+
this.assertInitialized();
|
|
385
|
+
const baseUrl = this.proxyConfigManager.getBaseUrl();
|
|
386
|
+
console.warn(`[DotBotsAuth] ai() — feature: ${feature}, messages: ${request.messages.length}`);
|
|
387
|
+
const response = await this.buildRequest(`${baseUrl}/ai/call`, {
|
|
388
|
+
method: 'POST',
|
|
389
|
+
headers: { 'Content-Type': 'application/json' },
|
|
390
|
+
body: JSON.stringify({
|
|
391
|
+
feature,
|
|
392
|
+
messages: request.messages,
|
|
393
|
+
tools: request.tools,
|
|
394
|
+
stream: false,
|
|
395
|
+
maxTokens: request.maxTokens,
|
|
396
|
+
}),
|
|
397
|
+
});
|
|
398
|
+
if (response.status === 402) {
|
|
399
|
+
console.error('[DotBotsAuth] ai() failed — 402: insufficient balance');
|
|
400
|
+
throw new DotBotsAuthError('AI_INSUFFICIENT_BALANCE', 'Insufficient balance for AI call');
|
|
401
|
+
}
|
|
402
|
+
if (response.status === 400) {
|
|
403
|
+
const body = await response.json();
|
|
404
|
+
console.error(`[DotBotsAuth] ai() failed — 400: ${body.error}`);
|
|
405
|
+
throw new DotBotsAuthError(body.error || 'AI_FEATURE_NOT_FOUND', body.message ?? 'AI request failed');
|
|
406
|
+
}
|
|
407
|
+
if (!response.ok) {
|
|
408
|
+
console.error(`[DotBotsAuth] ai() failed — HTTP ${response.status}`);
|
|
409
|
+
throw new DotBotsAuthError('AI_CALL_FAILED', `AI call failed: ${response.status}`);
|
|
410
|
+
}
|
|
411
|
+
const data = await response.json();
|
|
412
|
+
console.warn(`[DotBotsAuth] ai() success — model: ${data.model}, tokens: ${data.usage.totalTokens}, transactionId: ${data.transactionId}`);
|
|
413
|
+
return data;
|
|
414
|
+
}
|
|
415
|
+
async aiStream(feature, request, onDelta, onDone) {
|
|
416
|
+
this.assertInitialized();
|
|
417
|
+
const baseUrl = this.proxyConfigManager.getBaseUrl();
|
|
418
|
+
console.warn(`[DotBotsAuth] aiStream() — feature: ${feature}, messages: ${request.messages.length}`);
|
|
419
|
+
const response = await this.buildRequest(`${baseUrl}/ai/call`, {
|
|
420
|
+
method: 'POST',
|
|
421
|
+
headers: { 'Content-Type': 'application/json' },
|
|
422
|
+
body: JSON.stringify({
|
|
423
|
+
feature,
|
|
424
|
+
messages: request.messages,
|
|
425
|
+
tools: request.tools,
|
|
426
|
+
stream: true,
|
|
427
|
+
maxTokens: request.maxTokens,
|
|
428
|
+
}),
|
|
429
|
+
});
|
|
430
|
+
if (response.status === 402) {
|
|
431
|
+
console.error('[DotBotsAuth] aiStream() failed — 402: insufficient balance');
|
|
432
|
+
throw new DotBotsAuthError('AI_INSUFFICIENT_BALANCE', 'Insufficient balance for AI call');
|
|
433
|
+
}
|
|
434
|
+
if (!response.ok) {
|
|
435
|
+
console.error(`[DotBotsAuth] aiStream() failed — HTTP ${response.status}`);
|
|
436
|
+
throw new DotBotsAuthError('AI_CALL_FAILED', `AI stream failed: ${response.status}`);
|
|
437
|
+
}
|
|
438
|
+
const reader = response.body.getReader();
|
|
439
|
+
const decoder = new TextDecoder();
|
|
440
|
+
let buffer = '';
|
|
441
|
+
try {
|
|
442
|
+
while (true) {
|
|
443
|
+
const { done, value } = await reader.read();
|
|
444
|
+
if (done)
|
|
445
|
+
break;
|
|
446
|
+
buffer += decoder.decode(value, { stream: true });
|
|
447
|
+
const lines = buffer.split('\n');
|
|
448
|
+
buffer = lines.pop() ?? '';
|
|
449
|
+
for (const line of lines) {
|
|
450
|
+
if (!line.startsWith('data: '))
|
|
451
|
+
continue;
|
|
452
|
+
const data = JSON.parse(line.slice(6));
|
|
453
|
+
if (data.type === 'text') {
|
|
454
|
+
onDelta(data.delta);
|
|
455
|
+
}
|
|
456
|
+
else if (data.type === 'done') {
|
|
457
|
+
const finalResponse = {
|
|
458
|
+
content: '',
|
|
459
|
+
model: data.model,
|
|
460
|
+
provider: data.provider,
|
|
461
|
+
usage: data.usage,
|
|
462
|
+
cost: data.cost,
|
|
463
|
+
transactionId: data.transactionId,
|
|
464
|
+
toolCalls: data.toolCalls,
|
|
465
|
+
};
|
|
466
|
+
console.warn(`[DotBotsAuth] aiStream() done — model: ${data.model}, tokens: ${data.usage.totalTokens}, transactionId: ${data.transactionId}`);
|
|
467
|
+
onDone?.(finalResponse);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
catch (err) {
|
|
473
|
+
console.error('[DotBotsAuth] aiStream() error:', err instanceof Error ? err.message : err);
|
|
474
|
+
throw new DotBotsAuthError('AI_STREAM_ERROR', 'Streaming connection failed', err instanceof Error ? err : undefined);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
383
477
|
async logout() {
|
|
384
478
|
this.assertInitialized();
|
|
385
479
|
await this.tokenManager.revoke();
|
|
@@ -456,12 +550,12 @@ class DotBotsAuth {
|
|
|
456
550
|
throw new DotBotsAuthError('NOT_INITIALIZED', 'Call initialize() before using the SDK');
|
|
457
551
|
}
|
|
458
552
|
}
|
|
459
|
-
emit(event) {
|
|
553
|
+
emit(event, data) {
|
|
460
554
|
const handlers = this.listeners.get(event);
|
|
461
555
|
if (handlers) {
|
|
462
556
|
for (const handler of handlers) {
|
|
463
557
|
try {
|
|
464
|
-
handler();
|
|
558
|
+
handler(data);
|
|
465
559
|
}
|
|
466
560
|
catch {
|
|
467
561
|
// Don't let listener errors break the SDK
|
|
@@ -470,6 +564,6 @@ class DotBotsAuth {
|
|
|
470
564
|
}
|
|
471
565
|
}
|
|
472
566
|
}
|
|
473
|
-
DotBotsAuth.SDK_VERSION = '1.0.
|
|
567
|
+
DotBotsAuth.SDK_VERSION = '1.0.18';
|
|
474
568
|
|
|
475
569
|
export { DotBotsAuth, DotBotsAuthError };
|
package/dist/esm/react/index.js
CHANGED
|
@@ -29,6 +29,15 @@ function DotBotsAuthProvider({ auth, children, loadingComponent, errorComponent,
|
|
|
29
29
|
cancelled = true;
|
|
30
30
|
};
|
|
31
31
|
}, [auth]);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
const handler = (newUser) => {
|
|
34
|
+
setUser(newUser);
|
|
35
|
+
};
|
|
36
|
+
auth.on('userChanged', handler);
|
|
37
|
+
return () => {
|
|
38
|
+
auth.off('userChanged', handler);
|
|
39
|
+
};
|
|
40
|
+
}, [auth]);
|
|
32
41
|
if (isLoading) {
|
|
33
42
|
return jsx(Fragment, { children: loadingComponent ?? jsx("div", { children: "Loading..." }) });
|
|
34
43
|
}
|
|
@@ -48,6 +57,8 @@ function DotBotsAuthProvider({ auth, children, loadingComponent, errorComponent,
|
|
|
48
57
|
hasRole: (role) => auth.hasRole(role),
|
|
49
58
|
fetch: (url, options) => auth.fetch(url, options),
|
|
50
59
|
charge: (featureCode, paidBy, quantity) => auth.charge(featureCode, paidBy, quantity),
|
|
60
|
+
ai: (feature, request) => auth.ai(feature, request),
|
|
61
|
+
aiStream: (feature, request, onDelta, onDone) => auth.aiStream(feature, request, onDelta, onDone),
|
|
51
62
|
logout: () => auth.logout(),
|
|
52
63
|
};
|
|
53
64
|
return (jsx(DotBotsAuthContext.Provider, { value: value, children: children }));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DotBotsConfig, DotBotsUser, DotBotsAuthEvent, DotBotsProxyConfig } from './types.js';
|
|
1
|
+
import type { DotBotsConfig, DotBotsUser, DotBotsAuthEvent, DotBotsProxyConfig, AiRequest, AiResponse } from './types.js';
|
|
2
2
|
export declare class DotBotsAuth {
|
|
3
3
|
private readonly config;
|
|
4
4
|
private readonly environment;
|
|
@@ -10,7 +10,7 @@ export declare class DotBotsAuth {
|
|
|
10
10
|
private cachedUser;
|
|
11
11
|
private initialized;
|
|
12
12
|
private initializePromise;
|
|
13
|
-
static readonly SDK_VERSION = "1.0.
|
|
13
|
+
static readonly SDK_VERSION = "1.0.18";
|
|
14
14
|
constructor(config: DotBotsConfig);
|
|
15
15
|
initialize(): Promise<void>;
|
|
16
16
|
private _doInitialize;
|
|
@@ -23,6 +23,8 @@ export declare class DotBotsAuth {
|
|
|
23
23
|
charge(featureCode: string, paidBy: 'org' | 'app', quantity?: number): Promise<{
|
|
24
24
|
transactionId: string;
|
|
25
25
|
}>;
|
|
26
|
+
ai(feature: string, request: AiRequest): Promise<AiResponse>;
|
|
27
|
+
aiStream(feature: string, request: AiRequest, onDelta: (delta: string) => void, onDone?: (response: AiResponse) => void): Promise<void>;
|
|
26
28
|
logout(): Promise<void>;
|
|
27
29
|
on(event: DotBotsAuthEvent, handler: Function): void;
|
|
28
30
|
off(event: DotBotsAuthEvent, handler: Function): void;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { DotBotsAuth } from './DotBotsAuth.js';
|
|
2
2
|
export { DotBotsAuthError } from './DotBotsAuthError.js';
|
|
3
|
-
export type { DotBotsConfig, DotBotsUser, DotBotsAuthEvent, DotBotsAuthErrorCode, DotBotsProxyConfig, ProxyFeature, DotBotsEnvironment, } from './types.js';
|
|
3
|
+
export type { DotBotsConfig, DotBotsUser, DotBotsAuthEvent, DotBotsAuthErrorCode, DotBotsProxyConfig, ProxyFeature, DotBotsEnvironment, AiMessage, AiContentBlock, AiTool, AiRequest, AiResponse, AiToolCall, } from './types.js';
|
package/dist/types/index.js
CHANGED
|
@@ -295,8 +295,8 @@ class DotBotsAuth {
|
|
|
295
295
|
this.cachedUser = null;
|
|
296
296
|
this.tokenManager.clear();
|
|
297
297
|
await this.tokenManager.exchangeCode(event.data.code);
|
|
298
|
-
await this.getUser();
|
|
299
|
-
this.emit('userChanged');
|
|
298
|
+
const newUser = await this.getUser();
|
|
299
|
+
this.emit('userChanged', newUser);
|
|
300
300
|
});
|
|
301
301
|
}
|
|
302
302
|
async getUser() {
|
|
@@ -380,6 +380,100 @@ class DotBotsAuth {
|
|
|
380
380
|
this.emit('charged');
|
|
381
381
|
return data;
|
|
382
382
|
}
|
|
383
|
+
async ai(feature, request) {
|
|
384
|
+
this.assertInitialized();
|
|
385
|
+
const baseUrl = this.proxyConfigManager.getBaseUrl();
|
|
386
|
+
console.warn(`[DotBotsAuth] ai() — feature: ${feature}, messages: ${request.messages.length}`);
|
|
387
|
+
const response = await this.buildRequest(`${baseUrl}/ai/call`, {
|
|
388
|
+
method: 'POST',
|
|
389
|
+
headers: { 'Content-Type': 'application/json' },
|
|
390
|
+
body: JSON.stringify({
|
|
391
|
+
feature,
|
|
392
|
+
messages: request.messages,
|
|
393
|
+
tools: request.tools,
|
|
394
|
+
stream: false,
|
|
395
|
+
maxTokens: request.maxTokens,
|
|
396
|
+
}),
|
|
397
|
+
});
|
|
398
|
+
if (response.status === 402) {
|
|
399
|
+
console.error('[DotBotsAuth] ai() failed — 402: insufficient balance');
|
|
400
|
+
throw new DotBotsAuthError('AI_INSUFFICIENT_BALANCE', 'Insufficient balance for AI call');
|
|
401
|
+
}
|
|
402
|
+
if (response.status === 400) {
|
|
403
|
+
const body = await response.json();
|
|
404
|
+
console.error(`[DotBotsAuth] ai() failed — 400: ${body.error}`);
|
|
405
|
+
throw new DotBotsAuthError(body.error || 'AI_FEATURE_NOT_FOUND', body.message ?? 'AI request failed');
|
|
406
|
+
}
|
|
407
|
+
if (!response.ok) {
|
|
408
|
+
console.error(`[DotBotsAuth] ai() failed — HTTP ${response.status}`);
|
|
409
|
+
throw new DotBotsAuthError('AI_CALL_FAILED', `AI call failed: ${response.status}`);
|
|
410
|
+
}
|
|
411
|
+
const data = await response.json();
|
|
412
|
+
console.warn(`[DotBotsAuth] ai() success — model: ${data.model}, tokens: ${data.usage.totalTokens}, transactionId: ${data.transactionId}`);
|
|
413
|
+
return data;
|
|
414
|
+
}
|
|
415
|
+
async aiStream(feature, request, onDelta, onDone) {
|
|
416
|
+
this.assertInitialized();
|
|
417
|
+
const baseUrl = this.proxyConfigManager.getBaseUrl();
|
|
418
|
+
console.warn(`[DotBotsAuth] aiStream() — feature: ${feature}, messages: ${request.messages.length}`);
|
|
419
|
+
const response = await this.buildRequest(`${baseUrl}/ai/call`, {
|
|
420
|
+
method: 'POST',
|
|
421
|
+
headers: { 'Content-Type': 'application/json' },
|
|
422
|
+
body: JSON.stringify({
|
|
423
|
+
feature,
|
|
424
|
+
messages: request.messages,
|
|
425
|
+
tools: request.tools,
|
|
426
|
+
stream: true,
|
|
427
|
+
maxTokens: request.maxTokens,
|
|
428
|
+
}),
|
|
429
|
+
});
|
|
430
|
+
if (response.status === 402) {
|
|
431
|
+
console.error('[DotBotsAuth] aiStream() failed — 402: insufficient balance');
|
|
432
|
+
throw new DotBotsAuthError('AI_INSUFFICIENT_BALANCE', 'Insufficient balance for AI call');
|
|
433
|
+
}
|
|
434
|
+
if (!response.ok) {
|
|
435
|
+
console.error(`[DotBotsAuth] aiStream() failed — HTTP ${response.status}`);
|
|
436
|
+
throw new DotBotsAuthError('AI_CALL_FAILED', `AI stream failed: ${response.status}`);
|
|
437
|
+
}
|
|
438
|
+
const reader = response.body.getReader();
|
|
439
|
+
const decoder = new TextDecoder();
|
|
440
|
+
let buffer = '';
|
|
441
|
+
try {
|
|
442
|
+
while (true) {
|
|
443
|
+
const { done, value } = await reader.read();
|
|
444
|
+
if (done)
|
|
445
|
+
break;
|
|
446
|
+
buffer += decoder.decode(value, { stream: true });
|
|
447
|
+
const lines = buffer.split('\n');
|
|
448
|
+
buffer = lines.pop() ?? '';
|
|
449
|
+
for (const line of lines) {
|
|
450
|
+
if (!line.startsWith('data: '))
|
|
451
|
+
continue;
|
|
452
|
+
const data = JSON.parse(line.slice(6));
|
|
453
|
+
if (data.type === 'text') {
|
|
454
|
+
onDelta(data.delta);
|
|
455
|
+
}
|
|
456
|
+
else if (data.type === 'done') {
|
|
457
|
+
const finalResponse = {
|
|
458
|
+
content: '',
|
|
459
|
+
model: data.model,
|
|
460
|
+
provider: data.provider,
|
|
461
|
+
usage: data.usage,
|
|
462
|
+
cost: data.cost,
|
|
463
|
+
transactionId: data.transactionId,
|
|
464
|
+
toolCalls: data.toolCalls,
|
|
465
|
+
};
|
|
466
|
+
console.warn(`[DotBotsAuth] aiStream() done — model: ${data.model}, tokens: ${data.usage.totalTokens}, transactionId: ${data.transactionId}`);
|
|
467
|
+
onDone?.(finalResponse);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
catch (err) {
|
|
473
|
+
console.error('[DotBotsAuth] aiStream() error:', err instanceof Error ? err.message : err);
|
|
474
|
+
throw new DotBotsAuthError('AI_STREAM_ERROR', 'Streaming connection failed', err instanceof Error ? err : undefined);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
383
477
|
async logout() {
|
|
384
478
|
this.assertInitialized();
|
|
385
479
|
await this.tokenManager.revoke();
|
|
@@ -456,12 +550,12 @@ class DotBotsAuth {
|
|
|
456
550
|
throw new DotBotsAuthError('NOT_INITIALIZED', 'Call initialize() before using the SDK');
|
|
457
551
|
}
|
|
458
552
|
}
|
|
459
|
-
emit(event) {
|
|
553
|
+
emit(event, data) {
|
|
460
554
|
const handlers = this.listeners.get(event);
|
|
461
555
|
if (handlers) {
|
|
462
556
|
for (const handler of handlers) {
|
|
463
557
|
try {
|
|
464
|
-
handler();
|
|
558
|
+
handler(data);
|
|
465
559
|
}
|
|
466
560
|
catch {
|
|
467
561
|
// Don't let listener errors break the SDK
|
|
@@ -470,6 +564,6 @@ class DotBotsAuth {
|
|
|
470
564
|
}
|
|
471
565
|
}
|
|
472
566
|
}
|
|
473
|
-
DotBotsAuth.SDK_VERSION = '1.0.
|
|
567
|
+
DotBotsAuth.SDK_VERSION = '1.0.18';
|
|
474
568
|
|
|
475
569
|
export { DotBotsAuth, DotBotsAuthError };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import type { DotBotsAuth } from '../DotBotsAuth.js';
|
|
3
|
-
import type { DotBotsUser } from '../types.js';
|
|
3
|
+
import type { DotBotsUser, AiRequest, AiResponse } from '../types.js';
|
|
4
4
|
import type { DotBotsAuthError } from '../DotBotsAuthError.js';
|
|
5
5
|
export interface DotBotsAuthContextValue {
|
|
6
6
|
user: DotBotsUser | null;
|
|
@@ -14,6 +14,8 @@ export interface DotBotsAuthContextValue {
|
|
|
14
14
|
charge: (featureCode: string, paidBy: 'org' | 'app', quantity?: number) => Promise<{
|
|
15
15
|
transactionId: string;
|
|
16
16
|
}>;
|
|
17
|
+
ai: (feature: string, request: AiRequest) => Promise<AiResponse>;
|
|
18
|
+
aiStream: (feature: string, request: AiRequest, onDelta: (delta: string) => void, onDone?: (response: AiResponse) => void) => Promise<void>;
|
|
17
19
|
logout: () => Promise<void>;
|
|
18
20
|
}
|
|
19
21
|
export declare const DotBotsAuthContext: import("react").Context<DotBotsAuthContextValue | null>;
|
|
@@ -29,6 +29,15 @@ function DotBotsAuthProvider({ auth, children, loadingComponent, errorComponent,
|
|
|
29
29
|
cancelled = true;
|
|
30
30
|
};
|
|
31
31
|
}, [auth]);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
const handler = (newUser) => {
|
|
34
|
+
setUser(newUser);
|
|
35
|
+
};
|
|
36
|
+
auth.on('userChanged', handler);
|
|
37
|
+
return () => {
|
|
38
|
+
auth.off('userChanged', handler);
|
|
39
|
+
};
|
|
40
|
+
}, [auth]);
|
|
32
41
|
if (isLoading) {
|
|
33
42
|
return jsx(Fragment, { children: loadingComponent ?? jsx("div", { children: "Loading..." }) });
|
|
34
43
|
}
|
|
@@ -48,6 +57,8 @@ function DotBotsAuthProvider({ auth, children, loadingComponent, errorComponent,
|
|
|
48
57
|
hasRole: (role) => auth.hasRole(role),
|
|
49
58
|
fetch: (url, options) => auth.fetch(url, options),
|
|
50
59
|
charge: (featureCode, paidBy, quantity) => auth.charge(featureCode, paidBy, quantity),
|
|
60
|
+
ai: (feature, request) => auth.ai(feature, request),
|
|
61
|
+
aiStream: (feature, request, onDelta, onDone) => auth.aiStream(feature, request, onDelta, onDone),
|
|
51
62
|
logout: () => auth.logout(),
|
|
52
63
|
};
|
|
53
64
|
return (jsx(DotBotsAuthContext.Provider, { value: value, children: children }));
|
package/dist/types/types.d.ts
CHANGED
|
@@ -41,10 +41,51 @@ export interface DotBotsProxyConfig {
|
|
|
41
41
|
}
|
|
42
42
|
export type ProxyFeature = 'cache' | 'localdb' | 'webhooks' | 'ratelimit';
|
|
43
43
|
export type DotBotsAuthEvent = 'tokenRefreshed' | 'loggedOut' | 'sessionExpired' | 'userLoaded' | 'charged' | 'userChanged';
|
|
44
|
-
export type DotBotsAuthErrorCode = 'IFRAME_TIMEOUT' | 'CODE_EXPIRED' | 'UNAUTHORIZED' | 'REFRESH_FAILED' | 'NETWORK_ERROR' | 'NOT_INITIALIZED' | 'PROXY_UNAVAILABLE' | 'PAYMENT_FAILED';
|
|
44
|
+
export type DotBotsAuthErrorCode = 'IFRAME_TIMEOUT' | 'CODE_EXPIRED' | 'UNAUTHORIZED' | 'REFRESH_FAILED' | 'NETWORK_ERROR' | 'NOT_INITIALIZED' | 'PROXY_UNAVAILABLE' | 'PAYMENT_FAILED' | 'AI_FEATURE_NOT_FOUND' | 'AI_PROVIDER_NOT_CONFIGURED' | 'AI_INSUFFICIENT_BALANCE' | 'AI_CALL_FAILED' | 'AI_STREAM_ERROR';
|
|
45
45
|
export interface TokenPair {
|
|
46
46
|
accessToken: string;
|
|
47
47
|
refreshToken: string;
|
|
48
48
|
expiresIn: number;
|
|
49
49
|
}
|
|
50
50
|
export type DotBotsEnvironment = 'test' | 'prod';
|
|
51
|
+
export interface AiMessage {
|
|
52
|
+
role: 'user' | 'assistant' | 'system';
|
|
53
|
+
content: string | AiContentBlock[];
|
|
54
|
+
}
|
|
55
|
+
export interface AiContentBlock {
|
|
56
|
+
type: 'text' | 'image_url';
|
|
57
|
+
text?: string;
|
|
58
|
+
image_url?: {
|
|
59
|
+
url: string;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export interface AiTool {
|
|
63
|
+
name: string;
|
|
64
|
+
description: string;
|
|
65
|
+
parameters: Record<string, unknown>;
|
|
66
|
+
}
|
|
67
|
+
export interface AiRequest {
|
|
68
|
+
messages: AiMessage[];
|
|
69
|
+
tools?: AiTool[];
|
|
70
|
+
maxTokens?: number;
|
|
71
|
+
}
|
|
72
|
+
export interface AiResponse {
|
|
73
|
+
content: string;
|
|
74
|
+
model: string;
|
|
75
|
+
provider: string;
|
|
76
|
+
usage: {
|
|
77
|
+
inputTokens: number;
|
|
78
|
+
outputTokens: number;
|
|
79
|
+
totalTokens: number;
|
|
80
|
+
};
|
|
81
|
+
cost: {
|
|
82
|
+
tokens: number;
|
|
83
|
+
eur: number;
|
|
84
|
+
};
|
|
85
|
+
transactionId: string;
|
|
86
|
+
toolCalls?: AiToolCall[];
|
|
87
|
+
}
|
|
88
|
+
export interface AiToolCall {
|
|
89
|
+
name: string;
|
|
90
|
+
input: Record<string, unknown>;
|
|
91
|
+
}
|