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.
- package/lib/agent.mjs +4 -2
- package/lib/ai.mjs +24 -10
- package/lib/allowance.mjs +53 -15
- package/lib/apps.mjs +13 -11
- package/lib/argparse.mjs +147 -0
- package/lib/auth.mjs +24 -9
- package/lib/billing.mjs +33 -17
- package/lib/blob.mjs +58 -26
- package/lib/cdn.mjs +3 -4
- package/lib/config.mjs +32 -8
- package/lib/contracts.mjs +38 -21
- package/lib/deploy-v2.mjs +38 -23
- package/lib/deploy.mjs +43 -44
- package/lib/domains.mjs +24 -8
- package/lib/email.mjs +38 -29
- package/lib/functions.mjs +77 -34
- package/lib/image.mjs +8 -2
- package/lib/message.mjs +4 -2
- package/lib/projects.mjs +115 -40
- package/lib/sdk-errors.mjs +66 -10
- package/lib/secrets.mjs +14 -8
- package/lib/sender-domain.mjs +11 -5
- package/lib/sites.mjs +9 -7
- package/lib/status.mjs +5 -2
- package/lib/subdomains.mjs +26 -11
- package/lib/tier.mjs +8 -2
- package/lib/webhooks.mjs +27 -13
- package/package.json +1 -1
- package/sdk/dist/index.d.ts +1 -0
- package/sdk/dist/index.d.ts.map +1 -1
- package/sdk/dist/index.js +5 -0
- package/sdk/dist/index.js.map +1 -1
- package/sdk/dist/namespaces/auth.d.ts +7 -0
- package/sdk/dist/namespaces/auth.d.ts.map +1 -1
- package/sdk/dist/namespaces/auth.js +24 -0
- package/sdk/dist/namespaces/auth.js.map +1 -1
- package/sdk/dist/namespaces/billing.d.ts +3 -0
- package/sdk/dist/namespaces/billing.d.ts.map +1 -1
- package/sdk/dist/namespaces/billing.js +6 -0
- package/sdk/dist/namespaces/billing.js.map +1 -1
- package/sdk/dist/namespaces/contracts.d.ts +3 -0
- package/sdk/dist/namespaces/contracts.d.ts.map +1 -1
- package/sdk/dist/namespaces/contracts.js +6 -0
- package/sdk/dist/namespaces/contracts.js.map +1 -1
- package/sdk/dist/namespaces/email.d.ts +4 -0
- package/sdk/dist/namespaces/email.d.ts.map +1 -1
- package/sdk/dist/namespaces/email.js +8 -0
- package/sdk/dist/namespaces/email.js.map +1 -1
- package/sdk/dist/namespaces/projects.d.ts +14 -0
- package/sdk/dist/namespaces/projects.d.ts.map +1 -1
- package/sdk/dist/namespaces/projects.js +72 -0
- package/sdk/dist/namespaces/projects.js.map +1 -1
- package/sdk/dist/namespaces/sender-domain.d.ts +2 -0
- package/sdk/dist/namespaces/sender-domain.d.ts.map +1 -1
- package/sdk/dist/namespaces/sender-domain.js +4 -0
- package/sdk/dist/namespaces/sender-domain.js.map +1 -1
- package/sdk/dist/scoped.d.ts +8 -1
- package/sdk/dist/scoped.d.ts.map +1 -1
- package/sdk/dist/scoped.js +21 -0
- package/sdk/dist/scoped.js.map +1 -1
- package/core-dist/wallet-auth.js +0 -62
- package/core-dist/wallet.js +0 -25
- package/sdk/core-dist/wallet-auth.js +0 -62
- package/sdk/core-dist/wallet.js +0 -25
package/lib/agent.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 agent — Manage agent identity
|
|
6
6
|
|
|
@@ -24,7 +24,9 @@ async function contact(args) {
|
|
|
24
24
|
if (args[i] === "--email" && args[i + 1]) email = args[++i];
|
|
25
25
|
if (args[i] === "--webhook" && args[i + 1]) webhook = args[++i];
|
|
26
26
|
}
|
|
27
|
-
if (!name) {
|
|
27
|
+
if (!name) {
|
|
28
|
+
fail({ code: "BAD_USAGE", message: "Missing --name <name>" });
|
|
29
|
+
}
|
|
28
30
|
// Preserve the aggressive early exit when no allowance is configured.
|
|
29
31
|
allowanceAuthHeaders("/agent/v1/contact");
|
|
30
32
|
|
package/lib/ai.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 ai — AI translation and moderation tools
|
|
6
6
|
|
|
@@ -13,10 +13,10 @@ Subcommands:
|
|
|
13
13
|
usage <project_id>
|
|
14
14
|
|
|
15
15
|
Examples:
|
|
16
|
-
run402 ai translate
|
|
17
|
-
run402 ai translate
|
|
18
|
-
run402 ai moderate
|
|
19
|
-
run402 ai usage
|
|
16
|
+
run402 ai translate prj_abc123 "Hello world" --to es
|
|
17
|
+
run402 ai translate prj_abc123 "Hello" --to ja --from en --context "formal business email"
|
|
18
|
+
run402 ai moderate prj_abc123 "content to check"
|
|
19
|
+
run402 ai usage prj_abc123
|
|
20
20
|
|
|
21
21
|
Notes:
|
|
22
22
|
- translate requires the AI Translation add-on on the project
|
|
@@ -44,8 +44,8 @@ Notes:
|
|
|
44
44
|
- Counts against the project's translation word quota
|
|
45
45
|
|
|
46
46
|
Examples:
|
|
47
|
-
run402 ai translate
|
|
48
|
-
run402 ai translate
|
|
47
|
+
run402 ai translate prj_abc123 "Hello world" --to es
|
|
48
|
+
run402 ai translate prj_abc123 "Hello" --to ja --from en \\
|
|
49
49
|
--context "formal business email"
|
|
50
50
|
`,
|
|
51
51
|
};
|
|
@@ -76,8 +76,16 @@ async function translate(args) {
|
|
|
76
76
|
const from = parseFlag(args, "--from");
|
|
77
77
|
const context = parseFlag(args, "--context");
|
|
78
78
|
|
|
79
|
-
if (!text) {
|
|
80
|
-
|
|
79
|
+
if (!text) {
|
|
80
|
+
fail({
|
|
81
|
+
code: "BAD_USAGE",
|
|
82
|
+
message: "Text required.",
|
|
83
|
+
hint: "run402 ai translate <project_id> <text> --to <lang>",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
if (!to) {
|
|
87
|
+
fail({ code: "BAD_USAGE", message: "--to <lang> is required" });
|
|
88
|
+
}
|
|
81
89
|
|
|
82
90
|
try {
|
|
83
91
|
const data = await getSdk().ai.translate(projectId, { text, to, from: from ?? undefined, context: context ?? undefined });
|
|
@@ -101,7 +109,13 @@ async function moderate(args) {
|
|
|
101
109
|
const projectId = resolveProjectId(projectOpt || positional[0]);
|
|
102
110
|
text = positional[1] || null;
|
|
103
111
|
|
|
104
|
-
if (!text) {
|
|
112
|
+
if (!text) {
|
|
113
|
+
fail({
|
|
114
|
+
code: "BAD_USAGE",
|
|
115
|
+
message: "Text required.",
|
|
116
|
+
hint: "run402 ai moderate <project_id> <text>",
|
|
117
|
+
});
|
|
118
|
+
}
|
|
105
119
|
|
|
106
120
|
try {
|
|
107
121
|
const data = await getSdk().ai.moderate(projectId, text);
|
package/lib/allowance.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readAllowance, saveAllowance, ALLOWANCE_FILE, API } 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 allowance — Manage your agent allowance
|
|
6
6
|
|
|
@@ -82,7 +82,7 @@ async function status() {
|
|
|
82
82
|
const data = await getSdk().allowance.status();
|
|
83
83
|
if (!data.configured) {
|
|
84
84
|
console.log(JSON.stringify({ status: "no_wallet", message: "No agent allowance found. Run: run402 allowance create" }));
|
|
85
|
-
|
|
85
|
+
process.exit(1);
|
|
86
86
|
}
|
|
87
87
|
// Preserve CLI's rail field (SDK doesn't surface it; read from local allowance).
|
|
88
88
|
const w = readAllowance();
|
|
@@ -115,8 +115,11 @@ async function create() {
|
|
|
115
115
|
} catch (err) {
|
|
116
116
|
const msg = (err instanceof Error) ? err.message : String(err);
|
|
117
117
|
if (/already exists/i.test(msg)) {
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
fail({
|
|
119
|
+
code: "ALLOWANCE_EXISTS",
|
|
120
|
+
message: "Agent allowance already exists.",
|
|
121
|
+
hint: "Use 'status' to check it.",
|
|
122
|
+
});
|
|
120
123
|
}
|
|
121
124
|
reportSdkError(err);
|
|
122
125
|
}
|
|
@@ -124,7 +127,13 @@ async function create() {
|
|
|
124
127
|
|
|
125
128
|
async function fund() {
|
|
126
129
|
const w = readAllowance();
|
|
127
|
-
if (!w) {
|
|
130
|
+
if (!w) {
|
|
131
|
+
fail({
|
|
132
|
+
code: "NO_ALLOWANCE",
|
|
133
|
+
message: "No agent allowance.",
|
|
134
|
+
hint: "Run: run402 allowance create",
|
|
135
|
+
});
|
|
136
|
+
}
|
|
128
137
|
|
|
129
138
|
if (w.rail === "mpp") {
|
|
130
139
|
// Tempo Moderato faucet — instant, no polling needed
|
|
@@ -139,8 +148,11 @@ async function fund() {
|
|
|
139
148
|
});
|
|
140
149
|
const data = await res.json();
|
|
141
150
|
if (data.error) {
|
|
142
|
-
|
|
143
|
-
|
|
151
|
+
fail({
|
|
152
|
+
code: "FAUCET_FAILED",
|
|
153
|
+
message: data.error.message || "Tempo faucet failed",
|
|
154
|
+
details: { rail: "mpp" },
|
|
155
|
+
});
|
|
144
156
|
}
|
|
145
157
|
|
|
146
158
|
// Re-read balance once (instant confirmation)
|
|
@@ -164,8 +176,11 @@ async function fund() {
|
|
|
164
176
|
const res = await fetch(`${API}/faucet/v1`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ address: w.address }) });
|
|
165
177
|
const data = await res.json();
|
|
166
178
|
if (!res.ok) {
|
|
167
|
-
|
|
168
|
-
|
|
179
|
+
fail({
|
|
180
|
+
code: data?.code || "FAUCET_FAILED",
|
|
181
|
+
message: data?.message || "Faucet request failed",
|
|
182
|
+
details: { http: res.status, ...data },
|
|
183
|
+
});
|
|
169
184
|
}
|
|
170
185
|
|
|
171
186
|
const MAX_WAIT = 30;
|
|
@@ -196,7 +211,13 @@ async function readUsdcBalance(client, usdc, address) {
|
|
|
196
211
|
|
|
197
212
|
async function balance() {
|
|
198
213
|
const w = readAllowance();
|
|
199
|
-
if (!w) {
|
|
214
|
+
if (!w) {
|
|
215
|
+
fail({
|
|
216
|
+
code: "NO_ALLOWANCE",
|
|
217
|
+
message: "No agent allowance.",
|
|
218
|
+
hint: "Run: run402 allowance create",
|
|
219
|
+
});
|
|
220
|
+
}
|
|
200
221
|
|
|
201
222
|
const { createPublicClient, http, base, baseSepolia, tempoModerato } = await loadDeps();
|
|
202
223
|
const mainnetClient = createPublicClient({ chain: base, transport: http() });
|
|
@@ -229,19 +250,30 @@ async function exportAddr() {
|
|
|
229
250
|
const address = await getSdk().allowance.export();
|
|
230
251
|
console.log(address);
|
|
231
252
|
} catch {
|
|
232
|
-
|
|
233
|
-
process.exit(1);
|
|
253
|
+
fail({ code: "NO_ALLOWANCE", message: "No agent allowance." });
|
|
234
254
|
}
|
|
235
255
|
}
|
|
236
256
|
|
|
237
257
|
async function checkout(args) {
|
|
238
258
|
const w = readAllowance();
|
|
239
|
-
if (!w) {
|
|
259
|
+
if (!w) {
|
|
260
|
+
fail({
|
|
261
|
+
code: "NO_ALLOWANCE",
|
|
262
|
+
message: "No agent allowance.",
|
|
263
|
+
hint: "Run: run402 allowance create",
|
|
264
|
+
});
|
|
265
|
+
}
|
|
240
266
|
let amount = null;
|
|
241
267
|
for (let i = 0; i < args.length; i++) {
|
|
242
268
|
if (args[i] === "--amount" && args[i + 1]) amount = parseInt(args[++i], 10);
|
|
243
269
|
}
|
|
244
|
-
if (!amount) {
|
|
270
|
+
if (!amount) {
|
|
271
|
+
fail({
|
|
272
|
+
code: "BAD_USAGE",
|
|
273
|
+
message: "Missing --amount <usd_micros>",
|
|
274
|
+
hint: "e.g. --amount 5000000 for $5",
|
|
275
|
+
});
|
|
276
|
+
}
|
|
245
277
|
const res = await fetch(`${API}/billing/v1/checkouts`, {
|
|
246
278
|
method: "POST",
|
|
247
279
|
headers: { "Content-Type": "application/json" },
|
|
@@ -254,7 +286,13 @@ async function checkout(args) {
|
|
|
254
286
|
|
|
255
287
|
async function history(args) {
|
|
256
288
|
const w = readAllowance();
|
|
257
|
-
if (!w) {
|
|
289
|
+
if (!w) {
|
|
290
|
+
fail({
|
|
291
|
+
code: "NO_ALLOWANCE",
|
|
292
|
+
message: "No agent allowance.",
|
|
293
|
+
hint: "Run: run402 allowance create",
|
|
294
|
+
});
|
|
295
|
+
}
|
|
258
296
|
let limit = 20;
|
|
259
297
|
for (let i = 0; i < args.length; i++) {
|
|
260
298
|
if (args[i] === "--limit" && args[i + 1]) limit = parseInt(args[++i], 10);
|
package/lib/apps.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { allowanceAuthHeaders, saveProject } 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 apps — Browse and manage the app marketplace
|
|
6
6
|
|
|
@@ -23,11 +23,11 @@ Examples:
|
|
|
23
23
|
run402 apps browse
|
|
24
24
|
run402 apps browse --tag auth
|
|
25
25
|
run402 apps fork ver_abc123 my-todo --tier prototype
|
|
26
|
-
run402 apps publish
|
|
27
|
-
run402 apps versions
|
|
26
|
+
run402 apps publish prj_abc123 --description "Todo app" --tags todo,auth --visibility public --fork-allowed
|
|
27
|
+
run402 apps versions prj_abc123
|
|
28
28
|
run402 apps inspect ver_abc123
|
|
29
|
-
run402 apps update
|
|
30
|
-
run402 apps delete
|
|
29
|
+
run402 apps update prj_abc123 ver_abc123 --description "Updated" --tags todo
|
|
30
|
+
run402 apps delete prj_abc123 ver_abc123
|
|
31
31
|
`;
|
|
32
32
|
|
|
33
33
|
const SUB_HELP = {
|
|
@@ -76,8 +76,8 @@ Options:
|
|
|
76
76
|
--fork-allowed Allow other users to fork this app
|
|
77
77
|
|
|
78
78
|
Examples:
|
|
79
|
-
run402 apps publish
|
|
80
|
-
run402 apps publish
|
|
79
|
+
run402 apps publish prj_abc123 --description "Todo app" --tags todo,auth
|
|
80
|
+
run402 apps publish prj_abc123 --visibility public --fork-allowed
|
|
81
81
|
`,
|
|
82
82
|
update: `run402 apps update — Update a published version's metadata
|
|
83
83
|
|
|
@@ -96,9 +96,9 @@ Options:
|
|
|
96
96
|
--no-fork Disable forking for this version
|
|
97
97
|
|
|
98
98
|
Examples:
|
|
99
|
-
run402 apps update
|
|
100
|
-
run402 apps update
|
|
101
|
-
run402 apps update
|
|
99
|
+
run402 apps update prj_abc123 ver_abc123 --description "Updated"
|
|
100
|
+
run402 apps update prj_abc123 ver_abc123 --tags todo,auth --fork-allowed
|
|
101
|
+
run402 apps update prj_abc123 ver_abc123 --no-fork
|
|
102
102
|
`,
|
|
103
103
|
};
|
|
104
104
|
|
|
@@ -177,7 +177,9 @@ async function versions(projectId) {
|
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
async function inspect(versionId) {
|
|
180
|
-
if (!versionId) {
|
|
180
|
+
if (!versionId) {
|
|
181
|
+
fail({ code: "BAD_USAGE", message: "Missing version ID" });
|
|
182
|
+
}
|
|
181
183
|
try {
|
|
182
184
|
const data = await getSdk().apps.getApp(versionId);
|
|
183
185
|
console.log(JSON.stringify(data, null, 2));
|
package/lib/argparse.mjs
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { fail } from "./sdk-errors.mjs";
|
|
2
|
+
|
|
3
|
+
export function normalizeArgv(argv = []) {
|
|
4
|
+
const out = [];
|
|
5
|
+
for (const arg of argv ?? []) {
|
|
6
|
+
if (typeof arg === "string" && arg.startsWith("--") && arg.includes("=")) {
|
|
7
|
+
const eq = arg.indexOf("=");
|
|
8
|
+
out.push(arg.slice(0, eq), arg.slice(eq + 1));
|
|
9
|
+
} else {
|
|
10
|
+
out.push(arg);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return out;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function hasHelp(args = []) {
|
|
17
|
+
return args.includes("--help") || args.includes("-h");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function assertKnownFlags(args = [], knownFlags = [], flagsWithValues = []) {
|
|
21
|
+
const known = new Set(knownFlags);
|
|
22
|
+
const valueFlags = new Set(flagsWithValues);
|
|
23
|
+
for (let i = 0; i < args.length; i++) {
|
|
24
|
+
const arg = args[i];
|
|
25
|
+
if (valueFlags.has(arg)) {
|
|
26
|
+
i += 1;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (typeof arg !== "string" || !arg.startsWith("-") || arg === "-") continue;
|
|
30
|
+
if (known.has(arg)) continue;
|
|
31
|
+
failUnknownFlag(arg, known);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function failUnknownFlag(flag, knownFlags = []) {
|
|
36
|
+
const known = [...knownFlags].filter((f) => typeof f === "string" && f.startsWith("-"));
|
|
37
|
+
const closest = closestFlag(flag, known);
|
|
38
|
+
fail({
|
|
39
|
+
code: "UNKNOWN_FLAG",
|
|
40
|
+
message: closest ? `Unknown flag: ${flag}. Did you mean ${closest}?` : `Unknown flag: ${flag}.`,
|
|
41
|
+
details: { flag, closest: closest ? [closest] : [] },
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function flagValue(args, flag) {
|
|
46
|
+
const idx = args.indexOf(flag);
|
|
47
|
+
if (idx === -1) return null;
|
|
48
|
+
if (idx + 1 >= args.length) {
|
|
49
|
+
fail({
|
|
50
|
+
code: "BAD_FLAG",
|
|
51
|
+
message: `${flag} requires a value`,
|
|
52
|
+
details: { flag },
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return args[idx + 1];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function parseIntegerFlag(name, value, { min = 1, max = Number.POSITIVE_INFINITY, def } = {}) {
|
|
59
|
+
if (value === undefined || value === null) {
|
|
60
|
+
if (def !== undefined) return def;
|
|
61
|
+
fail({
|
|
62
|
+
code: "BAD_FLAG",
|
|
63
|
+
message: `${name} requires an integer value`,
|
|
64
|
+
details: { flag: name },
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const raw = String(value);
|
|
68
|
+
if (!/^-?\d+$/.test(raw)) {
|
|
69
|
+
fail({
|
|
70
|
+
code: "BAD_FLAG",
|
|
71
|
+
message: `${name} must be an integer, got: ${raw}`,
|
|
72
|
+
details: { flag: name, value: raw },
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const n = Number.parseInt(raw, 10);
|
|
76
|
+
if (n < min) {
|
|
77
|
+
fail({
|
|
78
|
+
code: "BAD_FLAG",
|
|
79
|
+
message: `${name} must be >= ${min}, got: ${n}`,
|
|
80
|
+
details: { flag: name, value: n, min },
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (n > max) {
|
|
84
|
+
fail({
|
|
85
|
+
code: "BAD_FLAG",
|
|
86
|
+
message: `${name} must be <= ${max}, got: ${n}`,
|
|
87
|
+
details: { flag: name, value: n, max },
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return n;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function failBadProjectId(value) {
|
|
94
|
+
fail({
|
|
95
|
+
code: "BAD_PROJECT_ID",
|
|
96
|
+
message: `Argument '${value}' is not a project id. Project IDs must start with 'prj_'.`,
|
|
97
|
+
hint: "Omit the project id to use the active project, or pass the full prj_... id.",
|
|
98
|
+
details: { value, expected_prefix: "prj_" },
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function positionalArgs(args = [], flagsWithValues = []) {
|
|
103
|
+
const valueFlags = new Set(flagsWithValues);
|
|
104
|
+
const out = [];
|
|
105
|
+
for (let i = 0; i < args.length; i++) {
|
|
106
|
+
const arg = args[i];
|
|
107
|
+
if (valueFlags.has(arg)) {
|
|
108
|
+
i += 1;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (typeof arg === "string" && arg.startsWith("-")) continue;
|
|
112
|
+
out.push(arg);
|
|
113
|
+
}
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function closestFlag(flag, candidates) {
|
|
118
|
+
let best = null;
|
|
119
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
120
|
+
for (const candidate of candidates) {
|
|
121
|
+
const d = levenshtein(flag, candidate);
|
|
122
|
+
if (d < bestDistance) {
|
|
123
|
+
best = candidate;
|
|
124
|
+
bestDistance = d;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (!best) return null;
|
|
128
|
+
return bestDistance <= 3 ? best : null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function levenshtein(a, b) {
|
|
132
|
+
const prev = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
133
|
+
const curr = Array.from({ length: b.length + 1 }, () => 0);
|
|
134
|
+
for (let i = 1; i <= a.length; i++) {
|
|
135
|
+
curr[0] = i;
|
|
136
|
+
for (let j = 1; j <= b.length; j++) {
|
|
137
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
138
|
+
curr[j] = Math.min(
|
|
139
|
+
curr[j - 1] + 1,
|
|
140
|
+
prev[j] + 1,
|
|
141
|
+
prev[j - 1] + cost,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
for (let j = 0; j <= b.length; j++) prev[j] = curr[j];
|
|
145
|
+
}
|
|
146
|
+
return prev[b.length];
|
|
147
|
+
}
|
package/lib/auth.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { findProject, resolveProjectId, API } 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 auth — Manage project user authentication
|
|
6
6
|
|
|
@@ -93,7 +93,7 @@ Notes:
|
|
|
93
93
|
|
|
94
94
|
Examples:
|
|
95
95
|
run402 auth settings --allow-password-set true
|
|
96
|
-
run402 auth settings --allow-password-set false --project
|
|
96
|
+
run402 auth settings --allow-password-set false --project prj_abc123
|
|
97
97
|
`,
|
|
98
98
|
providers: `run402 auth providers — List available auth providers
|
|
99
99
|
|
|
@@ -105,7 +105,7 @@ Options:
|
|
|
105
105
|
|
|
106
106
|
Examples:
|
|
107
107
|
run402 auth providers
|
|
108
|
-
run402 auth providers --project
|
|
108
|
+
run402 auth providers --project prj_abc123
|
|
109
109
|
`,
|
|
110
110
|
};
|
|
111
111
|
|
|
@@ -121,8 +121,12 @@ async function magicLink(args) {
|
|
|
121
121
|
const redirect = parseFlag(args, "--redirect");
|
|
122
122
|
const projectId = resolveProjectId(parseFlag(args, "--project"));
|
|
123
123
|
|
|
124
|
-
if (!email) {
|
|
125
|
-
|
|
124
|
+
if (!email) {
|
|
125
|
+
fail({ code: "BAD_USAGE", message: "Missing --email" });
|
|
126
|
+
}
|
|
127
|
+
if (!redirect) {
|
|
128
|
+
fail({ code: "BAD_USAGE", message: "Missing --redirect <url>" });
|
|
129
|
+
}
|
|
126
130
|
|
|
127
131
|
try {
|
|
128
132
|
await getSdk().auth.requestMagicLink(projectId, { email, redirectUrl: redirect });
|
|
@@ -136,7 +140,9 @@ async function verify(args) {
|
|
|
136
140
|
const token = parseFlag(args, "--token");
|
|
137
141
|
const projectId = resolveProjectId(parseFlag(args, "--project"));
|
|
138
142
|
|
|
139
|
-
if (!token) {
|
|
143
|
+
if (!token) {
|
|
144
|
+
fail({ code: "BAD_USAGE", message: "Missing --token" });
|
|
145
|
+
}
|
|
140
146
|
|
|
141
147
|
try {
|
|
142
148
|
const data = await getSdk().auth.verifyMagicLink(projectId, token);
|
|
@@ -152,8 +158,12 @@ async function setPassword(args) {
|
|
|
152
158
|
const currentPassword = parseFlag(args, "--current");
|
|
153
159
|
const projectId = resolveProjectId(parseFlag(args, "--project"));
|
|
154
160
|
|
|
155
|
-
if (!accessToken) {
|
|
156
|
-
|
|
161
|
+
if (!accessToken) {
|
|
162
|
+
fail({ code: "BAD_USAGE", message: "Missing --token <bearer_token>" });
|
|
163
|
+
}
|
|
164
|
+
if (!newPassword) {
|
|
165
|
+
fail({ code: "BAD_USAGE", message: "Missing --new <password>" });
|
|
166
|
+
}
|
|
157
167
|
|
|
158
168
|
try {
|
|
159
169
|
await getSdk().auth.setUserPassword(projectId, {
|
|
@@ -171,7 +181,12 @@ async function settings(args) {
|
|
|
171
181
|
const allowPasswordSet = parseFlag(args, "--allow-password-set");
|
|
172
182
|
const projectId = resolveProjectId(parseFlag(args, "--project"));
|
|
173
183
|
|
|
174
|
-
if (allowPasswordSet === null) {
|
|
184
|
+
if (allowPasswordSet === null) {
|
|
185
|
+
fail({
|
|
186
|
+
code: "BAD_USAGE",
|
|
187
|
+
message: "Missing --allow-password-set <true|false>",
|
|
188
|
+
});
|
|
189
|
+
}
|
|
175
190
|
|
|
176
191
|
try {
|
|
177
192
|
await getSdk().auth.settings(projectId, { allow_password_set: allowPasswordSet === "true" });
|
package/lib/billing.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { API } 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 billing — Email billing accounts, Stripe tier checkout, email packs
|
|
6
6
|
|
|
@@ -97,8 +97,11 @@ function parseFlag(args, flag) {
|
|
|
97
97
|
async function createEmail(args) {
|
|
98
98
|
const email = args[0];
|
|
99
99
|
if (!email) {
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
fail({
|
|
101
|
+
code: "BAD_USAGE",
|
|
102
|
+
message: "Missing email.",
|
|
103
|
+
hint: "run402 billing create-email <email>",
|
|
104
|
+
});
|
|
102
105
|
}
|
|
103
106
|
try {
|
|
104
107
|
const data = await getSdk().billing.createEmailAccount(email);
|
|
@@ -112,8 +115,11 @@ async function linkWallet(args) {
|
|
|
112
115
|
const accountId = args[0];
|
|
113
116
|
const wallet = args[1];
|
|
114
117
|
if (!accountId || !wallet) {
|
|
115
|
-
|
|
116
|
-
|
|
118
|
+
fail({
|
|
119
|
+
code: "BAD_USAGE",
|
|
120
|
+
message: "Missing <account_id> and/or <wallet>.",
|
|
121
|
+
hint: "run402 billing link-wallet <account_id> <wallet>",
|
|
122
|
+
});
|
|
117
123
|
}
|
|
118
124
|
try {
|
|
119
125
|
await getSdk().billing.linkWallet(accountId, wallet);
|
|
@@ -126,14 +132,16 @@ async function linkWallet(args) {
|
|
|
126
132
|
async function tierCheckout(args) {
|
|
127
133
|
const tier = args[0];
|
|
128
134
|
if (!tier) {
|
|
129
|
-
|
|
130
|
-
|
|
135
|
+
fail({
|
|
136
|
+
code: "BAD_USAGE",
|
|
137
|
+
message: "Missing <tier>.",
|
|
138
|
+
hint: "run402 billing tier-checkout <tier> [--email <e> | --wallet <w>]",
|
|
139
|
+
});
|
|
131
140
|
}
|
|
132
141
|
const email = parseFlag(args, "--email");
|
|
133
142
|
const wallet = parseFlag(args, "--wallet");
|
|
134
143
|
if (!email && !wallet) {
|
|
135
|
-
|
|
136
|
-
process.exit(1);
|
|
144
|
+
fail({ code: "BAD_USAGE", message: "Must provide --email or --wallet" });
|
|
137
145
|
}
|
|
138
146
|
try {
|
|
139
147
|
const data = await getSdk().billing.tierCheckout(tier, { email: email ?? undefined, wallet: wallet ?? undefined });
|
|
@@ -147,8 +155,7 @@ async function buyPack(args) {
|
|
|
147
155
|
const email = parseFlag(args, "--email");
|
|
148
156
|
const wallet = parseFlag(args, "--wallet");
|
|
149
157
|
if (!email && !wallet) {
|
|
150
|
-
|
|
151
|
-
process.exit(1);
|
|
158
|
+
fail({ code: "BAD_USAGE", message: "Must provide --email or --wallet" });
|
|
152
159
|
}
|
|
153
160
|
try {
|
|
154
161
|
const data = await getSdk().billing.buyEmailPack({ email: email ?? undefined, wallet: wallet ?? undefined });
|
|
@@ -162,8 +169,11 @@ async function autoRecharge(args) {
|
|
|
162
169
|
const accountId = args[0];
|
|
163
170
|
const state = args[1];
|
|
164
171
|
if (!accountId || !state || !["on", "off"].includes(state)) {
|
|
165
|
-
|
|
166
|
-
|
|
172
|
+
fail({
|
|
173
|
+
code: "BAD_USAGE",
|
|
174
|
+
message: "Missing <account_id> and/or <on|off>.",
|
|
175
|
+
hint: "run402 billing auto-recharge <account_id> <on|off> [--threshold <n>]",
|
|
176
|
+
});
|
|
167
177
|
}
|
|
168
178
|
const thresholdStr = parseFlag(args, "--threshold");
|
|
169
179
|
try {
|
|
@@ -182,8 +192,11 @@ async function balance(args) {
|
|
|
182
192
|
// Accepts email OR wallet — SDK only models wallet, so keep direct fetch.
|
|
183
193
|
const id = args[0];
|
|
184
194
|
if (!id) {
|
|
185
|
-
|
|
186
|
-
|
|
195
|
+
fail({
|
|
196
|
+
code: "BAD_USAGE",
|
|
197
|
+
message: "Missing <email-or-wallet>.",
|
|
198
|
+
hint: "run402 billing balance <email-or-wallet>",
|
|
199
|
+
});
|
|
187
200
|
}
|
|
188
201
|
const res = await fetch(`${API}/billing/v1/accounts/${encodeURIComponent(id)}`);
|
|
189
202
|
const data = await res.json();
|
|
@@ -194,8 +207,11 @@ async function balance(args) {
|
|
|
194
207
|
async function history(args) {
|
|
195
208
|
const id = args[0];
|
|
196
209
|
if (!id) {
|
|
197
|
-
|
|
198
|
-
|
|
210
|
+
fail({
|
|
211
|
+
code: "BAD_USAGE",
|
|
212
|
+
message: "Missing <email-or-wallet>.",
|
|
213
|
+
hint: "run402 billing history <email-or-wallet> [--limit <n>]",
|
|
214
|
+
});
|
|
199
215
|
}
|
|
200
216
|
const limit = parseFlag(args, "--limit") || "50";
|
|
201
217
|
const res = await fetch(`${API}/billing/v1/accounts/${encodeURIComponent(id)}/history?limit=${encodeURIComponent(limit)}`);
|