@switchboard.spot/cli 0.2.4 → 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 +18 -7
- package/bin/switchboard.js +1 -17
- package/lib/client.js +0 -3
- package/lib/commands/account.js +0 -9
- package/lib/commands/auth.js +0 -2
- package/lib/commands/billing.js +7 -69
- package/lib/commands/doctor.js +6 -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/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 +47 -7
- 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
|
|
@@ -57,7 +69,7 @@ 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
71
|
switchboard verify setup --app-url <app-url>
|
|
60
|
-
switchboard
|
|
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
|
|
61
73
|
switchboard verify publish --app-url <preview-url>
|
|
62
74
|
```
|
|
63
75
|
|
|
@@ -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 --app-url <app-url> --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
|
|
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();
|
|
@@ -51,8 +51,8 @@ export async function runDoctorChecks({
|
|
|
51
51
|
);
|
|
52
52
|
|
|
53
53
|
checks.push(
|
|
54
|
-
await check("
|
|
55
|
-
const res = await fetchImpl(`${gatewayApiUrl(config)}/
|
|
54
|
+
await check("models", async () => {
|
|
55
|
+
const res = await fetchImpl(`${gatewayApiUrl(config)}/models`);
|
|
56
56
|
const data = await res.json();
|
|
57
57
|
const status = res.status;
|
|
58
58
|
if (!res.ok) throw new Error(`HTTP ${status}`);
|
|
@@ -84,11 +84,11 @@ export async function runDoctorChecks({
|
|
|
84
84
|
checks.push({ name: "project", ok: false, error: "No project selected" });
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
if (config.
|
|
87
|
+
if (config.clientGatewayUrl) {
|
|
88
88
|
checks.push(
|
|
89
89
|
await check("client_gateway_config", async () => {
|
|
90
90
|
const res = await fetchImpl(
|
|
91
|
-
new URL("client/config", ensureTrailingSlash(config.
|
|
91
|
+
new URL("client/config", ensureTrailingSlash(config.clientGatewayUrl)).href,
|
|
92
92
|
{
|
|
93
93
|
headers: { Accept: "application/json" },
|
|
94
94
|
},
|
|
@@ -103,7 +103,7 @@ export async function runDoctorChecks({
|
|
|
103
103
|
|
|
104
104
|
return {
|
|
105
105
|
status: res.status,
|
|
106
|
-
clientUrl: config.
|
|
106
|
+
clientUrl: config.clientGatewayUrl,
|
|
107
107
|
browserChallengeProvider: data?.browser_challenge?.provider ?? null,
|
|
108
108
|
production_safety: productionSafety,
|
|
109
109
|
warning: productionBlocked,
|