@dotbots-boutique/auth-sdk 1.0.15 → 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 -4
- package/dist/cjs/react/index.js +11 -0
- package/dist/esm/index.js +99 -4
- 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 -4
- 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,7 +297,8 @@ class DotBotsAuth {
|
|
|
297
297
|
this.cachedUser = null;
|
|
298
298
|
this.tokenManager.clear();
|
|
299
299
|
await this.tokenManager.exchangeCode(event.data.code);
|
|
300
|
-
this.
|
|
300
|
+
const newUser = await this.getUser();
|
|
301
|
+
this.emit('userChanged', newUser);
|
|
301
302
|
});
|
|
302
303
|
}
|
|
303
304
|
async getUser() {
|
|
@@ -381,6 +382,100 @@ class DotBotsAuth {
|
|
|
381
382
|
this.emit('charged');
|
|
382
383
|
return data;
|
|
383
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
|
+
}
|
|
384
479
|
async logout() {
|
|
385
480
|
this.assertInitialized();
|
|
386
481
|
await this.tokenManager.revoke();
|
|
@@ -457,12 +552,12 @@ class DotBotsAuth {
|
|
|
457
552
|
throw new DotBotsAuthError('NOT_INITIALIZED', 'Call initialize() before using the SDK');
|
|
458
553
|
}
|
|
459
554
|
}
|
|
460
|
-
emit(event) {
|
|
555
|
+
emit(event, data) {
|
|
461
556
|
const handlers = this.listeners.get(event);
|
|
462
557
|
if (handlers) {
|
|
463
558
|
for (const handler of handlers) {
|
|
464
559
|
try {
|
|
465
|
-
handler();
|
|
560
|
+
handler(data);
|
|
466
561
|
}
|
|
467
562
|
catch {
|
|
468
563
|
// Don't let listener errors break the SDK
|
|
@@ -471,7 +566,7 @@ class DotBotsAuth {
|
|
|
471
566
|
}
|
|
472
567
|
}
|
|
473
568
|
}
|
|
474
|
-
DotBotsAuth.SDK_VERSION = '1.0.
|
|
569
|
+
DotBotsAuth.SDK_VERSION = '1.0.18';
|
|
475
570
|
|
|
476
571
|
exports.DotBotsAuth = DotBotsAuth;
|
|
477
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,7 +295,8 @@ class DotBotsAuth {
|
|
|
295
295
|
this.cachedUser = null;
|
|
296
296
|
this.tokenManager.clear();
|
|
297
297
|
await this.tokenManager.exchangeCode(event.data.code);
|
|
298
|
-
this.
|
|
298
|
+
const newUser = await this.getUser();
|
|
299
|
+
this.emit('userChanged', newUser);
|
|
299
300
|
});
|
|
300
301
|
}
|
|
301
302
|
async getUser() {
|
|
@@ -379,6 +380,100 @@ class DotBotsAuth {
|
|
|
379
380
|
this.emit('charged');
|
|
380
381
|
return data;
|
|
381
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
|
+
}
|
|
382
477
|
async logout() {
|
|
383
478
|
this.assertInitialized();
|
|
384
479
|
await this.tokenManager.revoke();
|
|
@@ -455,12 +550,12 @@ class DotBotsAuth {
|
|
|
455
550
|
throw new DotBotsAuthError('NOT_INITIALIZED', 'Call initialize() before using the SDK');
|
|
456
551
|
}
|
|
457
552
|
}
|
|
458
|
-
emit(event) {
|
|
553
|
+
emit(event, data) {
|
|
459
554
|
const handlers = this.listeners.get(event);
|
|
460
555
|
if (handlers) {
|
|
461
556
|
for (const handler of handlers) {
|
|
462
557
|
try {
|
|
463
|
-
handler();
|
|
558
|
+
handler(data);
|
|
464
559
|
}
|
|
465
560
|
catch {
|
|
466
561
|
// Don't let listener errors break the SDK
|
|
@@ -469,6 +564,6 @@ class DotBotsAuth {
|
|
|
469
564
|
}
|
|
470
565
|
}
|
|
471
566
|
}
|
|
472
|
-
DotBotsAuth.SDK_VERSION = '1.0.
|
|
567
|
+
DotBotsAuth.SDK_VERSION = '1.0.18';
|
|
473
568
|
|
|
474
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,7 +295,8 @@ class DotBotsAuth {
|
|
|
295
295
|
this.cachedUser = null;
|
|
296
296
|
this.tokenManager.clear();
|
|
297
297
|
await this.tokenManager.exchangeCode(event.data.code);
|
|
298
|
-
this.
|
|
298
|
+
const newUser = await this.getUser();
|
|
299
|
+
this.emit('userChanged', newUser);
|
|
299
300
|
});
|
|
300
301
|
}
|
|
301
302
|
async getUser() {
|
|
@@ -379,6 +380,100 @@ class DotBotsAuth {
|
|
|
379
380
|
this.emit('charged');
|
|
380
381
|
return data;
|
|
381
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
|
+
}
|
|
382
477
|
async logout() {
|
|
383
478
|
this.assertInitialized();
|
|
384
479
|
await this.tokenManager.revoke();
|
|
@@ -455,12 +550,12 @@ class DotBotsAuth {
|
|
|
455
550
|
throw new DotBotsAuthError('NOT_INITIALIZED', 'Call initialize() before using the SDK');
|
|
456
551
|
}
|
|
457
552
|
}
|
|
458
|
-
emit(event) {
|
|
553
|
+
emit(event, data) {
|
|
459
554
|
const handlers = this.listeners.get(event);
|
|
460
555
|
if (handlers) {
|
|
461
556
|
for (const handler of handlers) {
|
|
462
557
|
try {
|
|
463
|
-
handler();
|
|
558
|
+
handler(data);
|
|
464
559
|
}
|
|
465
560
|
catch {
|
|
466
561
|
// Don't let listener errors break the SDK
|
|
@@ -469,6 +564,6 @@ class DotBotsAuth {
|
|
|
469
564
|
}
|
|
470
565
|
}
|
|
471
566
|
}
|
|
472
|
-
DotBotsAuth.SDK_VERSION = '1.0.
|
|
567
|
+
DotBotsAuth.SDK_VERSION = '1.0.18';
|
|
473
568
|
|
|
474
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
|
+
}
|