@switchboard.spot/cli 0.2.3 → 0.2.5
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 +20 -9
- package/bin/switchboard.js +1 -17
- package/lib/client.js +0 -3
- package/lib/commands/account.js +0 -9
- package/lib/commands/auth.js +16 -124
- package/lib/commands/billing.js +7 -69
- package/lib/commands/doctor.js +10 -6
- package/lib/commands/env.js +31 -245
- package/lib/commands/projects.js +83 -63
- package/lib/commands/setup.js +90 -85
- package/lib/commands/usage.js +0 -23
- package/lib/commands/verify.js +4 -0
- package/lib/config.js +2 -3
- package/lib/credentialStore.js +0 -134
- package/lib/docsClient.js +1 -51
- package/lib/mcpServer.js +0 -21
- package/lib/verify/index.js +62 -10
- package/package.json +2 -2
- package/lib/commands/endUsers.js +0 -51
- package/lib/commands/init.js +0 -78
- package/lib/commands/integration.js +0 -38
- package/lib/commands/keys.js +0 -106
- package/lib/commands/launch.js +0 -213
- package/lib/commands/org.js +0 -55
- package/lib/commands/workspaces.js +0 -92
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @switchboard.spot/cli
|
|
2
2
|
|
|
3
3
|
Command-line management for [Switchboard](https://switchboard.spot): account
|
|
4
|
-
auth, project setup,
|
|
4
|
+
auth, project setup, integration setup, usage checks, and smoke
|
|
5
5
|
tests.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
@@ -29,6 +29,18 @@ The build output is written to `dist/` and is safe to delete.
|
|
|
29
29
|
|
|
30
30
|
## Publish to npm
|
|
31
31
|
|
|
32
|
+
From the repository root, use the release helper for normal interactive
|
|
33
|
+
publishing:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
bin/publish-cli patch
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The helper bumps `packages/cli/package.json` and `package-lock.json`, runs CLI
|
|
40
|
+
tests, runs `npm publish --dry-run`, publishes with public access, verifies the
|
|
41
|
+
npm `latest` dist-tag, and commits the version bump. Pass `minor`, `major`, or an
|
|
42
|
+
explicit version such as `0.3.0` when needed.
|
|
43
|
+
|
|
32
44
|
Before publishing, npm requires either an interactive account with 2FA enabled
|
|
33
45
|
or a granular access token with **bypass 2FA** enabled. For token-based
|
|
34
46
|
publishing, create a granular npm token with read/write access for
|
|
@@ -56,9 +68,9 @@ If a first publish fails, `npm view @switchboard.spot/cli` will continue to retu
|
|
|
56
68
|
switchboard auth login
|
|
57
69
|
switchboard projects create --name "My App" --slug my-app
|
|
58
70
|
switchboard setup project --origin http://localhost:5173 --json
|
|
59
|
-
switchboard verify setup
|
|
60
|
-
switchboard
|
|
61
|
-
switchboard verify publish
|
|
71
|
+
switchboard verify setup --app-url <app-url>
|
|
72
|
+
switchboard setup project --origin https://preview.example.com --production-origin https://app.example.com --end-user-terms-url https://app.example.com/terms --end-user-privacy-url https://app.example.com/privacy --support-email support@example.com
|
|
73
|
+
switchboard verify publish --app-url <preview-url>
|
|
62
74
|
```
|
|
63
75
|
|
|
64
76
|
Use `--json` for automation, CI, and coding agents.
|
|
@@ -67,9 +79,9 @@ Use `--json` for automation, CI, and coding agents.
|
|
|
67
79
|
|
|
68
80
|
CLI account login does not create Client Gateway end-user sessions. End-user sign-up, sign-in, and refresh require the SDK-managed browser challenge; curl, Node scripts, CI, and CLI account login cannot mint browser sessions without running that real browser/mobile flow.
|
|
69
81
|
|
|
70
|
-
For local Switchboard development, `switchboard verify setup --client-url http://localhost:4000/
|
|
82
|
+
For local Switchboard development, `switchboard verify setup --app-url <app-url> --client-url http://localhost:4000/g/<slug>/v1` uses the built-in `dev_browser_challenge` token unless a scenario supplies an explicit `browserChallengeToken`. Hosted Switchboard should use managed Turnstile and the SDK-managed real browser challenge. A local sandbox smoke path is: configure allowed origin plus legal/support fields, create an anonymous session through the SDK or browser verification flow, then confirm Client Gateway chat returns the guarded `402 sandbox_completion_requires_prepaid_balance` without calling a provider.
|
|
71
83
|
|
|
72
|
-
Model discovery is global. Use `GET /v1/models` for OpenAI-compatible discovery
|
|
84
|
+
Model discovery is global. Use `GET /v1/models` for OpenAI-compatible discovery; Client Gateway chat is project-scoped at `/g/<slug>/v1/chat/completions`.
|
|
73
85
|
|
|
74
86
|
Switchboard-managed Turnstile is the default production path. Developers do not paste Cloudflare secrets into the CLI or repo files:
|
|
75
87
|
|
|
@@ -78,7 +90,7 @@ switchboard setup project --origin <origin> --json
|
|
|
78
90
|
switchboard projects turnstile <project-id> --clear
|
|
79
91
|
```
|
|
80
92
|
|
|
81
|
-
`switchboard projects provision-turnstile` remains available as a low-level admin/debug command. If its help is missing, upgrade with `npm install -g @switchboard.spot/cli@latest` before
|
|
93
|
+
`switchboard projects provision-turnstile` remains available as a low-level admin/debug command. If its help is missing, upgrade with `npm install -g @switchboard.spot/cli@latest` before using manual Cloudflare keys.
|
|
82
94
|
|
|
83
95
|
## Configuration
|
|
84
96
|
|
|
@@ -91,7 +103,6 @@ Environment variables:
|
|
|
91
103
|
| --- | --- |
|
|
92
104
|
| `SWITCHBOARD_BASE_URL` | Switchboard app origin. Defaults to `https://switchboard.spot`; set to `http://localhost:4000` for local development. |
|
|
93
105
|
| `SWITCHBOARD_PROJECT_ID` | Project context for project-scoped commands. |
|
|
94
|
-
| `SWITCHBOARD_API_KEY` | Secret project key for trusted-server gateway smoke tests. |
|
|
95
106
|
| `SWITCHBOARD_CLIENT_URL` | Public Client Gateway URL for browser/mobile end-user auth and chat. |
|
|
96
107
|
| `VITE_SWITCHBOARD_CLIENT_URL` | Vite-safe public Client Gateway URL for browser apps. |
|
|
97
108
|
| `SWITCHBOARD_END_USER_SESSION` | Existing end-user session for Client Gateway checks; the CLI cannot mint one without browser challenge execution. |
|
|
@@ -107,7 +118,7 @@ clears both keychain and config-dir account sessions.
|
|
|
107
118
|
|
|
108
119
|
## Credential safety
|
|
109
120
|
|
|
110
|
-
Do not paste `sb_sess_`,
|
|
121
|
+
Do not paste `sb_sess_`, provider keys, private keys, or
|
|
111
122
|
webhook secrets into frontend, mobile, or public code. Browser and mobile code
|
|
112
123
|
should use `VITE_SWITCHBOARD_CLIENT_URL` in Vite apps or `SWITCHBOARD_CLIENT_URL`
|
|
113
124
|
in non-Vite tooling, plus end-user sessions.
|
package/bin/switchboard.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Switchboard CLI
|
|
3
|
+
* Switchboard CLI for account, project, gateway, and docs automation.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { Command } from "commander";
|
|
@@ -9,22 +9,14 @@ import path from "path";
|
|
|
9
9
|
import { fileURLToPath } from "url";
|
|
10
10
|
import { registerCommand as registerAuth } from "../lib/commands/auth.js";
|
|
11
11
|
import { registerAccountCommands } from "../lib/commands/account.js";
|
|
12
|
-
import { registerKeysCommands } from "../lib/commands/keys.js";
|
|
13
12
|
import { registerProjectsCommands } from "../lib/commands/projects.js";
|
|
14
|
-
import { registerWorkspacesCommands } from "../lib/commands/workspaces.js";
|
|
15
|
-
import { registerOrgCommands } from "../lib/commands/org.js";
|
|
16
|
-
import { registerEndUsersCommands } from "../lib/commands/endUsers.js";
|
|
17
13
|
import { registerBillingCommands } from "../lib/commands/billing.js";
|
|
18
|
-
import { registerEnvCommands } from "../lib/commands/env.js";
|
|
19
14
|
import { registerUsageCommands } from "../lib/commands/usage.js";
|
|
20
|
-
import { registerIntegrationCommands } from "../lib/commands/integration.js";
|
|
21
15
|
import { registerDocsCommands } from "../lib/commands/docs.js";
|
|
22
|
-
import { registerInitCommand } from "../lib/commands/init.js";
|
|
23
16
|
import { registerSetupCommand } from "../lib/commands/setup.js";
|
|
24
17
|
import { registerHealthCommand } from "../lib/commands/health.js";
|
|
25
18
|
import { registerDoctorCommand } from "../lib/commands/doctor.js";
|
|
26
19
|
import { registerVerifyCommands } from "../lib/commands/verify.js";
|
|
27
|
-
import { registerLaunchCommands } from "../lib/commands/launch.js";
|
|
28
20
|
|
|
29
21
|
const program = new Command();
|
|
30
22
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -39,23 +31,15 @@ program
|
|
|
39
31
|
.option("-q, --quiet", "Suppress non-essential output")
|
|
40
32
|
.version(packageJson.version);
|
|
41
33
|
|
|
42
|
-
registerInitCommand(program);
|
|
43
34
|
registerSetupCommand(program);
|
|
44
35
|
registerHealthCommand(program);
|
|
45
36
|
registerDoctorCommand(program);
|
|
46
37
|
registerVerifyCommands(program);
|
|
47
|
-
registerLaunchCommands(program);
|
|
48
38
|
registerAuth(program);
|
|
49
39
|
registerAccountCommands(program);
|
|
50
|
-
registerWorkspacesCommands(program);
|
|
51
|
-
registerKeysCommands(program);
|
|
52
40
|
registerProjectsCommands(program);
|
|
53
|
-
registerOrgCommands(program);
|
|
54
|
-
registerEndUsersCommands(program);
|
|
55
41
|
registerBillingCommands(program);
|
|
56
|
-
registerEnvCommands(program);
|
|
57
42
|
registerUsageCommands(program);
|
|
58
|
-
registerIntegrationCommands(program);
|
|
59
43
|
registerDocsCommands(program);
|
|
60
44
|
|
|
61
45
|
await program.parseAsync();
|
package/lib/client.js
CHANGED
|
@@ -6,11 +6,8 @@ import { accountApiUrl, resolveAccountConfig, resolveConfig } from "./config.js"
|
|
|
6
6
|
import { emitHttpError, fail, normalizeError } from "./output.js";
|
|
7
7
|
|
|
8
8
|
const PROJECT_SCOPED_ACCOUNT_PATHS = [
|
|
9
|
-
/^\/keys(?:\/|$)/,
|
|
10
|
-
/^\/end_users(?:\/|$)/,
|
|
11
9
|
/^\/billing\/(?:ledger|top_up|prepaid)(?:\/|\?|$)/,
|
|
12
10
|
/^\/usage(?:\?|$)/,
|
|
13
|
-
/^\/integration_kit(?:\?|$)/,
|
|
14
11
|
];
|
|
15
12
|
|
|
16
13
|
/**
|
package/lib/commands/account.js
CHANGED
|
@@ -69,13 +69,4 @@ export function registerAccountCommands(program) {
|
|
|
69
69
|
const { data } = await accountRequest("DELETE", "/me", { json: flags.json });
|
|
70
70
|
emit(flags.json ? data : `Deleted account ${email}`, flags);
|
|
71
71
|
});
|
|
72
|
-
|
|
73
|
-
account
|
|
74
|
-
.command("restore")
|
|
75
|
-
.description("Restore the authenticated account")
|
|
76
|
-
.action(async (_opts, cmd) => {
|
|
77
|
-
const flags = globalFlags(cmd);
|
|
78
|
-
const { data } = await accountRequest("POST", "/me/restore", { json: flags.json });
|
|
79
|
-
emit(flags.json ? data : `Restored account ${data.user.email}`, flags);
|
|
80
|
-
});
|
|
81
72
|
}
|
package/lib/commands/auth.js
CHANGED
|
@@ -85,7 +85,6 @@ export function registerCommand(program) {
|
|
|
85
85
|
deleteConfigDirAccountToken();
|
|
86
86
|
saveConfig({
|
|
87
87
|
projectId: null,
|
|
88
|
-
apiKey: null,
|
|
89
88
|
endUserSession: null,
|
|
90
89
|
});
|
|
91
90
|
emit(flags.json ? { ok: true } : "Logged out.", flags);
|
|
@@ -215,7 +214,6 @@ export async function exchangeCliLogin({
|
|
|
215
214
|
|
|
216
215
|
save({
|
|
217
216
|
projectId: null,
|
|
218
|
-
apiKey: null,
|
|
219
217
|
endUserSession: null,
|
|
220
218
|
});
|
|
221
219
|
|
|
@@ -320,8 +318,8 @@ function waitForCallback(callbackServer, timeoutSeconds, json) {
|
|
|
320
318
|
export function callbackPage(title, message, tone) {
|
|
321
319
|
const success = tone === "success";
|
|
322
320
|
const accent = success ? "#14b8a6" : "#ef4444";
|
|
323
|
-
const
|
|
324
|
-
const
|
|
321
|
+
const mark = success ? "✓" : "×";
|
|
322
|
+
const ariaLabel = success ? "Switchboard CLI login complete" : "Switchboard CLI login failed";
|
|
325
323
|
|
|
326
324
|
return `<!doctype html>
|
|
327
325
|
<html lang="en">
|
|
@@ -340,114 +338,41 @@ export function callbackPage(title, message, tone) {
|
|
|
340
338
|
body {
|
|
341
339
|
min-height: 100vh;
|
|
342
340
|
margin: 0;
|
|
341
|
+
display: grid;
|
|
342
|
+
place-items: center;
|
|
343
343
|
color: #1f2937;
|
|
344
344
|
background:
|
|
345
345
|
repeating-linear-gradient(125deg, transparent, transparent 6px, #e8e8e8 6px, #e8e8e8 7px),
|
|
346
346
|
#ffffff;
|
|
347
347
|
}
|
|
348
|
-
.
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
margin: 0 auto;
|
|
352
|
-
display: flex;
|
|
353
|
-
flex-direction: column;
|
|
354
|
-
border-inline: 1px solid #e5e7eb;
|
|
355
|
-
background:
|
|
356
|
-
repeating-linear-gradient(125deg, transparent, transparent 6px, #e8e8e8 6px, #e8e8e8 7px),
|
|
357
|
-
#ffffff;
|
|
358
|
-
}
|
|
359
|
-
header {
|
|
360
|
-
height: 4.5rem;
|
|
361
|
-
display: flex;
|
|
362
|
-
align-items: center;
|
|
363
|
-
justify-content: space-between;
|
|
364
|
-
padding: 0 1.5rem;
|
|
365
|
-
border-bottom: 1px solid #e5e7eb;
|
|
366
|
-
background: rgba(255, 255, 255, 0.88);
|
|
367
|
-
backdrop-filter: blur(12px);
|
|
368
|
-
}
|
|
369
|
-
.logo {
|
|
370
|
-
color: #1f2937;
|
|
371
|
-
font-size: 0.95rem;
|
|
372
|
-
font-weight: 800;
|
|
373
|
-
letter-spacing: 0;
|
|
374
|
-
}
|
|
375
|
-
.pill {
|
|
376
|
-
border: 1px solid #e5e7eb;
|
|
377
|
-
border-radius: 999px;
|
|
378
|
-
padding: 0.45rem 0.8rem;
|
|
379
|
-
background: #ffffff;
|
|
380
|
-
color: #4b5563;
|
|
381
|
-
font-size: 0.82rem;
|
|
382
|
-
font-weight: 600;
|
|
383
|
-
}
|
|
384
|
-
main {
|
|
385
|
-
flex: 1;
|
|
348
|
+
.panel {
|
|
349
|
+
width: min(100%, 12rem);
|
|
350
|
+
aspect-ratio: 1;
|
|
386
351
|
display: grid;
|
|
387
352
|
place-items: center;
|
|
388
|
-
padding: 5rem 1.5rem;
|
|
389
|
-
}
|
|
390
|
-
.panel {
|
|
391
|
-
width: min(100%, 34rem);
|
|
392
353
|
border: 1px solid #e5e7eb;
|
|
393
354
|
border-radius: 0.5rem;
|
|
394
|
-
padding:
|
|
355
|
+
padding: 1.5rem;
|
|
395
356
|
background: rgba(255, 255, 255, 0.94);
|
|
396
357
|
box-shadow:
|
|
397
358
|
0 1.34368px 0.537473px -0.625px rgba(0, 0, 0, 0.09),
|
|
398
359
|
0 15.5969px 6.23877px -3.125px rgba(0, 0, 0, 0.07),
|
|
399
360
|
0 43.962px 17.5848px -4.375px rgba(0, 0, 0, 0.04);
|
|
400
|
-
text-align: center;
|
|
401
|
-
}
|
|
402
|
-
.eyebrow {
|
|
403
|
-
margin: 0 0 1.25rem;
|
|
404
|
-
color: #4b5563;
|
|
405
|
-
font-size: 0.78rem;
|
|
406
|
-
font-weight: 700;
|
|
407
|
-
letter-spacing: 0.12em;
|
|
408
|
-
text-transform: uppercase;
|
|
409
361
|
}
|
|
410
362
|
.mark {
|
|
411
|
-
width:
|
|
412
|
-
height:
|
|
413
|
-
margin: 0 auto 1.25rem;
|
|
363
|
+
width: 5rem;
|
|
364
|
+
height: 5rem;
|
|
414
365
|
display: grid;
|
|
415
366
|
place-items: center;
|
|
416
367
|
border: 1px solid ${accent};
|
|
417
368
|
border-radius: 0.5rem;
|
|
418
369
|
background: ${accent};
|
|
419
370
|
color: #111827;
|
|
420
|
-
font-size:
|
|
371
|
+
font-size: 3rem;
|
|
372
|
+
line-height: 1;
|
|
421
373
|
font-weight: 900;
|
|
422
374
|
box-shadow: 0 18px 40px -20px ${accent};
|
|
423
375
|
}
|
|
424
|
-
h1 {
|
|
425
|
-
margin: 0;
|
|
426
|
-
color: #1f2937;
|
|
427
|
-
font-size: clamp(2rem, 7vw, 3.5rem);
|
|
428
|
-
line-height: 0.98;
|
|
429
|
-
letter-spacing: 0;
|
|
430
|
-
}
|
|
431
|
-
p {
|
|
432
|
-
margin: 1rem auto 0;
|
|
433
|
-
max-width: 26rem;
|
|
434
|
-
color: #4b5563;
|
|
435
|
-
font-size: 1rem;
|
|
436
|
-
line-height: 1.65;
|
|
437
|
-
}
|
|
438
|
-
.status {
|
|
439
|
-
display: inline-flex;
|
|
440
|
-
align-items: center;
|
|
441
|
-
gap: 0.5rem;
|
|
442
|
-
margin-top: 1.5rem;
|
|
443
|
-
border: 1px solid ${accent};
|
|
444
|
-
border-radius: 999px;
|
|
445
|
-
padding: 0.5rem 0.8rem;
|
|
446
|
-
background: ${accentSoft};
|
|
447
|
-
color: #1f2937;
|
|
448
|
-
font-size: 0.86rem;
|
|
449
|
-
font-weight: 700;
|
|
450
|
-
}
|
|
451
376
|
@media (prefers-color-scheme: dark) {
|
|
452
377
|
:root {
|
|
453
378
|
background: #020617;
|
|
@@ -459,53 +384,20 @@ export function callbackPage(title, message, tone) {
|
|
|
459
384
|
repeating-linear-gradient(125deg, transparent, transparent 6px, rgba(55, 65, 81, 0.6) 6px, rgba(55, 65, 81, 0.6) 7px),
|
|
460
385
|
#020617;
|
|
461
386
|
}
|
|
462
|
-
.page {
|
|
463
|
-
border-color: #1f2937;
|
|
464
|
-
background:
|
|
465
|
-
repeating-linear-gradient(125deg, transparent, transparent 6px, rgba(55, 65, 81, 0.6) 6px, rgba(55, 65, 81, 0.6) 7px),
|
|
466
|
-
#020617;
|
|
467
|
-
}
|
|
468
|
-
header,
|
|
469
387
|
.panel {
|
|
470
388
|
border-color: #1f2937;
|
|
471
389
|
background: rgba(2, 6, 23, 0.94);
|
|
472
390
|
}
|
|
473
|
-
.
|
|
474
|
-
h1 {
|
|
475
|
-
color: #f3f4f6;
|
|
476
|
-
}
|
|
477
|
-
.pill,
|
|
478
|
-
.eyebrow,
|
|
479
|
-
p {
|
|
480
|
-
color: #d1d5db;
|
|
481
|
-
}
|
|
482
|
-
.pill {
|
|
483
|
-
border-color: #1f2937;
|
|
484
|
-
background: #111827;
|
|
485
|
-
}
|
|
486
|
-
.mark,
|
|
487
|
-
.status {
|
|
391
|
+
.mark {
|
|
488
392
|
color: #020617;
|
|
489
393
|
}
|
|
490
394
|
}
|
|
491
395
|
</style>
|
|
492
396
|
</head>
|
|
493
397
|
<body>
|
|
494
|
-
<
|
|
495
|
-
<
|
|
496
|
-
|
|
497
|
-
<div class="pill">CLI session</div>
|
|
498
|
-
</header>
|
|
499
|
-
<main>
|
|
500
|
-
<section class="panel" aria-labelledby="callback-title">
|
|
501
|
-
<p class="eyebrow">Switchboard CLI</p>
|
|
502
|
-
<div class="mark" aria-hidden="true">${mark}</div>
|
|
503
|
-
<h1 id="callback-title">${escapeHtml(title)}</h1>
|
|
504
|
-
<p>${escapeHtml(message)}</p>
|
|
505
|
-
<div class="status">${success ? "Session approved" : "Session not approved"}</div>
|
|
506
|
-
</section>
|
|
507
|
-
</main>
|
|
508
|
-
</div>
|
|
398
|
+
<main class="panel" aria-label="${ariaLabel}">
|
|
399
|
+
<div class="mark" aria-hidden="true">${mark}</div>
|
|
400
|
+
</main>
|
|
509
401
|
</body>
|
|
510
402
|
</html>`;
|
|
511
403
|
}
|
package/lib/commands/billing.js
CHANGED
|
@@ -20,7 +20,7 @@ export function registerBillingCommands(program) {
|
|
|
20
20
|
|
|
21
21
|
billing
|
|
22
22
|
.command("ledger")
|
|
23
|
-
.description("List
|
|
23
|
+
.description("List project spend balance ledger entries")
|
|
24
24
|
.action(async (_opts, cmd) => {
|
|
25
25
|
const flags = globalFlags(cmd);
|
|
26
26
|
const { data } = await accountRequest("GET", "/billing/ledger", {
|
|
@@ -37,88 +37,26 @@ export function registerBillingCommands(program) {
|
|
|
37
37
|
|
|
38
38
|
billing
|
|
39
39
|
.command("top-up")
|
|
40
|
-
.description("Add
|
|
40
|
+
.description("Add funds to the project spend balance")
|
|
41
41
|
.requiredOption("--amount-micros <micros>", "Amount in micros", parseInt)
|
|
42
42
|
.option("--idempotency-key <key>")
|
|
43
|
+
.option("--success-url <url>")
|
|
44
|
+
.option("--cancel-url <url>")
|
|
45
|
+
.option("--open", "Open checkout URL in the default browser")
|
|
43
46
|
.action(async (opts, cmd) => {
|
|
44
47
|
const flags = globalFlags(cmd);
|
|
45
48
|
const body = { amount_micros: opts.amountMicros };
|
|
46
49
|
if (opts.idempotencyKey) body.idempotency_key = opts.idempotencyKey;
|
|
50
|
+
if (opts.successUrl) body.success_url = opts.successUrl;
|
|
51
|
+
if (opts.cancelUrl) body.cancel_url = opts.cancelUrl;
|
|
47
52
|
|
|
48
53
|
const { data } = await accountRequest("POST", "/billing/top_up", {
|
|
49
54
|
body,
|
|
50
55
|
json: flags.json,
|
|
51
56
|
});
|
|
52
|
-
emit(flags.json ? data : `Created top-up ${data.id}`, flags);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
billing
|
|
56
|
-
.command("checkout")
|
|
57
|
-
.description("Create a Stripe-hosted developer billing checkout URL")
|
|
58
|
-
.requiredOption("--success-url <url>")
|
|
59
|
-
.requiredOption("--cancel-url <url>")
|
|
60
|
-
.option("--open", "Open checkout URL in the default browser")
|
|
61
|
-
.action(async (opts, cmd) => {
|
|
62
|
-
const flags = globalFlags(cmd);
|
|
63
|
-
const { data } = await accountRequest("POST", "/billing/checkout", {
|
|
64
|
-
body: {
|
|
65
|
-
success_url: opts.successUrl,
|
|
66
|
-
cancel_url: opts.cancelUrl,
|
|
67
|
-
},
|
|
68
|
-
json: flags.json,
|
|
69
|
-
});
|
|
70
57
|
if (opts.open) openUrl(data.checkout_url);
|
|
71
58
|
emit(flags.json ? data : data.checkout_url, flags);
|
|
72
59
|
});
|
|
73
|
-
|
|
74
|
-
billing
|
|
75
|
-
.command("portal")
|
|
76
|
-
.description("Create a Stripe-hosted developer billing portal URL")
|
|
77
|
-
.requiredOption("--return-url <url>")
|
|
78
|
-
.option("--open", "Open portal URL in the default browser")
|
|
79
|
-
.action(async (opts, cmd) => {
|
|
80
|
-
const flags = globalFlags(cmd);
|
|
81
|
-
const { data } = await accountRequest("POST", "/billing/portal", {
|
|
82
|
-
body: { return_url: opts.returnUrl },
|
|
83
|
-
json: flags.json,
|
|
84
|
-
});
|
|
85
|
-
if (opts.open) openUrl(data.url);
|
|
86
|
-
emit(flags.json ? data : data.url, flags);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
billing
|
|
90
|
-
.command("prepaid")
|
|
91
|
-
.description("Update prepaid wallet settings")
|
|
92
|
-
.option("--monthly-auto-refill-micros <micros>", "Monthly auto-refill amount", parseInt)
|
|
93
|
-
.option("--outage-protection <enabled>", "true or false")
|
|
94
|
-
.option("--outage-threshold-micros <micros>", "Outage refill threshold", parseInt)
|
|
95
|
-
.option("--outage-refill-micros <micros>", "Outage refill amount", parseInt)
|
|
96
|
-
.option("--outage-monthly-cap-micros <micros>", "Outage monthly cap", parseInt)
|
|
97
|
-
.action(async (opts, cmd) => {
|
|
98
|
-
const flags = globalFlags(cmd);
|
|
99
|
-
const body = {};
|
|
100
|
-
if (opts.monthlyAutoRefillMicros != null) {
|
|
101
|
-
body.prepaid_monthly_auto_refill_micros = opts.monthlyAutoRefillMicros;
|
|
102
|
-
}
|
|
103
|
-
if (opts.outageProtection != null) {
|
|
104
|
-
body.prepaid_outage_protection_enabled = opts.outageProtection === "true";
|
|
105
|
-
}
|
|
106
|
-
if (opts.outageThresholdMicros != null) {
|
|
107
|
-
body.prepaid_outage_threshold_micros = opts.outageThresholdMicros;
|
|
108
|
-
}
|
|
109
|
-
if (opts.outageRefillMicros != null) {
|
|
110
|
-
body.prepaid_outage_refill_micros = opts.outageRefillMicros;
|
|
111
|
-
}
|
|
112
|
-
if (opts.outageMonthlyCapMicros != null) {
|
|
113
|
-
body.prepaid_outage_monthly_cap_micros = opts.outageMonthlyCapMicros;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const { data } = await accountRequest("PATCH", "/billing/prepaid", {
|
|
117
|
-
body,
|
|
118
|
-
json: flags.json,
|
|
119
|
-
});
|
|
120
|
-
emit(flags.json ? data : "Updated prepaid settings", flags);
|
|
121
|
-
});
|
|
122
60
|
}
|
|
123
61
|
|
|
124
62
|
function openUrl(url) {
|
package/lib/commands/doctor.js
CHANGED
|
@@ -18,7 +18,7 @@ async function check(name, fn) {
|
|
|
18
18
|
export function registerDoctorCommand(program) {
|
|
19
19
|
program
|
|
20
20
|
.command("doctor")
|
|
21
|
-
.description("Check health, auth, project,
|
|
21
|
+
.description("Check health, auth, project, models, and gateway readiness")
|
|
22
22
|
.action(async (_opts, cmd) => {
|
|
23
23
|
const flags = globalFlags(cmd);
|
|
24
24
|
const report = await runDoctorChecks();
|
|
@@ -42,13 +42,17 @@ export async function runDoctorChecks({
|
|
|
42
42
|
checks.push(
|
|
43
43
|
await check("health", async () => {
|
|
44
44
|
const result = await health(config);
|
|
45
|
+
if (!result.ok) {
|
|
46
|
+
throw new Error(result.data?.status || result.data?.message || `HTTP ${result.status}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
45
49
|
return { status: result.status, data: result.data };
|
|
46
50
|
}),
|
|
47
51
|
);
|
|
48
52
|
|
|
49
53
|
checks.push(
|
|
50
|
-
await check("
|
|
51
|
-
const res = await fetchImpl(`${gatewayApiUrl(config)}/
|
|
54
|
+
await check("models", async () => {
|
|
55
|
+
const res = await fetchImpl(`${gatewayApiUrl(config)}/models`);
|
|
52
56
|
const data = await res.json();
|
|
53
57
|
const status = res.status;
|
|
54
58
|
if (!res.ok) throw new Error(`HTTP ${status}`);
|
|
@@ -80,11 +84,11 @@ export async function runDoctorChecks({
|
|
|
80
84
|
checks.push({ name: "project", ok: false, error: "No project selected" });
|
|
81
85
|
}
|
|
82
86
|
|
|
83
|
-
if (config.
|
|
87
|
+
if (config.clientGatewayUrl) {
|
|
84
88
|
checks.push(
|
|
85
89
|
await check("client_gateway_config", async () => {
|
|
86
90
|
const res = await fetchImpl(
|
|
87
|
-
new URL("client/config", ensureTrailingSlash(config.
|
|
91
|
+
new URL("client/config", ensureTrailingSlash(config.clientGatewayUrl)).href,
|
|
88
92
|
{
|
|
89
93
|
headers: { Accept: "application/json" },
|
|
90
94
|
},
|
|
@@ -99,7 +103,7 @@ export async function runDoctorChecks({
|
|
|
99
103
|
|
|
100
104
|
return {
|
|
101
105
|
status: res.status,
|
|
102
|
-
clientUrl: config.
|
|
106
|
+
clientUrl: config.clientGatewayUrl,
|
|
103
107
|
browserChallengeProvider: data?.browser_challenge?.provider ?? null,
|
|
104
108
|
production_safety: productionSafety,
|
|
105
109
|
warning: productionBlocked,
|