run402 1.69.5 → 1.69.7

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 (44) hide show
  1. package/lib/agent.mjs +37 -11
  2. package/lib/apps.mjs +92 -40
  3. package/lib/argparse.mjs +53 -1
  4. package/lib/billing.mjs +65 -24
  5. package/lib/blob.mjs +87 -49
  6. package/lib/cdn.mjs +21 -8
  7. package/lib/contracts.mjs +112 -33
  8. package/lib/deploy-v2.mjs +159 -62
  9. package/lib/domains.mjs +22 -11
  10. package/lib/functions.mjs +42 -6
  11. package/lib/image.mjs +17 -8
  12. package/lib/message.mjs +4 -1
  13. package/lib/secrets.mjs +29 -12
  14. package/lib/sender-domain.mjs +32 -14
  15. package/lib/sites.mjs +39 -16
  16. package/lib/subdomains.mjs +31 -35
  17. package/lib/tier.mjs +26 -4
  18. package/package.json +1 -1
  19. package/sdk/dist/namespaces/ai.d.ts.map +1 -1
  20. package/sdk/dist/namespaces/ai.js +7 -2
  21. package/sdk/dist/namespaces/ai.js.map +1 -1
  22. package/sdk/dist/namespaces/blobs.d.ts +3 -2
  23. package/sdk/dist/namespaces/blobs.d.ts.map +1 -1
  24. package/sdk/dist/namespaces/blobs.js +38 -7
  25. package/sdk/dist/namespaces/blobs.js.map +1 -1
  26. package/sdk/dist/namespaces/blobs.types.d.ts +5 -6
  27. package/sdk/dist/namespaces/blobs.types.d.ts.map +1 -1
  28. package/sdk/dist/namespaces/contracts.d.ts.map +1 -1
  29. package/sdk/dist/namespaces/contracts.js +17 -4
  30. package/sdk/dist/namespaces/contracts.js.map +1 -1
  31. package/sdk/dist/namespaces/deploy.d.ts.map +1 -1
  32. package/sdk/dist/namespaces/deploy.js +13 -1
  33. package/sdk/dist/namespaces/deploy.js.map +1 -1
  34. package/sdk/dist/namespaces/functions.d.ts.map +1 -1
  35. package/sdk/dist/namespaces/functions.js +92 -1
  36. package/sdk/dist/namespaces/functions.js.map +1 -1
  37. package/sdk/dist/node/files.d.ts +11 -5
  38. package/sdk/dist/node/files.d.ts.map +1 -1
  39. package/sdk/dist/node/files.js +31 -7
  40. package/sdk/dist/node/files.js.map +1 -1
  41. package/sdk/dist/validation.d.ts +2 -0
  42. package/sdk/dist/validation.d.ts.map +1 -1
  43. package/sdk/dist/validation.js +10 -0
  44. package/sdk/dist/validation.js.map +1 -1
package/lib/deploy-v2.mjs CHANGED
@@ -32,6 +32,7 @@ import {
32
32
  import { getSdk } from "./sdk.mjs";
33
33
  import { reportSdkError, fail } from "./sdk-errors.mjs";
34
34
  import { API, allowanceAuthHeaders, getActiveProjectId, resolveProjectId } from "./config.mjs";
35
+ import { normalizeArgv } from "./argparse.mjs";
35
36
 
36
37
  const APPLY_HELP = `run402 deploy apply — Unified deploy primitive (v1.34+)
37
38
 
@@ -778,19 +779,18 @@ function rejectLegacySecretManifest(spec, details) {
778
779
  }
779
780
 
780
781
  async function resumeCmd(args) {
781
- const opts = { operationId: null, quiet: false };
782
- for (let i = 0; i < args.length; i++) {
783
- if (args[i] === "--help" || args[i] === "-h") { console.log(RESUME_HELP); process.exit(0); }
784
- if (args[i] === "--quiet") { opts.quiet = true; continue; }
785
- if (!args[i].startsWith("-") && !opts.operationId) opts.operationId = args[i];
786
- }
787
- if (!opts.operationId) {
788
- fail({
789
- code: "BAD_USAGE",
790
- message: "Missing <operation_id>.",
791
- hint: "run402 deploy resume <operation_id>",
792
- });
793
- }
782
+ const parsed = parseDeploySubcommandArgs(args, {
783
+ command: "deploy resume",
784
+ help: RESUME_HELP,
785
+ booleanFlags: ["--quiet"],
786
+ });
787
+ const [operationId] = expectPositionals(parsed.positionals, {
788
+ command: "run402 deploy resume <operation_id>",
789
+ min: 1,
790
+ max: 1,
791
+ missing: "Missing <operation_id>.",
792
+ });
793
+ const opts = { operationId, quiet: Boolean(parsed.flags["--quiet"]) };
794
794
 
795
795
  allowanceAuthHeaders("/deploy/v2/operations");
796
796
 
@@ -805,12 +805,19 @@ async function resumeCmd(args) {
805
805
  }
806
806
 
807
807
  async function listCmd(args) {
808
- const opts = { project: null, limit: null };
809
- for (let i = 0; i < args.length; i++) {
810
- if (args[i] === "--help" || args[i] === "-h") { console.log(LIST_HELP); process.exit(0); }
811
- if (args[i] === "--project" && args[i + 1]) { opts.project = args[++i]; continue; }
812
- if (args[i] === "--limit") { opts.limit = parsePositiveInt(args[++i], "--limit"); continue; }
813
- }
808
+ const parsed = parseDeploySubcommandArgs(args, {
809
+ command: "deploy list",
810
+ help: LIST_HELP,
811
+ valueFlags: ["--project", "--limit"],
812
+ });
813
+ expectPositionals(parsed.positionals, {
814
+ command: "run402 deploy list [--project <id>] [--limit <n>]",
815
+ max: 0,
816
+ });
817
+ const opts = {
818
+ project: parsed.flags["--project"] ?? null,
819
+ limit: parsed.flags["--limit"] === undefined ? null : parsePositiveInt(parsed.flags["--limit"], "--limit"),
820
+ };
814
821
 
815
822
  const project = resolveProjectId(opts.project);
816
823
  allowanceAuthHeaders("/deploy/v2/operations");
@@ -826,19 +833,18 @@ async function listCmd(args) {
826
833
  }
827
834
 
828
835
  async function eventsCmd(args) {
829
- const opts = { operationId: null, project: null };
830
- for (let i = 0; i < args.length; i++) {
831
- if (args[i] === "--help" || args[i] === "-h") { console.log(EVENTS_HELP); process.exit(0); }
832
- if (args[i] === "--project" && args[i + 1]) { opts.project = args[++i]; continue; }
833
- if (!args[i].startsWith("-") && !opts.operationId) opts.operationId = args[i];
834
- }
835
- if (!opts.operationId) {
836
- fail({
837
- code: "BAD_USAGE",
838
- message: "Missing <operation_id>.",
839
- hint: "run402 deploy events <operation_id>",
840
- });
841
- }
836
+ const parsed = parseDeploySubcommandArgs(args, {
837
+ command: "deploy events",
838
+ help: EVENTS_HELP,
839
+ valueFlags: ["--project"],
840
+ });
841
+ const [operationId] = expectPositionals(parsed.positionals, {
842
+ command: "run402 deploy events <operation_id>",
843
+ min: 1,
844
+ max: 1,
845
+ missing: "Missing <operation_id>.",
846
+ });
847
+ const opts = { operationId, project: parsed.flags["--project"] ?? null };
842
848
 
843
849
  const project = resolveProjectId(opts.project);
844
850
  allowanceAuthHeaders("/deploy/v2/operations");
@@ -868,20 +874,24 @@ async function releaseCmd(args) {
868
874
  }
869
875
 
870
876
  async function releaseGetCmd(args) {
871
- const opts = { releaseId: null, project: null, siteLimit: null };
872
- for (let i = 0; i < args.length; i++) {
873
- if (args[i] === "--help" || args[i] === "-h") { console.log(RELEASE_GET_HELP); process.exit(0); }
874
- if (args[i] === "--project" && args[i + 1]) { opts.project = args[++i]; continue; }
875
- if (args[i] === "--site-limit" && args[i + 1]) { opts.siteLimit = parsePositiveInt(args[++i], "--site-limit"); continue; }
876
- if (!args[i].startsWith("-") && !opts.releaseId) opts.releaseId = args[i];
877
- }
878
- if (!opts.releaseId) {
879
- fail({
880
- code: "BAD_USAGE",
881
- message: "Missing <release_id>.",
882
- hint: "run402 deploy release get <release_id>",
883
- });
884
- }
877
+ const parsed = parseDeploySubcommandArgs(args, {
878
+ command: "deploy release get",
879
+ help: RELEASE_GET_HELP,
880
+ valueFlags: ["--project", "--site-limit"],
881
+ });
882
+ const [releaseId] = expectPositionals(parsed.positionals, {
883
+ command: "run402 deploy release get <release_id>",
884
+ min: 1,
885
+ max: 1,
886
+ missing: "Missing <release_id>.",
887
+ });
888
+ const opts = {
889
+ releaseId,
890
+ project: parsed.flags["--project"] ?? null,
891
+ siteLimit: parsed.flags["--site-limit"] === undefined
892
+ ? null
893
+ : parsePositiveInt(parsed.flags["--site-limit"], "--site-limit"),
894
+ };
885
895
 
886
896
  const project = resolveProjectId(opts.project);
887
897
 
@@ -896,12 +906,21 @@ async function releaseGetCmd(args) {
896
906
  }
897
907
 
898
908
  async function releaseActiveCmd(args) {
899
- const opts = { project: null, siteLimit: null };
900
- for (let i = 0; i < args.length; i++) {
901
- if (args[i] === "--help" || args[i] === "-h") { console.log(RELEASE_ACTIVE_HELP); process.exit(0); }
902
- if (args[i] === "--project" && args[i + 1]) { opts.project = args[++i]; continue; }
903
- if (args[i] === "--site-limit" && args[i + 1]) { opts.siteLimit = parsePositiveInt(args[++i], "--site-limit"); continue; }
904
- }
909
+ const parsed = parseDeploySubcommandArgs(args, {
910
+ command: "deploy release active",
911
+ help: RELEASE_ACTIVE_HELP,
912
+ valueFlags: ["--project", "--site-limit"],
913
+ });
914
+ expectPositionals(parsed.positionals, {
915
+ command: "run402 deploy release active [--project <id>] [--site-limit <n>]",
916
+ max: 0,
917
+ });
918
+ const opts = {
919
+ project: parsed.flags["--project"] ?? null,
920
+ siteLimit: parsed.flags["--site-limit"] === undefined
921
+ ? null
922
+ : parsePositiveInt(parsed.flags["--site-limit"], "--site-limit"),
923
+ };
905
924
 
906
925
  const project = resolveProjectId(opts.project);
907
926
 
@@ -916,14 +935,21 @@ async function releaseActiveCmd(args) {
916
935
  }
917
936
 
918
937
  async function releaseDiffCmd(args) {
919
- const opts = { project: null, from: null, to: null, limit: null };
920
- for (let i = 0; i < args.length; i++) {
921
- if (args[i] === "--help" || args[i] === "-h") { console.log(RELEASE_DIFF_HELP); process.exit(0); }
922
- if (args[i] === "--project" && args[i + 1]) { opts.project = args[++i]; continue; }
923
- if (args[i] === "--from" && args[i + 1]) { opts.from = args[++i]; continue; }
924
- if (args[i] === "--to" && args[i + 1]) { opts.to = args[++i]; continue; }
925
- if (args[i] === "--limit" && args[i + 1]) { opts.limit = parsePositiveInt(args[++i], "--limit"); continue; }
926
- }
938
+ const parsed = parseDeploySubcommandArgs(args, {
939
+ command: "deploy release diff",
940
+ help: RELEASE_DIFF_HELP,
941
+ valueFlags: ["--project", "--from", "--to", "--limit"],
942
+ });
943
+ expectPositionals(parsed.positionals, {
944
+ command: "run402 deploy release diff --from <target> --to <target>",
945
+ max: 0,
946
+ });
947
+ const opts = {
948
+ project: parsed.flags["--project"] ?? null,
949
+ from: parsed.flags["--from"] ?? null,
950
+ to: parsed.flags["--to"] ?? null,
951
+ limit: parsed.flags["--limit"] === undefined ? null : parsePositiveInt(parsed.flags["--limit"], "--limit"),
952
+ };
927
953
  if (!opts.from || !opts.to) {
928
954
  fail({
929
955
  code: "BAD_USAGE",
@@ -1076,9 +1102,80 @@ function redactResolveInput(input) {
1076
1102
  return copy;
1077
1103
  }
1078
1104
 
1105
+ function parseDeploySubcommandArgs(rawArgs, { command, help, valueFlags = [], booleanFlags = [] }) {
1106
+ const args = normalizeArgv(rawArgs);
1107
+ const valueFlagSet = new Set(valueFlags);
1108
+ const booleanFlagSet = new Set(booleanFlags);
1109
+ const numericFlagSet = new Set(["--limit", "--site-limit"]);
1110
+ const allowedFlags = new Set([...valueFlags, ...booleanFlags, "--help", "-h"]);
1111
+ const flags = {};
1112
+ const positionals = [];
1113
+
1114
+ for (let i = 0; i < args.length; i++) {
1115
+ const arg = args[i];
1116
+ if (arg === "--help" || arg === "-h") {
1117
+ console.log(help);
1118
+ process.exit(0);
1119
+ }
1120
+ if (valueFlagSet.has(arg)) {
1121
+ const value = args[i + 1];
1122
+ if (value === undefined || (typeof value === "string" && value.startsWith("--"))) {
1123
+ if (numericFlagSet.has(arg)) parsePositiveInt(value, arg);
1124
+ fail({
1125
+ code: "BAD_USAGE",
1126
+ message: `${arg} requires a value`,
1127
+ details: { flag: arg },
1128
+ });
1129
+ }
1130
+ flags[arg] = value;
1131
+ i += 1;
1132
+ continue;
1133
+ }
1134
+ if (booleanFlagSet.has(arg)) {
1135
+ flags[arg] = true;
1136
+ continue;
1137
+ }
1138
+ if (typeof arg === "string" && arg.startsWith("-")) {
1139
+ fail({
1140
+ code: "BAD_USAGE",
1141
+ message: `Unknown flag for ${command}: ${arg}`,
1142
+ details: { flag: arg, allowed_flags: [...allowedFlags] },
1143
+ });
1144
+ }
1145
+ positionals.push(arg);
1146
+ }
1147
+
1148
+ return { flags, positionals };
1149
+ }
1150
+
1151
+ function expectPositionals(positionals, { command, min = 0, max = min, missing = "Missing required argument." }) {
1152
+ if (positionals.length < min) {
1153
+ fail({
1154
+ code: "BAD_USAGE",
1155
+ message: missing,
1156
+ hint: command,
1157
+ });
1158
+ }
1159
+ if (positionals.length > max) {
1160
+ fail({
1161
+ code: "BAD_USAGE",
1162
+ message: `Unexpected argument for ${command}: ${positionals[max]}`,
1163
+ hint: `Use \`${command}\`.`,
1164
+ });
1165
+ }
1166
+ return positionals;
1167
+ }
1168
+
1079
1169
  function parsePositiveInt(value, flag) {
1080
- const parsed = Number(value);
1081
- if (!Number.isInteger(parsed) || parsed < 1) {
1170
+ if (typeof value !== "string" || !/^\d+$/.test(value)) {
1171
+ fail({
1172
+ code: "BAD_USAGE",
1173
+ message: `${flag} must be a positive integer.`,
1174
+ details: { flag, value },
1175
+ });
1176
+ }
1177
+ const parsed = Number.parseInt(value, 10);
1178
+ if (!Number.isSafeInteger(parsed) || parsed < 1) {
1082
1179
  fail({
1083
1180
  code: "BAD_USAGE",
1084
1181
  message: `${flag} must be a positive integer.`,
package/lib/domains.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, flagValue, normalizeArgv, positionalArgs } from "./argparse.mjs";
4
5
 
5
6
  const HELP = `run402 domains — Manage custom domains
6
7
 
@@ -94,14 +95,15 @@ Examples:
94
95
  `,
95
96
  };
96
97
 
97
- function parseProjectFlag(args) {
98
- let project = null;
99
- const rest = [];
100
- for (let i = 0; i < args.length; i++) {
101
- if (args[i] === "--project" && args[i + 1]) { project = args[++i]; }
102
- else { rest.push(args[i]); }
103
- }
104
- return { project, rest };
98
+ function parseProjectFlag(args, extraKnown = []) {
99
+ const parsedArgs = normalizeArgv(args);
100
+ const valueFlags = ["--project"];
101
+ assertKnownFlags(parsedArgs, [...valueFlags, ...extraKnown, "--help", "-h"], valueFlags);
102
+ return {
103
+ project: flagValue(parsedArgs, "--project"),
104
+ rest: positionalArgs(parsedArgs, valueFlags),
105
+ args: parsedArgs,
106
+ };
105
107
  }
106
108
 
107
109
  async function add(args) {
@@ -115,6 +117,9 @@ async function add(args) {
115
117
  hint: "run402 domains add <domain> <subdomain_name> [--project <id>]",
116
118
  });
117
119
  }
120
+ if (rest.length > 2) {
121
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for domains add: ${rest[2]}` });
122
+ }
118
123
  const projectId = resolveProjectId(project);
119
124
  try {
120
125
  const data = await getSdk().domains.add(projectId, domain, subdomainName);
@@ -153,6 +158,9 @@ async function status(args) {
153
158
  hint: "run402 domains status <domain> [--project <id>]",
154
159
  });
155
160
  }
161
+ if (rest.length > 1) {
162
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for domains status: ${rest[1]}` });
163
+ }
156
164
  const projectId = resolveProjectId(project);
157
165
  try {
158
166
  const data = await getSdk().domains.status(projectId, domain);
@@ -163,8 +171,8 @@ async function status(args) {
163
171
  }
164
172
 
165
173
  async function deleteDomain(args) {
166
- const { project, rest } = parseProjectFlag(args);
167
- const domain = rest.find((a) => !a.startsWith("--"));
174
+ const { project, rest, args: parsedArgs } = parseProjectFlag(args, ["--confirm"]);
175
+ const domain = rest[0];
168
176
  if (!domain) {
169
177
  fail({
170
178
  code: "BAD_USAGE",
@@ -172,7 +180,10 @@ async function deleteDomain(args) {
172
180
  hint: "run402 domains delete <domain> --confirm [--project <id>]",
173
181
  });
174
182
  }
175
- if (!Array.isArray(args) || !args.includes("--confirm")) {
183
+ if (rest.length > 1) {
184
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for domains delete: ${rest[1]}` });
185
+ }
186
+ if (!parsedArgs.includes("--confirm")) {
176
187
  fail({
177
188
  code: "CONFIRMATION_REQUIRED",
178
189
  message: `Destructive: releasing custom domain '${domain}' detaches it from this project and clears its DNS/SSL configuration. This is irreversible. Re-run with --confirm to proceed.`,
package/lib/functions.mjs CHANGED
@@ -5,6 +5,7 @@ import { reportSdkError, fail } from "./sdk-errors.mjs";
5
5
  import { assertKnownFlags, hasHelp, normalizeArgv, parseIntegerFlag, validateRegularFile } from "./argparse.mjs";
6
6
 
7
7
  const FUNCTION_LOG_REQUEST_ID_RE = /^req_[A-Za-z0-9_-]{4,128}$/;
8
+ const FUNCTION_LOG_TAIL_MAX = 1000;
8
9
 
9
10
  const HELP = `run402 functions — Manage serverless functions
10
11
 
@@ -180,7 +181,7 @@ async function deploy(projectId, name, args) {
180
181
  if (args[i] === "--file" && args[i + 1]) opts.file = args[++i];
181
182
  if (args[i] === "--timeout") opts.timeout = parseIntegerFlag("--timeout", args[++i], { min: 1 });
182
183
  if (args[i] === "--memory") opts.memory = parseIntegerFlag("--memory", args[++i], { min: 1 });
183
- if (args[i] === "--deps" && args[i + 1]) opts.deps = args[++i].split(",");
184
+ if (args[i] === "--deps" && args[i + 1]) opts.deps = parseDepsFlag(args[++i]);
184
185
  if (args[i] === "--schedule" && i + 1 < args.length) opts.schedule = args[++i];
185
186
  }
186
187
  if (!opts.file) {
@@ -239,7 +240,7 @@ async function logs(projectId, name, args) {
239
240
  let requestId = undefined;
240
241
  let follow = false;
241
242
  for (let i = 0; i < args.length; i++) {
242
- if (args[i] === "--tail") tail = parseIntegerFlag("--tail", args[++i], { min: 1 });
243
+ if (args[i] === "--tail") tail = parseIntegerFlag("--tail", args[++i], { min: 1, max: FUNCTION_LOG_TAIL_MAX });
243
244
  if (args[i] === "--since" && args[i + 1]) since = args[++i];
244
245
  if (args[i] === "--request-id" && args[i + 1]) requestId = args[++i];
245
246
  if (args[i] === "--follow") follow = true;
@@ -360,6 +361,13 @@ async function update(projectId, name, args) {
360
361
  if (args[i] === "--timeout") timeout = parseIntegerFlag("--timeout", args[++i], { min: 1 });
361
362
  if (args[i] === "--memory") memory = parseIntegerFlag("--memory", args[++i], { min: 1 });
362
363
  }
364
+ if (scheduleRemove && schedule !== undefined) {
365
+ fail({
366
+ code: "BAD_USAGE",
367
+ message: "--schedule and --schedule-remove are mutually exclusive",
368
+ details: { flags: ["--schedule", "--schedule-remove"] },
369
+ });
370
+ }
363
371
 
364
372
  const updateOpts = {};
365
373
  if (scheduleRemove || schedule === "") {
@@ -385,8 +393,10 @@ async function update(projectId, name, args) {
385
393
  }
386
394
  }
387
395
 
388
- async function list(projectId) {
396
+ async function list(projectId, args = []) {
389
397
  assertRequiredProject(projectId, "run402 functions list <project_id>");
398
+ assertKnownFlags(args, ["--help", "-h"]);
399
+ assertNoExtraPositionals(args, "run402 functions list <project_id>");
390
400
  try {
391
401
  const data = await getSdk().functions.list(projectId);
392
402
  console.log(JSON.stringify(data, null, 2));
@@ -395,8 +405,10 @@ async function list(projectId) {
395
405
  }
396
406
  }
397
407
 
398
- async function deleteFunction(projectId, name) {
408
+ async function deleteFunction(projectId, name, args = []) {
399
409
  assertRequiredProjectAndName(projectId, name, "run402 functions delete <project_id> <name>");
410
+ assertKnownFlags(args, ["--help", "-h"]);
411
+ assertNoExtraPositionals(args, "run402 functions delete <project_id> <name>");
400
412
  try {
401
413
  await getSdk().functions.delete(projectId, name);
402
414
  console.log(JSON.stringify({ status: "ok", message: `Function '${name}' deleted.` }));
@@ -417,8 +429,8 @@ export async function run(sub, args) {
417
429
  case "invoke": await invoke(args[0], args[1], args.slice(2)); break;
418
430
  case "logs": await logs(args[0], args[1], args.slice(2)); break;
419
431
  case "update": await update(args[0], args[1], args.slice(2)); break;
420
- case "list": await list(args[0]); break;
421
- case "delete": await deleteFunction(args[0], args[1]); break;
432
+ case "list": await list(args[0], args.slice(1)); break;
433
+ case "delete": await deleteFunction(args[0], args[1], args.slice(2)); break;
422
434
  default:
423
435
  console.error(`Unknown subcommand: ${sub}\n`);
424
436
  console.log(HELP);
@@ -426,6 +438,30 @@ export async function run(sub, args) {
426
438
  }
427
439
  }
428
440
 
441
+ function parseDepsFlag(value) {
442
+ const raw = String(value);
443
+ const deps = raw.split(",").map((entry) => entry.trim());
444
+ if (deps.some((entry) => entry === "")) {
445
+ fail({
446
+ code: "BAD_USAGE",
447
+ message: "--deps must be a comma-separated list of non-empty package specs",
448
+ details: { flag: "--deps", value: raw },
449
+ });
450
+ }
451
+ return deps;
452
+ }
453
+
454
+ function assertNoExtraPositionals(args, usage) {
455
+ if (args.length > 0) {
456
+ fail({
457
+ code: "BAD_USAGE",
458
+ message: `Unexpected argument: ${args[0]}`,
459
+ hint: usage,
460
+ details: { argument: args[0] },
461
+ });
462
+ }
463
+ }
464
+
429
465
  function assertRequiredProject(projectId, usage) {
430
466
  if (!projectId || String(projectId).startsWith("-")) {
431
467
  fail({
package/lib/image.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { writeFileSync } from "fs";
2
2
  import { getSdk } from "./sdk.mjs";
3
3
  import { reportSdkError, fail } from "./sdk-errors.mjs";
4
+ import { assertAllowedValue, assertKnownFlags, flagValue, normalizeArgv, positionalArgs } from "./argparse.mjs";
4
5
 
5
6
  const HELP = `run402 image — Generate AI images via x402 micropayments
6
7
 
@@ -71,15 +72,22 @@ export async function run(sub, args) {
71
72
  process.exit(1);
72
73
  }
73
74
 
74
- const opts = { prompt: null, aspect: "square", output: null };
75
- let i = 0;
76
- if (i < args.length && !args[i].startsWith("--")) opts.prompt = args[i++];
77
- while (i < args.length) {
78
- if (args[i] === "--help" || args[i] === "-h") { console.log(SUB_HELP[sub] || HELP); process.exit(0); }
79
- else if (args[i] === "--aspect" && args[i + 1]) { opts.aspect = args[++i]; }
80
- else if (args[i] === "--output" && args[i + 1]) { opts.output = args[++i]; }
81
- i++;
75
+ const parsedArgs = normalizeArgv(args);
76
+ const valueFlags = ["--aspect", "--output"];
77
+ assertKnownFlags(parsedArgs, [...valueFlags, "--help", "-h"], valueFlags);
78
+ const positionals = positionalArgs(parsedArgs, valueFlags);
79
+ if (positionals.length > 1) {
80
+ fail({
81
+ code: "BAD_USAGE",
82
+ message: `Unexpected argument for image generate: ${positionals[1]}`,
83
+ hint: 'Quote multi-word prompts, e.g. run402 image generate "your prompt".',
84
+ });
82
85
  }
86
+ const opts = {
87
+ prompt: positionals[0] ?? null,
88
+ aspect: flagValue(parsedArgs, "--aspect") ?? "square",
89
+ output: flagValue(parsedArgs, "--output"),
90
+ };
83
91
 
84
92
  if (!opts.prompt) {
85
93
  fail({
@@ -88,6 +96,7 @@ export async function run(sub, args) {
88
96
  hint: 'run402 image generate "your prompt"',
89
97
  });
90
98
  }
99
+ assertAllowedValue(opts.aspect, ["square", "landscape", "portrait"], "--aspect");
91
100
 
92
101
  try {
93
102
  const data = await getSdk().ai.generateImage({ prompt: opts.prompt, aspect: opts.aspect });
package/lib/message.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { allowanceAuthHeaders } from "./config.mjs";
2
2
  import { getSdk } from "./sdk.mjs";
3
3
  import { reportSdkError, fail } from "./sdk-errors.mjs";
4
+ import { assertKnownFlags, normalizeArgv } from "./argparse.mjs";
4
5
 
5
6
  const HELP = `run402 message — Send messages to Run402 developers
6
7
 
@@ -88,5 +89,7 @@ export async function run(sub, args) {
88
89
  console.log(HELP);
89
90
  process.exit(1);
90
91
  }
91
- await send(args.join(" "));
92
+ const parsedArgs = normalizeArgv(args);
93
+ assertKnownFlags(parsedArgs, ["--help", "-h"]);
94
+ await send(parsedArgs.join(" "));
92
95
  }
package/lib/secrets.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { readFileSync } from "fs";
2
2
  import { getSdk } from "./sdk.mjs";
3
3
  import { reportSdkError, fail } from "./sdk-errors.mjs";
4
- import { validateRegularFile } from "./argparse.mjs";
4
+ import { assertKnownFlags, flagValue, normalizeArgv, positionalArgs, validateRegularFile } from "./argparse.mjs";
5
5
 
6
6
  const HELP = `run402 secrets — Manage project secrets
7
7
 
@@ -78,15 +78,20 @@ Examples:
78
78
  };
79
79
 
80
80
  async function set(projectId, key, args = []) {
81
- let file = null;
82
- let value = null;
83
- for (let i = 0; i < args.length; i++) {
84
- if (args[i] === "--file" && args[i + 1]) { file = args[++i]; }
85
- else if (!value && !args[i].startsWith("--")) { value = args[i]; }
81
+ const parsedArgs = normalizeArgv(args);
82
+ const valueFlags = ["--file"];
83
+ assertKnownFlags(parsedArgs, [...valueFlags, "--help", "-h"], valueFlags);
84
+ const values = positionalArgs(parsedArgs, valueFlags);
85
+ if (values.length > 1) {
86
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for secrets set: ${values[1]}` });
87
+ }
88
+ const file = flagValue(parsedArgs, "--file");
89
+ if (file && values.length > 0) {
90
+ fail({ code: "BAD_USAGE", message: "Provide either an inline value or --file, not both." });
86
91
  }
87
92
  if (file) validateRegularFile(file, "--file");
88
- const val = file ? readFileSync(file, "utf-8") : value;
89
- if (!val) {
93
+ const val = file ? readFileSync(file, "utf-8") : values.length === 1 ? values[0] : undefined;
94
+ if (val === undefined) {
90
95
  fail({
91
96
  code: "BAD_USAGE",
92
97
  message: "Missing secret value.",
@@ -101,7 +106,13 @@ async function set(projectId, key, args = []) {
101
106
  }
102
107
  }
103
108
 
104
- async function list(projectId) {
109
+ async function list(projectId, args = []) {
110
+ const parsedArgs = normalizeArgv(args);
111
+ assertKnownFlags(parsedArgs, ["--help", "-h"]);
112
+ const extra = positionalArgs(parsedArgs);
113
+ if (extra.length > 0) {
114
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for secrets list: ${extra[0]}` });
115
+ }
105
116
  try {
106
117
  const data = await getSdk().secrets.list(projectId);
107
118
  const sanitized = {
@@ -117,7 +128,13 @@ async function list(projectId) {
117
128
  }
118
129
  }
119
130
 
120
- async function deleteSecret(projectId, key) {
131
+ async function deleteSecret(projectId, key, args = []) {
132
+ const parsedArgs = normalizeArgv(args);
133
+ assertKnownFlags(parsedArgs, ["--help", "-h"]);
134
+ const extra = positionalArgs(parsedArgs);
135
+ if (extra.length > 0) {
136
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for secrets delete: ${extra[0]}` });
137
+ }
121
138
  try {
122
139
  await getSdk().secrets.delete(projectId, key);
123
140
  console.log(JSON.stringify({ status: "ok", message: `Secret '${key}' deleted.` }));
@@ -134,8 +151,8 @@ export async function run(sub, args) {
134
151
  }
135
152
  switch (sub) {
136
153
  case "set": await set(args[0], args[1], args.slice(2)); break;
137
- case "list": await list(args[0]); break;
138
- case "delete": await deleteSecret(args[0], args[1]); break;
154
+ case "list": await list(args[0], args.slice(1)); break;
155
+ case "delete": await deleteSecret(args[0], args[1], args.slice(2)); break;
139
156
  default:
140
157
  console.error(`Unknown subcommand: ${sub}\n`);
141
158
  console.log(HELP);