run402 1.69.5 → 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.
- package/lib/agent.mjs +37 -11
- package/lib/apps.mjs +92 -40
- package/lib/argparse.mjs +53 -1
- package/lib/billing.mjs +65 -24
- package/lib/blob.mjs +2 -2
- package/lib/cdn.mjs +21 -8
- package/lib/contracts.mjs +112 -33
- package/lib/domains.mjs +22 -11
- package/lib/image.mjs +17 -8
- package/lib/message.mjs +4 -1
- package/lib/secrets.mjs +29 -12
- package/lib/sender-domain.mjs +32 -14
- package/lib/sites.mjs +39 -16
- package/lib/subdomains.mjs +31 -35
- package/lib/tier.mjs +26 -4
- package/package.json +1 -1
- package/sdk/dist/namespaces/ai.d.ts.map +1 -1
- package/sdk/dist/namespaces/ai.js +7 -2
- package/sdk/dist/namespaces/ai.js.map +1 -1
- package/sdk/dist/namespaces/contracts.d.ts.map +1 -1
- package/sdk/dist/namespaces/contracts.js +17 -4
- package/sdk/dist/namespaces/contracts.js.map +1 -1
- package/sdk/dist/validation.d.ts +2 -0
- package/sdk/dist/validation.d.ts.map +1 -1
- package/sdk/dist/validation.js +10 -0
- package/sdk/dist/validation.js.map +1 -1
package/lib/agent.mjs
CHANGED
|
@@ -1,7 +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 { validateWebhookUrl } from "./argparse.mjs";
|
|
4
|
+
import { assertKnownFlags, flagValue, normalizeArgv, positionalArgs, validateWebhookUrl } from "./argparse.mjs";
|
|
5
5
|
|
|
6
6
|
const HELP = `run402 agent — Manage agent identity
|
|
7
7
|
|
|
@@ -76,12 +76,20 @@ contact email. Requires assurance_level=email_verified first.
|
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
async function contact(args) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
79
|
+
const parsedArgs = normalizeArgv(args);
|
|
80
|
+
const valueFlags = ["--name", "--email", "--webhook"];
|
|
81
|
+
assertKnownFlags(parsedArgs, [...valueFlags, "--help", "-h"], valueFlags);
|
|
82
|
+
const extra = positionalArgs(parsedArgs, valueFlags);
|
|
83
|
+
if (extra.length > 0) {
|
|
84
|
+
fail({
|
|
85
|
+
code: "BAD_USAGE",
|
|
86
|
+
message: `Unexpected argument for agent contact: ${extra[0]}`,
|
|
87
|
+
hint: "Use `run402 agent contact --name <name> [--email <email>] [--webhook <url>]`.",
|
|
88
|
+
});
|
|
84
89
|
}
|
|
90
|
+
const name = flagValue(parsedArgs, "--name");
|
|
91
|
+
const email = flagValue(parsedArgs, "--email");
|
|
92
|
+
const webhook = flagValue(parsedArgs, "--webhook");
|
|
85
93
|
if (!name) {
|
|
86
94
|
fail({ code: "BAD_USAGE", message: "Missing --name <name>" });
|
|
87
95
|
}
|
|
@@ -104,7 +112,13 @@ async function contact(args) {
|
|
|
104
112
|
}
|
|
105
113
|
}
|
|
106
114
|
|
|
107
|
-
async function status() {
|
|
115
|
+
async function status(args = []) {
|
|
116
|
+
const parsedArgs = normalizeArgv(args);
|
|
117
|
+
assertKnownFlags(parsedArgs, ["--help", "-h"]);
|
|
118
|
+
const extra = positionalArgs(parsedArgs);
|
|
119
|
+
if (extra.length > 0) {
|
|
120
|
+
fail({ code: "BAD_USAGE", message: `Unexpected argument for agent status: ${extra[0]}` });
|
|
121
|
+
}
|
|
108
122
|
allowanceAuthHeaders("/agent/v1/contact/status");
|
|
109
123
|
|
|
110
124
|
try {
|
|
@@ -115,7 +129,13 @@ async function status() {
|
|
|
115
129
|
}
|
|
116
130
|
}
|
|
117
131
|
|
|
118
|
-
async function verifyEmail() {
|
|
132
|
+
async function verifyEmail(args = []) {
|
|
133
|
+
const parsedArgs = normalizeArgv(args);
|
|
134
|
+
assertKnownFlags(parsedArgs, ["--help", "-h"]);
|
|
135
|
+
const extra = positionalArgs(parsedArgs);
|
|
136
|
+
if (extra.length > 0) {
|
|
137
|
+
fail({ code: "BAD_USAGE", message: `Unexpected argument for agent verify-email: ${extra[0]}` });
|
|
138
|
+
}
|
|
119
139
|
allowanceAuthHeaders("/agent/v1/contact/verify-email");
|
|
120
140
|
|
|
121
141
|
try {
|
|
@@ -127,7 +147,13 @@ async function verifyEmail() {
|
|
|
127
147
|
}
|
|
128
148
|
|
|
129
149
|
async function passkey(args) {
|
|
130
|
-
const
|
|
150
|
+
const parsedArgs = normalizeArgv(args);
|
|
151
|
+
assertKnownFlags(parsedArgs, ["--help", "-h"]);
|
|
152
|
+
const positionals = positionalArgs(parsedArgs);
|
|
153
|
+
const action = positionals[0];
|
|
154
|
+
if (positionals.length > 1) {
|
|
155
|
+
fail({ code: "BAD_USAGE", message: `Unexpected argument for agent passkey: ${positionals[1]}` });
|
|
156
|
+
}
|
|
131
157
|
if (action !== "enroll") {
|
|
132
158
|
fail({ code: "BAD_USAGE", message: "Usage: run402 agent passkey enroll" });
|
|
133
159
|
}
|
|
@@ -152,10 +178,10 @@ export async function run(sub, args) {
|
|
|
152
178
|
await contact(args);
|
|
153
179
|
return;
|
|
154
180
|
case "status":
|
|
155
|
-
await status();
|
|
181
|
+
await status(args);
|
|
156
182
|
return;
|
|
157
183
|
case "verify-email":
|
|
158
|
-
await verifyEmail();
|
|
184
|
+
await verifyEmail(args);
|
|
159
185
|
return;
|
|
160
186
|
case "passkey":
|
|
161
187
|
await passkey(args);
|
package/lib/apps.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { allowanceAuthHeaders, saveProject } from "./config.mjs";
|
|
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 apps — Browse and manage the app marketplace
|
|
6
7
|
|
|
@@ -9,7 +10,7 @@ Usage:
|
|
|
9
10
|
|
|
10
11
|
Subcommands:
|
|
11
12
|
browse [--tag <tag>] Browse public apps
|
|
12
|
-
fork <version_id> <name> [--
|
|
13
|
+
fork <version_id> <name> [--subdomain <name>]
|
|
13
14
|
Fork a published app into your own project
|
|
14
15
|
publish <id> [--description <desc>] [--tags <t1,t2>] [--visibility <v>] [--fork-allowed]
|
|
15
16
|
Publish a project as an app
|
|
@@ -22,7 +23,7 @@ Subcommands:
|
|
|
22
23
|
Examples:
|
|
23
24
|
run402 apps browse
|
|
24
25
|
run402 apps browse --tag auth
|
|
25
|
-
run402 apps fork ver_abc123 my-todo
|
|
26
|
+
run402 apps fork ver_abc123 my-todo
|
|
26
27
|
run402 apps publish prj_abc123 --description "Todo app" --tags todo,auth --visibility public --fork-allowed
|
|
27
28
|
run402 apps versions prj_abc123
|
|
28
29
|
run402 apps inspect ver_abc123
|
|
@@ -54,12 +55,11 @@ Arguments:
|
|
|
54
55
|
<name> Name for the forked project
|
|
55
56
|
|
|
56
57
|
Options:
|
|
57
|
-
--tier <tier> Tier for the new project (default: prototype)
|
|
58
58
|
--subdomain <name> Claim a subdomain for the forked project
|
|
59
59
|
|
|
60
60
|
Examples:
|
|
61
61
|
run402 apps fork ver_abc123 my-todo
|
|
62
|
-
run402 apps fork ver_abc123 my-todo --
|
|
62
|
+
run402 apps fork ver_abc123 my-todo --subdomain todo-v2
|
|
63
63
|
`,
|
|
64
64
|
publish: `run402 apps publish — Publish a project as an app
|
|
65
65
|
|
|
@@ -137,9 +137,16 @@ Examples:
|
|
|
137
137
|
};
|
|
138
138
|
|
|
139
139
|
async function browse(args) {
|
|
140
|
+
const parsedArgs = normalizeArgv(args);
|
|
141
|
+
const valueFlags = ["--tag"];
|
|
142
|
+
assertKnownFlags(parsedArgs, [...valueFlags, "--help", "-h"], valueFlags);
|
|
143
|
+
const extra = positionalArgs(parsedArgs, valueFlags);
|
|
144
|
+
if (extra.length > 0) {
|
|
145
|
+
fail({ code: "BAD_USAGE", message: `Unexpected argument for apps browse: ${extra[0]}` });
|
|
146
|
+
}
|
|
140
147
|
const tags = [];
|
|
141
|
-
for (let i = 0; i <
|
|
142
|
-
if (
|
|
148
|
+
for (let i = 0; i < parsedArgs.length; i++) {
|
|
149
|
+
if (parsedArgs[i] === "--tag") tags.push(parsedArgs[++i]);
|
|
143
150
|
}
|
|
144
151
|
try {
|
|
145
152
|
const data = await getSdk().apps.browse(tags.length > 0 ? tags : undefined);
|
|
@@ -150,18 +157,24 @@ async function browse(args) {
|
|
|
150
157
|
}
|
|
151
158
|
|
|
152
159
|
async function fork(versionId, name, args) {
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
160
|
+
const parsedArgs = normalizeArgv([versionId, name, ...args].filter((arg) => arg !== undefined));
|
|
161
|
+
const valueFlags = ["--subdomain"];
|
|
162
|
+
assertKnownFlags(parsedArgs, [...valueFlags, "--help", "-h"], valueFlags);
|
|
163
|
+
const positionals = positionalArgs(parsedArgs, valueFlags);
|
|
164
|
+
if (positionals.length < 2) {
|
|
165
|
+
fail({ code: "BAD_USAGE", message: "Missing <version_id> and/or <name>." });
|
|
157
166
|
}
|
|
167
|
+
if (positionals.length > 2) {
|
|
168
|
+
fail({ code: "BAD_USAGE", message: `Unexpected argument for apps fork: ${positionals[2]}` });
|
|
169
|
+
}
|
|
170
|
+
const opts = { subdomain: flagValue(parsedArgs, "--subdomain") ?? undefined };
|
|
158
171
|
// Preserve the aggressive early exit when no allowance is configured.
|
|
159
172
|
allowanceAuthHeaders("/fork/v1");
|
|
160
173
|
|
|
161
174
|
try {
|
|
162
175
|
const data = await getSdk().apps.fork({
|
|
163
|
-
versionId,
|
|
164
|
-
name,
|
|
176
|
+
versionId: positionals[0],
|
|
177
|
+
name: positionals[1],
|
|
165
178
|
subdomain: opts.subdomain,
|
|
166
179
|
});
|
|
167
180
|
|
|
@@ -181,15 +194,24 @@ async function fork(versionId, name, args) {
|
|
|
181
194
|
}
|
|
182
195
|
|
|
183
196
|
async function publish(projectId, args) {
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
197
|
+
const parsedArgs = normalizeArgv([projectId, ...args].filter((arg) => arg !== undefined));
|
|
198
|
+
const valueFlags = ["--description", "--tags", "--visibility"];
|
|
199
|
+
assertKnownFlags(parsedArgs, [...valueFlags, "--fork-allowed", "--help", "-h"], valueFlags);
|
|
200
|
+
const positionals = positionalArgs(parsedArgs, valueFlags);
|
|
201
|
+
if (positionals.length < 1) {
|
|
202
|
+
fail({ code: "BAD_USAGE", message: "Missing <id>." });
|
|
190
203
|
}
|
|
204
|
+
if (positionals.length > 1) {
|
|
205
|
+
fail({ code: "BAD_USAGE", message: `Unexpected argument for apps publish: ${positionals[1]}` });
|
|
206
|
+
}
|
|
207
|
+
const opts = { description: undefined, tags: undefined, visibility: undefined, forkAllowed: undefined };
|
|
208
|
+
opts.description = flagValue(parsedArgs, "--description") ?? undefined;
|
|
209
|
+
opts.tags = flagValue(parsedArgs, "--tags")?.split(",");
|
|
210
|
+
opts.visibility = flagValue(parsedArgs, "--visibility") ?? undefined;
|
|
211
|
+
if (opts.visibility) assertAllowedValue(opts.visibility, ["public", "unlisted", "private"], "--visibility");
|
|
212
|
+
if (parsedArgs.includes("--fork-allowed")) opts.forkAllowed = true;
|
|
191
213
|
try {
|
|
192
|
-
const data = await getSdk().apps.publish(
|
|
214
|
+
const data = await getSdk().apps.publish(positionals[0], {
|
|
193
215
|
description: opts.description,
|
|
194
216
|
tags: opts.tags,
|
|
195
217
|
visibility: opts.visibility,
|
|
@@ -201,21 +223,30 @@ async function publish(projectId, args) {
|
|
|
201
223
|
}
|
|
202
224
|
}
|
|
203
225
|
|
|
204
|
-
async function versions(projectId) {
|
|
226
|
+
async function versions(projectId, args = []) {
|
|
227
|
+
const parsedArgs = normalizeArgv([projectId, ...args].filter((arg) => arg !== undefined));
|
|
228
|
+
assertKnownFlags(parsedArgs, ["--help", "-h"]);
|
|
229
|
+
const positionals = positionalArgs(parsedArgs);
|
|
230
|
+
if (positionals.length !== 1) {
|
|
231
|
+
fail({ code: "BAD_USAGE", message: positionals.length === 0 ? "Missing <id>." : `Unexpected argument for apps versions: ${positionals[1]}` });
|
|
232
|
+
}
|
|
205
233
|
try {
|
|
206
|
-
const data = await getSdk().apps.listVersions(
|
|
234
|
+
const data = await getSdk().apps.listVersions(positionals[0]);
|
|
207
235
|
console.log(JSON.stringify(data, null, 2));
|
|
208
236
|
} catch (err) {
|
|
209
237
|
reportSdkError(err);
|
|
210
238
|
}
|
|
211
239
|
}
|
|
212
240
|
|
|
213
|
-
async function inspect(versionId) {
|
|
214
|
-
|
|
215
|
-
|
|
241
|
+
async function inspect(versionId, args = []) {
|
|
242
|
+
const parsedArgs = normalizeArgv([versionId, ...args].filter((arg) => arg !== undefined));
|
|
243
|
+
assertKnownFlags(parsedArgs, ["--help", "-h"]);
|
|
244
|
+
const positionals = positionalArgs(parsedArgs);
|
|
245
|
+
if (positionals.length !== 1) {
|
|
246
|
+
fail({ code: "BAD_USAGE", message: positionals.length === 0 ? "Missing version ID" : `Unexpected argument for apps inspect: ${positionals[1]}` });
|
|
216
247
|
}
|
|
217
248
|
try {
|
|
218
|
-
const data = await getSdk().apps.getApp(
|
|
249
|
+
const data = await getSdk().apps.getApp(positionals[0]);
|
|
219
250
|
console.log(JSON.stringify(data, null, 2));
|
|
220
251
|
} catch (err) {
|
|
221
252
|
reportSdkError(err);
|
|
@@ -223,26 +254,47 @@ async function inspect(versionId) {
|
|
|
223
254
|
}
|
|
224
255
|
|
|
225
256
|
async function update(projectId, versionId, args) {
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
257
|
+
const parsedArgs = normalizeArgv([projectId, versionId, ...args].filter((arg) => arg !== undefined));
|
|
258
|
+
const valueFlags = ["--description", "--tags", "--visibility"];
|
|
259
|
+
assertKnownFlags(parsedArgs, [...valueFlags, "--fork-allowed", "--no-fork", "--help", "-h"], valueFlags);
|
|
260
|
+
const positionals = positionalArgs(parsedArgs, valueFlags);
|
|
261
|
+
if (positionals.length < 2) {
|
|
262
|
+
fail({ code: "BAD_USAGE", message: "Missing <project_id> and/or <version_id>." });
|
|
263
|
+
}
|
|
264
|
+
if (positionals.length > 2) {
|
|
265
|
+
fail({ code: "BAD_USAGE", message: `Unexpected argument for apps update: ${positionals[2]}` });
|
|
266
|
+
}
|
|
267
|
+
if (parsedArgs.includes("--fork-allowed") && parsedArgs.includes("--no-fork")) {
|
|
268
|
+
fail({ code: "BAD_USAGE", message: "Provide either --fork-allowed or --no-fork, not both." });
|
|
233
269
|
}
|
|
270
|
+
const opts = {};
|
|
271
|
+
opts.description = flagValue(parsedArgs, "--description") ?? undefined;
|
|
272
|
+
opts.tags = flagValue(parsedArgs, "--tags")?.split(",");
|
|
273
|
+
opts.visibility = flagValue(parsedArgs, "--visibility") ?? undefined;
|
|
274
|
+
if (opts.visibility) assertAllowedValue(opts.visibility, ["public", "unlisted", "private"], "--visibility");
|
|
275
|
+
if (parsedArgs.includes("--fork-allowed")) opts.fork_allowed = true;
|
|
276
|
+
if (parsedArgs.includes("--no-fork")) opts.fork_allowed = false;
|
|
234
277
|
try {
|
|
235
|
-
await getSdk().apps.updateVersion(
|
|
236
|
-
console.log(JSON.stringify({ status: "ok", project_id:
|
|
278
|
+
await getSdk().apps.updateVersion(positionals[0], positionals[1], opts);
|
|
279
|
+
console.log(JSON.stringify({ status: "ok", project_id: positionals[0], version_id: positionals[1] }));
|
|
237
280
|
} catch (err) {
|
|
238
281
|
reportSdkError(err);
|
|
239
282
|
}
|
|
240
283
|
}
|
|
241
284
|
|
|
242
|
-
async function deleteVersion(projectId, versionId) {
|
|
285
|
+
async function deleteVersion(projectId, versionId, args = []) {
|
|
286
|
+
const parsedArgs = normalizeArgv([projectId, versionId, ...args].filter((arg) => arg !== undefined));
|
|
287
|
+
assertKnownFlags(parsedArgs, ["--help", "-h"]);
|
|
288
|
+
const positionals = positionalArgs(parsedArgs);
|
|
289
|
+
if (positionals.length < 2) {
|
|
290
|
+
fail({ code: "BAD_USAGE", message: "Missing <project_id> and/or <version_id>." });
|
|
291
|
+
}
|
|
292
|
+
if (positionals.length > 2) {
|
|
293
|
+
fail({ code: "BAD_USAGE", message: `Unexpected argument for apps delete: ${positionals[2]}` });
|
|
294
|
+
}
|
|
243
295
|
try {
|
|
244
|
-
await getSdk().apps.deleteVersion(
|
|
245
|
-
console.log(JSON.stringify({ status: "ok", message: `Version ${
|
|
296
|
+
await getSdk().apps.deleteVersion(positionals[0], positionals[1]);
|
|
297
|
+
console.log(JSON.stringify({ status: "ok", message: `Version ${positionals[1]} deleted.` }));
|
|
246
298
|
} catch (err) {
|
|
247
299
|
reportSdkError(err);
|
|
248
300
|
}
|
|
@@ -255,10 +307,10 @@ export async function run(sub, args) {
|
|
|
255
307
|
case "browse": await browse(args); break;
|
|
256
308
|
case "fork": await fork(args[0], args[1], args.slice(2)); break;
|
|
257
309
|
case "publish": await publish(args[0], args.slice(1)); break;
|
|
258
|
-
case "versions": await versions(args[0]); break;
|
|
259
|
-
case "inspect": await inspect(args[0]); break;
|
|
310
|
+
case "versions": await versions(args[0], args.slice(1)); break;
|
|
311
|
+
case "inspect": await inspect(args[0], args.slice(1)); break;
|
|
260
312
|
case "update": await update(args[0], args[1], args.slice(2)); break;
|
|
261
|
-
case "delete": await deleteVersion(args[0], args[1]); break;
|
|
313
|
+
case "delete": await deleteVersion(args[0], args[1], args.slice(2)); break;
|
|
262
314
|
default:
|
|
263
315
|
console.error(`Unknown subcommand: ${sub}\n`);
|
|
264
316
|
console.log(HELP);
|
package/lib/argparse.mjs
CHANGED
|
@@ -25,6 +25,13 @@ export function assertKnownFlags(args = [], knownFlags = [], flagsWithValues = [
|
|
|
25
25
|
for (let i = 0; i < args.length; i++) {
|
|
26
26
|
const arg = args[i];
|
|
27
27
|
if (valueFlags.has(arg)) {
|
|
28
|
+
if (i + 1 >= args.length || (typeof args[i + 1] === "string" && args[i + 1].startsWith("--"))) {
|
|
29
|
+
fail({
|
|
30
|
+
code: "BAD_FLAG",
|
|
31
|
+
message: `${arg} requires a value`,
|
|
32
|
+
details: { flag: arg },
|
|
33
|
+
});
|
|
34
|
+
}
|
|
28
35
|
i += 1;
|
|
29
36
|
continue;
|
|
30
37
|
}
|
|
@@ -47,7 +54,7 @@ export function failUnknownFlag(flag, knownFlags = []) {
|
|
|
47
54
|
export function flagValue(args, flag) {
|
|
48
55
|
const idx = args.indexOf(flag);
|
|
49
56
|
if (idx === -1) return null;
|
|
50
|
-
if (idx + 1 >= args.length) {
|
|
57
|
+
if (idx + 1 >= args.length || (typeof args[idx + 1] === "string" && args[idx + 1].startsWith("--"))) {
|
|
51
58
|
fail({
|
|
52
59
|
code: "BAD_FLAG",
|
|
53
60
|
message: `${flag} requires a value`,
|
|
@@ -99,6 +106,26 @@ export function parseIntegerFlag(name, value, { min = 1, max = Number.POSITIVE_I
|
|
|
99
106
|
return n;
|
|
100
107
|
}
|
|
101
108
|
|
|
109
|
+
export function assertAllowedValue(value, allowed, fieldName) {
|
|
110
|
+
if (!allowed.includes(value)) {
|
|
111
|
+
fail({
|
|
112
|
+
code: "BAD_FLAG",
|
|
113
|
+
message: `${fieldName} must be one of: ${allowed.join(", ")}`,
|
|
114
|
+
details: { field: fieldName, value, allowed },
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function validateEvmAddress(value, fieldName = "address") {
|
|
120
|
+
if (typeof value !== "string" || !/^0x[a-fA-F0-9]{40}$/.test(value)) {
|
|
121
|
+
fail({
|
|
122
|
+
code: "BAD_FLAG",
|
|
123
|
+
message: `${fieldName} must be a 0x-prefixed 20-byte EVM address`,
|
|
124
|
+
details: { field: fieldName, value },
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
102
129
|
export function failBadProjectId(value) {
|
|
103
130
|
fail({
|
|
104
131
|
code: "BAD_PROJECT_ID",
|
|
@@ -206,6 +233,31 @@ export function positionalArgs(args = [], flagsWithValues = []) {
|
|
|
206
233
|
return out;
|
|
207
234
|
}
|
|
208
235
|
|
|
236
|
+
export function requirePositionalCount(args = [], flagsWithValues = [], opts = {}) {
|
|
237
|
+
const {
|
|
238
|
+
min = 0,
|
|
239
|
+
max = min,
|
|
240
|
+
command = "command",
|
|
241
|
+
missing = "Missing required argument.",
|
|
242
|
+
} = opts;
|
|
243
|
+
const pos = positionalArgs(args, flagsWithValues);
|
|
244
|
+
if (pos.length < min) {
|
|
245
|
+
fail({
|
|
246
|
+
code: "BAD_USAGE",
|
|
247
|
+
message: missing,
|
|
248
|
+
hint: command,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
if (pos.length > max) {
|
|
252
|
+
fail({
|
|
253
|
+
code: "BAD_USAGE",
|
|
254
|
+
message: `Unexpected argument for ${command}: ${pos[max]}`,
|
|
255
|
+
hint: `Use \`${command}\`.`,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
return pos;
|
|
259
|
+
}
|
|
260
|
+
|
|
209
261
|
// Resolve a positional project_id argument with active-project fallback (GH-102, GH-187).
|
|
210
262
|
// If the first positional starts with "prj_", treat it as the project id and
|
|
211
263
|
// strip it from the rest. Otherwise, fall through to the active project from
|
package/lib/billing.mjs
CHANGED
|
@@ -1,6 +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
|
+
import { assertKnownFlags, flagValue, normalizeArgv, parseIntegerFlag, positionalArgs } from "./argparse.mjs";
|
|
4
4
|
|
|
5
5
|
const HELP = `run402 billing — Email billing accounts, Stripe tier checkout, email packs
|
|
6
6
|
|
|
@@ -122,13 +122,6 @@ Examples:
|
|
|
122
122
|
`,
|
|
123
123
|
};
|
|
124
124
|
|
|
125
|
-
function parseFlag(args, flag) {
|
|
126
|
-
for (let i = 0; i < args.length; i++) {
|
|
127
|
-
if (args[i] === flag && args[i + 1]) return args[i + 1];
|
|
128
|
-
}
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
125
|
function requireSingleBillingIdentifier(email, wallet) {
|
|
133
126
|
if (email && wallet) {
|
|
134
127
|
fail({
|
|
@@ -147,7 +140,13 @@ function requireSingleBillingIdentifier(email, wallet) {
|
|
|
147
140
|
}
|
|
148
141
|
|
|
149
142
|
async function createEmail(args) {
|
|
150
|
-
const
|
|
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
|
+
}
|
|
151
150
|
if (!email) {
|
|
152
151
|
fail({
|
|
153
152
|
code: "BAD_USAGE",
|
|
@@ -164,8 +163,14 @@ async function createEmail(args) {
|
|
|
164
163
|
}
|
|
165
164
|
|
|
166
165
|
async function linkWallet(args) {
|
|
167
|
-
const
|
|
168
|
-
|
|
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
|
+
}
|
|
169
174
|
if (!accountId || !wallet) {
|
|
170
175
|
fail({
|
|
171
176
|
code: "BAD_USAGE",
|
|
@@ -182,7 +187,14 @@ async function linkWallet(args) {
|
|
|
182
187
|
}
|
|
183
188
|
|
|
184
189
|
async function tierCheckout(args) {
|
|
185
|
-
const
|
|
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
|
+
}
|
|
186
198
|
if (!tier) {
|
|
187
199
|
fail({
|
|
188
200
|
code: "BAD_USAGE",
|
|
@@ -190,8 +202,8 @@ async function tierCheckout(args) {
|
|
|
190
202
|
hint: "run402 billing tier-checkout <tier> [--email <e> | --wallet <w>]",
|
|
191
203
|
});
|
|
192
204
|
}
|
|
193
|
-
const email =
|
|
194
|
-
const wallet =
|
|
205
|
+
const email = flagValue(parsedArgs, "--email");
|
|
206
|
+
const wallet = flagValue(parsedArgs, "--wallet");
|
|
195
207
|
requireSingleBillingIdentifier(email, wallet);
|
|
196
208
|
try {
|
|
197
209
|
const data = await getSdk().billing.tierCheckout(tier, { email: email ?? undefined, wallet: wallet ?? undefined });
|
|
@@ -202,8 +214,15 @@ async function tierCheckout(args) {
|
|
|
202
214
|
}
|
|
203
215
|
|
|
204
216
|
async function buyPack(args) {
|
|
205
|
-
const
|
|
206
|
-
const
|
|
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]}` });
|
|
223
|
+
}
|
|
224
|
+
const email = flagValue(parsedArgs, "--email");
|
|
225
|
+
const wallet = flagValue(parsedArgs, "--wallet");
|
|
207
226
|
requireSingleBillingIdentifier(email, wallet);
|
|
208
227
|
try {
|
|
209
228
|
const data = await getSdk().billing.buyEmailPack({ email: email ?? undefined, wallet: wallet ?? undefined });
|
|
@@ -214,8 +233,15 @@ async function buyPack(args) {
|
|
|
214
233
|
}
|
|
215
234
|
|
|
216
235
|
async function autoRecharge(args) {
|
|
217
|
-
const
|
|
218
|
-
const
|
|
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
|
+
}
|
|
219
245
|
if (!accountId || !state || !["on", "off"].includes(state)) {
|
|
220
246
|
fail({
|
|
221
247
|
code: "BAD_USAGE",
|
|
@@ -223,8 +249,8 @@ async function autoRecharge(args) {
|
|
|
223
249
|
hint: "run402 billing auto-recharge <account_id> <on|off> [--threshold <n>]",
|
|
224
250
|
});
|
|
225
251
|
}
|
|
226
|
-
const thresholdStr =
|
|
227
|
-
const threshold =
|
|
252
|
+
const thresholdStr = flagValue(parsedArgs, "--threshold");
|
|
253
|
+
const threshold = parsedArgs.includes("--threshold")
|
|
228
254
|
? parseIntegerFlag("--threshold", thresholdStr, { min: 0 })
|
|
229
255
|
: undefined;
|
|
230
256
|
try {
|
|
@@ -240,7 +266,13 @@ async function autoRecharge(args) {
|
|
|
240
266
|
}
|
|
241
267
|
|
|
242
268
|
async function balance(args) {
|
|
243
|
-
const
|
|
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
|
+
}
|
|
244
276
|
if (!id) {
|
|
245
277
|
fail({
|
|
246
278
|
code: "BAD_USAGE",
|
|
@@ -257,7 +289,14 @@ async function balance(args) {
|
|
|
257
289
|
}
|
|
258
290
|
|
|
259
291
|
async function history(args) {
|
|
260
|
-
const
|
|
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
|
+
}
|
|
261
300
|
if (!id) {
|
|
262
301
|
fail({
|
|
263
302
|
code: "BAD_USAGE",
|
|
@@ -265,9 +304,11 @@ async function history(args) {
|
|
|
265
304
|
hint: "run402 billing history <email-or-wallet> [--limit <n>]",
|
|
266
305
|
});
|
|
267
306
|
}
|
|
268
|
-
const limit =
|
|
307
|
+
const limit = parsedArgs.includes("--limit")
|
|
308
|
+
? parseIntegerFlag("--limit", flagValue(parsedArgs, "--limit"), { min: 1 })
|
|
309
|
+
: 50;
|
|
269
310
|
try {
|
|
270
|
-
const data = await getSdk().billing.getHistory(id,
|
|
311
|
+
const data = await getSdk().billing.getHistory(id, limit);
|
|
271
312
|
console.log(JSON.stringify(data, null, 2));
|
|
272
313
|
} catch (err) {
|
|
273
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.
|
|
442
|
-
die("--key
|
|
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
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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 {
|