run402 1.54.0 → 1.54.2

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 (64) hide show
  1. package/lib/agent.mjs +4 -2
  2. package/lib/ai.mjs +24 -10
  3. package/lib/allowance.mjs +53 -15
  4. package/lib/apps.mjs +13 -11
  5. package/lib/argparse.mjs +147 -0
  6. package/lib/auth.mjs +24 -9
  7. package/lib/billing.mjs +33 -17
  8. package/lib/blob.mjs +58 -26
  9. package/lib/cdn.mjs +3 -4
  10. package/lib/config.mjs +32 -8
  11. package/lib/contracts.mjs +38 -21
  12. package/lib/deploy-v2.mjs +38 -23
  13. package/lib/deploy.mjs +43 -44
  14. package/lib/domains.mjs +24 -8
  15. package/lib/email.mjs +38 -29
  16. package/lib/functions.mjs +77 -34
  17. package/lib/image.mjs +8 -2
  18. package/lib/message.mjs +4 -2
  19. package/lib/projects.mjs +115 -40
  20. package/lib/sdk-errors.mjs +66 -10
  21. package/lib/secrets.mjs +14 -8
  22. package/lib/sender-domain.mjs +11 -5
  23. package/lib/sites.mjs +9 -7
  24. package/lib/status.mjs +5 -2
  25. package/lib/subdomains.mjs +26 -11
  26. package/lib/tier.mjs +8 -2
  27. package/lib/webhooks.mjs +27 -13
  28. package/package.json +1 -1
  29. package/sdk/dist/index.d.ts +1 -0
  30. package/sdk/dist/index.d.ts.map +1 -1
  31. package/sdk/dist/index.js +5 -0
  32. package/sdk/dist/index.js.map +1 -1
  33. package/sdk/dist/namespaces/auth.d.ts +7 -0
  34. package/sdk/dist/namespaces/auth.d.ts.map +1 -1
  35. package/sdk/dist/namespaces/auth.js +24 -0
  36. package/sdk/dist/namespaces/auth.js.map +1 -1
  37. package/sdk/dist/namespaces/billing.d.ts +3 -0
  38. package/sdk/dist/namespaces/billing.d.ts.map +1 -1
  39. package/sdk/dist/namespaces/billing.js +6 -0
  40. package/sdk/dist/namespaces/billing.js.map +1 -1
  41. package/sdk/dist/namespaces/contracts.d.ts +3 -0
  42. package/sdk/dist/namespaces/contracts.d.ts.map +1 -1
  43. package/sdk/dist/namespaces/contracts.js +6 -0
  44. package/sdk/dist/namespaces/contracts.js.map +1 -1
  45. package/sdk/dist/namespaces/email.d.ts +4 -0
  46. package/sdk/dist/namespaces/email.d.ts.map +1 -1
  47. package/sdk/dist/namespaces/email.js +8 -0
  48. package/sdk/dist/namespaces/email.js.map +1 -1
  49. package/sdk/dist/namespaces/projects.d.ts +14 -0
  50. package/sdk/dist/namespaces/projects.d.ts.map +1 -1
  51. package/sdk/dist/namespaces/projects.js +72 -0
  52. package/sdk/dist/namespaces/projects.js.map +1 -1
  53. package/sdk/dist/namespaces/sender-domain.d.ts +2 -0
  54. package/sdk/dist/namespaces/sender-domain.d.ts.map +1 -1
  55. package/sdk/dist/namespaces/sender-domain.js +4 -0
  56. package/sdk/dist/namespaces/sender-domain.js.map +1 -1
  57. package/sdk/dist/scoped.d.ts +8 -1
  58. package/sdk/dist/scoped.d.ts.map +1 -1
  59. package/sdk/dist/scoped.js +21 -0
  60. package/sdk/dist/scoped.js.map +1 -1
  61. package/core-dist/wallet-auth.js +0 -62
  62. package/core-dist/wallet.js +0 -25
  63. package/sdk/core-dist/wallet-auth.js +0 -62
  64. package/sdk/core-dist/wallet.js +0 -25
package/lib/domains.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { resolveProjectId } from "./config.mjs";
2
2
  import { getSdk } from "./sdk.mjs";
3
- import { reportSdkError } from "./sdk-errors.mjs";
3
+ import { reportSdkError, fail } from "./sdk-errors.mjs";
4
4
 
5
5
  const HELP = `run402 domains — Manage custom domains
6
6
 
@@ -40,7 +40,13 @@ async function add(args) {
40
40
  const { project, rest } = parseProjectFlag(args);
41
41
  const domain = rest[0];
42
42
  const subdomainName = rest[1];
43
- if (!domain || !subdomainName) { console.error("Usage: run402 domains add <domain> <subdomain_name> [--project <id>]"); process.exit(1); }
43
+ if (!domain || !subdomainName) {
44
+ fail({
45
+ code: "BAD_USAGE",
46
+ message: "Missing <domain> and/or <subdomain_name>.",
47
+ hint: "run402 domains add <domain> <subdomain_name> [--project <id>]",
48
+ });
49
+ }
44
50
  const projectId = resolveProjectId(project);
45
51
  try {
46
52
  const data = await getSdk().domains.add(projectId, domain, subdomainName);
@@ -63,7 +69,13 @@ async function list(projectIdArg) {
63
69
  async function status(args) {
64
70
  const { project, rest } = parseProjectFlag(args);
65
71
  const domain = rest[0];
66
- if (!domain) { console.error("Usage: run402 domains status <domain> [--project <id>]"); process.exit(1); }
72
+ if (!domain) {
73
+ fail({
74
+ code: "BAD_USAGE",
75
+ message: "Missing <domain>.",
76
+ hint: "run402 domains status <domain> [--project <id>]",
77
+ });
78
+ }
67
79
  const projectId = resolveProjectId(project);
68
80
  try {
69
81
  const data = await getSdk().domains.status(projectId, domain);
@@ -76,15 +88,19 @@ async function status(args) {
76
88
  async function deleteDomain(args) {
77
89
  const { project, rest } = parseProjectFlag(args);
78
90
  const domain = rest.find((a) => !a.startsWith("--"));
79
- if (!domain) { console.error("Usage: run402 domains delete <domain> --confirm [--project <id>]"); process.exit(1); }
91
+ if (!domain) {
92
+ fail({
93
+ code: "BAD_USAGE",
94
+ message: "Missing <domain>.",
95
+ hint: "run402 domains delete <domain> --confirm [--project <id>]",
96
+ });
97
+ }
80
98
  if (!Array.isArray(args) || !args.includes("--confirm")) {
81
- console.error(JSON.stringify({
82
- status: "error",
99
+ fail({
83
100
  code: "CONFIRMATION_REQUIRED",
84
101
  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.`,
85
102
  details: { domain },
86
- }));
87
- process.exit(1);
103
+ });
88
104
  }
89
105
  const projectId = resolveProjectId(project);
90
106
  try {
package/lib/email.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { resolveProjectId } from "./config.mjs";
2
2
  import { getSdk } from "./sdk.mjs";
3
- import { reportSdkError } from "./sdk-errors.mjs";
3
+ import { reportSdkError, fail, parseFlagJson } from "./sdk-errors.mjs";
4
4
 
5
5
  const HELP = `run402 email — Send emails from your project
6
6
 
@@ -130,14 +130,12 @@ function parseVars(args) {
130
130
  for (let i = 0; i < args.length; i++) {
131
131
  if (args[i] === "--vars" && args[i + 1]) {
132
132
  const raw = args[++i];
133
- let parsed;
134
- try { parsed = JSON.parse(raw); } catch {
135
- console.error(JSON.stringify({ status: "error", message: "Invalid JSON for --vars. Expected a JSON object, e.g. '{\"key\":\"value\"}'" }));
136
- process.exit(1);
137
- }
133
+ const parsed = parseFlagJson("--vars", raw);
138
134
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
139
- console.error(JSON.stringify({ status: "error", message: "--vars must be a JSON object, e.g. '{\"key\":\"value\"}'" }));
140
- process.exit(1);
135
+ fail({
136
+ code: "BAD_USAGE",
137
+ message: "--vars must be a JSON object, e.g. '{\"key\":\"value\"}'",
138
+ });
141
139
  }
142
140
  for (const [k, v] of Object.entries(parsed)) vars[k] = typeof v === "string" ? v : String(v);
143
141
  }
@@ -161,8 +159,11 @@ async function create(args) {
161
159
  }
162
160
  const projectId = resolveProjectId(projectOpt);
163
161
  if (!slug) {
164
- console.error(JSON.stringify({ status: "error", message: "Missing slug. Usage: run402 email create <slug>" }));
165
- process.exit(1);
162
+ fail({
163
+ code: "BAD_USAGE",
164
+ message: "Missing slug.",
165
+ hint: "run402 email create <slug>",
166
+ });
166
167
  }
167
168
 
168
169
  try {
@@ -184,8 +185,7 @@ async function send(args) {
184
185
  const variables = parseVars(args);
185
186
 
186
187
  if (!to) {
187
- console.error(JSON.stringify({ status: "error", message: "Missing --to <email>" }));
188
- process.exit(1);
188
+ fail({ code: "BAD_USAGE", message: "Missing --to <email>" });
189
189
  }
190
190
 
191
191
  try {
@@ -228,8 +228,11 @@ async function get(args) {
228
228
  }
229
229
  const projectId = resolveProjectId(projectOpt);
230
230
  if (!messageId) {
231
- console.error(JSON.stringify({ status: "error", message: "Missing message_id. Usage: run402 email get <message_id>" }));
232
- process.exit(1);
231
+ fail({
232
+ code: "BAD_USAGE",
233
+ message: "Missing message_id.",
234
+ hint: "run402 email get <message_id>",
235
+ });
233
236
  }
234
237
  try {
235
238
  const data = await getSdk().email.get(projectId, messageId);
@@ -250,8 +253,11 @@ async function getRaw(args) {
250
253
  }
251
254
  const projectId = resolveProjectId(projectOpt);
252
255
  if (!messageId) {
253
- console.error(JSON.stringify({ status: "error", message: "Missing message_id. Usage: run402 email get-raw <message_id> [--output <file>]" }));
254
- process.exit(1);
256
+ fail({
257
+ code: "BAD_USAGE",
258
+ message: "Missing message_id.",
259
+ hint: "run402 email get-raw <message_id> [--output <file>]",
260
+ });
255
261
  }
256
262
 
257
263
  try {
@@ -286,12 +292,17 @@ async function reply(args) {
286
292
  const projectId = resolveProjectId(projectOpt);
287
293
 
288
294
  if (!messageId) {
289
- console.error(JSON.stringify({ status: "error", message: "Missing message_id. Usage: run402 email reply <message_id> --html \"...\"" }));
290
- process.exit(1);
295
+ fail({
296
+ code: "BAD_USAGE",
297
+ message: "Missing message_id.",
298
+ hint: 'run402 email reply <message_id> --html "..."',
299
+ });
291
300
  }
292
301
  if (!html && !text) {
293
- console.error(JSON.stringify({ status: "error", message: "Provide --html and/or --text for the reply body" }));
294
- process.exit(1);
302
+ fail({
303
+ code: "BAD_USAGE",
304
+ message: "Provide --html and/or --text for the reply body",
305
+ });
295
306
  }
296
307
 
297
308
  try {
@@ -299,12 +310,11 @@ async function reply(args) {
299
310
  const original = await getSdk().email.get(projectId, messageId);
300
311
  const replyTo = original.from || original.from_address || original.sender || null;
301
312
  if (!replyTo) {
302
- console.error(JSON.stringify({
303
- status: "error",
313
+ fail({
314
+ code: "BAD_USAGE",
304
315
  message: "Original message has no from address to reply to",
305
- original_keys: Object.keys(original),
306
- }));
307
- process.exit(1);
316
+ details: { original_keys: Object.keys(original) },
317
+ });
308
318
  }
309
319
  const origSubject = typeof original.subject === "string" ? original.subject : "";
310
320
  const defaultSubject = origSubject && origSubject.toLowerCase().startsWith("re:")
@@ -339,11 +349,10 @@ async function deleteMailbox(args) {
339
349
  const confirmed = args.includes("--confirm");
340
350
 
341
351
  if (!confirmed) {
342
- console.error(JSON.stringify({
343
- status: "error",
352
+ fail({
353
+ code: "CONFIRMATION_REQUIRED",
344
354
  message: "Destructive: deleting a mailbox is irreversible (drops all messages and webhook subscriptions). Re-run with --confirm to proceed.",
345
- }));
346
- process.exit(1);
355
+ });
347
356
  }
348
357
 
349
358
  try {
package/lib/functions.mjs CHANGED
@@ -1,7 +1,8 @@
1
1
  import { readFileSync } from "fs";
2
2
  import { findProject, API } from "./config.mjs";
3
3
  import { getSdk } from "./sdk.mjs";
4
- import { reportSdkError } from "./sdk-errors.mjs";
4
+ import { reportSdkError, fail } from "./sdk-errors.mjs";
5
+ import { assertKnownFlags, hasHelp, normalizeArgv, parseIntegerFlag } from "./argparse.mjs";
5
6
 
6
7
  const HELP = `run402 functions — Manage serverless functions
7
8
 
@@ -21,18 +22,18 @@ Subcommands:
21
22
  delete <id> <name> Delete a function
22
23
 
23
24
  Examples:
24
- run402 functions deploy abc123 stripe-webhook --file handler.ts
25
- run402 functions deploy abc123 send-reminders --file remind.ts --schedule '*/15 * * * *'
26
- run402 functions deploy abc123 send-reminders --file remind.ts --schedule '' # remove schedule
27
- run402 functions invoke abc123 stripe-webhook --body '{"event":"test"}'
28
- run402 functions logs abc123 stripe-webhook --tail 100
29
- run402 functions logs abc123 stripe-webhook --since 2026-03-29T14:00:00Z
30
- run402 functions logs abc123 stripe-webhook --follow
31
- run402 functions update abc123 send-reminders --schedule '0 */4 * * *'
32
- run402 functions update abc123 send-reminders --schedule-remove
33
- run402 functions update abc123 my-func --timeout 15 --memory 256
34
- run402 functions list abc123
35
- run402 functions delete abc123 stripe-webhook
25
+ run402 functions deploy prj_abc123 stripe-webhook --file handler.ts
26
+ run402 functions deploy prj_abc123 send-reminders --file remind.ts --schedule '*/15 * * * *'
27
+ run402 functions deploy prj_abc123 send-reminders --file remind.ts --schedule '' # remove schedule
28
+ run402 functions invoke prj_abc123 stripe-webhook --body '{"event":"test"}'
29
+ run402 functions logs prj_abc123 stripe-webhook --tail 100
30
+ run402 functions logs prj_abc123 stripe-webhook --since 2026-03-29T14:00:00Z
31
+ run402 functions logs prj_abc123 stripe-webhook --follow
32
+ run402 functions update prj_abc123 send-reminders --schedule '0 */4 * * *'
33
+ run402 functions update prj_abc123 send-reminders --schedule-remove
34
+ run402 functions update prj_abc123 my-func --timeout 15 --memory 256
35
+ run402 functions list prj_abc123
36
+ run402 functions delete prj_abc123 stripe-webhook
36
37
 
37
38
  Notes:
38
39
  - Code must export a default async function: export default async (req: Request) => Response
@@ -77,10 +78,10 @@ Notes:
77
78
  notes such as bundle-size advisories
78
79
 
79
80
  Examples:
80
- run402 functions deploy abc123 stripe-webhook --file handler.ts
81
- run402 functions deploy abc123 send-reminders --file remind.ts \\
81
+ run402 functions deploy prj_abc123 stripe-webhook --file handler.ts
82
+ run402 functions deploy prj_abc123 send-reminders --file remind.ts \\
82
83
  --schedule '*/15 * * * *'
83
- run402 functions deploy abc123 send-reminders --file remind.ts --schedule ''
84
+ run402 functions deploy prj_abc123 send-reminders --file remind.ts --schedule ''
84
85
  `,
85
86
  invoke: `run402 functions invoke — Invoke a deployed function
86
87
 
@@ -96,8 +97,8 @@ Options:
96
97
  --body <json> Request body (ignored for GET/HEAD)
97
98
 
98
99
  Examples:
99
- run402 functions invoke abc123 stripe-webhook --body '{"event":"test"}'
100
- run402 functions invoke abc123 ping --method GET
100
+ run402 functions invoke prj_abc123 stripe-webhook --body '{"event":"test"}'
101
+ run402 functions invoke prj_abc123 ping --method GET
101
102
  `,
102
103
  logs: `run402 functions logs — Fetch or tail function logs
103
104
 
@@ -114,9 +115,9 @@ Options:
114
115
  --follow Poll every 3s and stream new entries (Ctrl-C to stop)
115
116
 
116
117
  Examples:
117
- run402 functions logs abc123 stripe-webhook --tail 100
118
- run402 functions logs abc123 stripe-webhook --since 2026-03-29T14:00:00Z
119
- run402 functions logs abc123 stripe-webhook --follow
118
+ run402 functions logs prj_abc123 stripe-webhook --tail 100
119
+ run402 functions logs prj_abc123 stripe-webhook --since 2026-03-29T14:00:00Z
120
+ run402 functions logs prj_abc123 stripe-webhook --follow
120
121
  `,
121
122
  update: `run402 functions update — Update function config without re-deploying
122
123
 
@@ -137,22 +138,26 @@ Notes:
137
138
  Must provide at least one of the options above.
138
139
 
139
140
  Examples:
140
- run402 functions update abc123 send-reminders --schedule '0 */4 * * *'
141
- run402 functions update abc123 send-reminders --schedule-remove
142
- run402 functions update abc123 my-func --timeout 15 --memory 256
141
+ run402 functions update prj_abc123 send-reminders --schedule '0 */4 * * *'
142
+ run402 functions update prj_abc123 send-reminders --schedule-remove
143
+ run402 functions update prj_abc123 my-func --timeout 15 --memory 256
143
144
  `,
144
145
  };
145
146
 
146
147
  async function deploy(projectId, name, args) {
148
+ assertRequiredProjectAndName(projectId, name, "run402 functions deploy <project_id> <name> --file <file>");
149
+ assertKnownFlags(args, ["--file", "--timeout", "--memory", "--deps", "--schedule", "--help", "-h"], ["--file", "--timeout", "--memory", "--deps", "--schedule"]);
147
150
  const opts = { file: null, timeout: undefined, memory: undefined, deps: undefined, schedule: undefined };
148
151
  for (let i = 0; i < args.length; i++) {
149
152
  if (args[i] === "--file" && args[i + 1]) opts.file = args[++i];
150
- if (args[i] === "--timeout" && args[i + 1]) opts.timeout = parseInt(args[++i]);
151
- if (args[i] === "--memory" && args[i + 1]) opts.memory = parseInt(args[++i]);
153
+ if (args[i] === "--timeout") opts.timeout = parseIntegerFlag("--timeout", args[++i], { min: 1 });
154
+ if (args[i] === "--memory") opts.memory = parseIntegerFlag("--memory", args[++i], { min: 1 });
152
155
  if (args[i] === "--deps" && args[i + 1]) opts.deps = args[++i].split(",");
153
156
  if (args[i] === "--schedule" && i + 1 < args.length) opts.schedule = args[++i];
154
157
  }
155
- if (!opts.file) { console.error(JSON.stringify({ status: "error", message: "Missing --file <file>" })); process.exit(1); }
158
+ if (!opts.file) {
159
+ fail({ code: "BAD_USAGE", message: "Missing --file <file>" });
160
+ }
156
161
  const code = readFileSync(opts.file, "utf-8");
157
162
 
158
163
  const deployOpts = { name, code };
@@ -173,6 +178,8 @@ async function deploy(projectId, name, args) {
173
178
  }
174
179
 
175
180
  async function invoke(projectId, name, args) {
181
+ assertRequiredProjectAndName(projectId, name, "run402 functions invoke <project_id> <name> [--method <M>] [--body <json>]");
182
+ assertKnownFlags(args, ["--method", "--body", "--help", "-h"], ["--method", "--body"]);
176
183
  const opts = { method: "POST", body: undefined };
177
184
  for (let i = 0; i < args.length; i++) {
178
185
  if (args[i] === "--method" && args[i + 1]) opts.method = args[++i];
@@ -196,11 +203,13 @@ async function invoke(projectId, name, args) {
196
203
  }
197
204
 
198
205
  async function logs(projectId, name, args) {
206
+ assertRequiredProjectAndName(projectId, name, "run402 functions logs <project_id> <name> [--tail <n>]");
207
+ assertKnownFlags(args, ["--tail", "--since", "--follow", "--help", "-h"], ["--tail", "--since"]);
199
208
  let tail = 50;
200
209
  let since = undefined;
201
210
  let follow = false;
202
211
  for (let i = 0; i < args.length; i++) {
203
- if (args[i] === "--tail" && args[i + 1]) tail = parseInt(args[++i]);
212
+ if (args[i] === "--tail") tail = parseIntegerFlag("--tail", args[++i], { min: 1 });
204
213
  if (args[i] === "--since" && args[i + 1]) since = args[++i];
205
214
  if (args[i] === "--follow") follow = true;
206
215
  }
@@ -212,7 +221,13 @@ async function logs(projectId, name, args) {
212
221
  if (since !== undefined) {
213
222
  const parsed = Number(since);
214
223
  const ms = Number.isNaN(parsed) ? new Date(since).getTime() : parsed;
215
- if (Number.isNaN(ms)) { console.error(JSON.stringify({ status: "error", message: `Invalid --since value: ${since}` })); process.exit(1); }
224
+ if (Number.isNaN(ms)) {
225
+ fail({
226
+ code: "BAD_USAGE",
227
+ message: `Invalid --since value: ${since}`,
228
+ details: { flag: "--since", value: since },
229
+ });
230
+ }
216
231
  sinceIso = new Date(ms).toISOString();
217
232
  }
218
233
 
@@ -261,6 +276,8 @@ async function logs(projectId, name, args) {
261
276
  }
262
277
 
263
278
  async function update(projectId, name, args) {
279
+ assertRequiredProjectAndName(projectId, name, "run402 functions update <project_id> <name> [options]");
280
+ assertKnownFlags(args, ["--schedule", "--schedule-remove", "--timeout", "--memory", "--help", "-h"], ["--schedule", "--timeout", "--memory"]);
264
281
  let schedule = undefined;
265
282
  let scheduleRemove = false;
266
283
  let timeout = undefined;
@@ -268,8 +285,8 @@ async function update(projectId, name, args) {
268
285
  for (let i = 0; i < args.length; i++) {
269
286
  if (args[i] === "--schedule" && i + 1 < args.length) schedule = args[++i];
270
287
  if (args[i] === "--schedule-remove") scheduleRemove = true;
271
- if (args[i] === "--timeout" && args[i + 1]) timeout = parseInt(args[++i]);
272
- if (args[i] === "--memory" && args[i + 1]) memory = parseInt(args[++i]);
288
+ if (args[i] === "--timeout") timeout = parseIntegerFlag("--timeout", args[++i], { min: 1 });
289
+ if (args[i] === "--memory") memory = parseIntegerFlag("--memory", args[++i], { min: 1 });
273
290
  }
274
291
 
275
292
  const updateOpts = {};
@@ -282,8 +299,10 @@ async function update(projectId, name, args) {
282
299
  if (memory !== undefined) updateOpts.memory = memory;
283
300
 
284
301
  if (Object.keys(updateOpts).length === 0) {
285
- console.error(JSON.stringify({ status: "error", message: "Provide at least one of: --schedule, --schedule-remove, --timeout, --memory" }));
286
- process.exit(1);
302
+ fail({
303
+ code: "BAD_USAGE",
304
+ message: "Provide at least one of: --schedule, --schedule-remove, --timeout, --memory",
305
+ });
287
306
  }
288
307
 
289
308
  try {
@@ -295,6 +314,7 @@ async function update(projectId, name, args) {
295
314
  }
296
315
 
297
316
  async function list(projectId) {
317
+ assertRequiredProject(projectId, "run402 functions list <project_id>");
298
318
  try {
299
319
  const data = await getSdk().functions.list(projectId);
300
320
  console.log(JSON.stringify(data, null, 2));
@@ -304,6 +324,7 @@ async function list(projectId) {
304
324
  }
305
325
 
306
326
  async function deleteFunction(projectId, name) {
327
+ assertRequiredProjectAndName(projectId, name, "run402 functions delete <project_id> <name>");
307
328
  try {
308
329
  await getSdk().functions.delete(projectId, name);
309
330
  console.log(JSON.stringify({ status: "ok", message: `Function '${name}' deleted.` }));
@@ -314,7 +335,8 @@ async function deleteFunction(projectId, name) {
314
335
 
315
336
  export async function run(sub, args) {
316
337
  if (!sub || sub === '--help' || sub === '-h') { console.log(HELP); process.exit(0); }
317
- if (Array.isArray(args) && (args.includes("--help") || args.includes("-h"))) {
338
+ args = normalizeArgv(args);
339
+ if (Array.isArray(args) && hasHelp(args)) {
318
340
  console.log(SUB_HELP[sub] || HELP);
319
341
  process.exit(0);
320
342
  }
@@ -331,3 +353,24 @@ export async function run(sub, args) {
331
353
  process.exit(1);
332
354
  }
333
355
  }
356
+
357
+ function assertRequiredProject(projectId, usage) {
358
+ if (!projectId || String(projectId).startsWith("-")) {
359
+ fail({
360
+ code: "BAD_USAGE",
361
+ message: "Missing <project_id>.",
362
+ hint: usage,
363
+ });
364
+ }
365
+ }
366
+
367
+ function assertRequiredProjectAndName(projectId, name, usage) {
368
+ assertRequiredProject(projectId, usage);
369
+ if (!name || String(name).startsWith("-")) {
370
+ fail({
371
+ code: "BAD_USAGE",
372
+ message: "Missing <name>.",
373
+ hint: usage,
374
+ });
375
+ }
376
+ }
package/lib/image.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { writeFileSync } from "fs";
2
2
  import { getSdk } from "./sdk.mjs";
3
- import { reportSdkError } from "./sdk-errors.mjs";
3
+ import { reportSdkError, fail } from "./sdk-errors.mjs";
4
4
 
5
5
  const HELP = `run402 image — Generate AI images via x402 micropayments
6
6
 
@@ -49,7 +49,13 @@ export async function run(sub, args) {
49
49
  i++;
50
50
  }
51
51
 
52
- if (!opts.prompt) { console.error(JSON.stringify({ status: "error", message: "Prompt required. Usage: run402 image generate \"your prompt\"" })); process.exit(1); }
52
+ if (!opts.prompt) {
53
+ fail({
54
+ code: "BAD_USAGE",
55
+ message: "Prompt required.",
56
+ hint: 'run402 image generate "your prompt"',
57
+ });
58
+ }
53
59
 
54
60
  try {
55
61
  const data = await getSdk().ai.generateImage({ prompt: opts.prompt, aspect: opts.aspect });
package/lib/message.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { allowanceAuthHeaders } from "./config.mjs";
2
2
  import { getSdk } from "./sdk.mjs";
3
- import { reportSdkError } from "./sdk-errors.mjs";
3
+ import { reportSdkError, fail } from "./sdk-errors.mjs";
4
4
 
5
5
  const HELP = `run402 message — Send messages to Run402 developers
6
6
 
@@ -16,7 +16,9 @@ Examples:
16
16
  `;
17
17
 
18
18
  async function send(text) {
19
- if (!text) { console.error(JSON.stringify({ status: "error", message: "Missing message text" })); process.exit(1); }
19
+ if (!text) {
20
+ fail({ code: "BAD_USAGE", message: "Missing message text." });
21
+ }
20
22
  // Preserve the aggressive early exit when no allowance is configured.
21
23
  allowanceAuthHeaders("/message/v1");
22
24