dexto 1.11.0 → 1.12.1
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/dist/cli/auth/api-client.d.ts +9 -10
- package/dist/cli/auth/api-client.d.ts.map +1 -1
- package/dist/cli/auth/api-client.js +62 -98
- package/dist/cli/auth/constants.d.ts +2 -0
- package/dist/cli/auth/constants.d.ts.map +1 -1
- package/dist/cli/auth/constants.js +2 -0
- package/dist/cli/auth/index.d.ts +1 -1
- package/dist/cli/auth/index.d.ts.map +1 -1
- package/dist/cli/auth/index.js +1 -1
- package/dist/cli/commands/billing/status.d.ts.map +1 -1
- package/dist/cli/commands/billing/status.js +7 -17
- package/dist/webui/assets/{index-D-sh5G_P.js → index-DUKOkypA.js} +2 -2
- package/dist/webui/index.html +1 -1
- package/package.json +13 -13
|
@@ -30,17 +30,13 @@ export type DeviceCodePollResponse = {
|
|
|
30
30
|
apiKey: DeviceCodeApiKeyResponse;
|
|
31
31
|
};
|
|
32
32
|
export interface UsageSummaryResponse {
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
balance_usd: number;
|
|
34
|
+
last_30_days: {
|
|
35
|
+
since: string;
|
|
35
36
|
total_cost_usd: number;
|
|
36
|
-
|
|
37
|
-
by_model: Record<string, {
|
|
38
|
-
requests: number;
|
|
39
|
-
cost_usd: number;
|
|
40
|
-
tokens: number;
|
|
41
|
-
}>;
|
|
37
|
+
total_usage_events: number;
|
|
42
38
|
};
|
|
43
|
-
|
|
39
|
+
recent_model_usage: Array<{
|
|
44
40
|
timestamp: string;
|
|
45
41
|
model: string;
|
|
46
42
|
cost_usd: number;
|
|
@@ -63,13 +59,16 @@ interface RequestOptions {
|
|
|
63
59
|
*/
|
|
64
60
|
export declare class DextoApiClient {
|
|
65
61
|
private readonly platformBaseUrl;
|
|
62
|
+
private readonly publicApiBaseUrl;
|
|
66
63
|
private readonly timeoutMs;
|
|
67
64
|
constructor(baseUrl?: string | {
|
|
68
65
|
gatewayBaseUrl?: string | undefined;
|
|
69
66
|
platformBaseUrl?: string | undefined;
|
|
67
|
+
publicApiBaseUrl?: string | undefined;
|
|
70
68
|
});
|
|
71
69
|
private createRequestSignal;
|
|
72
70
|
private getPlatformUrl;
|
|
71
|
+
private getPublicApiUrl;
|
|
73
72
|
private listPlatformApiKeys;
|
|
74
73
|
private createPlatformApiKey;
|
|
75
74
|
private deletePlatformApiKey;
|
|
@@ -86,7 +85,7 @@ export declare class DextoApiClient {
|
|
|
86
85
|
keyId: string;
|
|
87
86
|
}>;
|
|
88
87
|
/**
|
|
89
|
-
* Get usage summary (balance +
|
|
88
|
+
* Get usage summary (balance + last 30 days + recent model activity)
|
|
90
89
|
*/
|
|
91
90
|
getUsageSummary(apiKey: string): Promise<UsageSummaryResponse>;
|
|
92
91
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../../src/cli/auth/api-client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../../src/cli/auth/api-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,13 +7,10 @@
|
|
|
7
7
|
// 3. Use tRPC if we want full-stack type safety
|
|
8
8
|
// Currently using plain fetch() with runtime response validation.
|
|
9
9
|
import { logger } from '@dexto/core';
|
|
10
|
-
import { DEXTO_PLATFORM_URL, SUPABASE_ANON_KEY, SUPABASE_URL } from './constants.js';
|
|
10
|
+
import { DEXTO_PLATFORM_URL, DEXTO_PUBLIC_API_URL, SUPABASE_ANON_KEY, SUPABASE_URL, } from './constants.js';
|
|
11
11
|
function parseString(value) {
|
|
12
12
|
return typeof value === 'string' && value.trim().length > 0 ? value : null;
|
|
13
13
|
}
|
|
14
|
-
function parseBoolean(value) {
|
|
15
|
-
return typeof value === 'boolean' ? value : null;
|
|
16
|
-
}
|
|
17
14
|
function parseNumber(value) {
|
|
18
15
|
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
19
16
|
return value;
|
|
@@ -110,37 +107,14 @@ function parseSupabaseUser(payload) {
|
|
|
110
107
|
name: fullName ?? email,
|
|
111
108
|
};
|
|
112
109
|
}
|
|
113
|
-
function parseUsageByModel(payload) {
|
|
114
|
-
if (typeof payload !== 'object' || payload === null) {
|
|
115
|
-
return {};
|
|
116
|
-
}
|
|
117
|
-
const byModel = {};
|
|
118
|
-
for (const [model, value] of Object.entries(payload)) {
|
|
119
|
-
if (typeof value !== 'object' || value === null) {
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
const requests = parseNumber(Reflect.get(value, 'requests'));
|
|
123
|
-
const costUsd = parseNumber(Reflect.get(value, 'cost_usd'));
|
|
124
|
-
const tokens = parseNumber(Reflect.get(value, 'tokens'));
|
|
125
|
-
if (requests === null || costUsd === null || tokens === null) {
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
byModel[model] = {
|
|
129
|
-
requests,
|
|
130
|
-
cost_usd: costUsd,
|
|
131
|
-
tokens,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
return byModel;
|
|
135
|
-
}
|
|
136
110
|
function parseRecentUsage(payload) {
|
|
137
111
|
if (!Array.isArray(payload)) {
|
|
138
|
-
|
|
112
|
+
throw new Error('Invalid response from API');
|
|
139
113
|
}
|
|
140
114
|
const entries = [];
|
|
141
115
|
for (const entry of payload) {
|
|
142
116
|
if (typeof entry !== 'object' || entry === null) {
|
|
143
|
-
|
|
117
|
+
throw new Error('Invalid response from API');
|
|
144
118
|
}
|
|
145
119
|
const timestamp = parseString(Reflect.get(entry, 'timestamp'));
|
|
146
120
|
const model = parseString(Reflect.get(entry, 'model'));
|
|
@@ -152,7 +126,7 @@ function parseRecentUsage(payload) {
|
|
|
152
126
|
costUsd === null ||
|
|
153
127
|
inputTokens === null ||
|
|
154
128
|
outputTokens === null) {
|
|
155
|
-
|
|
129
|
+
throw new Error('Invalid response from API');
|
|
156
130
|
}
|
|
157
131
|
entries.push({
|
|
158
132
|
timestamp,
|
|
@@ -168,26 +142,26 @@ function parseUsageSummaryResponse(payload) {
|
|
|
168
142
|
if (typeof payload !== 'object' || payload === null) {
|
|
169
143
|
throw new Error('Invalid response from API');
|
|
170
144
|
}
|
|
171
|
-
const
|
|
172
|
-
const
|
|
173
|
-
if (
|
|
145
|
+
const balanceUsd = parseNumber(Reflect.get(payload, 'balance_usd'));
|
|
146
|
+
const last30Days = Reflect.get(payload, 'last_30_days');
|
|
147
|
+
if (balanceUsd === null || typeof last30Days !== 'object' || last30Days === null) {
|
|
174
148
|
throw new Error('Invalid response from API');
|
|
175
149
|
}
|
|
176
|
-
const
|
|
177
|
-
const
|
|
178
|
-
|
|
150
|
+
const since = parseString(Reflect.get(last30Days, 'since'));
|
|
151
|
+
const totalCostUsd = parseNumber(Reflect.get(last30Days, 'total_cost_usd'));
|
|
152
|
+
const totalUsageEvents = parseNumber(Reflect.get(last30Days, 'total_usage_events'));
|
|
153
|
+
if (!since || totalCostUsd === null || totalUsageEvents === null) {
|
|
179
154
|
throw new Error('Invalid response from API');
|
|
180
155
|
}
|
|
181
|
-
const
|
|
182
|
-
const recent = parseRecentUsage(Reflect.get(payload, 'recent'));
|
|
156
|
+
const recent = parseRecentUsage(Reflect.get(payload, 'recent_model_usage'));
|
|
183
157
|
return {
|
|
184
|
-
|
|
185
|
-
|
|
158
|
+
balance_usd: balanceUsd,
|
|
159
|
+
last_30_days: {
|
|
160
|
+
since,
|
|
186
161
|
total_cost_usd: totalCostUsd,
|
|
187
|
-
|
|
188
|
-
by_model: byModel,
|
|
162
|
+
total_usage_events: totalUsageEvents,
|
|
189
163
|
},
|
|
190
|
-
recent,
|
|
164
|
+
recent_model_usage: recent,
|
|
191
165
|
};
|
|
192
166
|
}
|
|
193
167
|
function parseBillingBalanceResponse(payload) {
|
|
@@ -217,23 +191,11 @@ function parseBillingCheckoutSessionResponse(payload) {
|
|
|
217
191
|
checkoutSessionId,
|
|
218
192
|
};
|
|
219
193
|
}
|
|
220
|
-
function parseValidateResponse(payload) {
|
|
221
|
-
if (typeof payload !== 'object' || payload === null) {
|
|
222
|
-
return false;
|
|
223
|
-
}
|
|
224
|
-
const valid = parseBoolean(Reflect.get(payload, 'valid'));
|
|
225
|
-
return valid ?? false;
|
|
226
|
-
}
|
|
227
194
|
function parsePlatformKeyListResponse(payload) {
|
|
228
195
|
if (typeof payload !== 'object' || payload === null) {
|
|
229
196
|
throw new Error('Invalid response from API');
|
|
230
197
|
}
|
|
231
|
-
const
|
|
232
|
-
if (success !== true) {
|
|
233
|
-
const error = parseString(Reflect.get(payload, 'error'));
|
|
234
|
-
throw new Error(error || 'Failed to fetch API keys');
|
|
235
|
-
}
|
|
236
|
-
const data = Reflect.get(payload, 'data');
|
|
198
|
+
const data = Reflect.get(payload, 'apiKeys');
|
|
237
199
|
if (!Array.isArray(data)) {
|
|
238
200
|
throw new Error('Invalid response from API');
|
|
239
201
|
}
|
|
@@ -245,35 +207,29 @@ function parsePlatformKeyListResponse(payload) {
|
|
|
245
207
|
const id = parseString(Reflect.get(entry, 'id'));
|
|
246
208
|
const name = parseString(Reflect.get(entry, 'name'));
|
|
247
209
|
const status = parseString(Reflect.get(entry, 'status'));
|
|
248
|
-
|
|
249
|
-
if (!id || !status || isActive === null) {
|
|
210
|
+
if (!id || !name || !status) {
|
|
250
211
|
continue;
|
|
251
212
|
}
|
|
252
|
-
keys.push({ id, name, status
|
|
213
|
+
keys.push({ id, name, status });
|
|
253
214
|
}
|
|
254
|
-
return {
|
|
215
|
+
return { apiKeys: keys };
|
|
255
216
|
}
|
|
256
217
|
function parsePlatformCreateKeyResponse(payload) {
|
|
257
218
|
if (typeof payload !== 'object' || payload === null) {
|
|
258
219
|
throw new Error('Invalid response from API');
|
|
259
220
|
}
|
|
260
|
-
const
|
|
261
|
-
if (success !== true) {
|
|
262
|
-
const error = parseString(Reflect.get(payload, 'error'));
|
|
263
|
-
return { success: false, error: error || 'Failed to create API key' };
|
|
264
|
-
}
|
|
265
|
-
const data = Reflect.get(payload, 'data');
|
|
221
|
+
const data = Reflect.get(payload, 'apiKey');
|
|
266
222
|
if (typeof data !== 'object' || data === null) {
|
|
267
223
|
throw new Error('Invalid response from API');
|
|
268
224
|
}
|
|
269
225
|
const id = parseString(Reflect.get(data, 'id'));
|
|
270
|
-
const fullKey = parseString(Reflect.get(
|
|
226
|
+
const fullKey = parseString(Reflect.get(payload, 'fullKey'));
|
|
271
227
|
if (!id || !fullKey) {
|
|
272
228
|
throw new Error('Invalid response from API');
|
|
273
229
|
}
|
|
274
230
|
return {
|
|
275
|
-
|
|
276
|
-
|
|
231
|
+
apiKey: { id },
|
|
232
|
+
fullKey,
|
|
277
233
|
};
|
|
278
234
|
}
|
|
279
235
|
function formatHttpFailure(status, payload, rawText) {
|
|
@@ -285,21 +241,33 @@ function formatHttpFailure(status, payload, rawText) {
|
|
|
285
241
|
}
|
|
286
242
|
return `${status}`;
|
|
287
243
|
}
|
|
244
|
+
function normalizePublicApiBaseUrl(value) {
|
|
245
|
+
const normalized = value.replace(/\/+$/, '');
|
|
246
|
+
const url = new URL(normalized);
|
|
247
|
+
const isLoopback = ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname);
|
|
248
|
+
if (url.protocol !== 'https:' && !(url.protocol === 'http:' && isLoopback)) {
|
|
249
|
+
throw new Error('publicApiBaseUrl must use HTTPS unless it targets a loopback host');
|
|
250
|
+
}
|
|
251
|
+
return normalized;
|
|
252
|
+
}
|
|
288
253
|
/**
|
|
289
254
|
* Dexto API client for key management
|
|
290
255
|
*/
|
|
291
256
|
export class DextoApiClient {
|
|
292
257
|
platformBaseUrl;
|
|
258
|
+
publicApiBaseUrl;
|
|
293
259
|
timeoutMs = 10_000;
|
|
294
260
|
constructor(baseUrl = {}) {
|
|
295
261
|
if (typeof baseUrl === 'string') {
|
|
296
262
|
const normalized = baseUrl.replace(/\/+$/, '');
|
|
297
263
|
this.platformBaseUrl = normalized;
|
|
264
|
+
this.publicApiBaseUrl = normalizePublicApiBaseUrl(normalized);
|
|
298
265
|
return;
|
|
299
266
|
}
|
|
300
267
|
this.platformBaseUrl = (baseUrl.platformBaseUrl ??
|
|
301
268
|
baseUrl.gatewayBaseUrl ??
|
|
302
269
|
DEXTO_PLATFORM_URL).replace(/\/+$/, '');
|
|
270
|
+
this.publicApiBaseUrl = normalizePublicApiBaseUrl(baseUrl.publicApiBaseUrl ?? DEXTO_PUBLIC_API_URL);
|
|
303
271
|
}
|
|
304
272
|
createRequestSignal(signal) {
|
|
305
273
|
const timeoutSignal = AbortSignal.timeout(this.timeoutMs);
|
|
@@ -311,8 +279,11 @@ export class DextoApiClient {
|
|
|
311
279
|
getPlatformUrl(path) {
|
|
312
280
|
return `${this.platformBaseUrl}${path}`;
|
|
313
281
|
}
|
|
282
|
+
getPublicApiUrl(path) {
|
|
283
|
+
return `${this.publicApiBaseUrl}${path}`;
|
|
284
|
+
}
|
|
314
285
|
async listPlatformApiKeys(authToken) {
|
|
315
|
-
const response = await fetch(this.getPlatformUrl('/api/keys'), {
|
|
286
|
+
const response = await fetch(this.getPlatformUrl('/api/api-keys'), {
|
|
316
287
|
method: 'GET',
|
|
317
288
|
headers: {
|
|
318
289
|
Authorization: `Bearer ${authToken}`,
|
|
@@ -324,10 +295,10 @@ export class DextoApiClient {
|
|
|
324
295
|
throw new Error(`API request failed: ${formatHttpFailure(response.status, null, rawText)}`);
|
|
325
296
|
}
|
|
326
297
|
const payload = await response.json();
|
|
327
|
-
return parsePlatformKeyListResponse(payload).
|
|
298
|
+
return parsePlatformKeyListResponse(payload).apiKeys;
|
|
328
299
|
}
|
|
329
300
|
async createPlatformApiKey(authToken, name) {
|
|
330
|
-
const response = await fetch(this.getPlatformUrl('/api/keys'), {
|
|
301
|
+
const response = await fetch(this.getPlatformUrl('/api/api-keys'), {
|
|
331
302
|
method: 'POST',
|
|
332
303
|
headers: {
|
|
333
304
|
Authorization: `Bearer ${authToken}`,
|
|
@@ -342,16 +313,13 @@ export class DextoApiClient {
|
|
|
342
313
|
}
|
|
343
314
|
const payload = await response.json();
|
|
344
315
|
const result = parsePlatformCreateKeyResponse(payload);
|
|
345
|
-
if (!result.success || !result.data) {
|
|
346
|
-
throw new Error(result.error || 'Failed to create Dexto API key');
|
|
347
|
-
}
|
|
348
316
|
return {
|
|
349
|
-
dextoApiKey: result.
|
|
350
|
-
keyId: result.
|
|
317
|
+
dextoApiKey: result.fullKey,
|
|
318
|
+
keyId: result.apiKey.id,
|
|
351
319
|
};
|
|
352
320
|
}
|
|
353
321
|
async deletePlatformApiKey(authToken, keyId) {
|
|
354
|
-
const response = await fetch(this.getPlatformUrl(`/api/keys/${encodeURIComponent(keyId)}`), {
|
|
322
|
+
const response = await fetch(this.getPlatformUrl(`/api/api-keys/${encodeURIComponent(keyId)}`), {
|
|
355
323
|
method: 'DELETE',
|
|
356
324
|
headers: {
|
|
357
325
|
Authorization: `Bearer ${authToken}`,
|
|
@@ -367,25 +335,21 @@ export class DextoApiClient {
|
|
|
367
335
|
* Validate if a Dexto API key is valid
|
|
368
336
|
*/
|
|
369
337
|
async validateDextoApiKey(apiKey) {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
if (!response.ok) {
|
|
380
|
-
return false;
|
|
381
|
-
}
|
|
382
|
-
const payload = await response.json();
|
|
383
|
-
return parseValidateResponse(payload);
|
|
384
|
-
}
|
|
385
|
-
catch (error) {
|
|
386
|
-
logger.error(`Error validating Dexto API key: ${error}`);
|
|
338
|
+
logger.debug('Validating DEXTO_API_KEY');
|
|
339
|
+
const response = await fetch(this.getPublicApiUrl('/v1/models'), {
|
|
340
|
+
method: 'GET',
|
|
341
|
+
headers: {
|
|
342
|
+
Authorization: `Bearer ${apiKey}`,
|
|
343
|
+
},
|
|
344
|
+
signal: this.createRequestSignal(undefined),
|
|
345
|
+
});
|
|
346
|
+
if (response.status === 401) {
|
|
387
347
|
return false;
|
|
388
348
|
}
|
|
349
|
+
if (!response.ok) {
|
|
350
|
+
throw new Error(`Unable to validate Dexto API key: HTTP ${response.status}`);
|
|
351
|
+
}
|
|
352
|
+
return true;
|
|
389
353
|
}
|
|
390
354
|
/**
|
|
391
355
|
* Provision Dexto API key and always return a usable secret key value.
|
|
@@ -394,7 +358,7 @@ export class DextoApiClient {
|
|
|
394
358
|
async provisionDextoApiKey(authToken, name = 'Dexto CLI Key', regenerate = false) {
|
|
395
359
|
try {
|
|
396
360
|
logger.debug(`Provisioning DEXTO_API_KEY with name: ${name}, regenerate: ${regenerate}`);
|
|
397
|
-
const matchingKeys = (await this.listPlatformApiKeys(authToken)).filter((key) => key.
|
|
361
|
+
const matchingKeys = (await this.listPlatformApiKeys(authToken)).filter((key) => key.status === 'active' && key.name === name);
|
|
398
362
|
if (matchingKeys.length > 0) {
|
|
399
363
|
if (!regenerate) {
|
|
400
364
|
logger.debug(`Active key exists (${matchingKeys[0]?.id ?? 'unknown'}); rotating to obtain key value`);
|
|
@@ -416,12 +380,12 @@ export class DextoApiClient {
|
|
|
416
380
|
}
|
|
417
381
|
}
|
|
418
382
|
/**
|
|
419
|
-
* Get usage summary (balance +
|
|
383
|
+
* Get usage summary (balance + last 30 days + recent model activity)
|
|
420
384
|
*/
|
|
421
385
|
async getUsageSummary(apiKey) {
|
|
422
386
|
try {
|
|
423
387
|
logger.debug('Fetching usage summary');
|
|
424
|
-
const response = await fetch(this.
|
|
388
|
+
const response = await fetch(this.getPublicApiUrl('/v1/billing/summary'), {
|
|
425
389
|
method: 'GET',
|
|
426
390
|
headers: {
|
|
427
391
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* - SUPABASE_ANON_KEY: Override anon key (from `supabase start` output)
|
|
15
15
|
* - DEXTO_API_URL: Override Dexto gateway URL for data-plane calls (e.g., http://localhost:3001)
|
|
16
16
|
* - DEXTO_PLATFORM_URL: Override Dexto app URL for auth/key/account control-plane calls (e.g., http://localhost:3002)
|
|
17
|
+
* - DEXTO_PUBLIC_API_URL: Override versioned public API URL (e.g., http://localhost:3002)
|
|
17
18
|
*/
|
|
18
19
|
export declare const SUPABASE_URL: string;
|
|
19
20
|
export declare const SUPABASE_ANON_KEY: string;
|
|
@@ -25,6 +26,7 @@ export declare const DEXTO_API_URL: string;
|
|
|
25
26
|
* Dexto app URL for auth/key/account control-plane endpoints.
|
|
26
27
|
*/
|
|
27
28
|
export declare const DEXTO_PLATFORM_URL: string;
|
|
29
|
+
export declare const DEXTO_PUBLIC_API_URL: string;
|
|
28
30
|
/**
|
|
29
31
|
* Dexto Nova credits purchase URL
|
|
30
32
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/cli/auth/constants.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/cli/auth/constants.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,YAAY,QAAyE,CAAC;AACnG,eAAO,MAAM,iBAAiB,QAEwL,CAAC;AAEvN;;GAEG;AACH,eAAO,MAAM,aAAa,QAAsD,CAAC;AAEjF;;GAEG;AACH,eAAO,MAAM,kBAAkB,QAA2D,CAAC;AAE3F,eAAO,MAAM,oBAAoB,QAA6D,CAAC;AAE/F;;GAEG;AACH,eAAO,MAAM,iBAAiB,QACwD,CAAC"}
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* - SUPABASE_ANON_KEY: Override anon key (from `supabase start` output)
|
|
16
16
|
* - DEXTO_API_URL: Override Dexto gateway URL for data-plane calls (e.g., http://localhost:3001)
|
|
17
17
|
* - DEXTO_PLATFORM_URL: Override Dexto app URL for auth/key/account control-plane calls (e.g., http://localhost:3002)
|
|
18
|
+
* - DEXTO_PUBLIC_API_URL: Override versioned public API URL (e.g., http://localhost:3002)
|
|
18
19
|
*/
|
|
19
20
|
export const SUPABASE_URL = process.env.SUPABASE_URL || 'https://gdfbxznhnnsamvsrtwjq.supabase.co';
|
|
20
21
|
export const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY ||
|
|
@@ -27,6 +28,7 @@ export const DEXTO_API_URL = process.env.DEXTO_API_URL || 'https://app.dexto.ai'
|
|
|
27
28
|
* Dexto app URL for auth/key/account control-plane endpoints.
|
|
28
29
|
*/
|
|
29
30
|
export const DEXTO_PLATFORM_URL = process.env.DEXTO_PLATFORM_URL || 'https://app.dexto.ai';
|
|
31
|
+
export const DEXTO_PUBLIC_API_URL = process.env.DEXTO_PUBLIC_API_URL || 'https://api.dexto.ai';
|
|
30
32
|
/**
|
|
31
33
|
* Dexto Nova credits purchase URL
|
|
32
34
|
*/
|
package/dist/cli/auth/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { type DeviceApiKeyLoginResult } from './types.js';
|
|
|
4
4
|
export { type DeviceLoginPrompt, performDeviceCodeLogin } from './device.js';
|
|
5
5
|
export { type PersistOAuthLoginOptions, type PersistedLoginResult, persistDeviceApiKeyLoginResult, persistOAuthLoginResult, } from './login-persistence.js';
|
|
6
6
|
export { type UsageSummaryResponse, DextoApiClient, getDextoApiClient } from './api-client.js';
|
|
7
|
-
export { SUPABASE_URL, SUPABASE_ANON_KEY, DEXTO_API_URL, DEXTO_PLATFORM_URL } from './constants.js';
|
|
7
|
+
export { SUPABASE_URL, SUPABASE_ANON_KEY, DEXTO_API_URL, DEXTO_PLATFORM_URL, DEXTO_PUBLIC_API_URL, } from './constants.js';
|
|
8
8
|
export { buildDextoBillingUrl, getBillingBalanceForCurrentLogin, createBillingCheckoutForCurrentLogin, openDextoBillingPage, } from './billing.js';
|
|
9
9
|
export { type DextoApiKeyProvisionStatus, type DextoApiKeyProvisionStatusLevel, type EnsureDextoApiKeyOptions, ensureDextoApiKeyForAuthToken, saveDextoApiKeyToEnv, removeDextoApiKeyFromEnv, } from './dexto-api-key.js';
|
|
10
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/auth/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACH,KAAK,UAAU,EACf,SAAS,EACT,QAAQ,EACR,UAAU,EACV,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,cAAc,EACd,eAAe,GAClB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,KAAK,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAE7E,OAAO,EACH,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,8BAA8B,EAC9B,uBAAuB,GAC1B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,KAAK,oBAAoB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAE/F,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/auth/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACH,KAAK,UAAU,EACf,SAAS,EACT,QAAQ,EACR,UAAU,EACV,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,cAAc,EACd,eAAe,GAClB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,KAAK,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAE7E,OAAO,EACH,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,8BAA8B,EAC9B,uBAAuB,GAC1B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,KAAK,oBAAoB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAE/F,OAAO,EACH,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EAClB,oBAAoB,GACvB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACH,oBAAoB,EACpB,gCAAgC,EAChC,oCAAoC,EACpC,oBAAoB,GACvB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACH,KAAK,0BAA0B,EAC/B,KAAK,+BAA+B,EACpC,KAAK,wBAAwB,EAC7B,6BAA6B,EAC7B,oBAAoB,EACpB,wBAAwB,GAC3B,MAAM,oBAAoB,CAAC"}
|
package/dist/cli/auth/index.js
CHANGED
|
@@ -4,6 +4,6 @@ export { storeAuth, loadAuth, removeAuth, isAuthenticated, getAuthToken, getAuth
|
|
|
4
4
|
export { performDeviceCodeLogin } from './device.js';
|
|
5
5
|
export { persistDeviceApiKeyLoginResult, persistOAuthLoginResult, } from './login-persistence.js';
|
|
6
6
|
export { DextoApiClient, getDextoApiClient } from './api-client.js';
|
|
7
|
-
export { SUPABASE_URL, SUPABASE_ANON_KEY, DEXTO_API_URL, DEXTO_PLATFORM_URL } from './constants.js';
|
|
7
|
+
export { SUPABASE_URL, SUPABASE_ANON_KEY, DEXTO_API_URL, DEXTO_PLATFORM_URL, DEXTO_PUBLIC_API_URL, } from './constants.js';
|
|
8
8
|
export { buildDextoBillingUrl, getBillingBalanceForCurrentLogin, createBillingCheckoutForCurrentLogin, openDextoBillingPage, } from './billing.js';
|
|
9
9
|
export { ensureDextoApiKeyForAuthToken, saveDextoApiKeyToEnv, removeDextoApiKeyFromEnv, } from './dexto-api-key.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/billing/status.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,wBAAsB,0BAA0B,CAAC,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/billing/status.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,wBAAsB,0BAA0B,CAAC,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAmE/F"}
|
|
@@ -34,27 +34,17 @@ export async function handleBillingStatusCommand(options = {}) {
|
|
|
34
34
|
const usage = await apiClient.getUsageSummary(auth.dextoApiKey);
|
|
35
35
|
// Display balance
|
|
36
36
|
console.log(chalk.cyan('💰 Balance'));
|
|
37
|
-
console.log(` ${chalk.bold('$' + usage.
|
|
37
|
+
console.log(` ${chalk.bold('$' + usage.balance_usd.toFixed(2))} remaining`);
|
|
38
38
|
console.log(chalk.dim(` Buy more credits: run ${chalk.cyan('dexto billing --buy')}`));
|
|
39
39
|
console.log();
|
|
40
|
-
|
|
41
|
-
console.log(chalk.
|
|
42
|
-
console.log(`
|
|
43
|
-
console.log(` Requests: ${chalk.yellow(usage.mtd_usage.total_requests.toString())}`);
|
|
44
|
-
// Show usage by model if there's any
|
|
45
|
-
const modelEntries = Object.entries(usage.mtd_usage.by_model);
|
|
46
|
-
if (modelEntries.length > 0) {
|
|
47
|
-
console.log();
|
|
48
|
-
console.log(chalk.cyan('📈 Usage by Model'));
|
|
49
|
-
for (const [model, stats] of modelEntries) {
|
|
50
|
-
console.log(` ${chalk.dim(model)}: $${stats.cost_usd.toFixed(4)} (${stats.requests} requests)`);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
40
|
+
console.log(chalk.cyan('📊 Last 30 Days'));
|
|
41
|
+
console.log(` Spent: ${chalk.yellow('$' + usage.last_30_days.total_cost_usd.toFixed(4))}`);
|
|
42
|
+
console.log(` Usage events: ${chalk.yellow(usage.last_30_days.total_usage_events.toString())}`);
|
|
53
43
|
// Show recent usage if any
|
|
54
|
-
if (usage.
|
|
44
|
+
if (usage.recent_model_usage.length > 0) {
|
|
55
45
|
console.log();
|
|
56
46
|
console.log(chalk.cyan('🕐 Recent Activity'));
|
|
57
|
-
for (const entry of usage.
|
|
47
|
+
for (const entry of usage.recent_model_usage.slice(0, 5)) {
|
|
58
48
|
const date = new Date(entry.timestamp).toLocaleString();
|
|
59
49
|
console.log(` ${chalk.dim(date)} - ${entry.model}: $${entry.cost_usd.toFixed(4)}`);
|
|
60
50
|
}
|
|
@@ -63,7 +53,7 @@ export async function handleBillingStatusCommand(options = {}) {
|
|
|
63
53
|
catch (error) {
|
|
64
54
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
65
55
|
console.log(chalk.red(`❌ Failed to fetch billing info: ${errorMessage}`));
|
|
66
|
-
console.log(chalk.dim('
|
|
56
|
+
console.log(chalk.dim('Try again shortly. If your API key is invalid, run `dexto login`.'));
|
|
67
57
|
}
|
|
68
58
|
if (options.buy) {
|
|
69
59
|
await openCreditsPage();
|