@lunora/cli 1.0.0-alpha.9 → 1.0.0-alpha.90

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 (59) hide show
  1. package/LICENSE.md +6 -0
  2. package/__assets__/package-og.svg +1 -1
  3. package/dist/bin.mjs +1 -1
  4. package/dist/index.d.mts +358 -300
  5. package/dist/index.d.ts +358 -300
  6. package/dist/index.mjs +8 -9
  7. package/dist/packem_chunks/handler.mjs +15 -5
  8. package/dist/packem_chunks/handler10.mjs +8 -14
  9. package/dist/packem_chunks/handler11.mjs +20 -189
  10. package/dist/packem_chunks/handler12.mjs +176 -115
  11. package/dist/packem_chunks/handler13.mjs +118 -52
  12. package/dist/packem_chunks/handler14.mjs +50 -43
  13. package/dist/packem_chunks/handler15.mjs +48 -67
  14. package/dist/packem_chunks/handler16.mjs +94 -37
  15. package/dist/packem_chunks/handler17.mjs +39 -100
  16. package/dist/packem_chunks/handler18.mjs +87 -152
  17. package/dist/packem_chunks/handler19.mjs +148 -67
  18. package/dist/packem_chunks/handler2.mjs +6 -4
  19. package/dist/packem_chunks/handler20.mjs +77 -75
  20. package/dist/packem_chunks/handler21.mjs +71 -288
  21. package/dist/packem_chunks/handler3.mjs +8 -4
  22. package/dist/packem_chunks/handler4.mjs +2 -2
  23. package/dist/packem_chunks/handler5.mjs +7 -4
  24. package/dist/packem_chunks/handler6.mjs +10 -7
  25. package/dist/packem_chunks/handler7.mjs +2 -2
  26. package/dist/packem_chunks/handler8.mjs +1 -1
  27. package/dist/packem_chunks/handler9.mjs +330 -12
  28. package/dist/packem_chunks/planDevCommand.mjs +762 -81
  29. package/dist/packem_chunks/runCodegenCommand.mjs +5 -4
  30. package/dist/packem_chunks/runDeployCommand.mjs +137 -22
  31. package/dist/packem_chunks/runInitCommand.mjs +1648 -188
  32. package/dist/packem_chunks/runMigrateGenerateCommand.mjs +94 -47
  33. package/dist/packem_chunks/runResetCommand.mjs +2 -2
  34. package/dist/packem_chunks/runRpcCommand.mjs +3 -2
  35. package/dist/packem_shared/{COMMANDS-Dh0bSERi.mjs → COMMANDS-DZ0NnuqI.mjs} +103 -27
  36. package/dist/packem_shared/{DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs → DEFAULT_IMPORT_BATCH_SIZE-D0VOTerB.mjs} +7 -2
  37. package/dist/packem_shared/{api-spec-CtA6ilu4.mjs → api-spec-Bx0iKbxA.mjs} +3 -1
  38. package/dist/packem_shared/{buildRegistryIndex-BcYe607_.mjs → buildRegistryIndex-BS5ig822.mjs} +1 -1
  39. package/dist/packem_shared/codegen-error-DJG-ghs_.mjs +31 -0
  40. package/dist/packem_shared/{command-BDXcJCCJ.mjs → command-lYnl4QyF.mjs} +6 -1
  41. package/dist/packem_shared/{commands-CkkATMMx.mjs → commands-D5Yxt9VY.mjs} +199 -20
  42. package/dist/packem_shared/{createLogger-CHPNjFw2.mjs → createLogger-CIWSHrTL.mjs} +40 -8
  43. package/dist/packem_shared/{createRecordingSpawner-DxI3mebw.mjs → createRecordingSpawner-WuSn20kb.mjs} +23 -2
  44. package/dist/packem_shared/detect-package-manager-v4hHpQd0.mjs +62 -0
  45. package/dist/packem_shared/{insertSchemaExtension-BuzF6-t2.mjs → insertSchemaExtension-DAqbfr9Z.mjs} +15 -10
  46. package/dist/packem_shared/{open-url-Dfq6fAyT.mjs → open-url-4PBLY9X0.mjs} +3 -2
  47. package/dist/packem_shared/{output-format-7gyGR3h8.mjs → output-format-B4642rjE.mjs} +1 -1
  48. package/dist/packem_shared/{parseManifest--vZf2FY1.mjs → parseManifest-Dbp-Q2q3.mjs} +36 -9
  49. package/dist/packem_shared/prompt-cancelled-APzX1Im-.mjs +9 -0
  50. package/dist/packem_shared/runAddCommand-D1hgfqFQ.mjs +4 -0
  51. package/dist/packem_shared/{storage-Bjo35hPa.mjs → storage-BXU4ax4O.mjs} +1 -1
  52. package/dist/packem_shared/tui-prompts-BjEN8XgP.mjs +658 -0
  53. package/dist/packem_shared/wrangler-secrets-Coni-mER.mjs +49 -0
  54. package/package.json +17 -14
  55. package/skills/lunora-quickstart/SKILL.md +26 -5
  56. package/dist/packem_shared/detect-package-manager-DYp7n3mJ.mjs +0 -61
  57. package/dist/packem_shared/parseArgs-YXFuKdEk.mjs +0 -56
  58. package/dist/packem_shared/runAddCommand-DepS9W9W.mjs +0 -4
  59. package/dist/packem_shared/tui-prompts-CA9lngSS.mjs +0 -269
@@ -1,5 +1,6 @@
1
- import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
2
- import { defaultSpawner } from '../packem_shared/createRecordingSpawner-DxI3mebw.mjs';
1
+ import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
2
+ import { e as execArgsFor, d as detectPackageManager } from '../packem_shared/detect-package-manager-v4hHpQd0.mjs';
3
+ import { defaultSpawner } from '../packem_shared/createRecordingSpawner-WuSn20kb.mjs';
3
4
 
4
5
  const withEnv = (args, env) => {
5
6
  if (env !== void 0) {
@@ -8,7 +9,7 @@ const withEnv = (args, env) => {
8
9
  return args;
9
10
  };
10
11
  const buildListArgs = (options) => {
11
- const args = withEnv(["exec", "wrangler", "deployments", "list"], options.env);
12
+ const args = withEnv(["deployments", "list"], options.env);
12
13
  if (options.json) {
13
14
  args.push("--json");
14
15
  }
@@ -20,7 +21,7 @@ const buildArgs = (options) => {
20
21
  if (options.versionId === void 0) {
21
22
  return { error: "deployments inspect requires a version id. Usage: lunora deployments inspect <version-id>" };
22
23
  }
23
- return { args: withEnv(["exec", "wrangler", "versions", "view", options.versionId], options.env) };
24
+ return { args: withEnv(["versions", "view", options.versionId], options.env) };
24
25
  }
25
26
  case "list": {
26
27
  return { args: buildListArgs(options) };
@@ -32,7 +33,7 @@ const buildArgs = (options) => {
32
33
  if (!options.yes) {
33
34
  return { error: "deployments promote shifts 100% of live traffic. Re-run with --yes to confirm." };
34
35
  }
35
- const args = withEnv(["exec", "wrangler", "versions", "deploy", `${options.versionId}@100%`, "--yes"], options.env);
36
+ const args = withEnv(["versions", "deploy", `${options.versionId}@100%`, "--yes"], options.env);
36
37
  if (options.message !== void 0) {
37
38
  args.push("--message", options.message);
38
39
  }
@@ -42,7 +43,7 @@ const buildArgs = (options) => {
42
43
  if (!options.yes) {
43
44
  return { error: "deployments rollback changes the live version. Re-run with --yes to confirm." };
44
45
  }
45
- const args = withEnv(["exec", "wrangler", "rollback"], options.env);
46
+ const args = withEnv(["rollback"], options.env);
46
47
  if (options.versionId !== void 0) {
47
48
  args.push(options.versionId);
48
49
  }
@@ -63,7 +64,9 @@ const runDeploymentsCommand = async (options) => {
63
64
  options.logger.error(error ?? "deployments: nothing to run");
64
65
  return { code: 1, descriptor: void 0, error };
65
66
  }
66
- const descriptor = { args, command: "pnpm", cwd: options.cwd ?? process.cwd() };
67
+ const cwd = options.cwd ?? process.cwd();
68
+ const exec = execArgsFor(detectPackageManager(cwd), "wrangler", args);
69
+ const descriptor = { args: exec.args, command: exec.command, cwd };
67
70
  options.logger.info(`${descriptor.command} ${descriptor.args.join(" ")}`);
68
71
  const spawner = options.spawner ?? defaultSpawner;
69
72
  const result = await spawner(descriptor);
@@ -1,5 +1,5 @@
1
- import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
2
- import { o as openUrl } from '../packem_shared/open-url-Dfq6fAyT.mjs';
1
+ import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
2
+ import { o as openUrl } from '../packem_shared/open-url-4PBLY9X0.mjs';
3
3
 
4
4
  const DEFAULT_DOCS_URL = "https://lunora.anolilab.dev/docs";
5
5
  const trimSlashes = (value) => {
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { findWranglerFile, readWranglerJsonc, validateWranglerConfig, isPlaceholderValue, DEV_VARS_FILE, parseDevVariableEntries, inferLunoraBindings } from '@lunora/config';
4
- import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
4
+ import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
5
5
 
6
6
  const SECRET_KEY_PATTERN = /(?:KEY|PASSWORD|SECRET|TOKEN)$/u;
7
7
  const isD1PlaceholderId = (databaseId) => {
@@ -1,16 +1,334 @@
1
- import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
2
- import { a as resolveProductionWorkerUrl } from '../packem_shared/resolve-target-qbsJ_5sF.mjs';
3
- import { runExportCommand } from '../packem_shared/DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs';
1
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { DEV_VARS_FILE, DEV_VARS_KEY_PATTERN, generateSecretValue, DEV_VARS_EXAMPLE_FILE, parseDevVariableEntries, isPlaceholderValue, packageNamesFromBindings, inferLunoraBindings, requiredSecrets, isMintableSecretKey } from '@lunora/config';
4
+ import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
5
+ import { d as detectPackageManager, e as execArgsFor } from '../packem_shared/detect-package-manager-v4hHpQd0.mjs';
6
+ import { defaultSpawner } from '../packem_shared/createRecordingSpawner-WuSn20kb.mjs';
7
+ import { l as listRemoteSecrets } from '../packem_shared/wrangler-secrets-Coni-mER.mjs';
4
8
 
5
- const execute = defineHandler(
6
- ({ argument, cwd, logger, options }) => runExportCommand({
9
+ const NEWLINE_PRESENT = /[\r\n]/u;
10
+ const UNREPRESENTABLE_PRESENT = /["\\]/u;
11
+ const parseDevVariables = (content) => {
12
+ const map = /* @__PURE__ */ new Map();
13
+ for (const entry of parseDevVariableEntries(content)) {
14
+ map.set(entry.key, entry);
15
+ }
16
+ return map;
17
+ };
18
+ const escapeRegExp = (value) => value.replaceAll(/[.*+?^${}()|[\]\\]/gu, String.raw`\$&`);
19
+ const devVariableLinePattern = (key, global) => new RegExp(String.raw`^[ \t]*${escapeRegExp(key)}[ \t]*=.*(\r?\n|$)`, global ? "gmu" : "mu");
20
+ const upsertDevVariableLine = (content, key, value) => {
21
+ const rendered = `${key}="${value}"`;
22
+ if (!devVariableLinePattern(key, false).test(content)) {
23
+ if (content === "") {
24
+ return `${rendered}
25
+ `;
26
+ }
27
+ return content.endsWith("\n") ? `${content}${rendered}
28
+ ` : `${content}
29
+ ${rendered}
30
+ `;
31
+ }
32
+ let replacedFirst = false;
33
+ return content.replace(devVariableLinePattern(key, true), (_match, newline) => {
34
+ if (replacedFirst) {
35
+ return "";
36
+ }
37
+ replacedFirst = true;
38
+ return `${rendered}${newline}`;
39
+ });
40
+ };
41
+ const removeDevVariableLine = (content, key) => content.replaceAll(new RegExp(String.raw`^[ \t]*${key}[ \t]*=.*(?:\r?\n|$)`, "gmu"), "");
42
+ const redact = (value) => {
43
+ if (value.length <= 4) {
44
+ return "****";
45
+ }
46
+ return `${value.slice(0, 4)}${"*".repeat(Math.min(8, value.length - 4))}`;
47
+ };
48
+ const loadDevVariables = (devVariablesPath) => {
49
+ if (!existsSync(devVariablesPath)) {
50
+ return /* @__PURE__ */ new Map();
51
+ }
52
+ return parseDevVariables(readFileSync(devVariablesPath, "utf8"));
53
+ };
54
+ const runEnvList = (context) => {
55
+ const map = loadDevVariables(context.devVariablesPath);
56
+ if (map.size === 0) {
57
+ context.logger.info(`${DEV_VARS_FILE}: (empty)`);
58
+ return { code: 0, descriptors: [] };
59
+ }
60
+ for (const entry of map.values()) {
61
+ context.logger.info(`${entry.key}=${redact(entry.value)}`);
62
+ }
63
+ return { code: 0, descriptors: [] };
64
+ };
65
+ const runEnvGet = (context) => {
66
+ const { devVariablesPath, logger, options } = context;
67
+ if (!options.key) {
68
+ logger.error("env get requires a key. Usage: lunora env get <KEY>");
69
+ return { code: 1, descriptors: [] };
70
+ }
71
+ const entry = loadDevVariables(devVariablesPath).get(options.key);
72
+ if (!entry) {
73
+ logger.error(`env: ${options.key} is not set in ${DEV_VARS_FILE}`);
74
+ return { code: 1, descriptors: [] };
75
+ }
76
+ process.stdout.write(`${entry.value}
77
+ `);
78
+ return { code: 0, descriptors: [] };
79
+ };
80
+ const runEnvSet = (context) => {
81
+ const { devVariablesPath, logger, options } = context;
82
+ if (!options.key) {
83
+ logger.error("env set requires a key. Usage: lunora env set <KEY> <VALUE>");
84
+ return { code: 1, descriptors: [] };
85
+ }
86
+ if (!DEV_VARS_KEY_PATTERN.test(options.key)) {
87
+ logger.error(`env: invalid key "${options.key}" — must match [A-Za-z_][A-Za-z0-9_]*`);
88
+ return { code: 1, descriptors: [] };
89
+ }
90
+ if (options.value === void 0) {
91
+ logger.error("env set requires a value. Usage: lunora env set <KEY> <VALUE>");
92
+ return { code: 1, descriptors: [] };
93
+ }
94
+ if (NEWLINE_PRESENT.test(options.value)) {
95
+ logger.error(`env: value for "${options.key}" contains a newline, which .dev.vars cannot represent`);
96
+ return { code: 1, descriptors: [] };
97
+ }
98
+ if (UNREPRESENTABLE_PRESENT.test(options.value)) {
99
+ logger.error(`env: value for "${options.key}" contains a double-quote or backslash, which .dev.vars cannot round-trip`);
100
+ return { code: 1, descriptors: [] };
101
+ }
102
+ const raw = existsSync(devVariablesPath) ? readFileSync(devVariablesPath, "utf8") : "";
103
+ writeFileSync(devVariablesPath, upsertDevVariableLine(raw, options.key, options.value), "utf8");
104
+ logger.success(`env: set ${options.key} (${redact(options.value)}) in ${DEV_VARS_FILE}`);
105
+ return { code: 0, descriptors: [] };
106
+ };
107
+ const runEnvUnset = (context) => {
108
+ const { devVariablesPath, logger, options } = context;
109
+ if (!options.key) {
110
+ logger.error("env unset requires a key. Usage: lunora env unset <KEY>");
111
+ return { code: 1, descriptors: [] };
112
+ }
113
+ const raw = existsSync(devVariablesPath) ? readFileSync(devVariablesPath, "utf8") : "";
114
+ if (!parseDevVariables(raw).has(options.key)) {
115
+ logger.warn(`env: ${options.key} was not set in ${DEV_VARS_FILE}`);
116
+ return { code: 0, descriptors: [] };
117
+ }
118
+ writeFileSync(devVariablesPath, removeDevVariableLine(raw, options.key), "utf8");
119
+ logger.success(`env: unset ${options.key} in ${DEV_VARS_FILE}`);
120
+ return { code: 0, descriptors: [] };
121
+ };
122
+ const runEnvPush = async (context) => {
123
+ const { devVariablesPath, logger, options } = context;
124
+ if (!options.yes) {
125
+ logger.error("env push uploads secrets to Cloudflare. Re-run with --yes to confirm.");
126
+ return { code: 1, descriptors: [] };
127
+ }
128
+ const map = loadDevVariables(devVariablesPath);
129
+ if (map.size === 0) {
130
+ logger.warn(`${DEV_VARS_FILE}: nothing to push (empty)`);
131
+ return { code: 0, descriptors: [] };
132
+ }
133
+ const spawner = options.spawner ?? defaultSpawner;
134
+ const cwd = options.cwd ?? process.cwd();
135
+ const manager = detectPackageManager(cwd);
136
+ const descriptors = [];
137
+ for (const entry of map.values()) {
138
+ const args = ["secret", "put", entry.key];
139
+ if (options.prod) {
140
+ args.push("--env", "production");
141
+ }
142
+ if (options.temporary) {
143
+ args.push("--temporary");
144
+ }
145
+ const exec = execArgsFor(manager, "wrangler", args);
146
+ const descriptor = {
147
+ args: exec.args,
148
+ command: exec.command,
149
+ cwd,
150
+ // `wrangler secret put <name>` reads the value from stdin. We
151
+ // pipe it through the spawner's `input` channel so the secret
152
+ // never lands on the command line, in env, or in shell history.
153
+ input: entry.value
154
+ };
155
+ descriptors.push(descriptor);
156
+ logger.info(`pushing ${entry.key} -> wrangler secret${options.prod ? " (production)" : ""}`);
157
+ const result = await spawner(descriptor);
158
+ if (result.code !== 0) {
159
+ logger.error(`env push: failed at ${entry.key} (exit ${String(result.code)})`);
160
+ return { code: result.code, descriptors };
161
+ }
162
+ }
163
+ logger.success(`env: pushed ${String(map.size)} secret(s)`);
164
+ return { code: 0, descriptors };
165
+ };
166
+ const fetchRemoteSecretNames = (context) => {
167
+ const lister = context.options.secretLister ?? listRemoteSecrets;
168
+ return lister({
169
+ cwd: context.cwd,
170
+ env: context.options.prod ? "production" : void 0,
171
+ temporary: context.options.temporary
172
+ });
173
+ };
174
+ const runEnvDiff = async (context) => {
175
+ const { devVariablesPath, logger } = context;
176
+ const remote = await fetchRemoteSecretNames(context);
177
+ if (!remote.ok) {
178
+ logger.error(`env diff: ${remote.error ?? "failed to list remote secrets"}`);
179
+ return { code: 1, descriptors: [] };
180
+ }
181
+ const localKeys = new Set(loadDevVariables(devVariablesPath).keys());
182
+ const remoteKeys = new Set(remote.names);
183
+ const localOnly = [...localKeys].filter((key) => !remoteKeys.has(key)).toSorted((a, b) => a.localeCompare(b));
184
+ const remoteOnly = remote.names.filter((name) => !localKeys.has(name));
185
+ const both = [...localKeys].filter((key) => remoteKeys.has(key)).toSorted((a, b) => a.localeCompare(b));
186
+ for (const key of localOnly) {
187
+ logger.info(`local only (run \`lunora env push\`): ${key}`);
188
+ }
189
+ for (const key of remoteOnly) {
190
+ logger.info(`remote only (in Cloudflare, not ${DEV_VARS_FILE}): ${key}`);
191
+ }
192
+ logger.info(`in both: ${String(both.length)} secret(s)`);
193
+ if (localOnly.length === 0 && remoteOnly.length === 0) {
194
+ logger.success("env diff: local and remote secret names match");
195
+ }
196
+ return { code: 0, descriptors: [] };
197
+ };
198
+ const runEnvDoctor = (context) => {
199
+ const { cwd, devVariablesPath, logger } = context;
200
+ const examplePath = join(cwd, DEV_VARS_EXAMPLE_FILE);
201
+ if (!existsSync(examplePath)) {
202
+ logger.info(`env doctor: no ${DEV_VARS_EXAMPLE_FILE} to check against — nothing to validate.`);
203
+ return { code: 0, descriptors: [] };
204
+ }
205
+ const exampleKeys = parseDevVariableEntries(readFileSync(examplePath, "utf8")).map((entry) => entry.key);
206
+ const current = loadDevVariables(devVariablesPath);
207
+ if (!existsSync(devVariablesPath)) {
208
+ logger.error(`env doctor: ${DEV_VARS_FILE} is missing. Run \`lunora dev\` to scaffold it, or \`lunora env set <KEY> <VALUE>\`.`);
209
+ logger.info(`expected (from ${DEV_VARS_EXAMPLE_FILE}): ${exampleKeys.join(", ")}`);
210
+ return { code: 1, descriptors: [] };
211
+ }
212
+ const missing = exampleKeys.filter((key) => !current.has(key));
213
+ const placeholders = [...current.values()].filter((entry) => isPlaceholderValue(entry.value)).map((entry) => entry.key);
214
+ const exampleKeySet = new Set(exampleKeys);
215
+ const extra = [...current.keys()].filter((key) => !exampleKeySet.has(key));
216
+ for (const key of missing) {
217
+ logger.error(`missing: ${key} is in ${DEV_VARS_EXAMPLE_FILE} but not ${DEV_VARS_FILE}`);
218
+ }
219
+ for (const key of placeholders) {
220
+ logger.error(`unset: ${key} still has a placeholder value`);
221
+ }
222
+ for (const key of extra) {
223
+ logger.info(`extra: ${key} is set locally but not listed in ${DEV_VARS_EXAMPLE_FILE}`);
224
+ }
225
+ if (missing.length === 0 && placeholders.length === 0) {
226
+ logger.success(`env doctor: ${DEV_VARS_FILE} looks good (${String(current.size)} var(s)).`);
227
+ return { code: 0, descriptors: [] };
228
+ }
229
+ return { code: 1, descriptors: [] };
230
+ };
231
+ const resolveMintableKeys = async (context) => {
232
+ let packages = [];
233
+ try {
234
+ packages = packageNamesFromBindings(await inferLunoraBindings({ projectRoot: context.cwd }));
235
+ } catch {
236
+ }
237
+ const fromPackages = requiredSecrets(packages).map((entry) => entry.key).filter((key) => isMintableSecretKey(key));
238
+ const fromLocal = [...loadDevVariables(context.devVariablesPath).keys()].filter((key) => isMintableSecretKey(key));
239
+ return [.../* @__PURE__ */ new Set([...fromPackages, ...fromLocal])];
240
+ };
241
+ const runEnvGenerate = async (context) => {
242
+ const { devVariablesPath, logger, options } = context;
243
+ let keys;
244
+ if (options.key === void 0) {
245
+ keys = await resolveMintableKeys(context);
246
+ if (keys.length === 0) {
247
+ logger.info("env generate: no locally-generatable secrets for this project. Name one explicitly: lunora env generate <KEY>");
248
+ return { code: 0, descriptors: [] };
249
+ }
250
+ } else {
251
+ if (!DEV_VARS_KEY_PATTERN.test(options.key)) {
252
+ logger.error(`env: invalid key "${options.key}" — must match [A-Za-z_][A-Za-z0-9_]*`);
253
+ return { code: 1, descriptors: [] };
254
+ }
255
+ keys = [options.key];
256
+ }
257
+ const generated = keys.map((key) => {
258
+ return { key, value: generateSecretValue() };
259
+ });
260
+ if (options.set === true) {
261
+ let raw = existsSync(devVariablesPath) ? readFileSync(devVariablesPath, "utf8") : "";
262
+ for (const entry of generated) {
263
+ raw = upsertDevVariableLine(raw, entry.key, entry.value);
264
+ }
265
+ writeFileSync(devVariablesPath, raw, "utf8");
266
+ logger.success(`env: generated ${String(generated.length)} secret(s) into ${DEV_VARS_FILE}: ${generated.map((entry) => entry.key).join(", ")}`);
267
+ return { code: 0, descriptors: [] };
268
+ }
269
+ for (const entry of generated) {
270
+ process.stdout.write(`${entry.key}=${entry.value}
271
+ `);
272
+ }
273
+ return { code: 0, descriptors: [] };
274
+ };
275
+ const runEnvCommand = async (options) => {
276
+ const cwd = options.cwd ?? process.cwd();
277
+ const context = {
278
+ cwd,
279
+ devVariablesPath: join(cwd, DEV_VARS_FILE),
280
+ logger: options.logger,
281
+ options
282
+ };
283
+ switch (options.subcommand) {
284
+ case "diff": {
285
+ return runEnvDiff(context);
286
+ }
287
+ case "doctor": {
288
+ return runEnvDoctor(context);
289
+ }
290
+ case "generate": {
291
+ return runEnvGenerate(context);
292
+ }
293
+ case "get": {
294
+ return runEnvGet(context);
295
+ }
296
+ case "list": {
297
+ return runEnvList(context);
298
+ }
299
+ case "push": {
300
+ return runEnvPush(context);
301
+ }
302
+ case "set": {
303
+ return runEnvSet(context);
304
+ }
305
+ case "unset": {
306
+ return runEnvUnset(context);
307
+ }
308
+ default: {
309
+ options.logger.error(`env: unknown subcommand "${options.subcommand}"`);
310
+ return { code: 1, descriptors: [] };
311
+ }
312
+ }
313
+ };
314
+ const isEnvSubcommand = (value) => value === "list" || value === "get" || value === "set" || value === "unset" || value === "push" || value === "diff" || value === "doctor" || value === "generate";
315
+ const execute = defineHandler(({ argument, cwd, logger, options }) => {
316
+ const sub = argument[0];
317
+ if (!isEnvSubcommand(sub)) {
318
+ logger.error(`env: unknown subcommand "${sub ?? ""}" — expected list | get | set | unset | push | diff | doctor | generate`);
319
+ return { code: 1 };
320
+ }
321
+ return runEnvCommand({
322
+ cwd,
323
+ key: argument[1],
7
324
  logger,
8
- out: argument[0] ?? options.out,
9
325
  prod: options.prod === true,
10
- tables: options.tables,
11
- token: options.token,
12
- url: resolveProductionWorkerUrl({ cwd, prod: options.prod === true, url: options.url })
13
- })
14
- );
326
+ set: options.set === true,
327
+ subcommand: sub,
328
+ temporary: options.temporary === true,
329
+ value: argument[2],
330
+ yes: options.yes === true
331
+ });
332
+ });
15
333
 
16
- export { execute };
334
+ export { execute, runEnvCommand };