@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 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, key rotation, integration setup, usage checks, and smoke
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 launch prepare --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 --contact-email owner@example.com --use-case "Browser chat for signed-in customers"
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/m/<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 call Client Gateway chat.
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 or `GET /v1/catalog/models` for catalog/pricing metadata; Client Gateway chat is project-scoped at `/m/<slug>/v1/chat/completions`.
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 trying dashboard automation or manual Cloudflare keys.
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_`, `sb_test_`, `sb_live_`, provider keys, private keys, or
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.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Switchboard CLI full dashboard parity via the account management API.
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
  /**
@@ -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
  }
@@ -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
 
@@ -20,7 +20,7 @@ export function registerBillingCommands(program) {
20
20
 
21
21
  billing
22
22
  .command("ledger")
23
- .description("List prepaid wallet ledger entries")
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 prepaid wallet funds")
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) {
@@ -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, catalog, and gateway readiness")
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("catalog", async () => {
55
- const res = await fetchImpl(`${gatewayApiUrl(config)}/catalog/models`);
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.virtualMicroserviceUrl) {
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.virtualMicroserviceUrl)).href,
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.virtualMicroserviceUrl,
106
+ clientUrl: config.clientGatewayUrl,
107
107
  browserChallengeProvider: data?.browser_challenge?.provider ?? null,
108
108
  production_safety: productionSafety,
109
109
  warning: productionBlocked,