dexto 1.14.6 → 2.0.0-beta.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/agents/coding-agent/coding-agent.yml +0 -2
- package/dist/cli/agent-audit.d.ts +12 -0
- package/dist/cli/agent-audit.d.ts.map +1 -0
- package/dist/cli/agent-audit.js +52 -0
- package/dist/cli/auth/billing.d.ts +0 -6
- package/dist/cli/auth/billing.d.ts.map +1 -1
- package/dist/cli/auth/billing.js +0 -23
- package/dist/cli/auth/constants.d.ts +3 -31
- package/dist/cli/auth/constants.d.ts.map +1 -1
- package/dist/cli/auth/constants.js +9 -33
- package/dist/cli/auth/index.d.ts +2 -9
- package/dist/cli/auth/index.d.ts.map +1 -1
- package/dist/cli/auth/index.js +2 -7
- package/dist/cli/auth/sign-in-runtime.d.ts +12 -0
- package/dist/cli/auth/sign-in-runtime.d.ts.map +1 -0
- package/dist/cli/auth/sign-in-runtime.js +59 -0
- package/dist/cli/cloud/app-scopes.d.ts +4 -0
- package/dist/cli/cloud/app-scopes.d.ts.map +1 -0
- package/dist/cli/cloud/app-scopes.js +19 -0
- package/dist/cli/cloud/command.d.ts +107 -0
- package/dist/cli/cloud/command.d.ts.map +1 -0
- package/dist/cli/cloud/command.js +154 -0
- package/dist/cli/cloud/commands/account.d.ts +11 -0
- package/dist/cli/cloud/commands/account.d.ts.map +1 -0
- package/dist/cli/cloud/commands/account.js +129 -0
- package/dist/cli/cloud/commands/agents.d.ts +3 -0
- package/dist/cli/cloud/commands/agents.d.ts.map +1 -0
- package/dist/cli/cloud/commands/agents.js +526 -0
- package/dist/cli/cloud/commands/api.d.ts +3 -0
- package/dist/cli/cloud/commands/api.d.ts.map +1 -0
- package/dist/cli/cloud/commands/api.js +26 -0
- package/dist/cli/cloud/commands/app-files.d.ts +29 -0
- package/dist/cli/cloud/commands/app-files.d.ts.map +1 -0
- package/dist/cli/cloud/commands/app-files.js +72 -0
- package/dist/cli/cloud/commands/artifacts.d.ts +3 -0
- package/dist/cli/cloud/commands/artifacts.d.ts.map +1 -0
- package/dist/cli/cloud/commands/artifacts.js +267 -0
- package/dist/cli/cloud/commands/automations.d.ts +3 -0
- package/dist/cli/cloud/commands/automations.d.ts.map +1 -0
- package/dist/cli/cloud/commands/automations.js +233 -0
- package/dist/cli/cloud/commands/chats.d.ts +3 -0
- package/dist/cli/cloud/commands/chats.d.ts.map +1 -0
- package/dist/cli/cloud/commands/chats.js +204 -0
- package/dist/cli/cloud/commands/data-deletions.d.ts +3 -0
- package/dist/cli/cloud/commands/data-deletions.d.ts.map +1 -0
- package/dist/cli/cloud/commands/data-deletions.js +55 -0
- package/dist/cli/cloud/commands/deploys.d.ts +10 -0
- package/dist/cli/cloud/commands/deploys.d.ts.map +1 -0
- package/dist/cli/cloud/commands/deploys.js +225 -0
- package/dist/cli/cloud/commands/drive.d.ts +12 -0
- package/dist/cli/cloud/commands/drive.d.ts.map +1 -0
- package/dist/cli/cloud/commands/drive.js +321 -0
- package/dist/cli/cloud/commands/files.d.ts +24 -0
- package/dist/cli/cloud/commands/files.d.ts.map +1 -0
- package/dist/cli/cloud/commands/files.js +284 -0
- package/dist/cli/cloud/commands/find.d.ts +3 -0
- package/dist/cli/cloud/commands/find.d.ts.map +1 -0
- package/dist/cli/cloud/commands/find.js +63 -0
- package/dist/cli/cloud/commands/models.d.ts +3 -0
- package/dist/cli/cloud/commands/models.d.ts.map +1 -0
- package/dist/cli/cloud/commands/models.js +250 -0
- package/dist/cli/cloud/commands/profile.d.ts +3 -0
- package/dist/cli/cloud/commands/profile.d.ts.map +1 -0
- package/dist/cli/cloud/commands/profile.js +120 -0
- package/dist/cli/cloud/commands/projects.d.ts +5 -0
- package/dist/cli/cloud/commands/projects.d.ts.map +1 -0
- package/dist/cli/cloud/commands/projects.js +66 -0
- package/dist/cli/cloud/commands/publish.d.ts +3 -0
- package/dist/cli/cloud/commands/publish.d.ts.map +1 -0
- package/dist/cli/cloud/commands/publish.js +139 -0
- package/dist/cli/cloud/commands/sandboxes.d.ts +3 -0
- package/dist/cli/cloud/commands/sandboxes.d.ts.map +1 -0
- package/dist/cli/cloud/commands/sandboxes.js +266 -0
- package/dist/cli/cloud/commands/secrets.d.ts +3 -0
- package/dist/cli/cloud/commands/secrets.d.ts.map +1 -0
- package/dist/cli/cloud/commands/secrets.js +111 -0
- package/dist/cli/cloud/commands/skills.d.ts +3 -0
- package/dist/cli/cloud/commands/skills.d.ts.map +1 -0
- package/dist/cli/cloud/commands/skills.js +203 -0
- package/dist/cli/cloud/commands/traces.d.ts +3 -0
- package/dist/cli/cloud/commands/traces.d.ts.map +1 -0
- package/dist/cli/cloud/commands/traces.js +142 -0
- package/dist/cli/cloud/commands/web.d.ts +3 -0
- package/dist/cli/cloud/commands/web.d.ts.map +1 -0
- package/dist/cli/cloud/commands/web.js +53 -0
- package/dist/cli/cloud/commands/workspace-apps.d.ts +58 -0
- package/dist/cli/cloud/commands/workspace-apps.d.ts.map +1 -0
- package/dist/cli/cloud/commands/workspace-apps.js +657 -0
- package/dist/cli/cloud/commands/workspace.d.ts +36 -0
- package/dist/cli/cloud/commands/workspace.d.ts.map +1 -0
- package/dist/cli/cloud/commands/workspace.js +281 -0
- package/dist/cli/cloud/connection.d.ts +33 -0
- package/dist/cli/cloud/connection.d.ts.map +1 -0
- package/dist/cli/cloud/connection.js +38 -0
- package/dist/cli/cloud/events.d.ts +63 -0
- package/dist/cli/cloud/events.d.ts.map +1 -0
- package/dist/cli/cloud/events.js +72 -0
- package/dist/cli/cloud/host.d.ts +9 -0
- package/dist/cli/cloud/host.d.ts.map +1 -0
- package/dist/cli/cloud/host.js +109 -0
- package/dist/cli/cloud/output.d.ts +34 -0
- package/dist/cli/cloud/output.d.ts.map +1 -0
- package/dist/cli/cloud/output.js +77 -0
- package/dist/cli/cloud/run.d.ts +26 -0
- package/dist/cli/cloud/run.d.ts.map +1 -0
- package/dist/cli/cloud/run.js +444 -0
- package/dist/cli/cloud/runtime.d.ts +33 -0
- package/dist/cli/cloud/runtime.d.ts.map +1 -0
- package/dist/cli/cloud/runtime.js +86 -0
- package/dist/cli/cloud/tar.d.ts +9 -0
- package/dist/cli/cloud/tar.d.ts.map +1 -0
- package/dist/cli/cloud/tar.js +69 -0
- package/dist/cli/cloud/transfer.d.ts +13 -0
- package/dist/cli/cloud/transfer.d.ts.map +1 -0
- package/dist/cli/cloud/transfer.js +36 -0
- package/dist/cli/cloud/v1.d.ts +20 -0
- package/dist/cli/cloud/v1.d.ts.map +1 -0
- package/dist/cli/cloud/v1.js +66 -0
- package/dist/cli/cloud/zip.d.ts +8 -0
- package/dist/cli/cloud/zip.d.ts.map +1 -0
- package/dist/cli/cloud/zip.js +66 -0
- package/dist/cli/commands/auth/sign-in-commands.d.ts +62 -0
- package/dist/cli/commands/auth/sign-in-commands.d.ts.map +1 -0
- package/dist/cli/commands/auth/sign-in-commands.js +248 -0
- package/dist/cli/commands/auth/sign-in-context.d.ts +16 -0
- package/dist/cli/commands/auth/sign-in-context.d.ts.map +1 -0
- package/dist/cli/commands/auth/sign-in-context.js +68 -0
- package/dist/cli/commands/cloud/register.d.ts +5 -0
- package/dist/cli/commands/cloud/register.d.ts.map +1 -1
- package/dist/cli/commands/cloud/register.js +44 -24
- package/dist/cli/commands/create-app.js +5 -6
- package/dist/cli/commands/create-image.d.ts.map +1 -1
- package/dist/cli/commands/create-image.js +9 -10
- package/dist/cli/commands/index.d.ts +0 -2
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +0 -2
- package/dist/cli/commands/init.d.ts +0 -2
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +5 -26
- package/dist/cli/commands/run/headless.d.ts.map +1 -1
- package/dist/cli/commands/run/headless.js +57 -2
- package/dist/cli/commands/run/output.d.ts +29 -10
- package/dist/cli/commands/run/output.d.ts.map +1 -1
- package/dist/cli/commands/run/output.js +44 -22
- package/dist/cli/commands/run/register.d.ts.map +1 -1
- package/dist/cli/commands/run/register.js +21 -9
- package/dist/cli/commands/run/step-log.d.ts +11 -0
- package/dist/cli/commands/run/step-log.d.ts.map +1 -0
- package/dist/cli/commands/run/step-log.js +42 -0
- package/dist/cli/commands/session/register.d.ts.map +1 -1
- package/dist/cli/commands/session/register.js +51 -21
- package/dist/cli/commands/session-commands.js +1 -1
- package/dist/cli/commands/setup.js +17 -14
- package/dist/cli/failure.d.ts +29 -0
- package/dist/cli/failure.d.ts.map +1 -0
- package/dist/cli/failure.js +31 -0
- package/dist/cli/help-json.d.ts +22 -0
- package/dist/cli/help-json.d.ts.map +1 -0
- package/dist/cli/help-json.js +20 -0
- package/dist/cli/import/claude-code.d.ts +10 -0
- package/dist/cli/import/claude-code.d.ts.map +1 -0
- package/dist/cli/import/claude-code.js +131 -0
- package/dist/cli/import/codex.d.ts +27 -0
- package/dist/cli/import/codex.d.ts.map +1 -0
- package/dist/cli/import/codex.js +148 -0
- package/dist/cli/import/git.d.ts +19 -0
- package/dist/cli/import/git.d.ts.map +1 -0
- package/dist/cli/import/git.js +95 -0
- package/dist/cli/import/local-chats.d.ts +28 -0
- package/dist/cli/import/local-chats.d.ts.map +1 -0
- package/dist/cli/import/local-chats.js +129 -0
- package/dist/cli/import/redaction.d.ts +37 -0
- package/dist/cli/import/redaction.d.ts.map +1 -0
- package/dist/cli/import/redaction.js +148 -0
- package/dist/cli/import/test-utils.d.ts +16 -0
- package/dist/cli/import/test-utils.d.ts.map +1 -0
- package/dist/cli/import/test-utils.js +223 -0
- package/dist/cli/import/transcript.d.ts +75 -0
- package/dist/cli/import/transcript.d.ts.map +1 -0
- package/dist/cli/import/transcript.js +123 -0
- package/dist/cli/interaction.d.ts +18 -0
- package/dist/cli/interaction.d.ts.map +1 -0
- package/dist/cli/interaction.js +30 -0
- package/dist/cli/modes/cli.d.ts.map +1 -1
- package/dist/cli/modes/cli.js +42 -9
- package/dist/cli/modes/server.js +8 -8
- package/dist/cli/modes/web.js +3 -3
- package/dist/cli/output.d.ts +9 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +6 -0
- package/dist/cli/utils/dexto-auth-check.d.ts +1 -1
- package/dist/cli/utils/dexto-auth-check.d.ts.map +1 -1
- package/dist/cli/utils/dexto-auth-check.js +4 -5
- package/dist/cli/utils/dexto-setup.d.ts +4 -5
- package/dist/cli/utils/dexto-setup.d.ts.map +1 -1
- package/dist/cli/utils/dexto-setup.js +17 -14
- package/dist/cli/utils/provider-setup.js +1 -1
- package/dist/cli/utils/scaffolding-utils.d.ts +9 -3
- package/dist/cli/utils/scaffolding-utils.d.ts.map +1 -1
- package/dist/cli/utils/scaffolding-utils.js +48 -40
- package/dist/cli/utils/version-check.d.ts.map +1 -1
- package/dist/cli/utils/version-check.js +2 -1
- package/dist/index-main.js +44 -65
- package/dist/webui/assets/index-D9xiOs62.js +1649 -0
- package/dist/webui/index.html +1 -1
- package/package.json +15 -14
- package/dist/cli/auth/api-client.d.ts +0 -125
- package/dist/cli/auth/api-client.d.ts.map +0 -1
- package/dist/cli/auth/api-client.js +0 -585
- package/dist/cli/auth/device.d.ts +0 -14
- package/dist/cli/auth/device.d.ts.map +0 -1
- package/dist/cli/auth/device.js +0 -84
- package/dist/cli/auth/dexto-api-key.d.ts +0 -34
- package/dist/cli/auth/dexto-api-key.d.ts.map +0 -1
- package/dist/cli/auth/dexto-api-key.js +0 -160
- package/dist/cli/auth/login-persistence.d.ts +0 -15
- package/dist/cli/auth/login-persistence.d.ts.map +0 -1
- package/dist/cli/auth/login-persistence.js +0 -72
- package/dist/cli/auth/oauth.d.ts +0 -39
- package/dist/cli/auth/oauth.d.ts.map +0 -1
- package/dist/cli/auth/oauth.js +0 -434
- package/dist/cli/auth/service.d.ts +0 -24
- package/dist/cli/auth/service.d.ts.map +0 -1
- package/dist/cli/auth/service.js +0 -161
- package/dist/cli/auth/types.d.ts +0 -11
- package/dist/cli/auth/types.d.ts.map +0 -1
- package/dist/cli/auth/types.js +0 -1
- package/dist/cli/cloud-chat.d.ts +0 -18
- package/dist/cli/cloud-chat.d.ts.map +0 -1
- package/dist/cli/cloud-chat.js +0 -734
- package/dist/cli/commands/auth/index.d.ts +0 -4
- package/dist/cli/commands/auth/index.d.ts.map +0 -1
- package/dist/cli/commands/auth/index.js +0 -4
- package/dist/cli/commands/auth/login.d.ts +0 -12
- package/dist/cli/commands/auth/login.d.ts.map +0 -1
- package/dist/cli/commands/auth/login.js +0 -153
- package/dist/cli/commands/auth/logout.d.ts +0 -5
- package/dist/cli/commands/auth/logout.d.ts.map +0 -1
- package/dist/cli/commands/auth/logout.js +0 -81
- package/dist/cli/commands/auth/register.d.ts +0 -3
- package/dist/cli/commands/auth/register.d.ts.map +0 -1
- package/dist/cli/commands/auth/register.js +0 -96
- package/dist/cli/commands/auth/status.d.ts +0 -2
- package/dist/cli/commands/auth/status.d.ts.map +0 -1
- package/dist/cli/commands/auth/status.js +0 -22
- package/dist/cli/commands/billing/index.d.ts +0 -2
- package/dist/cli/commands/billing/index.d.ts.map +0 -1
- package/dist/cli/commands/billing/index.js +0 -2
- package/dist/cli/commands/billing/register.d.ts +0 -3
- package/dist/cli/commands/billing/register.d.ts.map +0 -1
- package/dist/cli/commands/billing/register.js +0 -20
- package/dist/cli/commands/billing/status.d.ts +0 -8
- package/dist/cli/commands/billing/status.d.ts.map +0 -1
- package/dist/cli/commands/billing/status.js +0 -71
- package/dist/cli/commands/cloud/discovery.d.ts +0 -17
- package/dist/cli/commands/cloud/discovery.d.ts.map +0 -1
- package/dist/cli/commands/cloud/discovery.js +0 -88
- package/dist/cli/commands/deploy/client.d.ts +0 -113
- package/dist/cli/commands/deploy/client.d.ts.map +0 -1
- package/dist/cli/commands/deploy/client.js +0 -519
- package/dist/cli/commands/deploy/config.d.ts +0 -37
- package/dist/cli/commands/deploy/config.d.ts.map +0 -1
- package/dist/cli/commands/deploy/config.js +0 -144
- package/dist/cli/commands/deploy/entry-agent.d.ts +0 -3
- package/dist/cli/commands/deploy/entry-agent.d.ts.map +0 -1
- package/dist/cli/commands/deploy/entry-agent.js +0 -70
- package/dist/cli/commands/deploy/index.d.ts +0 -12
- package/dist/cli/commands/deploy/index.d.ts.map +0 -1
- package/dist/cli/commands/deploy/index.js +0 -370
- package/dist/cli/commands/deploy/links.d.ts +0 -3
- package/dist/cli/commands/deploy/links.d.ts.map +0 -1
- package/dist/cli/commands/deploy/links.js +0 -53
- package/dist/cli/commands/deploy/register.d.ts +0 -6
- package/dist/cli/commands/deploy/register.d.ts.map +0 -1
- package/dist/cli/commands/deploy/register.js +0 -128
- package/dist/cli/commands/deploy/snapshot.d.ts +0 -12
- package/dist/cli/commands/deploy/snapshot.d.ts.map +0 -1
- package/dist/cli/commands/deploy/snapshot.js +0 -76
- package/dist/cli/commands/deploy/state.d.ts +0 -13
- package/dist/cli/commands/deploy/state.d.ts.map +0 -1
- package/dist/cli/commands/deploy/state.js +0 -122
- package/dist/cli/commands/span/register.d.ts +0 -6
- package/dist/cli/commands/span/register.d.ts.map +0 -1
- package/dist/cli/commands/span/register.js +0 -25
- package/dist/cli/commands/trace/client.d.ts +0 -98
- package/dist/cli/commands/trace/client.d.ts.map +0 -1
- package/dist/cli/commands/trace/client.js +0 -150
- package/dist/cli/commands/trace/format.d.ts +0 -5
- package/dist/cli/commands/trace/format.d.ts.map +0 -1
- package/dist/cli/commands/trace/format.js +0 -58
- package/dist/cli/commands/trace/index.d.ts +0 -21
- package/dist/cli/commands/trace/index.d.ts.map +0 -1
- package/dist/cli/commands/trace/index.js +0 -49
- package/dist/cli/commands/trace/register.d.ts +0 -6
- package/dist/cli/commands/trace/register.d.ts.map +0 -1
- package/dist/cli/commands/trace/register.js +0 -71
- package/dist/webui/assets/index-DEfRFmFT.js +0 -1667
|
@@ -1,585 +0,0 @@
|
|
|
1
|
-
// packages/cli/src/cli/auth/api-client.ts
|
|
2
|
-
// Dexto API client for auth/key/account APIs
|
|
3
|
-
// TODO: Migrate to typed client for type safety and better DX
|
|
4
|
-
// Options:
|
|
5
|
-
// 1. Migrate dexto-web APIs to Hono and use @hono/client (like packages/server)
|
|
6
|
-
// 2. Use openapi-fetch with generated types from OpenAPI spec
|
|
7
|
-
// 3. Use tRPC if we want full-stack type safety
|
|
8
|
-
// Currently using plain fetch() with runtime response validation.
|
|
9
|
-
import { logger } from '@dexto/core';
|
|
10
|
-
import { DEXTO_PLATFORM_URL, DEXTO_PUBLIC_API_URL, SUPABASE_ANON_KEY, SUPABASE_URL, } from './constants.js';
|
|
11
|
-
function parseString(value) {
|
|
12
|
-
return typeof value === 'string' && value.trim().length > 0 ? value : null;
|
|
13
|
-
}
|
|
14
|
-
function parseNumber(value) {
|
|
15
|
-
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
16
|
-
return value;
|
|
17
|
-
}
|
|
18
|
-
if (typeof value === 'string') {
|
|
19
|
-
const parsed = Number.parseFloat(value);
|
|
20
|
-
if (Number.isFinite(parsed)) {
|
|
21
|
-
return parsed;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
function parseDeviceCodeStartResponse(payload) {
|
|
27
|
-
if (typeof payload !== 'object' || payload === null) {
|
|
28
|
-
throw new Error('Invalid device start response');
|
|
29
|
-
}
|
|
30
|
-
const deviceCode = parseString(Reflect.get(payload, 'deviceCode'));
|
|
31
|
-
const userCode = parseString(Reflect.get(payload, 'userCode'));
|
|
32
|
-
const verificationUrl = parseString(Reflect.get(payload, 'verificationUri'));
|
|
33
|
-
const verificationUrlComplete = parseString(Reflect.get(payload, 'verificationUriComplete'));
|
|
34
|
-
const expiresIn = parseNumber(Reflect.get(payload, 'expiresIn'));
|
|
35
|
-
const interval = parseNumber(Reflect.get(payload, 'interval'));
|
|
36
|
-
if (!deviceCode || !userCode || !verificationUrl || !expiresIn || !interval) {
|
|
37
|
-
throw new Error('Device start response missing required fields');
|
|
38
|
-
}
|
|
39
|
-
return {
|
|
40
|
-
deviceCode,
|
|
41
|
-
userCode,
|
|
42
|
-
verificationUrl,
|
|
43
|
-
verificationUrlComplete,
|
|
44
|
-
expiresIn,
|
|
45
|
-
interval,
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
function parseStringArray(value) {
|
|
49
|
-
if (!Array.isArray(value)) {
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
const strings = value.filter((item) => typeof item === 'string');
|
|
53
|
-
return strings.length === value.length ? strings : null;
|
|
54
|
-
}
|
|
55
|
-
function parseDeviceCodeApiKeyResponse(payload) {
|
|
56
|
-
if (typeof payload !== 'object' || payload === null) {
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
const status = parseString(Reflect.get(payload, 'status'));
|
|
60
|
-
if (status !== 'approved') {
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
const fullKey = parseString(Reflect.get(payload, 'fullKey'));
|
|
64
|
-
const apiKey = Reflect.get(payload, 'apiKey');
|
|
65
|
-
if (!fullKey || typeof apiKey !== 'object' || apiKey === null) {
|
|
66
|
-
throw new Error('Device login approved response missing API key fields');
|
|
67
|
-
}
|
|
68
|
-
const id = parseString(Reflect.get(apiKey, 'id'));
|
|
69
|
-
const keyDisplay = parseString(Reflect.get(apiKey, 'keyDisplay'));
|
|
70
|
-
const name = parseString(Reflect.get(apiKey, 'name'));
|
|
71
|
-
const scopes = parseStringArray(Reflect.get(apiKey, 'scopes'));
|
|
72
|
-
const keyStatus = parseString(Reflect.get(apiKey, 'status'));
|
|
73
|
-
if (!id || !keyDisplay || !name || !scopes || !keyStatus) {
|
|
74
|
-
throw new Error('Device login approved response missing API key fields');
|
|
75
|
-
}
|
|
76
|
-
return {
|
|
77
|
-
id,
|
|
78
|
-
keyDisplay,
|
|
79
|
-
name,
|
|
80
|
-
scopes,
|
|
81
|
-
status: keyStatus,
|
|
82
|
-
fullKey,
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
function parseDeviceErrorCode(payload) {
|
|
86
|
-
if (typeof payload !== 'object' || payload === null) {
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
return parseString(Reflect.get(payload, 'error'));
|
|
90
|
-
}
|
|
91
|
-
function parseSupabaseUser(payload) {
|
|
92
|
-
if (typeof payload !== 'object' || payload === null) {
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
const id = parseString(Reflect.get(payload, 'id'));
|
|
96
|
-
const email = parseString(Reflect.get(payload, 'email'));
|
|
97
|
-
if (!id || !email) {
|
|
98
|
-
return null;
|
|
99
|
-
}
|
|
100
|
-
const userMetadata = Reflect.get(payload, 'user_metadata');
|
|
101
|
-
const fullName = typeof userMetadata === 'object' && userMetadata !== null
|
|
102
|
-
? parseString(Reflect.get(userMetadata, 'full_name'))
|
|
103
|
-
: null;
|
|
104
|
-
return {
|
|
105
|
-
id,
|
|
106
|
-
email,
|
|
107
|
-
name: fullName ?? email,
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
function parseRecentUsage(payload) {
|
|
111
|
-
if (!Array.isArray(payload)) {
|
|
112
|
-
throw new Error('Invalid response from API');
|
|
113
|
-
}
|
|
114
|
-
const entries = [];
|
|
115
|
-
for (const entry of payload) {
|
|
116
|
-
if (typeof entry !== 'object' || entry === null) {
|
|
117
|
-
throw new Error('Invalid response from API');
|
|
118
|
-
}
|
|
119
|
-
const timestamp = parseString(Reflect.get(entry, 'timestamp'));
|
|
120
|
-
const model = parseString(Reflect.get(entry, 'model'));
|
|
121
|
-
const costUsd = parseNumber(Reflect.get(entry, 'cost_usd'));
|
|
122
|
-
const inputTokens = parseNumber(Reflect.get(entry, 'input_tokens'));
|
|
123
|
-
const outputTokens = parseNumber(Reflect.get(entry, 'output_tokens'));
|
|
124
|
-
if (!timestamp ||
|
|
125
|
-
!model ||
|
|
126
|
-
costUsd === null ||
|
|
127
|
-
inputTokens === null ||
|
|
128
|
-
outputTokens === null) {
|
|
129
|
-
throw new Error('Invalid response from API');
|
|
130
|
-
}
|
|
131
|
-
entries.push({
|
|
132
|
-
timestamp,
|
|
133
|
-
model,
|
|
134
|
-
cost_usd: costUsd,
|
|
135
|
-
input_tokens: inputTokens,
|
|
136
|
-
output_tokens: outputTokens,
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
return entries;
|
|
140
|
-
}
|
|
141
|
-
function parseUsageSummaryResponse(payload) {
|
|
142
|
-
if (typeof payload !== 'object' || payload === null) {
|
|
143
|
-
throw new Error('Invalid response from API');
|
|
144
|
-
}
|
|
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) {
|
|
148
|
-
throw new Error('Invalid response from API');
|
|
149
|
-
}
|
|
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) {
|
|
154
|
-
throw new Error('Invalid response from API');
|
|
155
|
-
}
|
|
156
|
-
const recent = parseRecentUsage(Reflect.get(payload, 'recent_model_usage'));
|
|
157
|
-
return {
|
|
158
|
-
balance_usd: balanceUsd,
|
|
159
|
-
last_30_days: {
|
|
160
|
-
since,
|
|
161
|
-
total_cost_usd: totalCostUsd,
|
|
162
|
-
total_usage_events: totalUsageEvents,
|
|
163
|
-
},
|
|
164
|
-
recent_model_usage: recent,
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
function parseBillingBalanceResponse(payload) {
|
|
168
|
-
if (typeof payload !== 'object' || payload === null) {
|
|
169
|
-
throw new Error('Invalid response from API');
|
|
170
|
-
}
|
|
171
|
-
const creditsUsd = parseNumber(Reflect.get(payload, 'credits_usd')) ??
|
|
172
|
-
parseNumber(Reflect.get(payload, 'creditsUsd'));
|
|
173
|
-
if (creditsUsd === null) {
|
|
174
|
-
throw new Error('Invalid response from API');
|
|
175
|
-
}
|
|
176
|
-
return { creditsUsd };
|
|
177
|
-
}
|
|
178
|
-
function parseBillingCheckoutSessionResponse(payload) {
|
|
179
|
-
if (typeof payload !== 'object' || payload === null) {
|
|
180
|
-
throw new Error('Invalid response from API');
|
|
181
|
-
}
|
|
182
|
-
const checkoutUrl = parseString(Reflect.get(payload, 'checkout_url')) ??
|
|
183
|
-
parseString(Reflect.get(payload, 'checkoutUrl'));
|
|
184
|
-
const checkoutSessionId = parseString(Reflect.get(payload, 'checkout_session_id')) ??
|
|
185
|
-
parseString(Reflect.get(payload, 'checkoutSessionId'));
|
|
186
|
-
if (!checkoutUrl || !checkoutSessionId) {
|
|
187
|
-
throw new Error('Invalid response from API');
|
|
188
|
-
}
|
|
189
|
-
return {
|
|
190
|
-
checkoutUrl,
|
|
191
|
-
checkoutSessionId,
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
function parsePlatformKeyListResponse(payload) {
|
|
195
|
-
if (typeof payload !== 'object' || payload === null) {
|
|
196
|
-
throw new Error('Invalid response from API');
|
|
197
|
-
}
|
|
198
|
-
const data = Reflect.get(payload, 'apiKeys');
|
|
199
|
-
if (!Array.isArray(data)) {
|
|
200
|
-
throw new Error('Invalid response from API');
|
|
201
|
-
}
|
|
202
|
-
const keys = [];
|
|
203
|
-
for (const entry of data) {
|
|
204
|
-
if (typeof entry !== 'object' || entry === null) {
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
const id = parseString(Reflect.get(entry, 'id'));
|
|
208
|
-
const name = parseString(Reflect.get(entry, 'name'));
|
|
209
|
-
const status = parseString(Reflect.get(entry, 'status'));
|
|
210
|
-
if (!id || !name || !status) {
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
keys.push({ id, name, status });
|
|
214
|
-
}
|
|
215
|
-
return { apiKeys: keys };
|
|
216
|
-
}
|
|
217
|
-
function parsePlatformCreateKeyResponse(payload) {
|
|
218
|
-
if (typeof payload !== 'object' || payload === null) {
|
|
219
|
-
throw new Error('Invalid response from API');
|
|
220
|
-
}
|
|
221
|
-
const data = Reflect.get(payload, 'apiKey');
|
|
222
|
-
if (typeof data !== 'object' || data === null) {
|
|
223
|
-
throw new Error('Invalid response from API');
|
|
224
|
-
}
|
|
225
|
-
const id = parseString(Reflect.get(data, 'id'));
|
|
226
|
-
const fullKey = parseString(Reflect.get(payload, 'fullKey'));
|
|
227
|
-
if (!id || !fullKey) {
|
|
228
|
-
throw new Error('Invalid response from API');
|
|
229
|
-
}
|
|
230
|
-
return {
|
|
231
|
-
apiKey: { id },
|
|
232
|
-
fullKey,
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
function formatHttpFailure(status, payload, rawText) {
|
|
236
|
-
if (rawText.trim().length > 0) {
|
|
237
|
-
return `${status} ${rawText}`;
|
|
238
|
-
}
|
|
239
|
-
if (payload != null) {
|
|
240
|
-
return `${status} ${JSON.stringify(payload)}`;
|
|
241
|
-
}
|
|
242
|
-
return `${status}`;
|
|
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
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Dexto API client for key management
|
|
255
|
-
*/
|
|
256
|
-
export class DextoApiClient {
|
|
257
|
-
platformBaseUrl;
|
|
258
|
-
publicApiBaseUrl;
|
|
259
|
-
timeoutMs = 10_000;
|
|
260
|
-
constructor(baseUrl = {}) {
|
|
261
|
-
if (typeof baseUrl === 'string') {
|
|
262
|
-
const normalized = baseUrl.replace(/\/+$/, '');
|
|
263
|
-
this.platformBaseUrl = normalized;
|
|
264
|
-
this.publicApiBaseUrl = normalizePublicApiBaseUrl(normalized);
|
|
265
|
-
return;
|
|
266
|
-
}
|
|
267
|
-
this.platformBaseUrl = (baseUrl.platformBaseUrl ??
|
|
268
|
-
baseUrl.gatewayBaseUrl ??
|
|
269
|
-
DEXTO_PLATFORM_URL).replace(/\/+$/, '');
|
|
270
|
-
this.publicApiBaseUrl = normalizePublicApiBaseUrl(baseUrl.publicApiBaseUrl ?? DEXTO_PUBLIC_API_URL);
|
|
271
|
-
}
|
|
272
|
-
createRequestSignal(signal) {
|
|
273
|
-
const timeoutSignal = AbortSignal.timeout(this.timeoutMs);
|
|
274
|
-
if (!signal) {
|
|
275
|
-
return timeoutSignal;
|
|
276
|
-
}
|
|
277
|
-
return AbortSignal.any([signal, timeoutSignal]);
|
|
278
|
-
}
|
|
279
|
-
getPlatformUrl(path) {
|
|
280
|
-
return `${this.platformBaseUrl}${path}`;
|
|
281
|
-
}
|
|
282
|
-
getPublicApiUrl(path) {
|
|
283
|
-
return `${this.publicApiBaseUrl}${path}`;
|
|
284
|
-
}
|
|
285
|
-
async listPlatformApiKeys(authToken) {
|
|
286
|
-
const response = await fetch(this.getPlatformUrl('/api/api-keys'), {
|
|
287
|
-
method: 'GET',
|
|
288
|
-
headers: {
|
|
289
|
-
Authorization: `Bearer ${authToken}`,
|
|
290
|
-
},
|
|
291
|
-
signal: this.createRequestSignal(undefined),
|
|
292
|
-
});
|
|
293
|
-
if (!response.ok) {
|
|
294
|
-
const rawText = await response.text();
|
|
295
|
-
throw new Error(`API request failed: ${formatHttpFailure(response.status, null, rawText)}`);
|
|
296
|
-
}
|
|
297
|
-
const payload = await response.json();
|
|
298
|
-
return parsePlatformKeyListResponse(payload).apiKeys;
|
|
299
|
-
}
|
|
300
|
-
async createPlatformApiKey(authToken, name) {
|
|
301
|
-
const response = await fetch(this.getPlatformUrl('/api/api-keys'), {
|
|
302
|
-
method: 'POST',
|
|
303
|
-
headers: {
|
|
304
|
-
Authorization: `Bearer ${authToken}`,
|
|
305
|
-
'Content-Type': 'application/json',
|
|
306
|
-
},
|
|
307
|
-
body: JSON.stringify({ name }),
|
|
308
|
-
signal: this.createRequestSignal(undefined),
|
|
309
|
-
});
|
|
310
|
-
if (!response.ok) {
|
|
311
|
-
const rawText = await response.text();
|
|
312
|
-
throw new Error(`API request failed: ${formatHttpFailure(response.status, null, rawText)}`);
|
|
313
|
-
}
|
|
314
|
-
const payload = await response.json();
|
|
315
|
-
const result = parsePlatformCreateKeyResponse(payload);
|
|
316
|
-
return {
|
|
317
|
-
dextoApiKey: result.fullKey,
|
|
318
|
-
keyId: result.apiKey.id,
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
async deletePlatformApiKey(authToken, keyId) {
|
|
322
|
-
const response = await fetch(this.getPlatformUrl(`/api/api-keys/${encodeURIComponent(keyId)}`), {
|
|
323
|
-
method: 'DELETE',
|
|
324
|
-
headers: {
|
|
325
|
-
Authorization: `Bearer ${authToken}`,
|
|
326
|
-
},
|
|
327
|
-
signal: this.createRequestSignal(undefined),
|
|
328
|
-
});
|
|
329
|
-
if (!response.ok) {
|
|
330
|
-
const rawText = await response.text();
|
|
331
|
-
throw new Error(`API request failed: ${formatHttpFailure(response.status, null, rawText)}`);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
/**
|
|
335
|
-
* Validate if a Dexto API key is valid
|
|
336
|
-
*/
|
|
337
|
-
async validateDextoApiKey(apiKey) {
|
|
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) {
|
|
347
|
-
return false;
|
|
348
|
-
}
|
|
349
|
-
if (!response.ok) {
|
|
350
|
-
throw new Error(`Unable to validate Dexto API key: HTTP ${response.status}`);
|
|
351
|
-
}
|
|
352
|
-
return true;
|
|
353
|
-
}
|
|
354
|
-
/**
|
|
355
|
-
* Provision Dexto API key and always return a usable secret key value.
|
|
356
|
-
* @param regenerate - If true, delete existing key(s) and create a new one
|
|
357
|
-
*/
|
|
358
|
-
async provisionDextoApiKey(authToken, name = 'Dexto CLI Key', regenerate = false) {
|
|
359
|
-
try {
|
|
360
|
-
logger.debug(`Provisioning DEXTO_API_KEY with name: ${name}, regenerate: ${regenerate}`);
|
|
361
|
-
const matchingKeys = (await this.listPlatformApiKeys(authToken)).filter((key) => key.status === 'active' && key.name === name);
|
|
362
|
-
if (matchingKeys.length > 0) {
|
|
363
|
-
if (!regenerate) {
|
|
364
|
-
logger.debug(`Active key exists (${matchingKeys[0]?.id ?? 'unknown'}); rotating to obtain key value`);
|
|
365
|
-
}
|
|
366
|
-
for (const key of matchingKeys) {
|
|
367
|
-
await this.deletePlatformApiKey(authToken, key.id);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
const createdKey = await this.createPlatformApiKey(authToken, name);
|
|
371
|
-
logger.debug(`Successfully provisioned DEXTO_API_KEY: ${createdKey.keyId}`);
|
|
372
|
-
return {
|
|
373
|
-
dextoApiKey: createdKey.dextoApiKey,
|
|
374
|
-
keyId: createdKey.keyId,
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
|
-
catch (error) {
|
|
378
|
-
logger.error(`Error provisioning Dexto API key: ${error}`);
|
|
379
|
-
throw error;
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
/**
|
|
383
|
-
* Get usage summary (balance + last 30 days + recent model activity)
|
|
384
|
-
*/
|
|
385
|
-
async getUsageSummary(apiKey) {
|
|
386
|
-
try {
|
|
387
|
-
logger.debug('Fetching usage summary');
|
|
388
|
-
const response = await fetch(this.getPublicApiUrl('/v1/billing/summary'), {
|
|
389
|
-
method: 'GET',
|
|
390
|
-
headers: {
|
|
391
|
-
Authorization: `Bearer ${apiKey}`,
|
|
392
|
-
},
|
|
393
|
-
signal: this.createRequestSignal(undefined),
|
|
394
|
-
});
|
|
395
|
-
if (!response.ok) {
|
|
396
|
-
const rawText = await response.text();
|
|
397
|
-
throw new Error(`API request failed: ${formatHttpFailure(response.status, null, rawText)}`);
|
|
398
|
-
}
|
|
399
|
-
const payload = await response.json();
|
|
400
|
-
return parseUsageSummaryResponse(payload);
|
|
401
|
-
}
|
|
402
|
-
catch (error) {
|
|
403
|
-
logger.error(`Error fetching usage summary: ${error}`);
|
|
404
|
-
throw error;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
/**
|
|
408
|
-
* Get the current Dexto billing balance for the authenticated user.
|
|
409
|
-
*/
|
|
410
|
-
async getBillingBalance(authToken, options) {
|
|
411
|
-
try {
|
|
412
|
-
logger.debug('Fetching billing balance');
|
|
413
|
-
const response = await fetch(this.getPlatformUrl('/api/billing/balance'), {
|
|
414
|
-
method: 'GET',
|
|
415
|
-
headers: {
|
|
416
|
-
Authorization: `Bearer ${authToken}`,
|
|
417
|
-
},
|
|
418
|
-
signal: this.createRequestSignal(options.signal),
|
|
419
|
-
});
|
|
420
|
-
if (!response.ok) {
|
|
421
|
-
const rawText = await response.text();
|
|
422
|
-
throw new Error(`API request failed: ${formatHttpFailure(response.status, null, rawText)}`);
|
|
423
|
-
}
|
|
424
|
-
const payload = await response.json();
|
|
425
|
-
return parseBillingBalanceResponse(payload);
|
|
426
|
-
}
|
|
427
|
-
catch (error) {
|
|
428
|
-
logger.error('Error fetching billing balance', {
|
|
429
|
-
error: error instanceof Error ? error.message : String(error),
|
|
430
|
-
});
|
|
431
|
-
throw error;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
/**
|
|
435
|
-
* Create a hosted top-up checkout session for the authenticated user.
|
|
436
|
-
*/
|
|
437
|
-
async createBillingCheckoutSession(authToken, options) {
|
|
438
|
-
try {
|
|
439
|
-
if (!Number.isFinite(options.creditsUsd) || options.creditsUsd <= 0) {
|
|
440
|
-
throw new Error('creditsUsd must be a positive number');
|
|
441
|
-
}
|
|
442
|
-
logger.debug('Creating billing checkout session', {
|
|
443
|
-
creditsUsd: options.creditsUsd,
|
|
444
|
-
});
|
|
445
|
-
const response = await fetch(this.getPlatformUrl('/api/billing/checkout-session'), {
|
|
446
|
-
method: 'POST',
|
|
447
|
-
headers: {
|
|
448
|
-
Authorization: `Bearer ${authToken}`,
|
|
449
|
-
'Content-Type': 'application/json',
|
|
450
|
-
},
|
|
451
|
-
body: JSON.stringify({
|
|
452
|
-
credits_usd: options.creditsUsd,
|
|
453
|
-
...(options.returnUrl ? { return_url: options.returnUrl } : {}),
|
|
454
|
-
}),
|
|
455
|
-
signal: this.createRequestSignal(options.signal),
|
|
456
|
-
});
|
|
457
|
-
if (!response.ok) {
|
|
458
|
-
const rawText = await response.text();
|
|
459
|
-
throw new Error(`API request failed: ${formatHttpFailure(response.status, null, rawText)}`);
|
|
460
|
-
}
|
|
461
|
-
const payload = await response.json();
|
|
462
|
-
return parseBillingCheckoutSessionResponse(payload);
|
|
463
|
-
}
|
|
464
|
-
catch (error) {
|
|
465
|
-
logger.error('Error creating billing checkout session', {
|
|
466
|
-
error: error instanceof Error ? error.message : String(error),
|
|
467
|
-
creditsUsd: options.creditsUsd,
|
|
468
|
-
});
|
|
469
|
-
throw error;
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
/**
|
|
473
|
-
* Start device code login at the platform.
|
|
474
|
-
*/
|
|
475
|
-
async startDeviceCodeLogin(client = 'dexto-cli', options = {}) {
|
|
476
|
-
const response = await fetch(this.getPlatformUrl('/api/auth/device/start'), {
|
|
477
|
-
method: 'POST',
|
|
478
|
-
headers: {
|
|
479
|
-
'Content-Type': 'application/json',
|
|
480
|
-
},
|
|
481
|
-
body: JSON.stringify({ clientId: client }),
|
|
482
|
-
signal: this.createRequestSignal(options.signal),
|
|
483
|
-
});
|
|
484
|
-
if (!response.ok) {
|
|
485
|
-
if (response.status === 404 || response.status === 403) {
|
|
486
|
-
throw new Error('Device code login is not enabled for this Dexto server');
|
|
487
|
-
}
|
|
488
|
-
const rawText = await response.text();
|
|
489
|
-
throw new Error(`Device code login failed to start: ${formatHttpFailure(response.status, null, rawText)}`);
|
|
490
|
-
}
|
|
491
|
-
const payload = await response.json();
|
|
492
|
-
return parseDeviceCodeStartResponse(payload);
|
|
493
|
-
}
|
|
494
|
-
/**
|
|
495
|
-
* Poll the platform for device-code login completion.
|
|
496
|
-
*/
|
|
497
|
-
async pollDeviceCodeLogin(deviceCode, options = {}) {
|
|
498
|
-
let response;
|
|
499
|
-
try {
|
|
500
|
-
response = await fetch(this.getPlatformUrl('/api/auth/device/poll'), {
|
|
501
|
-
method: 'POST',
|
|
502
|
-
headers: {
|
|
503
|
-
'Content-Type': 'application/json',
|
|
504
|
-
},
|
|
505
|
-
body: JSON.stringify({ deviceCode }),
|
|
506
|
-
signal: this.createRequestSignal(options.signal),
|
|
507
|
-
});
|
|
508
|
-
}
|
|
509
|
-
catch (error) {
|
|
510
|
-
if (options.signal?.aborted) {
|
|
511
|
-
throw options.signal.reason instanceof Error
|
|
512
|
-
? options.signal.reason
|
|
513
|
-
: new Error('Authentication cancelled');
|
|
514
|
-
}
|
|
515
|
-
logger.debug(`Device login poll request failed transiently: ${error instanceof Error ? error.message : String(error)}`);
|
|
516
|
-
return { status: 'transientError' };
|
|
517
|
-
}
|
|
518
|
-
const payload = await response.json().catch(() => null);
|
|
519
|
-
if (!response.ok) {
|
|
520
|
-
if (response.status === 404 || response.status === 403) {
|
|
521
|
-
throw new Error('Device code login is not enabled for this Dexto server');
|
|
522
|
-
}
|
|
523
|
-
if (response.status === 429) {
|
|
524
|
-
return { status: 'slowDown' };
|
|
525
|
-
}
|
|
526
|
-
if (response.status >= 500) {
|
|
527
|
-
return { status: 'transientError' };
|
|
528
|
-
}
|
|
529
|
-
const errorCode = parseDeviceErrorCode(payload);
|
|
530
|
-
if (errorCode) {
|
|
531
|
-
throw new Error(`Device login failed: ${errorCode}`);
|
|
532
|
-
}
|
|
533
|
-
throw new Error(`Device login polling failed with status ${response.status}`);
|
|
534
|
-
}
|
|
535
|
-
const apiKey = parseDeviceCodeApiKeyResponse(payload);
|
|
536
|
-
if (apiKey) {
|
|
537
|
-
return {
|
|
538
|
-
status: 'approved',
|
|
539
|
-
apiKey,
|
|
540
|
-
};
|
|
541
|
-
}
|
|
542
|
-
const errorCode = parseDeviceErrorCode(payload);
|
|
543
|
-
if (errorCode) {
|
|
544
|
-
throw new Error(`Device login failed: ${errorCode}`);
|
|
545
|
-
}
|
|
546
|
-
return { status: 'pending' };
|
|
547
|
-
}
|
|
548
|
-
/**
|
|
549
|
-
* Fetch Supabase user profile for a bearer token.
|
|
550
|
-
*/
|
|
551
|
-
async fetchSupabaseUser(accessToken, options = {}) {
|
|
552
|
-
try {
|
|
553
|
-
const response = await fetch(`${SUPABASE_URL}/auth/v1/user`, {
|
|
554
|
-
headers: {
|
|
555
|
-
Authorization: `Bearer ${accessToken}`,
|
|
556
|
-
apikey: SUPABASE_ANON_KEY,
|
|
557
|
-
'User-Agent': 'dexto-cli/1.0.0',
|
|
558
|
-
},
|
|
559
|
-
signal: this.createRequestSignal(options.signal),
|
|
560
|
-
});
|
|
561
|
-
if (!response.ok) {
|
|
562
|
-
return undefined;
|
|
563
|
-
}
|
|
564
|
-
const payload = await response.json();
|
|
565
|
-
return parseSupabaseUser(payload) ?? undefined;
|
|
566
|
-
}
|
|
567
|
-
catch (error) {
|
|
568
|
-
logger.debug(`Failed to fetch Supabase user profile: ${error instanceof Error ? error.message : String(error)}`);
|
|
569
|
-
return undefined;
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
/**
|
|
573
|
-
* Validate Supabase access token by checking if /auth/v1/user resolves.
|
|
574
|
-
*/
|
|
575
|
-
async validateSupabaseAccessToken(accessToken, options = {}) {
|
|
576
|
-
const user = await this.fetchSupabaseUser(accessToken, options);
|
|
577
|
-
return Boolean(user?.id);
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
/**
|
|
581
|
-
* Get default Dexto API client
|
|
582
|
-
*/
|
|
583
|
-
export function getDextoApiClient() {
|
|
584
|
-
return new DextoApiClient();
|
|
585
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { DeviceApiKeyLoginResult } from './types.js';
|
|
2
|
-
export interface DeviceLoginPrompt {
|
|
3
|
-
userCode: string;
|
|
4
|
-
verificationUrl: string;
|
|
5
|
-
verificationUrlComplete: string | null;
|
|
6
|
-
expiresIn: number;
|
|
7
|
-
}
|
|
8
|
-
export interface DeviceLoginOptionsInput {
|
|
9
|
-
apiUrl?: string;
|
|
10
|
-
signal?: AbortSignal | undefined;
|
|
11
|
-
onPrompt?: ((prompt: DeviceLoginPrompt) => void | Promise<void>) | undefined;
|
|
12
|
-
}
|
|
13
|
-
export declare function performDeviceCodeLogin(optionsInput?: DeviceLoginOptionsInput): Promise<DeviceApiKeyLoginResult>;
|
|
14
|
-
//# sourceMappingURL=device.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../../src/cli/auth/device.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAI1D,MAAM,WAAW,iBAAiB;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,uBAAuB;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC;CAChF;AAoDD,wBAAsB,sBAAsB,CACxC,YAAY,GAAE,uBAA4B,GAC3C,OAAO,CAAC,uBAAuB,CAAC,CA0DlC"}
|
package/dist/cli/auth/device.js
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
// packages/cli/src/cli/auth/device.ts
|
|
2
|
-
// Device code login flow for browserless environments.
|
|
3
|
-
import { DextoApiClient, getDextoApiClient } from './api-client.js';
|
|
4
|
-
const TRANSIENT_POLL_BACKOFF_MS = 2_000;
|
|
5
|
-
const MAX_POLL_INTERVAL_MS = 30_000;
|
|
6
|
-
function resolveDeviceLoginOptions(options) {
|
|
7
|
-
return {
|
|
8
|
-
apiUrl: options.apiUrl,
|
|
9
|
-
signal: options.signal ?? null,
|
|
10
|
-
onPrompt: options.onPrompt ?? (() => undefined),
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
function getAuthClient(apiUrl) {
|
|
14
|
-
return apiUrl ? new DextoApiClient(apiUrl) : getDextoApiClient();
|
|
15
|
-
}
|
|
16
|
-
async function sleepWithAbort(ms, signal) {
|
|
17
|
-
if (signal?.aborted) {
|
|
18
|
-
throw signal.reason instanceof Error
|
|
19
|
-
? signal.reason
|
|
20
|
-
: new Error('Authentication cancelled');
|
|
21
|
-
}
|
|
22
|
-
if (!signal) {
|
|
23
|
-
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
await new Promise((resolve, reject) => {
|
|
27
|
-
const timer = setTimeout(() => {
|
|
28
|
-
signal.removeEventListener('abort', onAbort);
|
|
29
|
-
resolve();
|
|
30
|
-
}, ms);
|
|
31
|
-
const onAbort = () => {
|
|
32
|
-
clearTimeout(timer);
|
|
33
|
-
signal.removeEventListener('abort', onAbort);
|
|
34
|
-
reject(signal.reason instanceof Error
|
|
35
|
-
? signal.reason
|
|
36
|
-
: new Error('Authentication cancelled'));
|
|
37
|
-
};
|
|
38
|
-
signal.addEventListener('abort', onAbort, { once: true });
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
export async function performDeviceCodeLogin(optionsInput = {}) {
|
|
42
|
-
const options = resolveDeviceLoginOptions(optionsInput);
|
|
43
|
-
const authClient = getAuthClient(options.apiUrl);
|
|
44
|
-
const start = await authClient.startDeviceCodeLogin('dexto-cli', {
|
|
45
|
-
signal: options.signal ?? undefined,
|
|
46
|
-
});
|
|
47
|
-
await options.onPrompt({
|
|
48
|
-
userCode: start.userCode,
|
|
49
|
-
verificationUrl: start.verificationUrl,
|
|
50
|
-
verificationUrlComplete: start.verificationUrlComplete,
|
|
51
|
-
expiresIn: start.expiresIn,
|
|
52
|
-
});
|
|
53
|
-
const deadline = Date.now() + start.expiresIn * 1000;
|
|
54
|
-
let pollIntervalMs = Math.max(1, Math.floor(start.interval)) * 1000;
|
|
55
|
-
while (Date.now() < deadline) {
|
|
56
|
-
await sleepWithAbort(pollIntervalMs, options.signal);
|
|
57
|
-
const pollResult = await authClient.pollDeviceCodeLogin(start.deviceCode, {
|
|
58
|
-
signal: options.signal ?? undefined,
|
|
59
|
-
});
|
|
60
|
-
if (pollResult.status === 'pending') {
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
if (pollResult.status === 'slowDown') {
|
|
64
|
-
pollIntervalMs = Math.min(pollIntervalMs + 5_000, MAX_POLL_INTERVAL_MS);
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
if (pollResult.status === 'transientError') {
|
|
68
|
-
pollIntervalMs = Math.min(pollIntervalMs + TRANSIENT_POLL_BACKOFF_MS, MAX_POLL_INTERVAL_MS);
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
if (pollResult.status === 'expired') {
|
|
72
|
-
throw new Error('Device login expired. Please restart login.');
|
|
73
|
-
}
|
|
74
|
-
if (pollResult.status === 'denied') {
|
|
75
|
-
throw new Error('Device login was denied.');
|
|
76
|
-
}
|
|
77
|
-
return {
|
|
78
|
-
dextoApiKey: pollResult.apiKey.fullKey,
|
|
79
|
-
dextoKeyId: pollResult.apiKey.id,
|
|
80
|
-
dextoKeyDisplay: pollResult.apiKey.keyDisplay,
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
throw new Error('Device login timed out. Please restart login.');
|
|
84
|
-
}
|