run402 1.69.4 → 1.69.6

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 (47) hide show
  1. package/lib/agent.mjs +37 -11
  2. package/lib/ai.mjs +16 -15
  3. package/lib/allowance.mjs +23 -7
  4. package/lib/apps.mjs +92 -40
  5. package/lib/argparse.mjs +60 -1
  6. package/lib/auth.mjs +62 -1
  7. package/lib/billing.mjs +83 -25
  8. package/lib/blob.mjs +2 -2
  9. package/lib/cdn.mjs +21 -8
  10. package/lib/contracts.mjs +123 -33
  11. package/lib/domains.mjs +22 -11
  12. package/lib/email.mjs +68 -61
  13. package/lib/image.mjs +17 -8
  14. package/lib/message.mjs +4 -1
  15. package/lib/secrets.mjs +29 -12
  16. package/lib/sender-domain.mjs +32 -14
  17. package/lib/sites.mjs +39 -16
  18. package/lib/subdomains.mjs +31 -35
  19. package/lib/tier.mjs +26 -4
  20. package/lib/webhooks.mjs +46 -33
  21. package/package.json +1 -1
  22. package/sdk/dist/errors.d.ts +4 -1
  23. package/sdk/dist/errors.d.ts.map +1 -1
  24. package/sdk/dist/errors.js +16 -2
  25. package/sdk/dist/errors.js.map +1 -1
  26. package/sdk/dist/namespaces/ai.d.ts.map +1 -1
  27. package/sdk/dist/namespaces/ai.js +7 -2
  28. package/sdk/dist/namespaces/ai.js.map +1 -1
  29. package/sdk/dist/namespaces/billing.d.ts.map +1 -1
  30. package/sdk/dist/namespaces/billing.js +20 -17
  31. package/sdk/dist/namespaces/billing.js.map +1 -1
  32. package/sdk/dist/namespaces/contracts.d.ts.map +1 -1
  33. package/sdk/dist/namespaces/contracts.js +21 -4
  34. package/sdk/dist/namespaces/contracts.js.map +1 -1
  35. package/sdk/dist/namespaces/deploy.js +9 -0
  36. package/sdk/dist/namespaces/deploy.js.map +1 -1
  37. package/sdk/dist/namespaces/functions.d.ts.map +1 -1
  38. package/sdk/dist/namespaces/functions.js +20 -0
  39. package/sdk/dist/namespaces/functions.js.map +1 -1
  40. package/sdk/dist/retry.d.ts +2 -1
  41. package/sdk/dist/retry.d.ts.map +1 -1
  42. package/sdk/dist/retry.js +2 -1
  43. package/sdk/dist/retry.js.map +1 -1
  44. package/sdk/dist/validation.d.ts +3 -0
  45. package/sdk/dist/validation.d.ts.map +1 -1
  46. package/sdk/dist/validation.js +15 -0
  47. package/sdk/dist/validation.js.map +1 -1
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 { assertKnownFlags, flagValue, normalizeArgv, parseIntegerFlag, positionalArgs } from "./argparse.mjs";
3
4
 
4
5
  const HELP = `run402 billing — Email billing accounts, Stripe tier checkout, email packs
5
6
 
@@ -121,15 +122,31 @@ Examples:
121
122
  `,
122
123
  };
123
124
 
124
- function parseFlag(args, flag) {
125
- for (let i = 0; i < args.length; i++) {
126
- if (args[i] === flag && args[i + 1]) return args[i + 1];
125
+ function requireSingleBillingIdentifier(email, wallet) {
126
+ if (email && wallet) {
127
+ fail({
128
+ code: "BAD_USAGE",
129
+ message: "Provide either --email or --wallet, not both.",
130
+ hint: "[--email <e> | --wallet <w>]",
131
+ });
132
+ }
133
+ if (!email && !wallet) {
134
+ fail({
135
+ code: "BAD_USAGE",
136
+ message: "Must provide --email or --wallet",
137
+ hint: "[--email <e> | --wallet <w>]",
138
+ });
127
139
  }
128
- return null;
129
140
  }
130
141
 
131
142
  async function createEmail(args) {
132
- const email = args[0];
143
+ const parsedArgs = normalizeArgv(args);
144
+ assertKnownFlags(parsedArgs, ["--help", "-h"]);
145
+ const positionals = positionalArgs(parsedArgs);
146
+ const email = positionals[0];
147
+ if (positionals.length > 1) {
148
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for billing create-email: ${positionals[1]}` });
149
+ }
133
150
  if (!email) {
134
151
  fail({
135
152
  code: "BAD_USAGE",
@@ -146,8 +163,14 @@ async function createEmail(args) {
146
163
  }
147
164
 
148
165
  async function linkWallet(args) {
149
- const accountId = args[0];
150
- const wallet = args[1];
166
+ const parsedArgs = normalizeArgv(args);
167
+ assertKnownFlags(parsedArgs, ["--help", "-h"]);
168
+ const positionals = positionalArgs(parsedArgs);
169
+ const accountId = positionals[0];
170
+ const wallet = positionals[1];
171
+ if (positionals.length > 2) {
172
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for billing link-wallet: ${positionals[2]}` });
173
+ }
151
174
  if (!accountId || !wallet) {
152
175
  fail({
153
176
  code: "BAD_USAGE",
@@ -164,7 +187,14 @@ async function linkWallet(args) {
164
187
  }
165
188
 
166
189
  async function tierCheckout(args) {
167
- const tier = args[0];
190
+ const parsedArgs = normalizeArgv(args);
191
+ const valueFlags = ["--email", "--wallet"];
192
+ assertKnownFlags(parsedArgs, [...valueFlags, "--help", "-h"], valueFlags);
193
+ const positionals = positionalArgs(parsedArgs, valueFlags);
194
+ const tier = positionals[0];
195
+ if (positionals.length > 1) {
196
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for billing tier-checkout: ${positionals[1]}` });
197
+ }
168
198
  if (!tier) {
169
199
  fail({
170
200
  code: "BAD_USAGE",
@@ -172,11 +202,9 @@ async function tierCheckout(args) {
172
202
  hint: "run402 billing tier-checkout <tier> [--email <e> | --wallet <w>]",
173
203
  });
174
204
  }
175
- const email = parseFlag(args, "--email");
176
- const wallet = parseFlag(args, "--wallet");
177
- if (!email && !wallet) {
178
- fail({ code: "BAD_USAGE", message: "Must provide --email or --wallet" });
179
- }
205
+ const email = flagValue(parsedArgs, "--email");
206
+ const wallet = flagValue(parsedArgs, "--wallet");
207
+ requireSingleBillingIdentifier(email, wallet);
180
208
  try {
181
209
  const data = await getSdk().billing.tierCheckout(tier, { email: email ?? undefined, wallet: wallet ?? undefined });
182
210
  console.log(JSON.stringify(data, null, 2));
@@ -186,11 +214,16 @@ async function tierCheckout(args) {
186
214
  }
187
215
 
188
216
  async function buyPack(args) {
189
- const email = parseFlag(args, "--email");
190
- const wallet = parseFlag(args, "--wallet");
191
- if (!email && !wallet) {
192
- fail({ code: "BAD_USAGE", message: "Must provide --email or --wallet" });
217
+ const parsedArgs = normalizeArgv(args);
218
+ const valueFlags = ["--email", "--wallet"];
219
+ assertKnownFlags(parsedArgs, [...valueFlags, "--help", "-h"], valueFlags);
220
+ const extra = positionalArgs(parsedArgs, valueFlags);
221
+ if (extra.length > 0) {
222
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for billing buy-email-pack: ${extra[0]}` });
193
223
  }
224
+ const email = flagValue(parsedArgs, "--email");
225
+ const wallet = flagValue(parsedArgs, "--wallet");
226
+ requireSingleBillingIdentifier(email, wallet);
194
227
  try {
195
228
  const data = await getSdk().billing.buyEmailPack({ email: email ?? undefined, wallet: wallet ?? undefined });
196
229
  console.log(JSON.stringify(data, null, 2));
@@ -200,8 +233,15 @@ async function buyPack(args) {
200
233
  }
201
234
 
202
235
  async function autoRecharge(args) {
203
- const accountId = args[0];
204
- const state = args[1];
236
+ const parsedArgs = normalizeArgv(args);
237
+ const valueFlags = ["--threshold"];
238
+ assertKnownFlags(parsedArgs, [...valueFlags, "--help", "-h"], valueFlags);
239
+ const positionals = positionalArgs(parsedArgs, valueFlags);
240
+ const accountId = positionals[0];
241
+ const state = positionals[1];
242
+ if (positionals.length > 2) {
243
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for billing auto-recharge: ${positionals[2]}` });
244
+ }
205
245
  if (!accountId || !state || !["on", "off"].includes(state)) {
206
246
  fail({
207
247
  code: "BAD_USAGE",
@@ -209,12 +249,15 @@ async function autoRecharge(args) {
209
249
  hint: "run402 billing auto-recharge <account_id> <on|off> [--threshold <n>]",
210
250
  });
211
251
  }
212
- const thresholdStr = parseFlag(args, "--threshold");
252
+ const thresholdStr = flagValue(parsedArgs, "--threshold");
253
+ const threshold = parsedArgs.includes("--threshold")
254
+ ? parseIntegerFlag("--threshold", thresholdStr, { min: 0 })
255
+ : undefined;
213
256
  try {
214
257
  await getSdk().billing.setAutoRecharge({
215
258
  billingAccountId: accountId,
216
259
  enabled: state === "on",
217
- threshold: thresholdStr ? Number(thresholdStr) : undefined,
260
+ threshold,
218
261
  });
219
262
  console.log(JSON.stringify({ status: "ok", billing_account_id: accountId, enabled: state === "on" }));
220
263
  } catch (err) {
@@ -223,7 +266,13 @@ async function autoRecharge(args) {
223
266
  }
224
267
 
225
268
  async function balance(args) {
226
- const id = args[0];
269
+ const parsedArgs = normalizeArgv(args);
270
+ assertKnownFlags(parsedArgs, ["--help", "-h"]);
271
+ const positionals = positionalArgs(parsedArgs);
272
+ const id = positionals[0];
273
+ if (positionals.length > 1) {
274
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for billing balance: ${positionals[1]}` });
275
+ }
227
276
  if (!id) {
228
277
  fail({
229
278
  code: "BAD_USAGE",
@@ -240,7 +289,14 @@ async function balance(args) {
240
289
  }
241
290
 
242
291
  async function history(args) {
243
- const id = args[0];
292
+ const parsedArgs = normalizeArgv(args);
293
+ const valueFlags = ["--limit"];
294
+ assertKnownFlags(parsedArgs, [...valueFlags, "--help", "-h"], valueFlags);
295
+ const positionals = positionalArgs(parsedArgs, valueFlags);
296
+ const id = positionals[0];
297
+ if (positionals.length > 1) {
298
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for billing history: ${positionals[1]}` });
299
+ }
244
300
  if (!id) {
245
301
  fail({
246
302
  code: "BAD_USAGE",
@@ -248,9 +304,11 @@ async function history(args) {
248
304
  hint: "run402 billing history <email-or-wallet> [--limit <n>]",
249
305
  });
250
306
  }
251
- const limit = parseFlag(args, "--limit") || "50";
307
+ const limit = parsedArgs.includes("--limit")
308
+ ? parseIntegerFlag("--limit", flagValue(parsedArgs, "--limit"), { min: 1 })
309
+ : 50;
252
310
  try {
253
- const data = await getSdk().billing.getHistory(id, Number(limit));
311
+ const data = await getSdk().billing.getHistory(id, limit);
254
312
  console.log(JSON.stringify(data, null, 2));
255
313
  } catch (err) {
256
314
  reportSdkError(err);
package/lib/blob.mjs CHANGED
@@ -438,8 +438,8 @@ async function put(projectId, argv) {
438
438
  const resolvedId = resolveProjectId(opts.project);
439
439
 
440
440
  if (opts.positional.length === 0) die("At least one file path is required");
441
- if (opts.immutable && opts.positional.length > 1 && opts.key && !opts.key.endsWith("/")) {
442
- die("--key with --immutable across multiple files requires a directory prefix (ending with /)");
441
+ if (opts.positional.length > 1 && opts.key && !opts.key.endsWith("/")) {
442
+ die("--key across multiple files requires a directory prefix (ending with /)");
443
443
  }
444
444
 
445
445
  const results = [];
package/lib/cdn.mjs CHANGED
@@ -16,6 +16,7 @@
16
16
  import { resolveProjectId } from "./config.mjs";
17
17
  import { getSdk } from "./sdk.mjs";
18
18
  import { reportSdkError, fail } from "./sdk-errors.mjs";
19
+ import { assertKnownFlags, flagValue, normalizeArgv, parseIntegerFlag, positionalArgs } from "./argparse.mjs";
19
20
 
20
21
  const HELP = `run402 cdn — CloudFront CDN diagnostics for public blob URLs
21
22
 
@@ -73,14 +74,19 @@ function die(msg, exit_code = 1) {
73
74
  }
74
75
 
75
76
  function parseArgs(args) {
76
- const opts = { positional: [] };
77
- for (let i = 0; i < args.length; i++) {
78
- const a = args[i];
79
- if (a === "--sha") opts.sha = args[++i];
80
- else if (a === "--timeout") opts.timeout = Number(args[++i]);
81
- else if (a === "--project") opts.project = args[++i];
82
- else if (a.startsWith("--")) die(`Unknown option: ${a}`);
83
- else opts.positional.push(a);
77
+ const normalized = normalizeArgv(args);
78
+ const valueFlags = ["--sha", "--timeout", "--project"];
79
+ assertKnownFlags(normalized, [...valueFlags, "--help", "-h"], valueFlags);
80
+ const opts = {
81
+ positional: positionalArgs(normalized, valueFlags),
82
+ sha: flagValue(normalized, "--sha"),
83
+ timeout: normalized.includes("--timeout")
84
+ ? parseIntegerFlag("--timeout", flagValue(normalized, "--timeout"), { min: 1 })
85
+ : undefined,
86
+ project: flagValue(normalized, "--project"),
87
+ };
88
+ if (opts.positional.length > 1) {
89
+ die(`Unexpected argument for cdn wait-fresh: ${opts.positional[1]}`);
84
90
  }
85
91
  return opts;
86
92
  }
@@ -92,6 +98,13 @@ async function waitFresh(projectId, argv) {
92
98
  if (opts.positional.length === 0) die("URL required");
93
99
  const url = opts.positional[0];
94
100
  if (!opts.sha) die("--sha is required");
101
+ if (!/^[a-fA-F0-9]{64}$/.test(opts.sha)) {
102
+ fail({
103
+ code: "BAD_FLAG",
104
+ message: "--sha must be a 64-character hex SHA-256 digest",
105
+ details: { flag: "--sha", value: opts.sha },
106
+ });
107
+ }
95
108
 
96
109
  const timeoutMs = (opts.timeout ?? 60) * 1000;
97
110
  try {
package/lib/contracts.mjs CHANGED
@@ -1,5 +1,13 @@
1
1
  import { getSdk } from "./sdk.mjs";
2
2
  import { reportSdkError, fail, parseFlagJson } from "./sdk-errors.mjs";
3
+ import {
4
+ assertAllowedValue,
5
+ assertKnownFlags,
6
+ flagValue,
7
+ normalizeArgv,
8
+ positionalArgs,
9
+ validateEvmAddress,
10
+ } from "./argparse.mjs";
3
11
 
4
12
  const HELP = `run402 contracts — KMS-backed Ethereum wallets for smart-contract calls
5
13
 
@@ -126,32 +134,44 @@ Examples:
126
134
  `,
127
135
  };
128
136
 
129
- function parseFlag(args, flag) {
130
- for (let i = 0; i < args.length; i++) {
131
- if (args[i] === flag && args[i + 1]) return args[i + 1];
132
- }
133
- return null;
134
- }
135
137
  function hasFlag(args, flag) {
136
138
  return args.includes(flag);
137
139
  }
140
+ function validateWeiFlag(flag, value) {
141
+ if (!/^\d+$/.test(String(value))) {
142
+ fail({
143
+ code: "BAD_FLAG",
144
+ message: `${flag} must be a decimal non-negative integer string in wei`,
145
+ details: { flag, value: String(value) },
146
+ });
147
+ }
148
+ }
138
149
 
139
150
  async function provisionWallet(projectId, args) {
140
- const chain = parseFlag(args, "--chain");
151
+ const parsedArgs = normalizeArgv(args);
152
+ const valueFlags = ["--chain", "--recovery"];
153
+ assertKnownFlags(parsedArgs, [...valueFlags, "--yes", "--help", "-h"], valueFlags);
154
+ const extra = positionalArgs(parsedArgs, valueFlags);
155
+ if (extra.length > 0) {
156
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for contracts provision-wallet: ${extra[0]}` });
157
+ }
158
+ const chain = flagValue(parsedArgs, "--chain");
141
159
  if (!chain) {
142
160
  fail({
143
161
  code: "BAD_USAGE",
144
162
  message: "Missing --chain (base-mainnet or base-sepolia)",
145
163
  });
146
164
  }
147
- const recovery = parseFlag(args, "--recovery");
165
+ assertAllowedValue(chain, ["base-mainnet", "base-sepolia"], "--chain");
166
+ const recovery = flagValue(parsedArgs, "--recovery");
167
+ if (recovery) validateEvmAddress(recovery, "--recovery");
148
168
  // Soft default of one wallet — confirm if project already has one.
149
169
  let activeWallets = null;
150
170
  try {
151
171
  const list = await getSdk().contracts.listWallets(projectId);
152
172
  activeWallets = (list.wallets || []).filter((w) => w.status === "active").length;
153
173
  } catch { /* best-effort */ }
154
- if (activeWallets !== null && activeWallets >= 1 && !hasFlag(args, "--yes")) {
174
+ if (activeWallets !== null && activeWallets >= 1 && !hasFlag(parsedArgs, "--yes")) {
155
175
  fail({
156
176
  code: "CONFIRMATION_REQUIRED",
157
177
  message: `This project already has ${activeWallets} active wallet(s). Adding another costs $0.04/day each ($1.20/month). Re-run with --yes to confirm.`,
@@ -170,7 +190,13 @@ async function provisionWallet(projectId, args) {
170
190
  }
171
191
  }
172
192
 
173
- async function getWallet(projectId, walletId) {
193
+ async function getWallet(projectId, walletId, args = []) {
194
+ const parsedArgs = normalizeArgv(args);
195
+ assertKnownFlags(parsedArgs, ["--help", "-h"]);
196
+ const extra = positionalArgs(parsedArgs);
197
+ if (extra.length > 0) {
198
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for contracts get-wallet: ${extra[0]}` });
199
+ }
174
200
  try {
175
201
  const data = await getSdk().contracts.getWallet(projectId, walletId);
176
202
  console.log(JSON.stringify(data, null, 2));
@@ -179,7 +205,13 @@ async function getWallet(projectId, walletId) {
179
205
  }
180
206
  }
181
207
 
182
- async function listWallets(projectId) {
208
+ async function listWallets(projectId, args = []) {
209
+ const parsedArgs = normalizeArgv(args);
210
+ assertKnownFlags(parsedArgs, ["--help", "-h"]);
211
+ const extra = positionalArgs(parsedArgs);
212
+ if (extra.length > 0) {
213
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for contracts list-wallets: ${extra[0]}` });
214
+ }
183
215
  try {
184
216
  const data = await getSdk().contracts.listWallets(projectId);
185
217
  console.log(JSON.stringify(data, null, 2));
@@ -189,14 +221,25 @@ async function listWallets(projectId) {
189
221
  }
190
222
 
191
223
  async function setRecovery(projectId, walletId, args) {
192
- const clear = hasFlag(args, "--clear");
193
- const address = parseFlag(args, "--address");
224
+ const parsedArgs = normalizeArgv(args);
225
+ const valueFlags = ["--address"];
226
+ assertKnownFlags(parsedArgs, [...valueFlags, "--clear", "--help", "-h"], valueFlags);
227
+ const extra = positionalArgs(parsedArgs, valueFlags);
228
+ if (extra.length > 0) {
229
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for contracts set-recovery: ${extra[0]}` });
230
+ }
231
+ const clear = hasFlag(parsedArgs, "--clear");
232
+ const address = flagValue(parsedArgs, "--address");
233
+ if (clear && address) {
234
+ fail({ code: "BAD_USAGE", message: "Provide either --address or --clear, not both." });
235
+ }
194
236
  if (!clear && !address) {
195
237
  fail({
196
238
  code: "BAD_USAGE",
197
239
  message: "Provide --address 0x... or --clear",
198
240
  });
199
241
  }
242
+ if (address) validateEvmAddress(address, "--address");
200
243
  try {
201
244
  await getSdk().contracts.setRecovery(projectId, walletId, clear ? null : address);
202
245
  console.log(JSON.stringify({ status: "ok", wallet_id: walletId, recovery_address: clear ? null : address }));
@@ -206,10 +249,18 @@ async function setRecovery(projectId, walletId, args) {
206
249
  }
207
250
 
208
251
  async function setAlert(projectId, walletId, args) {
209
- const threshold = parseFlag(args, "--threshold-wei");
252
+ const parsedArgs = normalizeArgv(args);
253
+ const valueFlags = ["--threshold-wei"];
254
+ assertKnownFlags(parsedArgs, [...valueFlags, "--help", "-h"], valueFlags);
255
+ const extra = positionalArgs(parsedArgs, valueFlags);
256
+ if (extra.length > 0) {
257
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for contracts set-alert: ${extra[0]}` });
258
+ }
259
+ const threshold = flagValue(parsedArgs, "--threshold-wei");
210
260
  if (!threshold) {
211
261
  fail({ code: "BAD_USAGE", message: "Missing --threshold-wei <n>" });
212
262
  }
263
+ validateWeiFlag("--threshold-wei", threshold);
213
264
  try {
214
265
  await getSdk().contracts.setLowBalanceAlert(projectId, walletId, threshold);
215
266
  console.log(JSON.stringify({ status: "ok", wallet_id: walletId, threshold_wei: threshold }));
@@ -219,13 +270,20 @@ async function setAlert(projectId, walletId, args) {
219
270
  }
220
271
 
221
272
  async function call(projectId, walletId, args) {
222
- const to = parseFlag(args, "--to");
223
- const abi = parseFlag(args, "--abi");
224
- const fn = parseFlag(args, "--fn");
225
- const argsJson = parseFlag(args, "--args");
226
- const value = parseFlag(args, "--value-wei");
227
- const chain = parseFlag(args, "--chain") || "base-mainnet";
228
- const idempotency = parseFlag(args, "--idempotency-key");
273
+ const parsedArgs = normalizeArgv(args);
274
+ const valueFlags = ["--to", "--abi", "--fn", "--args", "--value-wei", "--chain", "--idempotency-key"];
275
+ assertKnownFlags(parsedArgs, [...valueFlags, "--help", "-h"], valueFlags);
276
+ const extra = positionalArgs(parsedArgs, valueFlags);
277
+ if (extra.length > 0) {
278
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for contracts call: ${extra[0]}` });
279
+ }
280
+ const to = flagValue(parsedArgs, "--to");
281
+ const abi = flagValue(parsedArgs, "--abi");
282
+ const fn = flagValue(parsedArgs, "--fn");
283
+ const argsJson = flagValue(parsedArgs, "--args");
284
+ const value = flagValue(parsedArgs, "--value-wei");
285
+ const chain = flagValue(parsedArgs, "--chain") || "base-mainnet";
286
+ const idempotency = flagValue(parsedArgs, "--idempotency-key");
229
287
  if (!to || !abi || !fn || !argsJson) {
230
288
  fail({
231
289
  code: "BAD_USAGE",
@@ -233,8 +291,11 @@ async function call(projectId, walletId, args) {
233
291
  hint: "Cost: chain gas + $0.000005 KMS sign fee.",
234
292
  });
235
293
  }
294
+ assertAllowedValue(chain, ["base-mainnet", "base-sepolia"], "--chain");
295
+ if (value !== null) validateWeiFlag("--value-wei", value);
236
296
  const abiFragment = parseFlagJson("--abi", abi);
237
297
  const callArgs = parseFlagJson("--args", argsJson);
298
+ validateEvmAddress(to, "--to");
238
299
  try {
239
300
  const data = await getSdk().contracts.call(projectId, {
240
301
  walletId,
@@ -253,19 +314,28 @@ async function call(projectId, walletId, args) {
253
314
  }
254
315
 
255
316
  async function read(args) {
256
- const chain = parseFlag(args, "--chain");
257
- const to = parseFlag(args, "--to");
258
- const abi = parseFlag(args, "--abi");
259
- const fn = parseFlag(args, "--fn");
260
- const argsJson = parseFlag(args, "--args");
317
+ const parsedArgs = normalizeArgv(args);
318
+ const valueFlags = ["--chain", "--to", "--abi", "--fn", "--args"];
319
+ assertKnownFlags(parsedArgs, [...valueFlags, "--help", "-h"], valueFlags);
320
+ const extra = positionalArgs(parsedArgs, valueFlags);
321
+ if (extra.length > 0) {
322
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for contracts read: ${extra[0]}` });
323
+ }
324
+ const chain = flagValue(parsedArgs, "--chain");
325
+ const to = flagValue(parsedArgs, "--to");
326
+ const abi = flagValue(parsedArgs, "--abi");
327
+ const fn = flagValue(parsedArgs, "--fn");
328
+ const argsJson = flagValue(parsedArgs, "--args");
261
329
  if (!chain || !to || !abi || !fn || !argsJson) {
262
330
  fail({
263
331
  code: "BAD_USAGE",
264
332
  message: "Required flags: --chain, --to, --abi, --fn, --args",
265
333
  });
266
334
  }
335
+ assertAllowedValue(chain, ["base-mainnet", "base-sepolia"], "--chain");
267
336
  const abiFragment = parseFlagJson("--abi", abi);
268
337
  const callArgs = parseFlagJson("--args", argsJson);
338
+ validateEvmAddress(to, "--to");
269
339
  try {
270
340
  const data = await getSdk().contracts.read({
271
341
  chain,
@@ -280,7 +350,13 @@ async function read(args) {
280
350
  }
281
351
  }
282
352
 
283
- async function status(projectId, callId) {
353
+ async function status(projectId, callId, args = []) {
354
+ const parsedArgs = normalizeArgv(args);
355
+ assertKnownFlags(parsedArgs, ["--help", "-h"]);
356
+ const extra = positionalArgs(parsedArgs);
357
+ if (extra.length > 0) {
358
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for contracts status: ${extra[0]}` });
359
+ }
284
360
  try {
285
361
  const data = await getSdk().contracts.callStatus(projectId, callId);
286
362
  console.log(JSON.stringify(data, null, 2));
@@ -290,14 +366,22 @@ async function status(projectId, callId) {
290
366
  }
291
367
 
292
368
  async function drain(projectId, walletId, args) {
293
- const to = parseFlag(args, "--to");
294
- if (!to || !hasFlag(args, "--confirm")) {
369
+ const parsedArgs = normalizeArgv(args);
370
+ const valueFlags = ["--to"];
371
+ assertKnownFlags(parsedArgs, [...valueFlags, "--confirm", "--help", "-h"], valueFlags);
372
+ const extra = positionalArgs(parsedArgs, valueFlags);
373
+ if (extra.length > 0) {
374
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for contracts drain: ${extra[0]}` });
375
+ }
376
+ const to = flagValue(parsedArgs, "--to");
377
+ if (!to || !hasFlag(parsedArgs, "--confirm")) {
295
378
  fail({
296
379
  code: "BAD_USAGE",
297
380
  message: "Required: --to 0x... and --confirm.",
298
381
  hint: "Cost: chain gas + $0.000005 KMS sign fee.",
299
382
  });
300
383
  }
384
+ validateEvmAddress(to, "--to");
301
385
  try {
302
386
  const data = await getSdk().contracts.drain(projectId, walletId, to);
303
387
  console.log(JSON.stringify(data, null, 2));
@@ -307,7 +391,13 @@ async function drain(projectId, walletId, args) {
307
391
  }
308
392
 
309
393
  async function deleteWallet(projectId, walletId, args) {
310
- if (!hasFlag(args, "--confirm")) {
394
+ const parsedArgs = normalizeArgv(args);
395
+ assertKnownFlags(parsedArgs, ["--confirm", "--help", "-h"]);
396
+ const extra = positionalArgs(parsedArgs);
397
+ if (extra.length > 0) {
398
+ fail({ code: "BAD_USAGE", message: `Unexpected argument for contracts delete: ${extra[0]}` });
399
+ }
400
+ if (!hasFlag(parsedArgs, "--confirm")) {
311
401
  fail({ code: "BAD_USAGE", message: "Required: --confirm" });
312
402
  }
313
403
  try {
@@ -323,13 +413,13 @@ export async function run(sub, args) {
323
413
  if (Array.isArray(args) && (args.includes("--help") || args.includes("-h"))) { console.log(SUB_HELP[sub] || HELP); process.exit(0); }
324
414
  switch (sub) {
325
415
  case "provision-wallet": await provisionWallet(args[0], args.slice(1)); break;
326
- case "get-wallet": await getWallet(args[0], args[1]); break;
327
- case "list-wallets": await listWallets(args[0]); break;
416
+ case "get-wallet": await getWallet(args[0], args[1], args.slice(2)); break;
417
+ case "list-wallets": await listWallets(args[0], args.slice(1)); break;
328
418
  case "set-recovery": await setRecovery(args[0], args[1], args.slice(2)); break;
329
419
  case "set-alert": await setAlert(args[0], args[1], args.slice(2)); break;
330
420
  case "call": await call(args[0], args[1], args.slice(2)); break;
331
421
  case "read": await read(args); break;
332
- case "status": await status(args[0], args[1]); break;
422
+ case "status": await status(args[0], args[1], args.slice(2)); break;
333
423
  case "drain": await drain(args[0], args[1], args.slice(2)); break;
334
424
  case "delete": await deleteWallet(args[0], args[1], args.slice(2)); break;
335
425
  default:
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.`,