@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
@@ -1,10 +1,7 @@
1
1
  import { writeFile } from "node:fs/promises";
2
2
 
3
- import { cancel, confirm, isCancel } from "@clack/prompts";
4
- import { BarekeyClient } from "@barekey/sdk/server";
5
- import type { BarekeyJsonConfig } from "@barekey/sdk/server";
3
+ import { cancel, confirm, isCancel, select, text } from "@clack/prompts";
6
4
  import { Command } from "commander";
7
- import pc from "picocolors";
8
5
 
9
6
  import { createCliAuthProvider } from "../auth-provider.js";
10
7
  import {
@@ -16,8 +13,14 @@ import {
16
13
  toJsonOutput,
17
14
  type EnvTargetOptions,
18
15
  } from "../command-utils.js";
16
+ import {
17
+ EnvEvaluateBatchResponseSchema,
18
+ EnvEvaluateResponseSchema,
19
+ EnvListResponseSchema,
20
+ EnvPullResponseSchema,
21
+ EnvWriteResponseSchema,
22
+ } from "../contracts/index.js";
19
23
  import { postJson } from "../http.js";
20
- import { loadRuntimeConfig } from "../runtime-config.js";
21
24
  import {
22
25
  collectOptionValues,
23
26
  parseRolloutFunction,
@@ -27,147 +30,184 @@ import {
27
30
  type CliVisibility,
28
31
  } from "./env-helpers.js";
29
32
 
30
- function createEnvClient(input: {
31
- organization: string | undefined;
32
- project: string;
33
- environment: string;
34
- runtimeConfig: Awaited<ReturnType<typeof loadRuntimeConfig>> | null;
35
- }): BarekeyClient {
36
- if (input.runtimeConfig !== null) {
37
- const organization = input.organization?.trim();
38
- const isStandalone = input.runtimeConfig.config.config?.mode === "standalone";
39
- if (!organization && !isStandalone) {
40
- throw new Error("Organization slug is required.");
41
- }
33
+ type EnvWriteOptions = EnvTargetOptions & {
34
+ ab?: string;
35
+ rollout?: string;
36
+ function?: string;
37
+ point?: Array<string>;
38
+ chance?: string;
39
+ visibility?: CliVisibility;
40
+ type?: "string" | "boolean" | "int64" | "float" | "date" | "json";
41
+ json?: boolean;
42
+ };
43
+
44
+ async function resolveEnvAccess(options: EnvTargetOptions) {
45
+ const local = await requireLocalSession();
46
+ const target = await resolveTarget(options, local);
47
+ const authProvider = createCliAuthProvider();
48
+ const accessToken = await authProvider.getAccessToken();
49
+ return {
50
+ local,
51
+ target,
52
+ accessToken,
53
+ };
54
+ }
42
55
 
43
- const jsonConfig: BarekeyJsonConfig = {
44
- ...(organization ? { organization } : {}),
45
- ...(input.project.trim().length > 0 ? { project: input.project } : {}),
46
- ...(input.environment.trim().length > 0 ? { environment: input.environment } : {}),
47
- ...(input.runtimeConfig.config.config === undefined
48
- ? {}
49
- : {
50
- config: {
51
- typegen: input.runtimeConfig.config.config.typegen,
52
- mode: input.runtimeConfig.config.config.mode,
53
- },
54
- }),
55
- };
56
+ function renderScalar(value: unknown): string {
57
+ if (typeof value === "string") {
58
+ return value;
59
+ }
60
+ return JSON.stringify(value);
61
+ }
56
62
 
57
- return new BarekeyClient({
58
- json: jsonConfig,
59
- });
63
+ async function promptForRequiredText(
64
+ currentValue: string | undefined,
65
+ message: string,
66
+ ): Promise<string> {
67
+ const existing = currentValue?.trim();
68
+ if (existing && existing.length > 0) {
69
+ return existing;
70
+ }
71
+ if (!process.stdout.isTTY) {
72
+ throw new Error(`${message} is required in non-interactive mode.`);
60
73
  }
61
74
 
62
- const organization = input.organization?.trim();
63
- if (!organization) {
64
- throw new Error("Organization slug is required.");
75
+ const prompted = await text({
76
+ message,
77
+ validate(value) {
78
+ return value.trim().length > 0 ? undefined : "This value is required.";
79
+ },
80
+ });
81
+ if (isCancel(prompted)) {
82
+ cancel("Command canceled.");
83
+ process.exit(0);
65
84
  }
85
+ return prompted.trim();
86
+ }
66
87
 
67
- return new BarekeyClient({
68
- organization,
69
- project: input.project,
70
- environment: input.environment,
88
+ async function maybePromptVariant(writeOptions: EnvWriteOptions): Promise<EnvWriteOptions> {
89
+ if (writeOptions.ab !== undefined || writeOptions.rollout !== undefined || !process.stdout.isTTY) {
90
+ return writeOptions;
91
+ }
92
+
93
+ const selectedKind = await select({
94
+ message: "What kind of variable is this?",
95
+ options: [
96
+ { value: "secret", label: "Secret", hint: "One stored value" },
97
+ { value: "ab_roll", label: "A/B roll", hint: "Two values plus traffic split" },
98
+ { value: "rollout", label: "Rollout", hint: "Two values plus rollout milestones" },
99
+ ],
100
+ initialValue: "secret",
71
101
  });
102
+ if (isCancel(selectedKind)) {
103
+ cancel("Command canceled.");
104
+ process.exit(0);
105
+ }
106
+
107
+ if (selectedKind === "ab_roll") {
108
+ const valueB = await promptForRequiredText(undefined, "What's the alternate value?");
109
+ const chance = await promptForRequiredText(
110
+ writeOptions.chance,
111
+ "What's the A-branch probability between 0 and 1?",
112
+ );
113
+ return {
114
+ ...writeOptions,
115
+ ab: valueB,
116
+ chance,
117
+ };
118
+ }
119
+
120
+ if (selectedKind === "rollout") {
121
+ const valueB = await promptForRequiredText(undefined, "What's the rollout value B?");
122
+ return {
123
+ ...writeOptions,
124
+ rollout: valueB,
125
+ };
126
+ }
127
+
128
+ return writeOptions;
72
129
  }
73
130
 
74
131
  async function runEnvGet(
75
- name: string,
132
+ name: string | undefined,
76
133
  options: EnvTargetOptions & {
77
134
  seed?: string;
78
135
  key?: string;
79
136
  json?: boolean;
80
137
  },
81
138
  ): Promise<void> {
82
- const runtime = await loadRuntimeConfig();
83
- const local = runtime?.config.config?.mode === "standalone" ? null : await requireLocalSession();
84
- const target = await resolveTarget(options, local);
85
- const client = createEnvClient({
86
- organization: target.orgSlug ?? local?.credentials.orgSlug,
87
- project: target.projectSlug,
88
- environment: target.stageSlug,
89
- runtimeConfig: runtime,
90
- });
91
- const value = await client.get(name, {
92
- seed: options.seed,
93
- key: options.key,
139
+ const resolvedName = await promptForRequiredText(name, "What's the name of this variable?");
140
+ const { local, target, accessToken } = await resolveEnvAccess(options);
141
+ const value = await postJson({
142
+ baseUrl: local.baseUrl,
143
+ path: "/v1/env/evaluate",
144
+ accessToken,
145
+ payload: {
146
+ orgSlug: target.orgSlug,
147
+ projectSlug: target.projectSlug,
148
+ stageSlug: target.stageSlug,
149
+ name: resolvedName,
150
+ seed: options.seed,
151
+ key: options.key,
152
+ },
153
+ schema: EnvEvaluateResponseSchema,
94
154
  });
95
155
 
96
156
  if (options.json) {
97
- toJsonOutput(true, {
98
- name,
99
- value,
100
- });
157
+ toJsonOutput(true, value);
101
158
  return;
102
159
  }
103
160
 
104
- console.log(String(value));
161
+ console.log(renderScalar(value.value));
105
162
  }
106
163
 
107
164
  async function runEnvGetMany(
108
165
  options: EnvTargetOptions & {
109
- names: string;
166
+ names?: string;
110
167
  seed?: string;
111
168
  key?: string;
112
169
  json?: boolean;
113
170
  },
114
171
  ): Promise<void> {
115
- const runtime = await loadRuntimeConfig();
116
- const local = runtime?.config.config?.mode === "standalone" ? null : await requireLocalSession();
117
- const target = await resolveTarget(options, local);
118
- const client = createEnvClient({
119
- organization: target.orgSlug ?? local?.credentials.orgSlug,
120
- project: target.projectSlug,
121
- environment: target.stageSlug,
122
- runtimeConfig: runtime,
123
- });
172
+ const namesCsv = await promptForRequiredText(
173
+ options.names,
174
+ "Which variable names do you want, comma-separated?",
175
+ );
176
+ const { local, target, accessToken } = await resolveEnvAccess(options);
124
177
 
125
- const names = options.names
178
+ const names = namesCsv
126
179
  .split(",")
127
180
  .map((value) => value.trim())
128
181
  .filter((value) => value.length > 0);
129
182
 
130
- const resolved = await Promise.all(
131
- names.map(async (resolvedName) => ({
132
- name: resolvedName,
133
- value: await client.get(resolvedName, {
134
- seed: options.seed,
135
- key: options.key,
136
- }),
137
- })),
138
- );
183
+ const response = await postJson({
184
+ baseUrl: local.baseUrl,
185
+ path: "/v1/env/evaluate-batch",
186
+ accessToken,
187
+ payload: {
188
+ orgSlug: target.orgSlug,
189
+ projectSlug: target.projectSlug,
190
+ stageSlug: target.stageSlug,
191
+ names,
192
+ seed: options.seed,
193
+ key: options.key,
194
+ },
195
+ schema: EnvEvaluateBatchResponseSchema,
196
+ });
139
197
 
140
198
  if (options.json) {
141
- toJsonOutput(true, resolved);
199
+ toJsonOutput(true, response.values);
142
200
  return;
143
201
  }
144
202
 
145
- for (const value of resolved.sort((left, right) => left.name.localeCompare(right.name))) {
146
- if (value) {
147
- console.log(`${value.name}=${String(value.value)}`);
148
- }
203
+ for (const value of [...response.values].sort((left, right) => left.name.localeCompare(right.name))) {
204
+ console.log(`${value.name}=${renderScalar(value.value)}`);
149
205
  }
150
206
  }
151
207
 
152
208
  async function runEnvList(options: EnvTargetOptions & { json?: boolean }): Promise<void> {
153
- const local = await requireLocalSession();
154
- const target = await resolveTarget(options, local);
155
- const authProvider = createCliAuthProvider();
156
- const accessToken = await authProvider.getAccessToken();
157
-
158
- const response = await postJson<{
159
- variables: Array<{
160
- name: string;
161
- visibility: CliVisibility;
162
- kind: "secret" | "ab_roll" | "rollout";
163
- declaredType: "string" | "boolean" | "int64" | "float" | "date" | "json";
164
- createdAtMs: number;
165
- updatedAtMs: number;
166
- chance: number | null;
167
- rolloutFunction: CliRolloutFunction | null;
168
- rolloutMilestones: Array<{ at: string; percentage: number }> | null;
169
- }>;
170
- }>({
209
+ const { local, target, accessToken } = await resolveEnvAccess(options);
210
+ const response = await postJson({
171
211
  baseUrl: local.baseUrl,
172
212
  path: "/v1/env/list",
173
213
  accessToken,
@@ -176,6 +216,7 @@ async function runEnvList(options: EnvTargetOptions & { json?: boolean }): Promi
176
216
  projectSlug: target.projectSlug,
177
217
  stageSlug: target.stageSlug,
178
218
  },
219
+ schema: EnvListResponseSchema,
179
220
  });
180
221
 
181
222
  if (options.json) {
@@ -192,85 +233,73 @@ async function runEnvList(options: EnvTargetOptions & { json?: boolean }): Promi
192
233
  const chanceSuffix = row.kind === "ab_roll" ? ` chance=${row.chance ?? 0}` : "";
193
234
  const rolloutSuffix =
194
235
  row.kind === "rollout"
195
- ? ` ${pc.dim(
196
- `${row.rolloutFunction ?? "linear"}(${row.rolloutMilestones?.length ?? 0} milestones)`,
197
- )}`
236
+ ? ` ${row.rolloutFunction ?? "linear"}(${row.rolloutMilestones?.length ?? 0} milestones)`
198
237
  : "";
199
238
  console.log(
200
- `${row.name} ${pc.dim(row.visibility)} ${pc.dim(row.kind)} ${pc.dim(row.declaredType)}${chanceSuffix}${rolloutSuffix}`,
239
+ `${row.name} ${row.visibility} ${row.kind} ${row.declaredType}${chanceSuffix}${rolloutSuffix}`,
201
240
  );
202
241
  }
203
242
  }
204
243
 
205
244
  async function runEnvWrite(
206
245
  operation: "create_only" | "upsert",
207
- name: string,
208
- value: string,
209
- options: EnvTargetOptions & {
210
- ab?: string;
211
- rollout?: string;
212
- function?: string;
213
- point?: Array<string>;
214
- chance?: string;
215
- visibility?: CliVisibility;
216
- type?: "string" | "boolean" | "int64" | "float" | "date" | "json";
217
- json?: boolean;
218
- },
246
+ name: string | undefined,
247
+ value: string | undefined,
248
+ options: EnvWriteOptions,
219
249
  ): Promise<void> {
220
- const local = await requireLocalSession();
221
- const target = await resolveTarget(options, local);
222
- const authProvider = createCliAuthProvider();
223
- const accessToken = await authProvider.getAccessToken();
250
+ const resolvedName = await promptForRequiredText(name, "What's the name of this variable?");
251
+ const resolvedValue = await promptForRequiredText(value, "What's the value of this variable?");
252
+ const resolvedOptions = await maybePromptVariant(options);
253
+ const { local, target, accessToken } = await resolveEnvAccess(resolvedOptions);
224
254
 
225
- if (options.ab !== undefined && options.rollout !== undefined) {
255
+ if (resolvedOptions.ab !== undefined && resolvedOptions.rollout !== undefined) {
226
256
  throw new Error("Use either --ab or --rollout, not both.");
227
257
  }
228
- const hasRolloutPoints = (options.point?.length ?? 0) > 0;
229
- if (options.rollout === undefined && (options.function !== undefined || hasRolloutPoints)) {
258
+ const hasRolloutPoints = (resolvedOptions.point?.length ?? 0) > 0;
259
+ if (
260
+ resolvedOptions.rollout === undefined &&
261
+ (resolvedOptions.function !== undefined || hasRolloutPoints)
262
+ ) {
230
263
  throw new Error("--function and --point can only be used together with --rollout.");
231
264
  }
232
- if (options.ab !== undefined && (options.function !== undefined || hasRolloutPoints)) {
265
+ if (resolvedOptions.ab !== undefined && (resolvedOptions.function !== undefined || hasRolloutPoints)) {
233
266
  throw new Error("--function and --point are only supported for --rollout, not --ab.");
234
267
  }
235
- if (options.rollout !== undefined && options.chance !== undefined) {
268
+ if (resolvedOptions.rollout !== undefined && resolvedOptions.chance !== undefined) {
236
269
  throw new Error("--chance only applies to --ab.");
237
270
  }
238
271
 
239
272
  const entry =
240
- options.rollout !== undefined
273
+ resolvedOptions.rollout !== undefined
241
274
  ? {
242
- name,
243
- visibility: parseVisibility(options.visibility),
275
+ name: resolvedName,
276
+ visibility: parseVisibility(resolvedOptions.visibility),
244
277
  kind: "rollout" as const,
245
- declaredType: options.type ?? "string",
246
- valueA: value,
247
- valueB: options.rollout,
248
- rolloutFunction: parseRolloutFunction(options.function),
249
- rolloutMilestones: parseRolloutMilestones(options.point),
278
+ declaredType: resolvedOptions.type ?? "string",
279
+ valueA: resolvedValue,
280
+ valueB: resolvedOptions.rollout,
281
+ rolloutFunction: parseRolloutFunction(resolvedOptions.function),
282
+ rolloutMilestones: parseRolloutMilestones(resolvedOptions.point),
250
283
  }
251
- : options.ab !== undefined
284
+ : resolvedOptions.ab !== undefined
252
285
  ? {
253
- name,
254
- visibility: parseVisibility(options.visibility),
286
+ name: resolvedName,
287
+ visibility: parseVisibility(resolvedOptions.visibility),
255
288
  kind: "ab_roll" as const,
256
- declaredType: options.type ?? "string",
257
- valueA: value,
258
- valueB: options.ab,
259
- chance: parseChance(options.chance),
289
+ declaredType: resolvedOptions.type ?? "string",
290
+ valueA: resolvedValue,
291
+ valueB: resolvedOptions.ab,
292
+ chance: parseChance(resolvedOptions.chance),
260
293
  }
261
294
  : {
262
- name,
263
- visibility: parseVisibility(options.visibility),
295
+ name: resolvedName,
296
+ visibility: parseVisibility(resolvedOptions.visibility),
264
297
  kind: "secret" as const,
265
- declaredType: options.type ?? "string",
266
- value,
298
+ declaredType: resolvedOptions.type ?? "string",
299
+ value: resolvedValue,
267
300
  };
268
301
 
269
- const result = await postJson<{
270
- createdCount: number;
271
- updatedCount: number;
272
- deletedCount: number;
273
- }>({
302
+ const result = await postJson({
274
303
  baseUrl: local.baseUrl,
275
304
  path: "/v1/env/write",
276
305
  accessToken,
@@ -282,9 +311,10 @@ async function runEnvWrite(
282
311
  entries: [entry],
283
312
  deletes: [],
284
313
  },
314
+ schema: EnvWriteResponseSchema,
285
315
  });
286
316
 
287
- if (options.json) {
317
+ if (resolvedOptions.json) {
288
318
  toJsonOutput(true, result);
289
319
  return;
290
320
  }
@@ -295,24 +325,18 @@ async function runEnvWrite(
295
325
  }
296
326
 
297
327
  async function runEnvDelete(
298
- name: string,
328
+ name: string | undefined,
299
329
  options: EnvTargetOptions & {
300
330
  yes?: boolean;
301
331
  ignoreMissing?: boolean;
302
332
  json?: boolean;
303
333
  },
304
334
  ): Promise<void> {
305
- const local = await requireLocalSession();
306
- const target = await resolveTarget(options, local);
307
- const authProvider = createCliAuthProvider();
308
- const accessToken = await authProvider.getAccessToken();
335
+ const resolvedName = await promptForRequiredText(name, "What's the name of the variable to delete?");
336
+ const { local, target, accessToken } = await resolveEnvAccess(options);
309
337
 
310
338
  if (!options.ignoreMissing) {
311
- const listed = await postJson<{
312
- variables: Array<{
313
- name: string;
314
- }>;
315
- }>({
339
+ const listed = await postJson({
316
340
  baseUrl: local.baseUrl,
317
341
  path: "/v1/env/list",
318
342
  accessToken,
@@ -321,10 +345,11 @@ async function runEnvDelete(
321
345
  projectSlug: target.projectSlug,
322
346
  stageSlug: target.stageSlug,
323
347
  },
348
+ schema: EnvListResponseSchema,
324
349
  });
325
- const exists = listed.variables.some((row) => row.name === name);
350
+ const exists = listed.variables.some((row) => row.name === resolvedName);
326
351
  if (!exists) {
327
- throw new Error(`Variable ${name} was not found in this stage.`);
352
+ throw new Error(`Variable ${resolvedName} was not found in this stage.`);
328
353
  }
329
354
  }
330
355
 
@@ -333,7 +358,7 @@ async function runEnvDelete(
333
358
  throw new Error("Deletion requires --yes in non-interactive mode.");
334
359
  }
335
360
  const confirmed = await confirm({
336
- message: `Delete variable ${name}?`,
361
+ message: `Delete variable ${resolvedName}?`,
337
362
  initialValue: false,
338
363
  });
339
364
  if (isCancel(confirmed)) {
@@ -345,11 +370,7 @@ async function runEnvDelete(
345
370
  }
346
371
  }
347
372
 
348
- const result = await postJson<{
349
- createdCount: number;
350
- updatedCount: number;
351
- deletedCount: number;
352
- }>({
373
+ const result = await postJson({
353
374
  baseUrl: local.baseUrl,
354
375
  path: "/v1/env/write",
355
376
  accessToken,
@@ -359,8 +380,9 @@ async function runEnvDelete(
359
380
  stageSlug: target.stageSlug,
360
381
  mode: "upsert",
361
382
  entries: [],
362
- deletes: [name],
383
+ deletes: [resolvedName],
363
384
  },
385
+ schema: EnvWriteResponseSchema,
364
386
  });
365
387
 
366
388
  if (options.json) {
@@ -380,20 +402,8 @@ async function runEnvPull(
380
402
  redact?: boolean;
381
403
  },
382
404
  ): Promise<void> {
383
- const local = await requireLocalSession();
384
- const target = await resolveTarget(options, local);
385
- const authProvider = createCliAuthProvider();
386
- const accessToken = await authProvider.getAccessToken();
387
-
388
- const response = await postJson<{
389
- values: Array<{
390
- name: string;
391
- kind: "secret" | "ab_roll" | "rollout";
392
- declaredType: "string" | "boolean" | "int64" | "float" | "date" | "json";
393
- value: string;
394
- }>;
395
- byName: Record<string, string>;
396
- }>({
405
+ const { local, target, accessToken } = await resolveEnvAccess(options);
406
+ const response = await postJson({
397
407
  baseUrl: local.baseUrl,
398
408
  path: "/v1/env/pull",
399
409
  accessToken,
@@ -404,6 +414,7 @@ async function runEnvPull(
404
414
  seed: options.seed,
405
415
  key: options.key,
406
416
  },
417
+ schema: EnvPullResponseSchema,
407
418
  });
408
419
 
409
420
  const format = options.format ?? "dotenv";
@@ -434,13 +445,13 @@ export function registerEnvCommands(program: Command): void {
434
445
  env
435
446
  .command("get")
436
447
  .description("Evaluate one variable")
437
- .argument("<name>", "Variable name")
448
+ .argument("[name]", "Variable name")
438
449
  .option("--seed <value>", "Deterministic seed")
439
450
  .option("--key <value>", "Deterministic key")
440
451
  .option("--json", "Machine-readable output", false),
441
452
  ).action(
442
453
  async (
443
- name: string,
454
+ name: string | undefined,
444
455
  options: EnvTargetOptions & { seed?: string; key?: string; json?: boolean },
445
456
  ) => {
446
457
  await runEnvGet(name, options);
@@ -451,14 +462,14 @@ export function registerEnvCommands(program: Command): void {
451
462
  env
452
463
  .command("get-many")
453
464
  .description("Evaluate a batch of variables")
454
- .requiredOption("--names <csv>", "Comma-separated variable names")
465
+ .option("--names <csv>", "Comma-separated variable names")
455
466
  .option("--seed <value>", "Deterministic seed")
456
467
  .option("--key <value>", "Deterministic key")
457
468
  .option("--json", "Machine-readable output", false),
458
469
  ).action(
459
470
  async (
460
471
  options: EnvTargetOptions & {
461
- names: string;
472
+ names?: string;
462
473
  seed?: string;
463
474
  key?: string;
464
475
  json?: boolean;
@@ -481,8 +492,8 @@ export function registerEnvCommands(program: Command): void {
481
492
  env
482
493
  .command("new")
483
494
  .description("Create one variable")
484
- .argument("<name>", "Variable name")
485
- .argument("<value>", "Variable value")
495
+ .argument("[name]", "Variable name")
496
+ .argument("[value]", "Variable value")
486
497
  .option("--ab <value-b>", "Second value for ab_roll")
487
498
  .option("--rollout <value-b>", "Second value for rollout")
488
499
  .option("--chance <number>", "A-branch probability between 0 and 1")
@@ -501,18 +512,9 @@ export function registerEnvCommands(program: Command): void {
501
512
  .option("--json", "Machine-readable output", false),
502
513
  ).action(
503
514
  async (
504
- name: string,
505
- value: string,
506
- options: EnvTargetOptions & {
507
- ab?: string;
508
- rollout?: string;
509
- function?: string;
510
- point?: Array<string>;
511
- chance?: string;
512
- visibility?: CliVisibility;
513
- type?: "string" | "boolean" | "int64" | "float" | "date" | "json";
514
- json?: boolean;
515
- },
515
+ name: string | undefined,
516
+ value: string | undefined,
517
+ options: EnvWriteOptions,
516
518
  ) => {
517
519
  await runEnvWrite("create_only", name, value, options);
518
520
  },
@@ -522,8 +524,8 @@ export function registerEnvCommands(program: Command): void {
522
524
  env
523
525
  .command("set")
524
526
  .description("Upsert one variable")
525
- .argument("<name>", "Variable name")
526
- .argument("<value>", "Variable value")
527
+ .argument("[name]", "Variable name")
528
+ .argument("[value]", "Variable value")
527
529
  .option("--ab <value-b>", "Second value for ab_roll")
528
530
  .option("--rollout <value-b>", "Second value for rollout")
529
531
  .option("--chance <number>", "A-branch probability between 0 and 1")
@@ -542,18 +544,9 @@ export function registerEnvCommands(program: Command): void {
542
544
  .option("--json", "Machine-readable output", false),
543
545
  ).action(
544
546
  async (
545
- name: string,
546
- value: string,
547
- options: EnvTargetOptions & {
548
- ab?: string;
549
- rollout?: string;
550
- function?: string;
551
- point?: Array<string>;
552
- chance?: string;
553
- visibility?: CliVisibility;
554
- type?: "string" | "boolean" | "int64" | "float" | "date" | "json";
555
- json?: boolean;
556
- },
547
+ name: string | undefined,
548
+ value: string | undefined,
549
+ options: EnvWriteOptions,
557
550
  ) => {
558
551
  await runEnvWrite("upsert", name, value, options);
559
552
  },
@@ -563,13 +556,13 @@ export function registerEnvCommands(program: Command): void {
563
556
  env
564
557
  .command("delete")
565
558
  .description("Delete one variable")
566
- .argument("<name>", "Variable name")
559
+ .argument("[name]", "Variable name")
567
560
  .option("--yes", "Skip confirmation", false)
568
561
  .option("--ignore-missing", "Do not fail when variable is missing", false)
569
562
  .option("--json", "Machine-readable output", false),
570
563
  ).action(
571
564
  async (
572
- name: string,
565
+ name: string | undefined,
573
566
  options: EnvTargetOptions & { yes?: boolean; ignoreMissing?: boolean; json?: boolean },
574
567
  ) => {
575
568
  await runEnvDelete(name, options);