@barekey/cli 0.4.0 → 0.5.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.
Files changed (60) hide show
  1. package/README.md +53 -12
  2. package/bun.lock +9 -3
  3. package/dist/auth-provider.js +7 -4
  4. package/dist/command-utils.js +6 -6
  5. package/dist/commands/audit.d.ts +2 -0
  6. package/dist/commands/audit.js +47 -0
  7. package/dist/commands/auth.js +31 -9
  8. package/dist/commands/billing.d.ts +2 -0
  9. package/dist/commands/billing.js +59 -0
  10. package/dist/commands/env.js +157 -125
  11. package/dist/commands/init.d.ts +2 -0
  12. package/dist/commands/init.js +32 -0
  13. package/dist/commands/org.d.ts +2 -0
  14. package/dist/commands/org.js +85 -0
  15. package/dist/commands/project.d.ts +2 -0
  16. package/dist/commands/project.js +99 -0
  17. package/dist/commands/stage.d.ts +2 -0
  18. package/dist/commands/stage.js +125 -0
  19. package/dist/commands/target-prompts.d.ts +184 -0
  20. package/dist/commands/target-prompts.js +312 -0
  21. package/dist/commands/typegen.d.ts +2 -2
  22. package/dist/commands/typegen.js +57 -32
  23. package/dist/constants.d.ts +1 -1
  24. package/dist/constants.js +1 -1
  25. package/dist/context/session-id.d.ts +11 -0
  26. package/dist/context/session-id.js +14 -0
  27. package/dist/contracts/index.d.ts +499 -0
  28. package/dist/contracts/index.js +313 -0
  29. package/dist/credentials-store.js +70 -11
  30. package/dist/http.d.ts +34 -0
  31. package/dist/http.js +56 -2
  32. package/dist/index.js +12 -0
  33. package/dist/runtime-config.js +14 -26
  34. package/dist/typegen/core.d.ts +45 -0
  35. package/dist/typegen/core.js +219 -0
  36. package/dist/types.d.ts +5 -3
  37. package/package.json +2 -2
  38. package/src/auth-provider.ts +8 -5
  39. package/src/command-utils.ts +6 -6
  40. package/src/commands/audit.ts +63 -0
  41. package/src/commands/auth.ts +45 -39
  42. package/src/commands/billing.ts +70 -0
  43. package/src/commands/env.ts +211 -218
  44. package/src/commands/init.ts +47 -0
  45. package/src/commands/org.ts +104 -0
  46. package/src/commands/project.ts +130 -0
  47. package/src/commands/stage.ts +167 -0
  48. package/src/commands/target-prompts.ts +357 -0
  49. package/src/commands/typegen.ts +71 -45
  50. package/src/constants.ts +1 -1
  51. package/src/context/session-id.ts +14 -0
  52. package/src/contracts/index.ts +376 -0
  53. package/src/credentials-store.ts +86 -12
  54. package/src/http.ts +78 -2
  55. package/src/index.ts +12 -0
  56. package/src/runtime-config.ts +19 -32
  57. package/src/typegen/core.ts +311 -0
  58. package/src/types.ts +5 -3
  59. package/test/command-utils.test.ts +47 -0
  60. package/test/credentials-store.test.ts +40 -0
package/README.md CHANGED
@@ -1,34 +1,75 @@
1
1
  # @barekey/cli
2
2
 
3
- CLI for logging into Barekey, managing environment variables, and pulling resolved values into local workflows.
3
+ CLI for Barekey login, variable management, pull workflows, and SDK type generation.
4
4
 
5
5
  ## Install
6
6
 
7
7
  ```bash
8
- bun add -g @barekey/cli
8
+ npm install -g @barekey/cli
9
9
  ```
10
10
 
11
11
  ## Quickstart
12
12
 
13
13
  ```bash
14
14
  barekey auth login
15
- barekey env list --org acme --project api --stage development
16
- barekey env get DATABASE_URL --org acme --project api --stage development
17
- barekey env pull --org acme --project api --stage development --out .env
15
+ barekey auth whoami
16
+ ```
17
+
18
+ Create `barekey.json`:
19
+
20
+ ```json
21
+ {
22
+ "organization": "acme",
23
+ "project": "web",
24
+ "environment": "development"
25
+ }
26
+ ```
27
+
28
+ Create and read a variable:
29
+
30
+ ```bash
31
+ barekey env new DATABASE_URL "postgres://localhost:5432/app" --type string
32
+ barekey env get DATABASE_URL
33
+ ```
34
+
35
+ Update it:
36
+
37
+ ```bash
38
+ barekey env set DATABASE_URL "postgres://localhost:5432/app_v2"
39
+ ```
40
+
41
+ Pull local files:
42
+
43
+ ```bash
44
+ barekey env pull --out .env.local
45
+ barekey env pull --format json --out barekey.local.json
46
+ ```
47
+
48
+ Generate SDK types:
49
+
50
+ ```bash
51
+ barekey typegen
52
+ barekey typegen --watch
18
53
  ```
19
54
 
20
55
  ## Common commands
21
56
 
22
57
  ```bash
23
- barekey auth whoami
24
- barekey env new FEATURE_FLAG true --type boolean --org acme --project api --stage development
25
- barekey env set CHECKOUT_COPY original --ab redesign --chance 0.25 --org acme --project api --stage development
26
- barekey env delete FEATURE_FLAG --yes --org acme --project api --stage development
27
- barekey env get-many --names DATABASE_URL,FEATURE_FLAG --org acme --project api --stage development
28
- barekey typegen --org acme --project api --stage development
29
- barekey typegen --watch --org acme --project api --stage development
58
+ barekey env list
59
+ barekey env get-many --names DATABASE_URL,REDIS_URL
60
+ barekey env new FEATURE_ENABLED true --type boolean
61
+ barekey env set PUBLIC_TITLE "Barekey Docs" --visibility public
62
+ barekey env set CHECKOUT_FLOW control --ab treatment --chance 0.5
63
+ barekey env delete FEATURE_ENABLED --yes
30
64
  ```
31
65
 
66
+ ## Notes
67
+
68
+ - `env new` creates with an initial value.
69
+ - `env set` is the upsert command.
70
+ - `get-many` uses a comma-separated `--names` value.
71
+ - Target resolution comes from flags, `barekey.json`, and the stored login org.
72
+
32
73
  ## Development
33
74
 
34
75
  ```bash
package/bun.lock CHANGED
@@ -5,9 +5,9 @@
5
5
  "": {
6
6
  "name": "@barekey/cli",
7
7
  "dependencies": {
8
- "@barekey/sdk": "^0.5.0",
9
8
  "@clack/prompts": "^0.11.0",
10
9
  "commander": "^14.0.1",
10
+ "effect": "3.19.19",
11
11
  "open": "^10.2.0",
12
12
  "picocolors": "^1.1.1",
13
13
  },
@@ -18,12 +18,12 @@
18
18
  },
19
19
  },
20
20
  "packages": {
21
- "@barekey/sdk": ["@barekey/sdk@0.5.0", "", {}, "sha512-+8RoKix9DZee3ZheGP5wJLnet6XaEGHTfcJOuvtJLmfQKRBllUy+wevsvELi1wOjw42pWaG3DYyb68XHznZRQw=="],
22
-
23
21
  "@clack/core": ["@clack/core@0.5.0", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow=="],
24
22
 
25
23
  "@clack/prompts": ["@clack/prompts@0.11.0", "", { "dependencies": { "@clack/core": "0.5.0", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw=="],
26
24
 
25
+ "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
26
+
27
27
  "@types/node": ["@types/node@24.12.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ=="],
28
28
 
29
29
  "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
@@ -36,6 +36,10 @@
36
36
 
37
37
  "define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="],
38
38
 
39
+ "effect": ["effect@3.19.19", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-Yc8U/SVXo2dHnaP7zNBlAo83h/nzSJpi7vph6Hzyl4ulgMBIgPmz3UzOjb9sBgpFE00gC0iETR244sfXDNLHRg=="],
40
+
41
+ "fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="],
42
+
39
43
  "is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
40
44
 
41
45
  "is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="],
@@ -46,6 +50,8 @@
46
50
 
47
51
  "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
48
52
 
53
+ "pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="],
54
+
49
55
  "run-applescript": ["run-applescript@7.1.0", "", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="],
50
56
 
51
57
  "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
@@ -1,5 +1,6 @@
1
1
  import { postJson } from "./http.js";
2
2
  import { loadConfig, loadCredentials, saveCredentials } from "./credentials-store.js";
3
+ import { buildSessionId } from "./context/session-id.js";
3
4
  export function createCliAuthProvider() {
4
5
  let cachedCredentials = null;
5
6
  let forceRefresh = false;
@@ -8,19 +9,19 @@ export function createCliAuthProvider() {
8
9
  if (config === null) {
9
10
  throw new Error("Not logged in. Run barekey login first.");
10
11
  }
11
- const credentials = await loadCredentials(config.activeAccountId);
12
+ const credentials = await loadCredentials(config.activeSessionId);
12
13
  if (credentials === null) {
13
14
  throw new Error("CLI credentials are missing. Run barekey login again.");
14
15
  }
15
16
  cachedCredentials = credentials;
16
17
  return {
17
18
  baseUrl: config.baseUrl,
18
- accountId: config.activeAccountId,
19
+ sessionId: config.activeSessionId,
19
20
  credentials,
20
21
  };
21
22
  }
22
23
  async function refreshIfNeeded() {
23
- const { baseUrl, accountId, credentials } = await readCurrentCredentials();
24
+ const { baseUrl, credentials } = await readCurrentCredentials();
24
25
  const now = Date.now();
25
26
  if (!forceRefresh && credentials.accessTokenExpiresAtMs > now + 10_000) {
26
27
  return credentials;
@@ -40,8 +41,10 @@ export function createCliAuthProvider() {
40
41
  clerkUserId: refreshed.clerkUserId,
41
42
  orgId: refreshed.orgId,
42
43
  orgSlug: refreshed.orgSlug,
44
+ lastOrgId: refreshed.orgId,
45
+ lastOrgSlug: refreshed.orgSlug,
43
46
  };
44
- await saveCredentials(accountId, nextCredentials);
47
+ await saveCredentials(buildSessionId(baseUrl, refreshed.clerkUserId), nextCredentials);
45
48
  cachedCredentials = nextCredentials;
46
49
  forceRefresh = false;
47
50
  return nextCredentials;
@@ -27,13 +27,13 @@ export async function requireLocalSession() {
27
27
  if (config === null) {
28
28
  throw new Error("Not logged in. Run barekey auth login first.");
29
29
  }
30
- const credentials = await loadCredentials(config.activeAccountId);
30
+ const credentials = await loadCredentials(config.activeSessionId);
31
31
  if (credentials === null) {
32
32
  throw new Error("Saved credentials not found. Run barekey auth login again.");
33
33
  }
34
34
  return {
35
35
  baseUrl: config.baseUrl,
36
- accountId: config.activeAccountId,
36
+ accountId: config.activeSessionId,
37
37
  credentials,
38
38
  };
39
39
  }
@@ -42,12 +42,12 @@ export async function resolveTarget(options, local) {
42
42
  const isStandalone = runtime?.config.config?.mode === "standalone";
43
43
  const projectSlug = options.project?.trim() || runtime?.config.project || "";
44
44
  const stageSlug = options.stage?.trim() || runtime?.config.environment || "";
45
- const orgSlug = options.org?.trim() || runtime?.config.org || local?.credentials.orgSlug || "";
46
- if (!isStandalone && (projectSlug.length === 0 || stageSlug.length === 0)) {
45
+ const orgSlug = options.org?.trim() || runtime?.config.org || "";
46
+ if (!isStandalone && (orgSlug.length === 0 || projectSlug.length === 0 || stageSlug.length === 0)) {
47
47
  const hint = runtime
48
- ? `Found ${runtime.path} but project/environment is incomplete.`
48
+ ? `Found ${runtime.path} but organization/project/environment is incomplete.`
49
49
  : "No barekey.json found in current directory tree.";
50
- throw new Error(`${hint} Pass --project/--stage, or create barekey.json with {"organization":"...","project":"...","environment":"..."}.`);
50
+ throw new Error(`${hint} Run barekey init or pass --org/--project/--stage.`);
51
51
  }
52
52
  return {
53
53
  projectSlug,
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerAuditCommands(program: Command): void;
@@ -0,0 +1,47 @@
1
+ import { createCliAuthProvider } from "../auth-provider.js";
2
+ import { requireLocalSession, toJsonOutput } from "../command-utils.js";
3
+ import { AuditListResponseSchema } from "../contracts/index.js";
4
+ import { postJson } from "../http.js";
5
+ import { promptForOrganizationSlug } from "./target-prompts.js";
6
+ async function runAuditList(options) {
7
+ const local = await requireLocalSession();
8
+ const authProvider = createCliAuthProvider();
9
+ const accessToken = await authProvider.getAccessToken();
10
+ const orgSlug = await promptForOrganizationSlug(options.org);
11
+ const limit = options.limit ? Number.parseInt(options.limit, 10) : 25;
12
+ const response = await postJson({
13
+ baseUrl: local.baseUrl,
14
+ path: "/v1/cli/audit/list",
15
+ accessToken,
16
+ payload: {
17
+ orgSlug,
18
+ projectSlug: options.project?.trim() || null,
19
+ limit: Number.isFinite(limit) ? limit : 25,
20
+ },
21
+ schema: AuditListResponseSchema,
22
+ });
23
+ if (options.json) {
24
+ toJsonOutput(true, response);
25
+ return;
26
+ }
27
+ if (response.items.length === 0) {
28
+ console.log("No audit events found.");
29
+ return;
30
+ }
31
+ for (const item of response.items) {
32
+ console.log(`${new Date(item.occurredAtMs).toISOString()} ${item.category} ${item.title}`);
33
+ }
34
+ }
35
+ export function registerAuditCommands(program) {
36
+ const audit = program.command("audit").description("Audit history");
37
+ audit
38
+ .command("list")
39
+ .description("List recent audit events")
40
+ .option("--org <slug>", "Organization slug")
41
+ .option("--project <slug>", "Project slug")
42
+ .option("--limit <count>", "Maximum number of events", "25")
43
+ .option("--json", "Machine-readable output", false)
44
+ .action(async (options) => {
45
+ await runAuditList(options);
46
+ });
47
+ }
@@ -4,7 +4,9 @@ import { intro, outro, spinner } from "@clack/prompts";
4
4
  import pc from "picocolors";
5
5
  import open from "open";
6
6
  import { createCliAuthProvider } from "../auth-provider.js";
7
+ import { buildSessionId } from "../context/session-id.js";
7
8
  import { clearConfig, deleteCredentials, saveConfig, saveCredentials, } from "../credentials-store.js";
9
+ import { CliSessionResponseSchema, DevicePollResponseSchema, DeviceStartResponseSchema, RefreshResponseSchema, } from "../contracts/index.js";
8
10
  import { getJson, postJson } from "../http.js";
9
11
  import { requireLocalSession, resolveBaseUrl, toJsonOutput } from "../command-utils.js";
10
12
  function resolveClientName() {
@@ -33,6 +35,10 @@ function resolveVerificationUri(baseUrl, verificationUri) {
33
35
  }
34
36
  return verificationUri;
35
37
  }
38
+ function resolveDisplayName(input) {
39
+ const displayName = input.displayName?.trim();
40
+ return displayName && displayName.length > 0 ? displayName : input.clerkUserId;
41
+ }
36
42
  async function runLogin(options) {
37
43
  const baseUrl = await resolveBaseUrl(options.baseUrl);
38
44
  intro("Barekey CLI login");
@@ -48,6 +54,7 @@ async function runLogin(options) {
48
54
  payload: {
49
55
  clientName: resolveClientName(),
50
56
  },
57
+ schema: DeviceStartResponseSchema,
51
58
  });
52
59
  const verificationUri = resolveVerificationUri(baseUrl, started.verificationUri);
53
60
  loading.stop("Authorization initialized");
@@ -72,28 +79,42 @@ async function runLogin(options) {
72
79
  payload: {
73
80
  deviceCode: started.deviceCode,
74
81
  },
82
+ schema: DevicePollResponseSchema,
75
83
  });
76
84
  if (poll.status === "pending") {
77
85
  await wait(Math.max(1, poll.intervalSec) * 1000);
78
86
  continue;
79
87
  }
80
- const accountId = `${poll.orgSlug}:${poll.clerkUserId}`;
81
- await saveCredentials(accountId, {
88
+ const refreshed = RefreshResponseSchema.make({
82
89
  accessToken: poll.accessToken,
83
90
  refreshToken: poll.refreshToken,
84
91
  accessTokenExpiresAtMs: poll.accessTokenExpiresAtMs,
85
92
  refreshTokenExpiresAtMs: poll.refreshTokenExpiresAtMs,
86
- clerkUserId: poll.clerkUserId,
87
93
  orgId: poll.orgId,
88
94
  orgSlug: poll.orgSlug,
95
+ clerkUserId: poll.clerkUserId,
96
+ displayName: poll.displayName,
97
+ email: poll.email,
98
+ });
99
+ const sessionId = buildSessionId(baseUrl, refreshed.clerkUserId);
100
+ await saveCredentials(sessionId, {
101
+ accessToken: refreshed.accessToken,
102
+ refreshToken: refreshed.refreshToken,
103
+ accessTokenExpiresAtMs: refreshed.accessTokenExpiresAtMs,
104
+ refreshTokenExpiresAtMs: refreshed.refreshTokenExpiresAtMs,
105
+ clerkUserId: refreshed.clerkUserId,
106
+ orgId: refreshed.orgId,
107
+ orgSlug: refreshed.orgSlug,
108
+ lastOrgId: refreshed.orgId,
109
+ lastOrgSlug: refreshed.orgSlug,
89
110
  });
90
111
  await saveConfig({
91
112
  baseUrl,
92
- activeAccountId: accountId,
113
+ activeSessionId: sessionId,
93
114
  });
94
115
  pollSpinner.stop("Login approved");
95
116
  pollActive = false;
96
- outro(`Logged in as ${pc.bold(poll.clerkUserId)} in ${pc.bold(poll.orgSlug)}.`);
117
+ outro(`Signed in as ${pc.bold(resolveDisplayName(refreshed))}.`);
97
118
  return;
98
119
  }
99
120
  pollSpinner.stop("Timed out");
@@ -131,14 +152,16 @@ async function runWhoami(options) {
131
152
  baseUrl: local.baseUrl,
132
153
  path: "/v1/cli/session",
133
154
  accessToken,
155
+ schema: CliSessionResponseSchema,
134
156
  });
135
157
  if (options.json) {
136
158
  toJsonOutput(true, session);
137
159
  return;
138
160
  }
139
- console.log(`${pc.bold("User")}: ${session.clerkUserId}`);
140
- console.log(`${pc.bold("Org")}: ${session.orgSlug}`);
141
- console.log(`${pc.bold("Source")}: ${session.source}`);
161
+ console.log(resolveDisplayName(session));
162
+ if (session.email !== null) {
163
+ console.log(session.email);
164
+ }
142
165
  }
143
166
  export function registerAuthCommands(program) {
144
167
  const auth = program.command("auth").description("Authentication commands");
@@ -162,7 +185,6 @@ export function registerAuthCommands(program) {
162
185
  .action(async (options) => {
163
186
  await runWhoami(options);
164
187
  });
165
- // Backward-compatible top-level auth aliases.
166
188
  program
167
189
  .command("login")
168
190
  .description("Alias for barekey auth login")
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerBillingCommands(program: Command): void;
@@ -0,0 +1,59 @@
1
+ import { createCliAuthProvider } from "../auth-provider.js";
2
+ import { requireLocalSession, toJsonOutput } from "../command-utils.js";
3
+ import { BillingCatalogResponseSchema, BillingStatusResponseSchema } from "../contracts/index.js";
4
+ import { getJson, postJson } from "../http.js";
5
+ import { promptForOrganizationSlug } from "./target-prompts.js";
6
+ async function runBillingCatalog(options) {
7
+ const local = await requireLocalSession();
8
+ const response = await getJson({
9
+ baseUrl: local.baseUrl,
10
+ path: "/v1/cli/billing/catalog",
11
+ schema: BillingCatalogResponseSchema,
12
+ });
13
+ if (options.json) {
14
+ toJsonOutput(true, response);
15
+ return;
16
+ }
17
+ console.log(`Plans: ${response.variants.length}`);
18
+ console.log(`Metered features: ${Object.values(response.featureIds).join(", ")}`);
19
+ }
20
+ async function runBillingStatus(options) {
21
+ const local = await requireLocalSession();
22
+ const authProvider = createCliAuthProvider();
23
+ const accessToken = await authProvider.getAccessToken();
24
+ const orgSlug = await promptForOrganizationSlug(options.org);
25
+ const response = await postJson({
26
+ baseUrl: local.baseUrl,
27
+ path: "/v1/cli/billing/status",
28
+ accessToken,
29
+ payload: {
30
+ orgSlug,
31
+ },
32
+ schema: BillingStatusResponseSchema,
33
+ });
34
+ if (options.json) {
35
+ toJsonOutput(true, response);
36
+ return;
37
+ }
38
+ console.log(`Tier: ${response.currentTier ?? "none"}`);
39
+ console.log(`Product: ${response.currentProductId ?? "none"}`);
40
+ console.log(`Can manage billing: ${response.canManageBilling ? "yes" : "no"}`);
41
+ }
42
+ export function registerBillingCommands(program) {
43
+ const billing = program.command("billing").description("Billing information");
44
+ billing
45
+ .command("catalog")
46
+ .description("Show the public billing catalog")
47
+ .option("--json", "Machine-readable output", false)
48
+ .action(async (options) => {
49
+ await runBillingCatalog(options);
50
+ });
51
+ billing
52
+ .command("status")
53
+ .description("Show billing status for an organization")
54
+ .option("--org <slug>", "Organization slug")
55
+ .option("--json", "Machine-readable output", false)
56
+ .action(async (options) => {
57
+ await runBillingStatus(options);
58
+ });
59
+ }