run402 1.69.3 → 1.69.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/lib/ai.mjs +16 -15
  2. package/lib/allowance.mjs +23 -7
  3. package/lib/argparse.mjs +7 -0
  4. package/lib/auth.mjs +62 -1
  5. package/lib/billing.mjs +24 -7
  6. package/lib/contracts.mjs +11 -0
  7. package/lib/deploy-v2.mjs +99 -15
  8. package/lib/email.mjs +68 -61
  9. package/lib/webhooks.mjs +46 -33
  10. package/package.json +1 -1
  11. package/sdk/dist/errors.d.ts +4 -1
  12. package/sdk/dist/errors.d.ts.map +1 -1
  13. package/sdk/dist/errors.js +16 -2
  14. package/sdk/dist/errors.js.map +1 -1
  15. package/sdk/dist/namespaces/billing.d.ts.map +1 -1
  16. package/sdk/dist/namespaces/billing.js +25 -18
  17. package/sdk/dist/namespaces/billing.js.map +1 -1
  18. package/sdk/dist/namespaces/blobs.d.ts.map +1 -1
  19. package/sdk/dist/namespaces/blobs.js +6 -1
  20. package/sdk/dist/namespaces/blobs.js.map +1 -1
  21. package/sdk/dist/namespaces/contracts.d.ts.map +1 -1
  22. package/sdk/dist/namespaces/contracts.js +5 -1
  23. package/sdk/dist/namespaces/contracts.js.map +1 -1
  24. package/sdk/dist/namespaces/deploy.d.ts +4 -7
  25. package/sdk/dist/namespaces/deploy.d.ts.map +1 -1
  26. package/sdk/dist/namespaces/deploy.js +41 -9
  27. package/sdk/dist/namespaces/deploy.js.map +1 -1
  28. package/sdk/dist/namespaces/deploy.types.d.ts +5 -0
  29. package/sdk/dist/namespaces/deploy.types.d.ts.map +1 -1
  30. package/sdk/dist/namespaces/email.d.ts.map +1 -1
  31. package/sdk/dist/namespaces/email.js +4 -0
  32. package/sdk/dist/namespaces/email.js.map +1 -1
  33. package/sdk/dist/namespaces/functions.d.ts.map +1 -1
  34. package/sdk/dist/namespaces/functions.js +20 -0
  35. package/sdk/dist/namespaces/functions.js.map +1 -1
  36. package/sdk/dist/retry.d.ts +2 -1
  37. package/sdk/dist/retry.d.ts.map +1 -1
  38. package/sdk/dist/retry.js +2 -1
  39. package/sdk/dist/retry.js.map +1 -1
  40. package/sdk/dist/validation.d.ts +3 -0
  41. package/sdk/dist/validation.d.ts.map +1 -0
  42. package/sdk/dist/validation.js +12 -0
  43. package/sdk/dist/validation.js.map +1 -0
package/lib/ai.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { resolveProjectId } from "./config.mjs";
2
2
  import { getSdk } from "./sdk.mjs";
3
3
  import { reportSdkError, fail } from "./sdk-errors.mjs";
4
- import { resolvePositionalProject } from "./argparse.mjs";
4
+ import { assertKnownFlags, flagValue, normalizeArgv, resolvePositionalProject } from "./argparse.mjs";
5
5
 
6
6
  const HELP = `run402 ai — AI translation and moderation tools
7
7
 
@@ -103,20 +103,19 @@ Examples:
103
103
  `,
104
104
  };
105
105
 
106
- function parseFlag(args, flag) {
107
- for (let i = 0; i < args.length; i++) {
108
- if (args[i] === flag && args[i + 1]) return args[i + 1];
109
- }
110
- return null;
111
- }
112
-
113
106
  // translate has value-bearing flags (--to, --from, --context, --project) that
114
107
  // must not be mistaken for positional bare args when prefix-matching.
115
108
  const TRANSLATE_VALUE_FLAGS = ["--to", "--from", "--context", "--project"];
116
109
 
117
110
  async function translate(args) {
111
+ args = normalizeArgv(args);
112
+ assertKnownFlags(args, TRANSLATE_VALUE_FLAGS, TRANSLATE_VALUE_FLAGS);
113
+
118
114
  // --project <id> wins over positional, mirroring previous behavior.
119
- const projectOpt = parseFlag(args, "--project");
115
+ const projectOpt = flagValue(args, "--project");
116
+ const to = flagValue(args, "--to");
117
+ const from = flagValue(args, "--from");
118
+ const context = flagValue(args, "--context");
120
119
  let projectId;
121
120
  let rest;
122
121
  if (projectOpt) {
@@ -139,10 +138,6 @@ async function translate(args) {
139
138
  break;
140
139
  }
141
140
 
142
- const to = parseFlag(args, "--to");
143
- const from = parseFlag(args, "--from");
144
- const context = parseFlag(args, "--context");
145
-
146
141
  if (!text) {
147
142
  fail({
148
143
  code: "BAD_USAGE",
@@ -165,7 +160,10 @@ async function translate(args) {
165
160
  const MODERATE_VALUE_FLAGS = ["--project"];
166
161
 
167
162
  async function moderate(args) {
168
- const projectOpt = parseFlag(args, "--project");
163
+ args = normalizeArgv(args);
164
+ assertKnownFlags(args, MODERATE_VALUE_FLAGS, MODERATE_VALUE_FLAGS);
165
+
166
+ const projectOpt = flagValue(args, "--project");
169
167
  let projectId;
170
168
  let rest;
171
169
  if (projectOpt) {
@@ -203,7 +201,10 @@ async function moderate(args) {
203
201
  }
204
202
 
205
203
  async function usage(args) {
206
- const projectOpt = parseFlag(args, "--project");
204
+ args = normalizeArgv(args);
205
+ assertKnownFlags(args, MODERATE_VALUE_FLAGS, MODERATE_VALUE_FLAGS);
206
+
207
+ const projectOpt = flagValue(args, "--project");
207
208
  let projectId;
208
209
  if (projectOpt) {
209
210
  projectId = resolveProjectId(projectOpt);
package/lib/allowance.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { readAllowance, saveAllowance, ALLOWANCE_FILE } from "./config.mjs";
2
2
  import { getSdk } from "./sdk.mjs";
3
3
  import { reportSdkError, fail } from "./sdk-errors.mjs";
4
+ import { assertKnownFlags, flagValue, normalizeArgv, parseIntegerFlag, positionalArgs } from "./argparse.mjs";
4
5
 
5
6
  const HELP = `run402 allowance — Manage your agent allowance
6
7
 
@@ -258,17 +259,25 @@ async function checkout(args) {
258
259
  hint: "Run: run402 allowance create",
259
260
  });
260
261
  }
261
- let amount = null;
262
- for (let i = 0; i < args.length; i++) {
263
- if (args[i] === "--amount" && args[i + 1]) amount = parseInt(args[++i], 10);
262
+ const parsedArgs = normalizeArgv(args);
263
+ assertKnownFlags(parsedArgs, ["--amount", "--help", "-h"], ["--amount"]);
264
+ const bare = positionalArgs(parsedArgs, ["--amount"]);
265
+ if (bare.length > 0) {
266
+ fail({
267
+ code: "BAD_USAGE",
268
+ message: `Unexpected argument for allowance checkout: ${bare[0]}`,
269
+ details: { argument: bare[0] },
270
+ });
264
271
  }
265
- if (!amount) {
272
+ const amountRaw = flagValue(parsedArgs, "--amount");
273
+ if (amountRaw === null) {
266
274
  fail({
267
275
  code: "BAD_USAGE",
268
276
  message: "Missing --amount <usd_micros>",
269
277
  hint: "e.g. --amount 5000000 for $5",
270
278
  });
271
279
  }
280
+ const amount = parseIntegerFlag("--amount", amountRaw, { min: 1 });
272
281
  try {
273
282
  const data = await getSdk().billing.createCheckout(w.address, amount);
274
283
  console.log(JSON.stringify(data, null, 2));
@@ -286,10 +295,17 @@ async function history(args) {
286
295
  hint: "Run: run402 allowance create",
287
296
  });
288
297
  }
289
- let limit = 20;
290
- for (let i = 0; i < args.length; i++) {
291
- if (args[i] === "--limit" && args[i + 1]) limit = parseInt(args[++i], 10);
298
+ const parsedArgs = normalizeArgv(args);
299
+ assertKnownFlags(parsedArgs, ["--limit", "--help", "-h"], ["--limit"]);
300
+ const bare = positionalArgs(parsedArgs, ["--limit"]);
301
+ if (bare.length > 0) {
302
+ fail({
303
+ code: "BAD_USAGE",
304
+ message: `Unexpected argument for allowance history: ${bare[0]}`,
305
+ details: { argument: bare[0] },
306
+ });
292
307
  }
308
+ const limit = parseIntegerFlag("--limit", flagValue(parsedArgs, "--limit"), { min: 1, def: 20 });
293
309
  try {
294
310
  const data = await getSdk().billing.history(w.address, limit);
295
311
  console.log(JSON.stringify(data, null, 2));
package/lib/argparse.mjs CHANGED
@@ -75,6 +75,13 @@ export function parseIntegerFlag(name, value, { min = 1, max = Number.POSITIVE_I
75
75
  });
76
76
  }
77
77
  const n = Number.parseInt(raw, 10);
78
+ if (!Number.isSafeInteger(n)) {
79
+ fail({
80
+ code: "BAD_FLAG",
81
+ message: `${name} must be a safe integer, got: ${raw}`,
82
+ details: { flag: name, value: raw },
83
+ });
84
+ }
78
85
  if (n < min) {
79
86
  fail({
80
87
  code: "BAD_FLAG",
package/lib/auth.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { resolveProjectId } from "./config.mjs";
2
2
  import { getSdk } from "./sdk.mjs";
3
3
  import { reportSdkError, fail } from "./sdk-errors.mjs";
4
+ import { assertKnownFlags, hasHelp, normalizeArgv } from "./argparse.mjs";
4
5
 
5
6
  const HELP = `run402 auth — Manage project user authentication
6
7
 
@@ -230,6 +231,61 @@ Examples:
230
231
  `,
231
232
  };
232
233
 
234
+ const AUTH_FLAGS = {
235
+ "magic-link": {
236
+ known: ["--email", "--redirect", "--intent", "--state", "--project", "--help", "-h"],
237
+ values: ["--email", "--redirect", "--intent", "--state", "--project"],
238
+ },
239
+ "create-user": {
240
+ known: ["--email", "--admin", "--invite", "--redirect", "--state", "--project", "--help", "-h"],
241
+ values: ["--email", "--admin", "--redirect", "--state", "--project"],
242
+ },
243
+ "invite-user": {
244
+ known: ["--email", "--redirect", "--admin", "--state", "--project", "--help", "-h"],
245
+ values: ["--email", "--redirect", "--admin", "--state", "--project"],
246
+ },
247
+ verify: {
248
+ known: ["--token", "--project", "--help", "-h"],
249
+ values: ["--token", "--project"],
250
+ },
251
+ "set-password": {
252
+ known: ["--token", "--new", "--current", "--project", "--help", "-h"],
253
+ values: ["--token", "--new", "--current", "--project"],
254
+ },
255
+ settings: {
256
+ known: ["--allow-password-set", "--preferred", "--public-signup", "--require-admin-passkey", "--project", "--help", "-h"],
257
+ values: ["--allow-password-set", "--preferred", "--public-signup", "--require-admin-passkey", "--project"],
258
+ },
259
+ "passkey-register-options": {
260
+ known: ["--token", "--app-origin", "--project", "--help", "-h"],
261
+ values: ["--token", "--app-origin", "--project"],
262
+ },
263
+ "passkey-register-verify": {
264
+ known: ["--token", "--challenge", "--response", "--label", "--project", "--help", "-h"],
265
+ values: ["--token", "--challenge", "--response", "--label", "--project"],
266
+ },
267
+ "passkey-login-options": {
268
+ known: ["--app-origin", "--email", "--project", "--help", "-h"],
269
+ values: ["--app-origin", "--email", "--project"],
270
+ },
271
+ "passkey-login-verify": {
272
+ known: ["--challenge", "--response", "--project", "--help", "-h"],
273
+ values: ["--challenge", "--response", "--project"],
274
+ },
275
+ passkeys: {
276
+ known: ["--token", "--project", "--help", "-h"],
277
+ values: ["--token", "--project"],
278
+ },
279
+ "delete-passkey": {
280
+ known: ["--token", "--id", "--project", "--help", "-h"],
281
+ values: ["--token", "--id", "--project"],
282
+ },
283
+ providers: {
284
+ known: ["--project", "--help", "-h"],
285
+ values: ["--project"],
286
+ },
287
+ };
288
+
233
289
  function parseFlag(args, flag) {
234
290
  for (let i = 0; i < args.length; i++) {
235
291
  if (args[i] === flag && args[i + 1]) return args[i + 1];
@@ -557,10 +613,15 @@ async function providers(args) {
557
613
 
558
614
  export async function run(sub, args) {
559
615
  if (!sub || sub === "--help" || sub === "-h") { console.log(HELP); process.exit(0); }
560
- if (Array.isArray(args) && (args.includes("--help") || args.includes("-h"))) {
616
+ args = normalizeArgv(args);
617
+ if (Array.isArray(args) && hasHelp(args)) {
561
618
  console.log(SUB_HELP[sub] || HELP);
562
619
  process.exit(0);
563
620
  }
621
+ const flagSpec = AUTH_FLAGS[sub];
622
+ if (flagSpec) {
623
+ assertKnownFlags(args, flagSpec.known, flagSpec.values);
624
+ }
564
625
  switch (sub) {
565
626
  case "magic-link": await magicLink(args); break;
566
627
  case "verify": await verify(args); break;
package/lib/billing.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import { getSdk } from "./sdk.mjs";
2
2
  import { reportSdkError, fail } from "./sdk-errors.mjs";
3
+ import { parseIntegerFlag } from "./argparse.mjs";
3
4
 
4
5
  const HELP = `run402 billing — Email billing accounts, Stripe tier checkout, email packs
5
6
 
@@ -128,6 +129,23 @@ function parseFlag(args, flag) {
128
129
  return null;
129
130
  }
130
131
 
132
+ function requireSingleBillingIdentifier(email, wallet) {
133
+ if (email && wallet) {
134
+ fail({
135
+ code: "BAD_USAGE",
136
+ message: "Provide either --email or --wallet, not both.",
137
+ hint: "[--email <e> | --wallet <w>]",
138
+ });
139
+ }
140
+ if (!email && !wallet) {
141
+ fail({
142
+ code: "BAD_USAGE",
143
+ message: "Must provide --email or --wallet",
144
+ hint: "[--email <e> | --wallet <w>]",
145
+ });
146
+ }
147
+ }
148
+
131
149
  async function createEmail(args) {
132
150
  const email = args[0];
133
151
  if (!email) {
@@ -174,9 +192,7 @@ async function tierCheckout(args) {
174
192
  }
175
193
  const email = parseFlag(args, "--email");
176
194
  const wallet = parseFlag(args, "--wallet");
177
- if (!email && !wallet) {
178
- fail({ code: "BAD_USAGE", message: "Must provide --email or --wallet" });
179
- }
195
+ requireSingleBillingIdentifier(email, wallet);
180
196
  try {
181
197
  const data = await getSdk().billing.tierCheckout(tier, { email: email ?? undefined, wallet: wallet ?? undefined });
182
198
  console.log(JSON.stringify(data, null, 2));
@@ -188,9 +204,7 @@ async function tierCheckout(args) {
188
204
  async function buyPack(args) {
189
205
  const email = parseFlag(args, "--email");
190
206
  const wallet = parseFlag(args, "--wallet");
191
- if (!email && !wallet) {
192
- fail({ code: "BAD_USAGE", message: "Must provide --email or --wallet" });
193
- }
207
+ requireSingleBillingIdentifier(email, wallet);
194
208
  try {
195
209
  const data = await getSdk().billing.buyEmailPack({ email: email ?? undefined, wallet: wallet ?? undefined });
196
210
  console.log(JSON.stringify(data, null, 2));
@@ -210,11 +224,14 @@ async function autoRecharge(args) {
210
224
  });
211
225
  }
212
226
  const thresholdStr = parseFlag(args, "--threshold");
227
+ const threshold = args.includes("--threshold")
228
+ ? parseIntegerFlag("--threshold", thresholdStr, { min: 0 })
229
+ : undefined;
213
230
  try {
214
231
  await getSdk().billing.setAutoRecharge({
215
232
  billingAccountId: accountId,
216
233
  enabled: state === "on",
217
- threshold: thresholdStr ? Number(thresholdStr) : undefined,
234
+ threshold,
218
235
  });
219
236
  console.log(JSON.stringify({ status: "ok", billing_account_id: accountId, enabled: state === "on" }));
220
237
  } catch (err) {
package/lib/contracts.mjs CHANGED
@@ -135,6 +135,15 @@ function parseFlag(args, flag) {
135
135
  function hasFlag(args, flag) {
136
136
  return args.includes(flag);
137
137
  }
138
+ function validateWeiFlag(flag, value) {
139
+ if (!/^\d+$/.test(String(value))) {
140
+ fail({
141
+ code: "BAD_FLAG",
142
+ message: `${flag} must be a decimal non-negative integer string in wei`,
143
+ details: { flag, value: String(value) },
144
+ });
145
+ }
146
+ }
138
147
 
139
148
  async function provisionWallet(projectId, args) {
140
149
  const chain = parseFlag(args, "--chain");
@@ -210,6 +219,7 @@ async function setAlert(projectId, walletId, args) {
210
219
  if (!threshold) {
211
220
  fail({ code: "BAD_USAGE", message: "Missing --threshold-wei <n>" });
212
221
  }
222
+ validateWeiFlag("--threshold-wei", threshold);
213
223
  try {
214
224
  await getSdk().contracts.setLowBalanceAlert(projectId, walletId, threshold);
215
225
  console.log(JSON.stringify({ status: "ok", wallet_id: walletId, threshold_wei: threshold }));
@@ -233,6 +243,7 @@ async function call(projectId, walletId, args) {
233
243
  hint: "Cost: chain gas + $0.000005 KMS sign fee.",
234
244
  });
235
245
  }
246
+ if (value !== null) validateWeiFlag("--value-wei", value);
236
247
  const abiFragment = parseFlagJson("--abi", abi);
237
248
  const callArgs = parseFlagJson("--args", argsJson);
238
249
  try {
package/lib/deploy-v2.mjs CHANGED
@@ -21,7 +21,7 @@
21
21
  * UTF-8 is the default; binary files pass `"encoding": "base64"`.
22
22
  */
23
23
 
24
- import { readFileSync } from "node:fs";
24
+ import { fstatSync, readFileSync } from "node:fs";
25
25
  import { resolve, dirname, isAbsolute } from "node:path";
26
26
  import {
27
27
  buildDeployResolveSummary,
@@ -266,6 +266,15 @@ async function readStdin() {
266
266
  return Buffer.concat(chunks).toString("utf-8");
267
267
  }
268
268
 
269
+ function hasStdinSource() {
270
+ try {
271
+ const stats = fstatSync(0);
272
+ return stats.isFIFO() || stats.isFile();
273
+ } catch {
274
+ return false;
275
+ }
276
+ }
277
+
269
278
  function makeStderrEventWriter(quiet) {
270
279
  if (quiet) return undefined;
271
280
  return (event) => {
@@ -273,22 +282,97 @@ function makeStderrEventWriter(quiet) {
273
282
  };
274
283
  }
275
284
 
276
- async function applyCmd(args) {
285
+ function parseApplyArgs(args) {
277
286
  const opts = { manifest: null, spec: null, project: null, quiet: false, allowWarnings: false };
287
+ const allowedFlags = ["--manifest", "--spec", "--project", "--quiet", "--allow-warnings", "--help", "-h"];
288
+
278
289
  for (let i = 0; i < args.length; i++) {
279
- if (args[i] === "--help" || args[i] === "-h") { console.log(APPLY_HELP); process.exit(0); }
280
- if (args[i] === "--manifest" && args[i + 1]) { opts.manifest = args[++i]; continue; }
281
- if (args[i] === "--spec" && args[i + 1]) { opts.spec = args[++i]; continue; }
282
- if (args[i] === "--project" && args[i + 1]) { opts.project = args[++i]; continue; }
283
- if (args[i] === "--quiet") { opts.quiet = true; continue; }
284
- if (args[i] === "--allow-warnings") { opts.allowWarnings = true; continue; }
290
+ const arg = args[i];
291
+ if (arg === "--help" || arg === "-h") {
292
+ console.log(APPLY_HELP);
293
+ process.exit(0);
294
+ }
295
+ if (arg === "--manifest" || arg === "--spec" || arg === "--project") {
296
+ const value = args[i + 1];
297
+ if (value === undefined || (typeof value === "string" && value.startsWith("--"))) {
298
+ fail({
299
+ code: "BAD_USAGE",
300
+ message: `${arg} requires a value`,
301
+ details: { flag: arg },
302
+ });
303
+ }
304
+ if (arg === "--manifest") {
305
+ if (opts.manifest !== null) {
306
+ fail({
307
+ code: "BAD_USAGE",
308
+ message: "--manifest may only be provided once",
309
+ details: { flag: "--manifest" },
310
+ });
311
+ }
312
+ opts.manifest = value;
313
+ } else if (arg === "--spec") {
314
+ if (opts.spec !== null) {
315
+ fail({
316
+ code: "BAD_USAGE",
317
+ message: "--spec may only be provided once",
318
+ details: { flag: "--spec" },
319
+ });
320
+ }
321
+ opts.spec = value;
322
+ } else {
323
+ opts.project = value;
324
+ }
325
+ i += 1;
326
+ continue;
327
+ }
328
+ if (arg === "--quiet") { opts.quiet = true; continue; }
329
+ if (arg === "--allow-warnings") { opts.allowWarnings = true; continue; }
330
+ if (typeof arg === "string" && arg.startsWith("-")) {
331
+ fail({
332
+ code: "BAD_USAGE",
333
+ message: `Unknown flag for deploy apply: ${arg}`,
334
+ details: { flag: arg, allowed_flags: allowedFlags },
335
+ });
336
+ }
337
+ fail({
338
+ code: "BAD_USAGE",
339
+ message: `Unexpected argument for deploy apply: ${arg}`,
340
+ details: { argument: arg },
341
+ });
342
+ }
343
+
344
+ return opts;
345
+ }
346
+
347
+ function applySourceField(opts) {
348
+ if (opts.manifest !== null) return "manifest";
349
+ if (opts.spec !== null) return "spec";
350
+ return "stdin";
351
+ }
352
+
353
+ function validateApplySources(opts) {
354
+ const sources = [];
355
+ if (opts.manifest !== null) sources.push("--manifest");
356
+ if (opts.spec !== null) sources.push("--spec");
357
+ if (hasStdinSource()) sources.push("stdin");
358
+ if (sources.length > 1) {
359
+ fail({
360
+ code: "BAD_USAGE",
361
+ message: "Only one deploy manifest source may be provided: --spec, --manifest, or stdin.",
362
+ details: { sources },
363
+ });
285
364
  }
365
+ }
366
+
367
+ async function applyCmd(args) {
368
+ const opts = parseApplyArgs(args);
369
+ validateApplySources(opts);
286
370
 
287
371
  let raw;
288
372
  let manifestPath = null;
289
- if (opts.spec) {
373
+ if (opts.spec !== null) {
290
374
  raw = opts.spec;
291
- } else if (opts.manifest) {
375
+ } else if (opts.manifest !== null) {
292
376
  try {
293
377
  manifestPath = isAbsolute(opts.manifest) ? opts.manifest : resolve(process.cwd(), opts.manifest);
294
378
  raw = readFileSync(manifestPath, "utf-8");
@@ -310,11 +394,11 @@ async function applyCmd(args) {
310
394
  fail({
311
395
  code: "BAD_USAGE",
312
396
  message: `Manifest is not valid JSON: ${err.message}`,
313
- details: { source: opts.manifest ? "manifest" : opts.spec ? "spec" : "stdin", parse_error: err.message },
397
+ details: { source: applySourceField(opts), parse_error: err.message },
314
398
  });
315
399
  }
316
400
  rejectLegacySecretManifest(spec, {
317
- source: opts.manifest ? "manifest" : opts.spec ? "spec" : "stdin",
401
+ source: applySourceField(opts),
318
402
  ...(manifestPath ? { path: manifestPath } : {}),
319
403
  });
320
404
 
@@ -355,7 +439,7 @@ async function applyCmd(args) {
355
439
  message: `Manifest contains no deployable sections. Expected at least one of: ${meaningful.join(", ")}`,
356
440
  hint: "Did you mean to write a 'site.replace' or 'database.migrations' block? See https://run402.com/schemas/manifest.v1.json",
357
441
  details: {
358
- field: opts.manifest ? "manifest" : opts.spec ? "spec" : "stdin",
442
+ field: applySourceField(opts),
359
443
  ...(manifestPath ? { path: manifestPath } : {}),
360
444
  meaningful_keys: meaningful,
361
445
  },
@@ -725,7 +809,7 @@ async function listCmd(args) {
725
809
  for (let i = 0; i < args.length; i++) {
726
810
  if (args[i] === "--help" || args[i] === "-h") { console.log(LIST_HELP); process.exit(0); }
727
811
  if (args[i] === "--project" && args[i + 1]) { opts.project = args[++i]; continue; }
728
- if (args[i] === "--limit" && args[i + 1]) { opts.limit = Number(args[++i]); continue; }
812
+ if (args[i] === "--limit") { opts.limit = parsePositiveInt(args[++i], "--limit"); continue; }
729
813
  }
730
814
 
731
815
  const project = resolveProjectId(opts.project);
@@ -733,7 +817,7 @@ async function listCmd(args) {
733
817
 
734
818
  try {
735
819
  const sdkOpts = { project };
736
- if (opts.limit !== null && Number.isFinite(opts.limit)) sdkOpts.limit = opts.limit;
820
+ if (opts.limit !== null) sdkOpts.limit = opts.limit;
737
821
  const result = await getSdk().deploy.list(sdkOpts);
738
822
  console.log(JSON.stringify({ status: "ok", ...result }, null, 2));
739
823
  } catch (err) {