@lunora/cli 1.0.0-alpha.10 → 1.0.0-alpha.100

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 (58) 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 +345 -320
  5. package/dist/index.d.ts +345 -320
  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 +27 -6
  14. package/dist/packem_chunks/handler16.mjs +3 -2
  15. package/dist/packem_chunks/handler17.mjs +1 -1
  16. package/dist/packem_chunks/handler18.mjs +3 -3
  17. package/dist/packem_chunks/handler19.mjs +44 -5
  18. package/dist/packem_chunks/handler2.mjs +6 -4
  19. package/dist/packem_chunks/handler20.mjs +2 -2
  20. package/dist/packem_chunks/handler21.mjs +158 -267
  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 +1193 -98
  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-D3h9Iwvl.mjs → COMMANDS-BOJIDoVY.mjs} +112 -25
  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-BC30oSBW.mjs → command-lYnl4QyF.mjs} +6 -1
  41. package/dist/packem_shared/{commands-DPKWlqqX.mjs → commands-D5Yxt9VY.mjs} +199 -20
  42. package/dist/packem_shared/{createLogger-B40gPzQo.mjs → createLogger-CIWSHrTL.mjs} +33 -6
  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-wUvAN6AL.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-2RJBhUC4.mjs → storage-BXU4ax4O.mjs} +1 -1
  52. package/dist/packem_shared/{tui-prompts-DEiPCKV-.mjs → tui-prompts-BjEN8XgP.mjs} +9 -12
  53. package/dist/packem_shared/wrangler-secrets-Coni-mER.mjs +49 -0
  54. package/package.json +19 -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-CTRA_JlL.mjs +0 -4
@@ -1,311 +1,202 @@
1
- import { writeFileSync, existsSync, readFileSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { DEV_VARS_FILE, DEV_VARS_KEY_PATTERN, DEV_VARS_EXAMPLE_FILE, parseDevVariableEntries, isPlaceholderValue } from '@lunora/config';
4
- import { d as defineHandler } from '../packem_shared/command-BC30oSBW.mjs';
5
- import { defaultSpawner } from '../packem_shared/createRecordingSpawner-DxI3mebw.mjs';
6
- import { execFile } from 'node:child_process';
1
+ import { readLinkedProject } from '@lunora/config';
2
+ import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
3
+ import { e as execArgsFor, d as detectPackageManager } from '../packem_shared/detect-package-manager-v4hHpQd0.mjs';
4
+ import { defaultSpawner } from '../packem_shared/createRecordingSpawner-WuSn20kb.mjs';
5
+ import { createR2Sql } from '@lunora/bindings/r2sql';
6
+ import { createPipelineLogReader } from '@lunora/runtime';
7
7
 
8
- const execCode = (error) => {
9
- if (!error) {
10
- return 0;
11
- }
12
- return typeof error.code === "number" ? error.code : 1;
13
- };
14
- const defaultRunner = (command, args, cwd) => new Promise((resolve) => {
15
- execFile(command, [...args], { cwd }, (error, stdout, stderr) => {
16
- resolve({ code: execCode(error), stderr, stdout });
17
- });
18
- });
19
- const parseSecretNames = (stdout) => {
20
- let parsed;
21
- try {
22
- parsed = JSON.parse(stdout);
23
- } catch {
8
+ const LOG_LEVELS = /* @__PURE__ */ new Set(["debug", "error", "fatal", "info", "log", "trace", "warn"]);
9
+ const EPOCH_MILLIS_RE = /^\d+$/;
10
+ const parseTimestamp = (value, flag) => {
11
+ if (value === void 0) {
24
12
  return void 0;
25
13
  }
26
- if (!Array.isArray(parsed)) {
27
- return void 0;
14
+ const trimmed = value.trim();
15
+ if (EPOCH_MILLIS_RE.test(trimmed)) {
16
+ return Number(trimmed);
28
17
  }
29
- const names = parsed.map((entry) => entry !== null && typeof entry === "object" ? entry.name : void 0).filter((name) => typeof name === "string" && name.length > 0);
30
- return [...names].toSorted((a, b) => a.localeCompare(b));
31
- };
32
- const listRemoteSecrets = async (inputs) => {
33
- const args = ["exec", "wrangler", "secret", "list", "--format", "json"];
34
- if (inputs.env !== void 0) {
35
- args.push("--env", inputs.env);
36
- }
37
- if (inputs.temporary) {
38
- args.push("--temporary");
39
- }
40
- const runner = inputs.runner ?? defaultRunner;
41
- const result = await runner("pnpm", args, inputs.cwd);
42
- if (result.code !== 0) {
43
- return { error: result.stderr.trim() || `wrangler secret list exited ${String(result.code)}`, names: [], ok: false };
44
- }
45
- const names = parseSecretNames(result.stdout);
46
- if (names === void 0) {
47
- return { error: "could not parse `wrangler secret list --format json` output", names: [], ok: false };
48
- }
49
- return { names, ok: true };
50
- };
51
-
52
- const NEWLINE_PRESENT = /[\r\n]/u;
53
- const UNREPRESENTABLE_PRESENT = /["\\]/u;
54
- const parseDevVariables = (content) => {
55
- const map = /* @__PURE__ */ new Map();
56
- for (const entry of parseDevVariableEntries(content)) {
57
- map.set(entry.key, entry);
18
+ const parsed = Date.parse(trimmed);
19
+ if (Number.isNaN(parsed)) {
20
+ throw new TypeError(`logs: invalid ${flag} "${value}" — pass epoch-millis or an ISO 8601 date`);
58
21
  }
59
- return map;
22
+ return parsed;
60
23
  };
61
- const serializeDevVariables = (map) => {
62
- const lines = [];
63
- for (const entry of map.values()) {
64
- lines.push(`${entry.key}="${entry.value}"`);
24
+ const parseLevel = (value, flag) => {
25
+ if (value === void 0) {
26
+ return void 0;
65
27
  }
66
- return `${lines.join("\n")}
67
- `;
68
- };
69
- const redact = (value) => {
70
- if (value.length <= 4) {
71
- return "****";
28
+ if (!LOG_LEVELS.has(value)) {
29
+ throw new TypeError(`logs: invalid ${flag} "${value}" — expected one of trace, debug, log, info, warn, error, fatal`);
72
30
  }
73
- return `${value.slice(0, 4)}${"*".repeat(Math.min(8, value.length - 4))}`;
31
+ return value;
74
32
  };
75
- const loadDevVariables = (devVariablesPath) => {
76
- if (!existsSync(devVariablesPath)) {
77
- return /* @__PURE__ */ new Map();
33
+ const buildQuery = (options) => {
34
+ const query = {};
35
+ const sinceTs = parseTimestamp(options.since, "--since");
36
+ if (sinceTs !== void 0) {
37
+ query.sinceTs = sinceTs;
78
38
  }
79
- return parseDevVariables(readFileSync(devVariablesPath, "utf8"));
80
- };
81
- const runEnvList = (context) => {
82
- const map = loadDevVariables(context.devVariablesPath);
83
- if (map.size === 0) {
84
- context.logger.info(`${DEV_VARS_FILE}: (empty)`);
85
- return { code: 0, descriptors: [] };
39
+ const untilTs = parseTimestamp(options.until, "--until");
40
+ if (untilTs !== void 0) {
41
+ query.untilTs = untilTs;
86
42
  }
87
- for (const entry of map.values()) {
88
- context.logger.info(`${entry.key}=${redact(entry.value)}`);
43
+ const level = parseLevel(options.level, "--level");
44
+ if (level !== void 0) {
45
+ query.level = level;
89
46
  }
90
- return { code: 0, descriptors: [] };
91
- };
92
- const runEnvGet = (context) => {
93
- const { devVariablesPath, logger, options } = context;
94
- if (!options.key) {
95
- logger.error("env get requires a key. Usage: lunora env get <KEY>");
96
- return { code: 1, descriptors: [] };
47
+ const minLevel = parseLevel(options.minLevel, "--min-level");
48
+ if (minLevel !== void 0) {
49
+ query.minLevel = minLevel;
97
50
  }
98
- const entry = loadDevVariables(devVariablesPath).get(options.key);
99
- if (!entry) {
100
- logger.error(`env: ${options.key} is not set in ${DEV_VARS_FILE}`);
101
- return { code: 1, descriptors: [] };
51
+ if (options.functionPrefix !== void 0) {
52
+ query.functionPathPrefix = options.functionPrefix;
102
53
  }
103
- process.stdout.write(`${entry.value}
104
- `);
105
- return { code: 0, descriptors: [] };
106
- };
107
- const runEnvSet = (context) => {
108
- const { devVariablesPath, logger, options } = context;
109
- if (!options.key) {
110
- logger.error("env set requires a key. Usage: lunora env set <KEY> <VALUE>");
111
- return { code: 1, descriptors: [] };
54
+ if (options.traceId !== void 0) {
55
+ query.traceId = options.traceId;
112
56
  }
113
- if (!DEV_VARS_KEY_PATTERN.test(options.key)) {
114
- logger.error(`env: invalid key "${options.key}" — must match [A-Za-z_][A-Za-z0-9_]*`);
115
- return { code: 1, descriptors: [] };
57
+ if (options.shardKey !== void 0) {
58
+ query.shardKey = options.shardKey;
116
59
  }
117
- if (options.value === void 0) {
118
- logger.error("env set requires a value. Usage: lunora env set <KEY> <VALUE>");
119
- return { code: 1, descriptors: [] };
60
+ if (options.userId !== void 0) {
61
+ query.userId = options.userId;
120
62
  }
121
- if (NEWLINE_PRESENT.test(options.value)) {
122
- logger.error(`env: value for "${options.key}" contains a newline, which .dev.vars cannot represent`);
123
- return { code: 1, descriptors: [] };
63
+ if (options.limit !== void 0) {
64
+ const limit = Number(options.limit);
65
+ if (!Number.isFinite(limit)) {
66
+ throw new TypeError(`logs: invalid --limit "${options.limit}" — expected a number`);
67
+ }
68
+ query.limit = limit;
124
69
  }
125
- if (UNREPRESENTABLE_PRESENT.test(options.value)) {
126
- logger.error(`env: value for "${options.key}" contains a double-quote or backslash, which .dev.vars cannot round-trip`);
127
- return { code: 1, descriptors: [] };
70
+ if (options.cursor !== void 0) {
71
+ const cursorTs = Number(options.cursor);
72
+ if (!Number.isFinite(cursorTs)) {
73
+ throw new TypeError(`logs: invalid --cursor "${options.cursor}" — expected the epoch-millis ts from a prior page's nextCursor`);
74
+ }
75
+ query.cursor = { ts: cursorTs };
128
76
  }
129
- const map = loadDevVariables(devVariablesPath);
130
- map.set(options.key, { key: options.key, value: options.value });
131
- writeFileSync(devVariablesPath, serializeDevVariables(map), "utf8");
132
- logger.success(`env: set ${options.key} (${redact(options.value)}) in ${DEV_VARS_FILE}`);
133
- return { code: 0, descriptors: [] };
77
+ return query;
134
78
  };
135
- const runEnvUnset = (context) => {
136
- const { devVariablesPath, logger, options } = context;
137
- if (!options.key) {
138
- logger.error("env unset requires a key. Usage: lunora env unset <KEY>");
139
- return { code: 1, descriptors: [] };
140
- }
141
- const map = loadDevVariables(devVariablesPath);
142
- if (!map.delete(options.key)) {
143
- logger.warn(`env: ${options.key} was not set in ${DEV_VARS_FILE}`);
144
- return { code: 0, descriptors: [] };
145
- }
146
- writeFileSync(devVariablesPath, serializeDevVariables(map), "utf8");
147
- logger.success(`env: unset ${options.key} in ${DEV_VARS_FILE}`);
148
- return { code: 0, descriptors: [] };
79
+ const formatRow = (row) => {
80
+ const time = new Date(row.ts).toISOString();
81
+ const level = row.level.toUpperCase().padEnd(5);
82
+ return `${time} ${level} ${row.functionPath} ${row.message}`;
149
83
  };
150
- const runEnvPush = async (context) => {
151
- const { devVariablesPath, logger, options } = context;
152
- if (!options.yes) {
153
- logger.error("env push uploads secrets to Cloudflare. Re-run with --yes to confirm.");
154
- return { code: 1, descriptors: [] };
84
+ const runDurableLogsCommand = async (options) => {
85
+ const environment = options.environment ?? process.env;
86
+ const accountId = environment.R2_SQL_ACCOUNT_ID;
87
+ const apiToken = environment.R2_SQL_TOKEN;
88
+ const bucket = environment.R2_SQL_BUCKET;
89
+ const missing = [];
90
+ if (accountId === void 0 || accountId.length === 0) {
91
+ missing.push("R2_SQL_ACCOUNT_ID");
155
92
  }
156
- const map = loadDevVariables(devVariablesPath);
157
- if (map.size === 0) {
158
- logger.warn(`${DEV_VARS_FILE}: nothing to push (empty)`);
159
- return { code: 0, descriptors: [] };
93
+ if (apiToken === void 0 || apiToken.length === 0) {
94
+ missing.push("R2_SQL_TOKEN");
160
95
  }
161
- const spawner = options.spawner ?? defaultSpawner;
162
- const descriptors = [];
163
- for (const entry of map.values()) {
164
- const args = ["exec", "wrangler", "secret", "put", entry.key];
165
- if (options.prod) {
166
- args.push("--env", "production");
167
- }
168
- if (options.temporary) {
169
- args.push("--temporary");
170
- }
171
- const descriptor = {
172
- args,
173
- command: "pnpm",
174
- cwd: options.cwd ?? process.cwd(),
175
- // `wrangler secret put <name>` reads the value from stdin. We
176
- // pipe it through the spawner's `input` channel so the secret
177
- // never lands on the command line, in env, or in shell history.
178
- input: entry.value
179
- };
180
- descriptors.push(descriptor);
181
- logger.info(`pushing ${entry.key} -> wrangler secret${options.prod ? " (production)" : ""}`);
182
- const result = await spawner(descriptor);
183
- if (result.code !== 0) {
184
- logger.error(`env push: failed at ${entry.key} (exit ${String(result.code)})`);
185
- return { code: result.code, descriptors };
186
- }
96
+ if (bucket === void 0 || bucket.length === 0) {
97
+ missing.push("R2_SQL_BUCKET");
187
98
  }
188
- logger.success(`env: pushed ${String(map.size)} secret(s)`);
189
- return { code: 0, descriptors };
190
- };
191
- const fetchRemoteSecretNames = (context) => {
192
- const lister = context.options.secretLister ?? listRemoteSecrets;
193
- return lister({
194
- cwd: context.cwd,
195
- env: context.options.prod ? "production" : void 0,
196
- temporary: context.options.temporary
197
- });
198
- };
199
- const runEnvDiff = async (context) => {
200
- const { devVariablesPath, logger } = context;
201
- const remote = await fetchRemoteSecretNames(context);
202
- if (!remote.ok) {
203
- logger.error(`env diff: ${remote.error ?? "failed to list remote secrets"}`);
204
- return { code: 1, descriptors: [] };
99
+ if (options.table === void 0 || options.table.length === 0) {
100
+ missing.push("--table");
205
101
  }
206
- const localKeys = new Set(loadDevVariables(devVariablesPath).keys());
207
- const remoteKeys = new Set(remote.names);
208
- const localOnly = [...localKeys].filter((key) => !remoteKeys.has(key)).toSorted((a, b) => a.localeCompare(b));
209
- const remoteOnly = remote.names.filter((name) => !localKeys.has(name));
210
- const both = [...localKeys].filter((key) => remoteKeys.has(key)).toSorted((a, b) => a.localeCompare(b));
211
- for (const key of localOnly) {
212
- logger.info(`local only (run \`lunora env push\`): ${key}`);
102
+ if (missing.length > 0) {
103
+ options.logger.error(
104
+ `logs --durable: R2 SQL not configured (missing ${missing.join(", ")}). The Pipeline must write to an R2 Data Catalog (Iceberg) table, and you must supply R2_SQL_ACCOUNT_ID / R2_SQL_TOKEN / R2_SQL_BUCKET plus --table — see the observability docs.`
105
+ );
106
+ return { code: 1, error: "not configured" };
213
107
  }
214
- for (const key of remoteOnly) {
215
- logger.info(`remote only (in Cloudflare, not ${DEV_VARS_FILE}): ${key}`);
108
+ let query;
109
+ try {
110
+ query = buildQuery(options);
111
+ } catch (error) {
112
+ options.logger.error(error instanceof Error ? error.message : String(error));
113
+ return { code: 1, error: "invalid option" };
114
+ }
115
+ const client = createR2Sql({ accountId, apiToken, bucket, fetch: options.fetch });
116
+ const reader = createPipelineLogReader(client, { namespace: options.namespace, table: options.table });
117
+ const page = await reader.query(query);
118
+ for (const row of page.rows) {
119
+ process.stdout.write(`${options.ndjson === true ? JSON.stringify(row) : formatRow(row)}
120
+ `);
216
121
  }
217
- logger.info(`in both: ${String(both.length)} secret(s)`);
218
- if (localOnly.length === 0 && remoteOnly.length === 0) {
219
- logger.success("env diff: local and remote secret names match");
122
+ if (page.rows.length === 0) {
123
+ options.logger.info("logs --durable: no matching log records");
124
+ } else if (page.nextCursor !== void 0) {
125
+ options.logger.info(`logs --durable: more rows available — pass --cursor ${String(page.nextCursor.ts)} for the next page`);
220
126
  }
221
- return { code: 0, descriptors: [] };
127
+ return { code: 0, rows: page.rows };
222
128
  };
223
- const runEnvDoctor = (context) => {
224
- const { cwd, devVariablesPath, logger } = context;
225
- const examplePath = join(cwd, DEV_VARS_EXAMPLE_FILE);
226
- if (!existsSync(examplePath)) {
227
- logger.info(`env doctor: no ${DEV_VARS_EXAMPLE_FILE} to check against — nothing to validate.`);
228
- return { code: 0, descriptors: [] };
129
+
130
+ const LOG_FORMATS = /* @__PURE__ */ new Set(["json", "pretty"]);
131
+ const runLogsCommand = async (options) => {
132
+ const cwd = options.cwd ?? process.cwd();
133
+ if (options.format !== void 0 && !LOG_FORMATS.has(options.format)) {
134
+ options.logger.error(`logs: unknown --format "${options.format}" expected pretty | json`);
135
+ return { code: 1, descriptor: void 0, error: "invalid format" };
229
136
  }
230
- const exampleKeys = parseDevVariableEntries(readFileSync(examplePath, "utf8")).map((entry) => entry.key);
231
- const current = loadDevVariables(devVariablesPath);
232
- if (!existsSync(devVariablesPath)) {
233
- logger.error(`env doctor: ${DEV_VARS_FILE} is missing. Run \`lunora dev\` to scaffold it, or \`lunora env set <KEY> <VALUE>\`.`);
234
- logger.info(`expected (from ${DEV_VARS_EXAMPLE_FILE}): ${exampleKeys.join(", ")}`);
235
- return { code: 1, descriptors: [] };
137
+ const args = ["tail"];
138
+ if (options.worker !== void 0) {
139
+ args.push(options.worker);
236
140
  }
237
- const missing = exampleKeys.filter((key) => !current.has(key));
238
- const placeholders = [...current.values()].filter((entry) => isPlaceholderValue(entry.value)).map((entry) => entry.key);
239
- const exampleKeySet = new Set(exampleKeys);
240
- const extra = [...current.keys()].filter((key) => !exampleKeySet.has(key));
241
- for (const key of missing) {
242
- logger.error(`missing: ${key} is in ${DEV_VARS_EXAMPLE_FILE} but not ${DEV_VARS_FILE}`);
141
+ const env = options.env ?? readLinkedProject(cwd)?.env;
142
+ if (env !== void 0) {
143
+ args.push("--env", env);
243
144
  }
244
- for (const key of placeholders) {
245
- logger.error(`unset: ${key} still has a placeholder value`);
145
+ if (options.format !== void 0) {
146
+ args.push("--format", options.format);
246
147
  }
247
- for (const key of extra) {
248
- logger.info(`extra: ${key} is set locally but not listed in ${DEV_VARS_EXAMPLE_FILE}`);
148
+ if (options.status !== void 0) {
149
+ args.push("--status", options.status);
249
150
  }
250
- if (missing.length === 0 && placeholders.length === 0) {
251
- logger.success(`env doctor: ${DEV_VARS_FILE} looks good (${String(current.size)} var(s)).`);
252
- return { code: 0, descriptors: [] };
151
+ if (options.search !== void 0) {
152
+ args.push("--search", options.search);
253
153
  }
254
- return { code: 1, descriptors: [] };
255
- };
256
- const runEnvCommand = async (options) => {
257
- const cwd = options.cwd ?? process.cwd();
258
- const context = {
259
- cwd,
260
- devVariablesPath: join(cwd, DEV_VARS_FILE),
261
- logger: options.logger,
262
- options
263
- };
264
- switch (options.subcommand) {
265
- case "diff": {
266
- return runEnvDiff(context);
267
- }
268
- case "doctor": {
269
- return runEnvDoctor(context);
270
- }
271
- case "get": {
272
- return runEnvGet(context);
273
- }
274
- case "list": {
275
- return runEnvList(context);
276
- }
277
- case "push": {
278
- return runEnvPush(context);
279
- }
280
- case "set": {
281
- return runEnvSet(context);
282
- }
283
- case "unset": {
284
- return runEnvUnset(context);
285
- }
286
- default: {
287
- options.logger.error(`env: unknown subcommand "${options.subcommand}"`);
288
- return { code: 1, descriptors: [] };
289
- }
154
+ if (options.temporary) {
155
+ args.push("--temporary");
290
156
  }
157
+ const exec = execArgsFor(detectPackageManager(cwd), "wrangler", args);
158
+ const descriptor = {
159
+ args: exec.args,
160
+ command: exec.command,
161
+ cwd
162
+ };
163
+ options.logger.info(`tailing logs via ${descriptor.command} ${descriptor.args.join(" ")}`);
164
+ const spawner = options.spawner ?? defaultSpawner;
165
+ const result = await spawner(descriptor);
166
+ return {
167
+ code: result.code,
168
+ descriptor
169
+ };
291
170
  };
292
- const isEnvSubcommand = (value) => value === "list" || value === "get" || value === "set" || value === "unset" || value === "push" || value === "diff" || value === "doctor";
293
171
  const execute = defineHandler(({ argument, cwd, logger, options }) => {
294
- const sub = argument[0];
295
- if (!isEnvSubcommand(sub)) {
296
- logger.error(`env: unknown subcommand "${sub ?? ""}" — expected list | get | set | unset | push | diff | doctor`);
297
- return { code: 1 };
298
- }
299
- return runEnvCommand({
172
+ if (options.durable === true) {
173
+ return runDurableLogsCommand({
174
+ cursor: options.cursor,
175
+ functionPrefix: options.functionPrefix,
176
+ level: options.level,
177
+ limit: options.limit,
178
+ logger,
179
+ minLevel: options.minLevel,
180
+ namespace: options.namespace,
181
+ ndjson: options.ndjson === true,
182
+ shardKey: options.shardKey,
183
+ since: options.since,
184
+ table: options.table,
185
+ traceId: options.traceId,
186
+ until: options.until,
187
+ userId: options.userId
188
+ });
189
+ }
190
+ return runLogsCommand({
300
191
  cwd,
301
- key: argument[1],
192
+ env: options.env,
193
+ format: options.format,
302
194
  logger,
303
- prod: options.prod === true,
304
- subcommand: sub,
195
+ search: options.search,
196
+ status: options.status,
305
197
  temporary: options.temporary === true,
306
- value: argument[2],
307
- yes: options.yes === true
198
+ worker: argument[0]
308
199
  });
309
200
  });
310
201
 
311
- export { execute, runEnvCommand };
202
+ export { execute, runLogsCommand };
@@ -1,10 +1,11 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { mkdir, readFile, writeFile } from 'node:fs/promises';
3
3
  import { join } from 'node:path';
4
+ import { LunoraError } from '@lunora/errors';
4
5
  import { r as resolveAdminBaseUrl } from '../packem_shared/admin-url-4UzT-CI4.mjs';
5
- import { d as defineHandler } from '../packem_shared/command-BC30oSBW.mjs';
6
+ import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
6
7
  import { a as resolveProductionWorkerUrl } from '../packem_shared/resolve-target-qbsJ_5sF.mjs';
7
- import { runExportCommand, runImportCommand } from '../packem_shared/DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs';
8
+ import { runExportCommand, runImportCommand } from '../packem_shared/DEFAULT_IMPORT_BATCH_SIZE-D0VOTerB.mjs';
8
9
 
9
10
  const DEFAULT_BACKUP_DIR = ".lunora-backups";
10
11
  const MANIFEST_FILE = "manifest.json";
@@ -22,7 +23,9 @@ const readManifest = async (directory) => {
22
23
  parsed = JSON.parse(await readFile(path, "utf8"));
23
24
  } catch (error) {
24
25
  const message = error instanceof Error ? error.message : String(error);
25
- throw new Error(`backup: ${path} exists but is not valid JSON (${message}) — refusing to overwrite it; fix or remove it manually`, { cause: error });
26
+ throw new LunoraError("INTERNAL", `backup: ${path} exists but is not valid JSON (${message}) — refusing to overwrite it; fix or remove it manually`, {
27
+ cause: error
28
+ });
26
29
  }
27
30
  if (!Array.isArray(parsed)) {
28
31
  throw new TypeError(`backup: ${path} exists but is not a JSON array — refusing to overwrite it; fix or remove it manually`);
@@ -86,7 +89,8 @@ const runBackupRestore = async (options, directory) => {
86
89
  logger: options.logger,
87
90
  prod: options.prod,
88
91
  token: options.token,
89
- url: options.url
92
+ url: options.url,
93
+ yes: options.yes
90
94
  });
91
95
  return { code: result.code };
92
96
  };
@@ -1,5 +1,5 @@
1
- import { p as parseApiSpec } from '../packem_shared/api-spec-CtA6ilu4.mjs';
2
- import { d as defineHandler } from '../packem_shared/command-BC30oSBW.mjs';
1
+ import { p as parseApiSpec } from '../packem_shared/api-spec-Bx0iKbxA.mjs';
2
+ import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
3
3
  import { runDeployCommand } from './runDeployCommand.mjs';
4
4
 
5
5
  const DEFAULT_OUT_DIR = ".lunora/build";
@@ -1,6 +1,7 @@
1
- import { d as defineHandler } from '../packem_shared/command-BC30oSBW.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';
2
3
  import { i as isDockerAvailable } from '../packem_shared/docker-hMQ97KSQ.mjs';
3
- import { defaultSpawner } from '../packem_shared/createRecordingSpawner-DxI3mebw.mjs';
4
+ import { defaultSpawner } from '../packem_shared/createRecordingSpawner-WuSn20kb.mjs';
4
5
 
5
6
  const SUBCOMMANDS = /* @__PURE__ */ new Set(["build", "delete", "images", "info", "list", "push"]);
6
7
  const NEEDS_DOCKER = /* @__PURE__ */ new Set(["build", "push"]);
@@ -18,7 +19,7 @@ const runContainersCommand = async (options) => {
18
19
  );
19
20
  return { code: 1 };
20
21
  }
21
- const args = ["exec", "wrangler", "containers", subcommand, ...rest];
22
+ const args = ["containers", subcommand, ...rest];
22
23
  if (options.tag !== void 0) {
23
24
  args.push("--tag", options.tag);
24
25
  }
@@ -28,7 +29,9 @@ const runContainersCommand = async (options) => {
28
29
  if (options.env !== void 0) {
29
30
  args.push("--env", options.env);
30
31
  }
31
- const descriptor = { args, command: "pnpm", cwd: options.cwd ?? process.cwd() };
32
+ const cwd = options.cwd ?? process.cwd();
33
+ const exec = execArgsFor(detectPackageManager(cwd), "wrangler", args);
34
+ const descriptor = { args: exec.args, command: exec.command, cwd };
32
35
  options.logger.info(`running ${descriptor.command} ${descriptor.args.join(" ")}`);
33
36
  const spawner = options.spawner ?? defaultSpawner;
34
37
  const result = await spawner(descriptor);
@@ -1,5 +1,6 @@
1
- import { d as defineHandler } from '../packem_shared/command-BC30oSBW.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-BC30oSBW.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-BC30oSBW.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) => {