@tokenite/sdk 2.3.0 → 2.5.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/LICENSE +21 -21
- package/README.md +632 -565
- package/dist/client.d.ts +12 -8
- package/dist/client.js +25 -17
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +80 -15
- package/package.json +54 -53
- package/dist/client.test.d.ts +0 -2
- package/dist/client.test.js +0 -66
package/dist/client.d.ts
CHANGED
|
@@ -41,11 +41,13 @@ export declare const Tokenite: (config: TokeniteConfig) => {
|
|
|
41
41
|
/**
|
|
42
42
|
* Build the authorization URL for a full-page redirect.
|
|
43
43
|
*
|
|
44
|
-
* Pass `prompt: '
|
|
45
|
-
* follow a sign-out
|
|
46
|
-
* instead of
|
|
44
|
+
* Pass `prompt: 'select_account'` on "Sign in with Tokenite" buttons
|
|
45
|
+
* that follow a sign-out — Tokenite shows a "Continue as <email>?"
|
|
46
|
+
* card with a "Use a different account" option instead of silently
|
|
47
|
+
* dropping the user back into the app. The user's existing spending
|
|
48
|
+
* limit is preserved (no budget re-entry):
|
|
47
49
|
* ```typescript
|
|
48
|
-
* res.redirect(tk.getAuthorizeUrl({ prompt: '
|
|
50
|
+
* res.redirect(tk.getAuthorizeUrl({ prompt: 'select_account' }));
|
|
49
51
|
* ```
|
|
50
52
|
*/
|
|
51
53
|
getAuthorizeUrl: (options?: AuthorizeOptions) => string;
|
|
@@ -71,11 +73,13 @@ export declare const Tokenite: (config: TokeniteConfig) => {
|
|
|
71
73
|
* });
|
|
72
74
|
* ```
|
|
73
75
|
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
76
|
+
* On a "Sign in with Tokenite" button shown after the user signed
|
|
77
|
+
* out, pass `prompt: 'select_account'` — Tokenite shows a "Continue
|
|
78
|
+
* as <email>?" card with a "Use a different account" option instead
|
|
79
|
+
* of silently re-authorizing. The user's existing spending limit is
|
|
80
|
+
* preserved:
|
|
77
81
|
* ```typescript
|
|
78
|
-
* const { code } = await tk.popup({ prompt: '
|
|
82
|
+
* const { code } = await tk.popup({ prompt: 'select_account' });
|
|
79
83
|
* ```
|
|
80
84
|
*/
|
|
81
85
|
popup: (options?: PopupOptions) => Promise<PopupResult>;
|
package/dist/client.js
CHANGED
|
@@ -100,11 +100,13 @@ export const Tokenite = (config) => {
|
|
|
100
100
|
/**
|
|
101
101
|
* Build the authorization URL for a full-page redirect.
|
|
102
102
|
*
|
|
103
|
-
* Pass `prompt: '
|
|
104
|
-
* follow a sign-out
|
|
105
|
-
* instead of
|
|
103
|
+
* Pass `prompt: 'select_account'` on "Sign in with Tokenite" buttons
|
|
104
|
+
* that follow a sign-out — Tokenite shows a "Continue as <email>?"
|
|
105
|
+
* card with a "Use a different account" option instead of silently
|
|
106
|
+
* dropping the user back into the app. The user's existing spending
|
|
107
|
+
* limit is preserved (no budget re-entry):
|
|
106
108
|
* ```typescript
|
|
107
|
-
* res.redirect(tk.getAuthorizeUrl({ prompt: '
|
|
109
|
+
* res.redirect(tk.getAuthorizeUrl({ prompt: 'select_account' }));
|
|
108
110
|
* ```
|
|
109
111
|
*/
|
|
110
112
|
getAuthorizeUrl: (options) => buildAuthorizeUrl(options),
|
|
@@ -130,11 +132,13 @@ export const Tokenite = (config) => {
|
|
|
130
132
|
* });
|
|
131
133
|
* ```
|
|
132
134
|
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
135
|
+
* On a "Sign in with Tokenite" button shown after the user signed
|
|
136
|
+
* out, pass `prompt: 'select_account'` — Tokenite shows a "Continue
|
|
137
|
+
* as <email>?" card with a "Use a different account" option instead
|
|
138
|
+
* of silently re-authorizing. The user's existing spending limit is
|
|
139
|
+
* preserved:
|
|
136
140
|
* ```typescript
|
|
137
|
-
* const { code } = await tk.popup({ prompt: '
|
|
141
|
+
* const { code } = await tk.popup({ prompt: 'select_account' });
|
|
138
142
|
* ```
|
|
139
143
|
*/
|
|
140
144
|
popup: (options) => {
|
|
@@ -231,6 +235,10 @@ export const Tokenite = (config) => {
|
|
|
231
235
|
...p,
|
|
232
236
|
logoUrl: absoluteUrl(p.logoUrl, baseUrl),
|
|
233
237
|
})),
|
|
238
|
+
models: data.models.map((m) => ({
|
|
239
|
+
...m,
|
|
240
|
+
creatorLogoUrl: absoluteUrl(m.creatorLogoUrl, baseUrl),
|
|
241
|
+
})),
|
|
234
242
|
};
|
|
235
243
|
},
|
|
236
244
|
/**
|
|
@@ -315,17 +323,17 @@ const handleAuthMessage = (event, baseUrl, resolve, reject, cleanup) => {
|
|
|
315
323
|
};
|
|
316
324
|
const openIframeModal = (url, width, height, baseUrl) => {
|
|
317
325
|
const overlay = document.createElement('div');
|
|
318
|
-
overlay.style.cssText = `
|
|
319
|
-
position: fixed; inset: 0; z-index: 999999;
|
|
320
|
-
background: rgba(0,0,0,0.5); backdrop-filter: blur(2px);
|
|
321
|
-
display: flex; align-items: center; justify-content: center;
|
|
326
|
+
overlay.style.cssText = `
|
|
327
|
+
position: fixed; inset: 0; z-index: 999999;
|
|
328
|
+
background: rgba(0,0,0,0.5); backdrop-filter: blur(2px);
|
|
329
|
+
display: flex; align-items: center; justify-content: center;
|
|
322
330
|
`;
|
|
323
331
|
const container = document.createElement('div');
|
|
324
|
-
container.style.cssText = `
|
|
325
|
-
background: white; border-radius: 12px; overflow: hidden;
|
|
326
|
-
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
|
327
|
-
width: ${width}px; max-width: calc(100vw - 32px);
|
|
328
|
-
height: ${height}px; max-height: calc(100vh - 32px);
|
|
332
|
+
container.style.cssText = `
|
|
333
|
+
background: white; border-radius: 12px; overflow: hidden;
|
|
334
|
+
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
|
335
|
+
width: ${width}px; max-width: calc(100vw - 32px);
|
|
336
|
+
height: ${height}px; max-height: calc(100vh - 32px);
|
|
329
337
|
`;
|
|
330
338
|
const iframe = document.createElement('iframe');
|
|
331
339
|
iframe.src = url;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { Tokenite } from './client.js';
|
|
2
|
-
export type { TokeniteConfig, AuthorizeOptions, OAuthPrompt, PopupOptions, PopupResult, TokenResponse, Provider, ProxyCallOptions, ProxyUsage, ProxySuccess, ProxyError, ProxyResponse, ErrorSource, ProviderInfo, AppInfo, UserInfo, AccessContext, TopUpOptions, TopUpResult, CallWithRecoveryOptions, } from './types.js';
|
|
2
|
+
export type { TokeniteConfig, AuthorizeOptions, OAuthPrompt, PopupOptions, PopupResult, TokenResponse, Provider, ProxyCallOptions, ProxyUsage, ProxySuccess, ProxyError, ProxyResponse, ErrorSource, ProviderInfo, ModelInfo, TierInfo, AppInfo, UserInfo, AccessContext, TopUpOptions, TopUpResult, CallWithRecoveryOptions, } from './types.js';
|
|
3
3
|
export { isProxyError, isProxySuccess } from './types.js';
|
|
4
4
|
export { parseCallback } from './parse-callback.js';
|
|
5
5
|
export type { CallbackResult, CallbackSuccess, CallbackError, CallbackReason, ParseCallbackOptions, } from './parse-callback.js';
|
package/dist/types.d.ts
CHANGED
|
@@ -15,18 +15,20 @@ export type TokeniteConfig = {
|
|
|
15
15
|
* re-prompt the user even when they have an existing session and/or
|
|
16
16
|
* an existing grant for this app.
|
|
17
17
|
*
|
|
18
|
-
* - `'
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
18
|
+
* - `'select_account'` — **recommended for "Sign in with Tokenite"
|
|
19
|
+
* buttons that follow a sign-out.** Interrupts the silent re-auth
|
|
20
|
+
* that would otherwise drop the user straight back into your app:
|
|
21
|
+
* instead Tokenite shows a "Continue as <email>?" confirmation card
|
|
22
|
+
* with a "Use a different account" option. The user's existing
|
|
23
|
+
* spending limit is preserved — they aren't asked to re-set it.
|
|
24
|
+
* - `'consent'` — re-show the **full** consent screen (budget input
|
|
25
|
+
* and all), discarding any existing grant. Use this only when you
|
|
26
|
+
* want the user to actively re-set their budget; for the common
|
|
27
|
+
* "they signed out, now they're signing back in" case, prefer
|
|
28
|
+
* `'select_account'`.
|
|
23
29
|
* - `'login'` — request that the user re-authenticate. Today this
|
|
24
30
|
* behaves the same as `'consent'` on Tokenite (full session-cookie
|
|
25
31
|
* clear is a TODO); the consent screen still shows.
|
|
26
|
-
* - `'select_account'` — show the account picker. Tokenite already
|
|
27
|
-
* does this automatically when the user has multiple accounts;
|
|
28
|
-
* passing it explicitly is a hint for future apps that always want
|
|
29
|
-
* the picker.
|
|
30
32
|
* - `'none'` — never show UI; fail if interaction is required.
|
|
31
33
|
* Currently treated as the default (silent reauth).
|
|
32
34
|
*
|
|
@@ -41,9 +43,11 @@ export type AuthorizeOptions = {
|
|
|
41
43
|
/** Suggested budget amount (user can override on consent screen) */
|
|
42
44
|
readonly suggestedBudget?: number;
|
|
43
45
|
/**
|
|
44
|
-
* OAuth `prompt` parameter. Most common use: pass `'
|
|
45
|
-
*
|
|
46
|
-
*
|
|
46
|
+
* OAuth `prompt` parameter. Most common use: pass `'select_account'`
|
|
47
|
+
* on "Sign in with Tokenite" buttons that follow a sign-out — it
|
|
48
|
+
* stops the silent reauth that would otherwise drop the user
|
|
49
|
+
* straight back into your app, and shows a "Continue as <email>?"
|
|
50
|
+
* confirmation card instead. See {@link OAuthPrompt}.
|
|
47
51
|
*/
|
|
48
52
|
readonly prompt?: OAuthPrompt;
|
|
49
53
|
};
|
|
@@ -67,9 +71,11 @@ export type PopupOptions = {
|
|
|
67
71
|
/** Modal/popup height in pixels. Default: 620 */
|
|
68
72
|
readonly height?: number;
|
|
69
73
|
/**
|
|
70
|
-
* OAuth `prompt` parameter. Most common use: pass `'
|
|
71
|
-
*
|
|
72
|
-
*
|
|
74
|
+
* OAuth `prompt` parameter. Most common use: pass `'select_account'`
|
|
75
|
+
* on "Sign in with Tokenite" buttons that follow a sign-out — it
|
|
76
|
+
* stops the silent reauth that would otherwise drop the user
|
|
77
|
+
* straight back into your app, and shows a "Continue as <email>?"
|
|
78
|
+
* confirmation card instead. See {@link OAuthPrompt}.
|
|
73
79
|
*/
|
|
74
80
|
readonly prompt?: OAuthPrompt;
|
|
75
81
|
};
|
|
@@ -244,6 +250,52 @@ export type ProviderInfo = {
|
|
|
244
250
|
/** Whether the logo is a glyph/symbol or a full wordmark */
|
|
245
251
|
readonly logoStyle: 'symbol' | 'wordmark';
|
|
246
252
|
};
|
|
253
|
+
/**
|
|
254
|
+
* A model the access token may call, scoped to the app's model strategy
|
|
255
|
+
* and the holder's provider keys.
|
|
256
|
+
*
|
|
257
|
+
* - `servedBy` — every provider that hosts this model (catalog fact).
|
|
258
|
+
* - `callableNow` — the subset the holder can run *right now* (they hold
|
|
259
|
+
* a key for it). Empty means the model is visible but not yet usable —
|
|
260
|
+
* render it disabled, or prompt the user to add a key.
|
|
261
|
+
*
|
|
262
|
+
* Pass `slug` as the `model` field in `tk.call()`.
|
|
263
|
+
*/
|
|
264
|
+
export type ModelInfo = {
|
|
265
|
+
/** Stable Tokenite model slug — pass this as `model` in tk.call() */
|
|
266
|
+
readonly slug: string;
|
|
267
|
+
/** Human-readable name, e.g. "Claude Haiku 4.5" */
|
|
268
|
+
readonly displayName: string;
|
|
269
|
+
/** The lab that built the model */
|
|
270
|
+
readonly creator: 'anthropic' | 'openai' | 'google' | 'grok';
|
|
271
|
+
/** Absolute URL to the creator's logo — use it as the model's icon in a picker */
|
|
272
|
+
readonly creatorLogoUrl: string;
|
|
273
|
+
/** Capability tiers this model satisfies (cheap / fast / smart / reasoning) */
|
|
274
|
+
readonly tiers: readonly string[];
|
|
275
|
+
/** Feature capabilities, e.g. "vision", "tools", "thinking" */
|
|
276
|
+
readonly capabilities: readonly string[];
|
|
277
|
+
/** Every provider that serves this model */
|
|
278
|
+
readonly servedBy: readonly Provider[];
|
|
279
|
+
/** Providers the holder can run it through right now (subset of servedBy) */
|
|
280
|
+
readonly callableNow: readonly Provider[];
|
|
281
|
+
/** Indicative price per million tokens */
|
|
282
|
+
readonly pricing: {
|
|
283
|
+
readonly inputPerMillion: number;
|
|
284
|
+
readonly outputPerMillion: number;
|
|
285
|
+
};
|
|
286
|
+
};
|
|
287
|
+
/**
|
|
288
|
+
* A provider-agnostic capability bucket. Use this for a "pick a speed /
|
|
289
|
+
* quality" UI where the user never sees a model name.
|
|
290
|
+
*/
|
|
291
|
+
export type TierInfo = {
|
|
292
|
+
/** Tier id: "cheap" | "fast" | "smart" | "reasoning" */
|
|
293
|
+
readonly id: string;
|
|
294
|
+
/** Whether the holder can run at least one model in this tier */
|
|
295
|
+
readonly reachable: boolean;
|
|
296
|
+
/** A representative callable model slug for this tier, or null */
|
|
297
|
+
readonly recommendedModel: string | null;
|
|
298
|
+
};
|
|
247
299
|
/** Summary of the app the access token belongs to */
|
|
248
300
|
export type AppInfo = {
|
|
249
301
|
readonly id: string;
|
|
@@ -268,5 +320,18 @@ export type AccessContext = {
|
|
|
268
320
|
readonly app: AppInfo;
|
|
269
321
|
readonly user: UserInfo;
|
|
270
322
|
readonly providers: readonly ProviderInfo[];
|
|
323
|
+
/**
|
|
324
|
+
* Models the token may call — already filtered to the app's strategy
|
|
325
|
+
* and the user's keys. Render a picker from this; no need to maintain
|
|
326
|
+
* your own model list. Each entry's `callableNow` says whether it's
|
|
327
|
+
* usable now or needs a key.
|
|
328
|
+
*/
|
|
329
|
+
readonly models: readonly ModelInfo[];
|
|
330
|
+
/**
|
|
331
|
+
* Provider-agnostic capability buckets. For a "pick a tier" UI where
|
|
332
|
+
* the user never sees a model name — `recommendedModel` gives you a
|
|
333
|
+
* concrete slug to pass to `tk.call()`.
|
|
334
|
+
*/
|
|
335
|
+
readonly tiers: readonly TierInfo[];
|
|
271
336
|
};
|
|
272
337
|
//# sourceMappingURL=types.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,53 +1,54 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@tokenite/sdk",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "SDK for integrating \"Login with Tokenite\" into your app. Your users bring their own AI tokens — you pay nothing.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"exports": {
|
|
7
|
-
".": {
|
|
8
|
-
"types": "./dist/index.d.ts",
|
|
9
|
-
"import": "./dist/index.js"
|
|
10
|
-
},
|
|
11
|
-
"./admin": {
|
|
12
|
-
"types": "./dist/admin/index.d.ts",
|
|
13
|
-
"import": "./dist/admin/index.js"
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
"files": [
|
|
17
|
-
"dist",
|
|
18
|
-
"README.md",
|
|
19
|
-
"LICENSE"
|
|
20
|
-
],
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@tokenite/sdk",
|
|
3
|
+
"version": "2.5.0",
|
|
4
|
+
"description": "SDK for integrating \"Login with Tokenite\" into your app. Your users bring their own AI tokens — you pay nothing.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./admin": {
|
|
12
|
+
"types": "./dist/admin/index.d.ts",
|
|
13
|
+
"import": "./dist/admin/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc && npm run generate-readme",
|
|
23
|
+
"typecheck": "tsc --noEmit",
|
|
24
|
+
"generate-readme": "npx tsx scripts/generate-readme.ts",
|
|
25
|
+
"prepack": "tsc && npm run generate-readme && node -e \"import('node:fs/promises').then(async fs => { const files = await fs.readdir('dist', { recursive: true }); for (const f of files) if (f.endsWith('.map') || f.endsWith('.tsbuildinfo')) await fs.rm('dist/' + f); })\""
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"tokenite",
|
|
29
|
+
"llm",
|
|
30
|
+
"ai",
|
|
31
|
+
"proxy",
|
|
32
|
+
"oauth",
|
|
33
|
+
"byok",
|
|
34
|
+
"anthropic",
|
|
35
|
+
"openai",
|
|
36
|
+
"google"
|
|
37
|
+
],
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18"
|
|
41
|
+
},
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/eran-broder/tokenite-sdk.git"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/eran-broder/tokenite-sdk#readme",
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/eran-broder/tokenite-sdk/issues"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"tsx": "^4.0.0",
|
|
52
|
+
"typescript": "^5.0.0"
|
|
53
|
+
}
|
|
54
|
+
}
|
package/dist/client.test.d.ts
DELETED
package/dist/client.test.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { TokenWallet } from './client.js';
|
|
3
|
-
const tw = TokenWallet({
|
|
4
|
-
clientId: 'test-app-id',
|
|
5
|
-
clientSecret: 'test-secret',
|
|
6
|
-
redirectUri: 'https://myapp.com/callback',
|
|
7
|
-
baseUrl: 'https://tokenwallet.ai',
|
|
8
|
-
proxyUrl: 'https://api.tokenwallet.ai',
|
|
9
|
-
});
|
|
10
|
-
describe('TokenWallet', () => {
|
|
11
|
-
describe('getAuthorizeUrl', () => {
|
|
12
|
-
it('builds correct OAuth URL', () => {
|
|
13
|
-
const url = tw.getAuthorizeUrl({ state: 'abc123' });
|
|
14
|
-
expect(url).toBe('https://tokenwallet.ai/oauth/authorize?client_id=test-app-id&redirect_uri=https%3A%2F%2Fmyapp.com%2Fcallback&response_type=code&state=abc123');
|
|
15
|
-
});
|
|
16
|
-
it('auto-generates state if not provided', () => {
|
|
17
|
-
const url = tw.getAuthorizeUrl();
|
|
18
|
-
expect(url).toContain('state=');
|
|
19
|
-
expect(url).toContain('client_id=test-app-id');
|
|
20
|
-
});
|
|
21
|
-
it('generates different state each time', () => {
|
|
22
|
-
const url1 = tw.getAuthorizeUrl();
|
|
23
|
-
const url2 = tw.getAuthorizeUrl();
|
|
24
|
-
const state1 = new URL(url1).searchParams.get('state');
|
|
25
|
-
const state2 = new URL(url2).searchParams.get('state');
|
|
26
|
-
expect(state1).not.toBe(state2);
|
|
27
|
-
});
|
|
28
|
-
it('includes suggested budget and period when provided', () => {
|
|
29
|
-
const url = tw.getAuthorizeUrl({ state: 's1', suggestedBudget: 10, suggestedPeriod: 'weekly' });
|
|
30
|
-
const parsed = new URL(url);
|
|
31
|
-
expect(parsed.searchParams.get('suggested_budget')).toBe('10');
|
|
32
|
-
expect(parsed.searchParams.get('suggested_period')).toBe('weekly');
|
|
33
|
-
});
|
|
34
|
-
it('omits budget params when not provided', () => {
|
|
35
|
-
const url = tw.getAuthorizeUrl({ state: 's2' });
|
|
36
|
-
const parsed = new URL(url);
|
|
37
|
-
expect(parsed.searchParams.has('suggested_budget')).toBe(false);
|
|
38
|
-
expect(parsed.searchParams.has('suggested_period')).toBe(false);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
describe('proxyUrl', () => {
|
|
42
|
-
it('returns correct URL for anthropic', () => {
|
|
43
|
-
expect(tw.proxyUrl('anthropic')).toBe('https://api.tokenwallet.ai/anthropic');
|
|
44
|
-
});
|
|
45
|
-
it('returns correct URL for openai', () => {
|
|
46
|
-
expect(tw.proxyUrl('openai')).toBe('https://api.tokenwallet.ai/openai');
|
|
47
|
-
});
|
|
48
|
-
it('returns correct URL for google', () => {
|
|
49
|
-
expect(tw.proxyUrl('google')).toBe('https://api.tokenwallet.ai/google');
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
describe('exchangeCode', () => {
|
|
53
|
-
it('throws if clientSecret is not set', async () => {
|
|
54
|
-
const noSecret = TokenWallet({ clientId: 'x', redirectUri: 'http://x.com/cb' });
|
|
55
|
-
await expect(noSecret.exchangeCode('code123')).rejects.toThrow('clientSecret is required');
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
describe('defaults', () => {
|
|
59
|
-
it('uses default base URL', () => {
|
|
60
|
-
const client = TokenWallet({ clientId: 'x', redirectUri: 'http://x.com/cb' });
|
|
61
|
-
expect(client.baseUrl).toBe('https://tokenwallet.ai');
|
|
62
|
-
expect(client.proxyBase).toBe('https://api.tokenwallet.ai');
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
//# sourceMappingURL=client.test.js.map
|