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/blob.mjs
CHANGED
|
@@ -36,7 +36,8 @@ import { pipeline } from "node:stream/promises";
|
|
|
36
36
|
|
|
37
37
|
import { resolveProject, resolveProjectId, API } from "./config.mjs";
|
|
38
38
|
import { getSdk } from "./sdk.mjs";
|
|
39
|
-
import { reportSdkError } from "./sdk-errors.mjs";
|
|
39
|
+
import { reportSdkError, fail } from "./sdk-errors.mjs";
|
|
40
|
+
import { assertKnownFlags, hasHelp, normalizeArgv, parseIntegerFlag } from "./argparse.mjs";
|
|
40
41
|
|
|
41
42
|
const HELP = `run402 blob — Direct-to-S3 blob storage
|
|
42
43
|
|
|
@@ -62,13 +63,13 @@ Options:
|
|
|
62
63
|
--ttl <seconds> Signed-URL TTL (sign only; default 3600, max 604800)
|
|
63
64
|
|
|
64
65
|
Examples:
|
|
65
|
-
run402 blob put ./artifact.tgz --project
|
|
66
|
-
run402 blob put ./dist/**/*.png --project
|
|
67
|
-
run402 blob put huge.bin --project
|
|
68
|
-
run402 blob get images/logo.png --output /tmp/logo.png --project
|
|
69
|
-
run402 blob ls --project
|
|
70
|
-
run402 blob rm images/logo.png --project
|
|
71
|
-
run402 blob sign images/logo.png --project
|
|
66
|
+
run402 blob put ./artifact.tgz --project prj_abc123
|
|
67
|
+
run402 blob put ./dist/**/*.png --project prj_abc123 --key assets/
|
|
68
|
+
run402 blob put huge.bin --project prj_abc123 --immutable
|
|
69
|
+
run402 blob get images/logo.png --output /tmp/logo.png --project prj_abc123
|
|
70
|
+
run402 blob ls --project prj_abc123 --prefix images/
|
|
71
|
+
run402 blob rm images/logo.png --project prj_abc123
|
|
72
|
+
run402 blob sign images/logo.png --project prj_abc123 --ttl 600
|
|
72
73
|
`;
|
|
73
74
|
|
|
74
75
|
const SUB_HELP = {
|
|
@@ -90,9 +91,9 @@ Options:
|
|
|
90
91
|
--json Emit NDJSON progress events on stdout (for agent consumption)
|
|
91
92
|
|
|
92
93
|
Examples:
|
|
93
|
-
run402 blob put ./artifact.tgz --project
|
|
94
|
-
run402 blob put ./dist/**/*.png --project
|
|
95
|
-
run402 blob put huge.bin --project
|
|
94
|
+
run402 blob put ./artifact.tgz --project prj_abc123
|
|
95
|
+
run402 blob put ./dist/**/*.png --project prj_abc123 --key assets/
|
|
96
|
+
run402 blob put huge.bin --project prj_abc123 --immutable --concurrency 8
|
|
96
97
|
`,
|
|
97
98
|
get: `run402 blob get — Download a blob by key
|
|
98
99
|
|
|
@@ -107,7 +108,7 @@ Options:
|
|
|
107
108
|
--project <id> Project ID (defaults to active project)
|
|
108
109
|
|
|
109
110
|
Examples:
|
|
110
|
-
run402 blob get images/logo.png --output /tmp/logo.png --project
|
|
111
|
+
run402 blob get images/logo.png --output /tmp/logo.png --project prj_abc123
|
|
111
112
|
`,
|
|
112
113
|
ls: `run402 blob ls — List blob keys in a project
|
|
113
114
|
|
|
@@ -120,8 +121,8 @@ Options:
|
|
|
120
121
|
--limit <n> Max results (default 100, max 1000)
|
|
121
122
|
|
|
122
123
|
Examples:
|
|
123
|
-
run402 blob ls --project
|
|
124
|
-
run402 blob ls --project
|
|
124
|
+
run402 blob ls --project prj_abc123
|
|
125
|
+
run402 blob ls --project prj_abc123 --prefix images/ --limit 500
|
|
125
126
|
`,
|
|
126
127
|
rm: `run402 blob rm — Delete a blob
|
|
127
128
|
|
|
@@ -135,7 +136,7 @@ Options:
|
|
|
135
136
|
--project <id> Project ID (defaults to active project)
|
|
136
137
|
|
|
137
138
|
Examples:
|
|
138
|
-
run402 blob rm images/logo.png --project
|
|
139
|
+
run402 blob rm images/logo.png --project prj_abc123
|
|
139
140
|
`,
|
|
140
141
|
sign: `run402 blob sign — Create a presigned download URL for a blob
|
|
141
142
|
|
|
@@ -150,7 +151,7 @@ Options:
|
|
|
150
151
|
--ttl <seconds> Signed-URL TTL (default 3600, max 604800)
|
|
151
152
|
|
|
152
153
|
Examples:
|
|
153
|
-
run402 blob sign reports/2025-q4.pdf --project
|
|
154
|
+
run402 blob sign reports/2025-q4.pdf --project prj_abc123 --ttl 600
|
|
154
155
|
`,
|
|
155
156
|
diagnose: `run402 blob diagnose — Inspect the live CDN state for a public blob URL
|
|
156
157
|
|
|
@@ -182,12 +183,42 @@ Examples:
|
|
|
182
183
|
|
|
183
184
|
const UPLOAD_STATE_DIR = join(homedir(), ".run402", "uploads");
|
|
184
185
|
|
|
185
|
-
function die(msg,
|
|
186
|
-
|
|
187
|
-
process.exit(code);
|
|
186
|
+
function die(msg, exit_code = 1) {
|
|
187
|
+
fail({ code: "BAD_USAGE", message: msg, exit_code });
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
function
|
|
190
|
+
function dieApiFailure(prefix, http, body) {
|
|
191
|
+
if (body && typeof body === "object" && !Array.isArray(body)) {
|
|
192
|
+
const envelope = { status: "error", http, ...body };
|
|
193
|
+
if (!envelope.message && envelope.error) envelope.message = envelope.error;
|
|
194
|
+
console.error(JSON.stringify(envelope));
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
fail({
|
|
198
|
+
message: `${prefix}: HTTP ${http}${typeof body === "string" && body ? `: ${body.slice(0, 500)}` : ""}`,
|
|
199
|
+
details: { http },
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function parseArgs(rawArgs) {
|
|
204
|
+
const args = normalizeArgv(rawArgs);
|
|
205
|
+
const valueFlags = ["--project", "--key", "--concurrency", "--prefix", "--limit", "--output", "-o", "--ttl"];
|
|
206
|
+
assertKnownFlags(args, [
|
|
207
|
+
"--project",
|
|
208
|
+
"--key",
|
|
209
|
+
"--private",
|
|
210
|
+
"--immutable",
|
|
211
|
+
"--concurrency",
|
|
212
|
+
"--no-resume",
|
|
213
|
+
"--json",
|
|
214
|
+
"--prefix",
|
|
215
|
+
"--limit",
|
|
216
|
+
"--output",
|
|
217
|
+
"-o",
|
|
218
|
+
"--ttl",
|
|
219
|
+
"--help",
|
|
220
|
+
"-h",
|
|
221
|
+
], valueFlags);
|
|
191
222
|
const out = { positional: [], project: null, key: null, private: false, immutable: false,
|
|
192
223
|
concurrency: 4, resume: true, json: false, prefix: null, limit: null,
|
|
193
224
|
output: null, ttl: null };
|
|
@@ -197,13 +228,13 @@ function parseArgs(args) {
|
|
|
197
228
|
else if (a === "--key") out.key = args[++i];
|
|
198
229
|
else if (a === "--private") out.private = true;
|
|
199
230
|
else if (a === "--immutable") out.immutable = true;
|
|
200
|
-
else if (a === "--concurrency") out.concurrency =
|
|
231
|
+
else if (a === "--concurrency") out.concurrency = parseIntegerFlag("--concurrency", args[++i], { min: 1 });
|
|
201
232
|
else if (a === "--no-resume") out.resume = false;
|
|
202
233
|
else if (a === "--json") out.json = true;
|
|
203
234
|
else if (a === "--prefix") out.prefix = args[++i];
|
|
204
|
-
else if (a === "--limit") out.limit =
|
|
235
|
+
else if (a === "--limit") out.limit = parseIntegerFlag("--limit", args[++i], { min: 1, max: 1000 });
|
|
205
236
|
else if (a === "--output" || a === "-o") out.output = args[++i];
|
|
206
|
-
else if (a === "--ttl") out.ttl =
|
|
237
|
+
else if (a === "--ttl") out.ttl = parseIntegerFlag("--ttl", args[++i], { min: 1, max: 604800 });
|
|
207
238
|
else if (!a.startsWith("--")) out.positional.push(a);
|
|
208
239
|
}
|
|
209
240
|
return out;
|
|
@@ -285,7 +316,7 @@ async function putOne(project, filePath, opts) {
|
|
|
285
316
|
immutable: opts.immutable,
|
|
286
317
|
sha256,
|
|
287
318
|
});
|
|
288
|
-
if (init.status !== 201)
|
|
319
|
+
if (init.status !== 201) dieApiFailure("Init failed", init.status, init.body);
|
|
289
320
|
initRes = init.body;
|
|
290
321
|
saveState({
|
|
291
322
|
upload_id: initRes.upload_id,
|
|
@@ -331,7 +362,7 @@ async function putOne(project, filePath, opts) {
|
|
|
331
362
|
? { parts: etags.map((e, i) => ({ part_number: i + 1, etag: e.etag })) }
|
|
332
363
|
: {};
|
|
333
364
|
const complete = await apiFetch(`${API}/storage/v1/uploads/${state.upload_id}/complete`, "POST", project, body);
|
|
334
|
-
if (complete.status !== 200)
|
|
365
|
+
if (complete.status !== 200) dieApiFailure("Complete failed", complete.status, complete.body);
|
|
335
366
|
|
|
336
367
|
removeState(state.upload_id);
|
|
337
368
|
log(opts, { event: "done", ...complete.body });
|
|
@@ -566,7 +597,8 @@ export async function run(sub, args) {
|
|
|
566
597
|
console.log(HELP);
|
|
567
598
|
process.exit(0);
|
|
568
599
|
}
|
|
569
|
-
|
|
600
|
+
args = normalizeArgv(args);
|
|
601
|
+
if (Array.isArray(args) && hasHelp(args)) {
|
|
570
602
|
console.log(SUB_HELP[sub] || HELP);
|
|
571
603
|
process.exit(0);
|
|
572
604
|
}
|
package/lib/cdn.mjs
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import { resolveProjectId } from "./config.mjs";
|
|
17
17
|
import { getSdk } from "./sdk.mjs";
|
|
18
|
-
import { reportSdkError } from "./sdk-errors.mjs";
|
|
18
|
+
import { reportSdkError, fail } from "./sdk-errors.mjs";
|
|
19
19
|
|
|
20
20
|
const HELP = `run402 cdn — CloudFront CDN diagnostics for public blob URLs
|
|
21
21
|
|
|
@@ -68,9 +68,8 @@ Examples:
|
|
|
68
68
|
`,
|
|
69
69
|
};
|
|
70
70
|
|
|
71
|
-
function die(msg,
|
|
72
|
-
|
|
73
|
-
process.exit(code);
|
|
71
|
+
function die(msg, exit_code = 1) {
|
|
72
|
+
fail({ code: "BAD_USAGE", message: msg, exit_code });
|
|
74
73
|
}
|
|
75
74
|
|
|
76
75
|
function parseArgs(args) {
|
package/lib/config.mjs
CHANGED
|
@@ -7,6 +7,7 @@ import { getApiBase, getConfigDir, getKeystorePath, getAllowancePath } from "../
|
|
|
7
7
|
import { readAllowance as coreReadAllowance, saveAllowance as coreSaveAllowance } from "../core-dist/allowance.js";
|
|
8
8
|
import { loadKeyStore, getProject, saveProject, updateProject, removeProject, saveKeyStore, getActiveProjectId, setActiveProjectId } from "../core-dist/keystore.js";
|
|
9
9
|
import { getAllowanceAuthHeaders as coreGetAllowanceAuthHeaders } from "../core-dist/allowance-auth.js";
|
|
10
|
+
import { fail } from "./sdk-errors.mjs";
|
|
10
11
|
|
|
11
12
|
export const CONFIG_DIR = getConfigDir();
|
|
12
13
|
export const ALLOWANCE_FILE = getAllowancePath();
|
|
@@ -23,31 +24,54 @@ export function saveAllowance(data) {
|
|
|
23
24
|
|
|
24
25
|
export function allowanceAuthHeaders(path) {
|
|
25
26
|
const headers = coreGetAllowanceAuthHeaders(path);
|
|
26
|
-
if (!headers) {
|
|
27
|
+
if (!headers) {
|
|
28
|
+
fail({
|
|
29
|
+
code: "NO_ALLOWANCE",
|
|
30
|
+
message: "No agent allowance found.",
|
|
31
|
+
hint: "Run: run402 allowance create",
|
|
32
|
+
});
|
|
33
|
+
}
|
|
27
34
|
return headers;
|
|
28
35
|
}
|
|
29
36
|
|
|
30
37
|
export function findProject(id) {
|
|
31
38
|
const p = getProject(id);
|
|
32
39
|
if (!p) {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
const idStr = id ?? "";
|
|
41
|
+
const hint = idStr && !String(idStr).startsWith("prj_")
|
|
42
|
+
? `project IDs start with "prj_". Check that the argument order is <project_id> <name>.`
|
|
43
|
+
: undefined;
|
|
44
|
+
fail({
|
|
45
|
+
code: "PROJECT_NOT_FOUND",
|
|
46
|
+
message: `Project ${idStr} not found in local registry.`,
|
|
47
|
+
hint,
|
|
48
|
+
details: { project_id: idStr, source: "local_registry" },
|
|
49
|
+
});
|
|
38
50
|
}
|
|
39
51
|
return p;
|
|
40
52
|
}
|
|
41
53
|
|
|
42
54
|
export function resolveProject(id) {
|
|
43
55
|
const projectId = id || getActiveProjectId();
|
|
44
|
-
if (!projectId) {
|
|
56
|
+
if (!projectId) {
|
|
57
|
+
fail({
|
|
58
|
+
code: "NO_ACTIVE_PROJECT",
|
|
59
|
+
message: "no project specified and no active project set.",
|
|
60
|
+
hint: "Run: run402 projects provision",
|
|
61
|
+
});
|
|
62
|
+
}
|
|
45
63
|
return findProject(projectId);
|
|
46
64
|
}
|
|
47
65
|
|
|
48
66
|
export function resolveProjectId(id) {
|
|
49
67
|
const projectId = id || getActiveProjectId();
|
|
50
|
-
if (!projectId) {
|
|
68
|
+
if (!projectId) {
|
|
69
|
+
fail({
|
|
70
|
+
code: "NO_ACTIVE_PROJECT",
|
|
71
|
+
message: "no project specified and no active project set.",
|
|
72
|
+
hint: "Run: run402 projects provision",
|
|
73
|
+
});
|
|
74
|
+
}
|
|
51
75
|
return projectId;
|
|
52
76
|
}
|
|
53
77
|
|
package/lib/contracts.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { findProject, API } 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 contracts — KMS-backed Ethereum wallets for smart-contract calls
|
|
6
6
|
|
|
@@ -100,8 +100,10 @@ async function provisionWallet(projectId, args) {
|
|
|
100
100
|
const p = findProject(projectId);
|
|
101
101
|
const chain = parseFlag(args, "--chain");
|
|
102
102
|
if (!chain) {
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
fail({
|
|
104
|
+
code: "BAD_USAGE",
|
|
105
|
+
message: "Missing --chain (base-mainnet or base-sepolia)",
|
|
106
|
+
});
|
|
105
107
|
}
|
|
106
108
|
const recovery = parseFlag(args, "--recovery");
|
|
107
109
|
// Soft default of one wallet — confirm if project already has one. This
|
|
@@ -115,8 +117,11 @@ async function provisionWallet(projectId, args) {
|
|
|
115
117
|
const list = await listRes.json();
|
|
116
118
|
const active = (list.wallets || []).filter((w) => w.status === "active");
|
|
117
119
|
if (active.length >= 1 && !hasFlag(args, "--yes")) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
+
fail({
|
|
121
|
+
code: "CONFIRMATION_REQUIRED",
|
|
122
|
+
message: `This project already has ${active.length} active wallet(s). Adding another costs $0.04/day each ($1.20/month). Re-run with --yes to confirm.`,
|
|
123
|
+
details: { active_wallets: active.length },
|
|
124
|
+
});
|
|
120
125
|
}
|
|
121
126
|
}
|
|
122
127
|
} catch { /* best-effort */ }
|
|
@@ -154,8 +159,10 @@ async function setRecovery(projectId, walletId, args) {
|
|
|
154
159
|
const clear = hasFlag(args, "--clear");
|
|
155
160
|
const address = parseFlag(args, "--address");
|
|
156
161
|
if (!clear && !address) {
|
|
157
|
-
|
|
158
|
-
|
|
162
|
+
fail({
|
|
163
|
+
code: "BAD_USAGE",
|
|
164
|
+
message: "Provide --address 0x... or --clear",
|
|
165
|
+
});
|
|
159
166
|
}
|
|
160
167
|
try {
|
|
161
168
|
await getSdk().contracts.setRecovery(projectId, walletId, clear ? null : address);
|
|
@@ -168,8 +175,7 @@ async function setRecovery(projectId, walletId, args) {
|
|
|
168
175
|
async function setAlert(projectId, walletId, args) {
|
|
169
176
|
const threshold = parseFlag(args, "--threshold-wei");
|
|
170
177
|
if (!threshold) {
|
|
171
|
-
|
|
172
|
-
process.exit(1);
|
|
178
|
+
fail({ code: "BAD_USAGE", message: "Missing --threshold-wei <n>" });
|
|
173
179
|
}
|
|
174
180
|
try {
|
|
175
181
|
await getSdk().contracts.setLowBalanceAlert(projectId, walletId, threshold);
|
|
@@ -188,17 +194,22 @@ async function call(projectId, walletId, args) {
|
|
|
188
194
|
const chain = parseFlag(args, "--chain") || "base-mainnet";
|
|
189
195
|
const idempotency = parseFlag(args, "--idempotency-key");
|
|
190
196
|
if (!to || !abi || !fn || !argsJson) {
|
|
191
|
-
|
|
192
|
-
|
|
197
|
+
fail({
|
|
198
|
+
code: "BAD_USAGE",
|
|
199
|
+
message: "Required flags: --to, --abi, --fn, --args.",
|
|
200
|
+
hint: "Cost: chain gas + $0.000005 KMS sign fee.",
|
|
201
|
+
});
|
|
193
202
|
}
|
|
203
|
+
const abiFragment = parseFlagJson("--abi", abi);
|
|
204
|
+
const callArgs = parseFlagJson("--args", argsJson);
|
|
194
205
|
try {
|
|
195
206
|
const data = await getSdk().contracts.call(projectId, {
|
|
196
207
|
walletId,
|
|
197
208
|
chain,
|
|
198
209
|
contractAddress: to,
|
|
199
|
-
abiFragment
|
|
210
|
+
abiFragment,
|
|
200
211
|
functionName: fn,
|
|
201
|
-
args:
|
|
212
|
+
args: callArgs,
|
|
202
213
|
value: value ?? undefined,
|
|
203
214
|
idempotencyKey: idempotency ?? undefined,
|
|
204
215
|
});
|
|
@@ -215,16 +226,20 @@ async function read(args) {
|
|
|
215
226
|
const fn = parseFlag(args, "--fn");
|
|
216
227
|
const argsJson = parseFlag(args, "--args");
|
|
217
228
|
if (!chain || !to || !abi || !fn || !argsJson) {
|
|
218
|
-
|
|
219
|
-
|
|
229
|
+
fail({
|
|
230
|
+
code: "BAD_USAGE",
|
|
231
|
+
message: "Required flags: --chain, --to, --abi, --fn, --args",
|
|
232
|
+
});
|
|
220
233
|
}
|
|
234
|
+
const abiFragment = parseFlagJson("--abi", abi);
|
|
235
|
+
const callArgs = parseFlagJson("--args", argsJson);
|
|
221
236
|
try {
|
|
222
237
|
const data = await getSdk().contracts.read({
|
|
223
238
|
chain,
|
|
224
239
|
contractAddress: to,
|
|
225
|
-
abiFragment
|
|
240
|
+
abiFragment,
|
|
226
241
|
functionName: fn,
|
|
227
|
-
args:
|
|
242
|
+
args: callArgs,
|
|
228
243
|
});
|
|
229
244
|
console.log(JSON.stringify(data, null, 2));
|
|
230
245
|
} catch (err) {
|
|
@@ -244,8 +259,11 @@ async function status(projectId, callId) {
|
|
|
244
259
|
async function drain(projectId, walletId, args) {
|
|
245
260
|
const to = parseFlag(args, "--to");
|
|
246
261
|
if (!to || !hasFlag(args, "--confirm")) {
|
|
247
|
-
|
|
248
|
-
|
|
262
|
+
fail({
|
|
263
|
+
code: "BAD_USAGE",
|
|
264
|
+
message: "Required: --to 0x... and --confirm.",
|
|
265
|
+
hint: "Cost: chain gas + $0.000005 KMS sign fee.",
|
|
266
|
+
});
|
|
249
267
|
}
|
|
250
268
|
try {
|
|
251
269
|
const data = await getSdk().contracts.drain(projectId, walletId, to);
|
|
@@ -257,8 +275,7 @@ async function drain(projectId, walletId, args) {
|
|
|
257
275
|
|
|
258
276
|
async function deleteWallet(projectId, walletId, args) {
|
|
259
277
|
if (!hasFlag(args, "--confirm")) {
|
|
260
|
-
|
|
261
|
-
process.exit(1);
|
|
278
|
+
fail({ code: "BAD_USAGE", message: "Required: --confirm" });
|
|
262
279
|
}
|
|
263
280
|
try {
|
|
264
281
|
const data = await getSdk().contracts.deleteWallet(projectId, walletId);
|
package/lib/deploy-v2.mjs
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
import { readFileSync } from "node:fs";
|
|
27
27
|
import { resolve, dirname, isAbsolute, join } from "node:path";
|
|
28
28
|
import { getSdk } from "./sdk.mjs";
|
|
29
|
-
import { reportSdkError } from "./sdk-errors.mjs";
|
|
29
|
+
import { reportSdkError, fail } from "./sdk-errors.mjs";
|
|
30
30
|
import { allowanceAuthHeaders, resolveProjectId } from "./config.mjs";
|
|
31
31
|
|
|
32
32
|
const APPLY_HELP = `run402 deploy apply — Unified deploy primitive (v1.34+)
|
|
@@ -107,8 +107,11 @@ export async function runDeployV2(sub, args) {
|
|
|
107
107
|
if (sub === "resume") return await resumeCmd(args);
|
|
108
108
|
if (sub === "list") return await listCmd(args);
|
|
109
109
|
if (sub === "events") return await eventsCmd(args);
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
fail({
|
|
111
|
+
code: "BAD_USAGE",
|
|
112
|
+
message: `Unknown deploy subcommand: ${sub}`,
|
|
113
|
+
details: { subcommand: sub },
|
|
114
|
+
});
|
|
112
115
|
}
|
|
113
116
|
|
|
114
117
|
async function readStdin() {
|
|
@@ -142,8 +145,11 @@ async function applyCmd(args) {
|
|
|
142
145
|
const manifestPath = isAbsolute(opts.manifest) ? opts.manifest : resolve(process.cwd(), opts.manifest);
|
|
143
146
|
raw = readFileSync(manifestPath, "utf-8");
|
|
144
147
|
} catch (err) {
|
|
145
|
-
|
|
146
|
-
|
|
148
|
+
fail({
|
|
149
|
+
code: "BAD_USAGE",
|
|
150
|
+
message: `Failed to read manifest: ${err.message}`,
|
|
151
|
+
details: { flag: "--manifest", path: opts.manifest },
|
|
152
|
+
});
|
|
147
153
|
}
|
|
148
154
|
} else {
|
|
149
155
|
raw = await readStdin();
|
|
@@ -153,18 +159,21 @@ async function applyCmd(args) {
|
|
|
153
159
|
try {
|
|
154
160
|
spec = JSON.parse(raw);
|
|
155
161
|
} catch (err) {
|
|
156
|
-
|
|
157
|
-
|
|
162
|
+
fail({
|
|
163
|
+
code: "BAD_USAGE",
|
|
164
|
+
message: `Manifest is not valid JSON: ${err.message}`,
|
|
165
|
+
details: { source: opts.manifest ? "manifest" : opts.spec ? "spec" : "stdin", parse_error: err.message },
|
|
166
|
+
});
|
|
158
167
|
}
|
|
159
168
|
|
|
160
169
|
if (opts.manifest) resolveFileDataPaths(spec, dirname(resolve(opts.manifest)));
|
|
161
170
|
|
|
162
171
|
if (opts.project && spec.project_id && spec.project_id !== opts.project) {
|
|
163
|
-
|
|
164
|
-
|
|
172
|
+
fail({
|
|
173
|
+
code: "BAD_USAGE",
|
|
165
174
|
message: `project_id conflict: spec.project_id=${spec.project_id} but --project=${opts.project}`,
|
|
166
|
-
|
|
167
|
-
|
|
175
|
+
details: { spec_project_id: spec.project_id, flag_project_id: opts.project },
|
|
176
|
+
});
|
|
168
177
|
}
|
|
169
178
|
if (opts.project) spec.project_id = opts.project;
|
|
170
179
|
if (!spec.project_id) spec.project_id = resolveProjectId(null);
|
|
@@ -198,8 +207,11 @@ async function resumeCmd(args) {
|
|
|
198
207
|
if (!args[i].startsWith("-") && !opts.operationId) opts.operationId = args[i];
|
|
199
208
|
}
|
|
200
209
|
if (!opts.operationId) {
|
|
201
|
-
|
|
202
|
-
|
|
210
|
+
fail({
|
|
211
|
+
code: "BAD_USAGE",
|
|
212
|
+
message: "Missing <operation_id>.",
|
|
213
|
+
hint: "run402 deploy resume <operation_id>",
|
|
214
|
+
});
|
|
203
215
|
}
|
|
204
216
|
|
|
205
217
|
allowanceAuthHeaders("/deploy/v2/operations");
|
|
@@ -243,8 +255,11 @@ async function eventsCmd(args) {
|
|
|
243
255
|
if (!args[i].startsWith("-") && !opts.operationId) opts.operationId = args[i];
|
|
244
256
|
}
|
|
245
257
|
if (!opts.operationId) {
|
|
246
|
-
|
|
247
|
-
|
|
258
|
+
fail({
|
|
259
|
+
code: "BAD_USAGE",
|
|
260
|
+
message: "Missing <operation_id>.",
|
|
261
|
+
hint: "run402 deploy events <operation_id>",
|
|
262
|
+
});
|
|
248
263
|
}
|
|
249
264
|
|
|
250
265
|
const project = resolveProjectId(opts.project);
|
|
@@ -383,11 +398,11 @@ function resolveFileDataPaths(spec, baseDir) {
|
|
|
383
398
|
m.sql = readFileSync(p, "utf-8");
|
|
384
399
|
delete m.sql_path;
|
|
385
400
|
} catch (err) {
|
|
386
|
-
|
|
387
|
-
|
|
401
|
+
fail({
|
|
402
|
+
code: "BAD_USAGE",
|
|
388
403
|
message: `Failed to read migration sql_path '${m.sql_path}': ${err.message}`,
|
|
389
|
-
|
|
390
|
-
|
|
404
|
+
details: { migration_id: m.id, sql_path: m.sql_path },
|
|
405
|
+
});
|
|
391
406
|
}
|
|
392
407
|
}
|
|
393
408
|
}
|
|
@@ -421,10 +436,10 @@ function readFileEntry(entry, baseDir) {
|
|
|
421
436
|
if (entry.contentType) out.contentType = entry.contentType;
|
|
422
437
|
return out;
|
|
423
438
|
} catch (err) {
|
|
424
|
-
|
|
425
|
-
|
|
439
|
+
fail({
|
|
440
|
+
code: "BAD_USAGE",
|
|
426
441
|
message: `Failed to read file '${entry.path}': ${err.message}`,
|
|
427
|
-
|
|
428
|
-
|
|
442
|
+
details: { path: entry.path },
|
|
443
|
+
});
|
|
429
444
|
}
|
|
430
445
|
}
|
package/lib/deploy.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { dirname, resolve } from "path";
|
|
|
3
3
|
import { resolveProjectId } from "./config.mjs";
|
|
4
4
|
import { resolveFilePathsInManifest, resolveMigrationsFile } from "./manifest.mjs";
|
|
5
5
|
import { getSdk } from "./sdk.mjs";
|
|
6
|
-
import { reportSdkError } from "./sdk-errors.mjs";
|
|
6
|
+
import { reportSdkError, fail } from "./sdk-errors.mjs";
|
|
7
7
|
|
|
8
8
|
const HELP = `run402 deploy — Deploy to an existing project on Run402
|
|
9
9
|
|
|
@@ -111,8 +111,8 @@ async function readStdin() {
|
|
|
111
111
|
* Load + parse the manifest from --manifest file or stdin, and resolve any
|
|
112
112
|
* referenced files[].path / migrations_file against the manifest's directory.
|
|
113
113
|
*
|
|
114
|
-
* Returns
|
|
115
|
-
*
|
|
114
|
+
* Returns the parsed manifest on success. On any fs / parse failure, calls
|
|
115
|
+
* `fail()` (which writes the canonical error envelope to stderr and exits 1).
|
|
116
116
|
*/
|
|
117
117
|
async function loadManifest(opts) {
|
|
118
118
|
let raw;
|
|
@@ -125,21 +125,18 @@ async function loadManifest(opts) {
|
|
|
125
125
|
raw = readFileSync(opts.manifest, "utf-8");
|
|
126
126
|
} catch (err) {
|
|
127
127
|
if (err && err.code === "ENOENT") {
|
|
128
|
-
|
|
129
|
-
|
|
128
|
+
fail({
|
|
129
|
+
code: "BAD_USAGE",
|
|
130
130
|
message: `File not found: ${manifestAbs}`,
|
|
131
|
-
field: "manifest",
|
|
132
|
-
path: manifestAbs,
|
|
133
131
|
hint: "Check that --manifest points to an existing JSON file.",
|
|
134
|
-
|
|
132
|
+
details: { field: "manifest", path: manifestAbs },
|
|
133
|
+
});
|
|
135
134
|
}
|
|
136
|
-
|
|
137
|
-
|
|
135
|
+
fail({
|
|
136
|
+
code: "BAD_USAGE",
|
|
138
137
|
message: err && err.message ? err.message : String(err),
|
|
139
|
-
field: "manifest",
|
|
140
|
-
|
|
141
|
-
...(err && err.code ? { code: err.code } : {}),
|
|
142
|
-
} };
|
|
138
|
+
details: { field: "manifest", path: manifestAbs, ...(err && err.code ? { syscall_code: err.code } : {}) },
|
|
139
|
+
});
|
|
143
140
|
}
|
|
144
141
|
} else {
|
|
145
142
|
raw = await readStdin();
|
|
@@ -149,12 +146,15 @@ async function loadManifest(opts) {
|
|
|
149
146
|
try {
|
|
150
147
|
manifest = JSON.parse(raw);
|
|
151
148
|
} catch (err) {
|
|
152
|
-
|
|
153
|
-
|
|
149
|
+
fail({
|
|
150
|
+
code: "BAD_USAGE",
|
|
154
151
|
message: `Manifest is not valid JSON: ${err.message}`,
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
152
|
+
details: {
|
|
153
|
+
field: opts.manifest ? "manifest" : "stdin",
|
|
154
|
+
...(opts.manifest ? { path: resolve(opts.manifest) } : {}),
|
|
155
|
+
parse_error: err.message,
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
if (opts.manifest) {
|
|
@@ -163,25 +163,29 @@ async function loadManifest(opts) {
|
|
|
163
163
|
resolveFilePathsInManifest(manifest, baseDir);
|
|
164
164
|
} catch (err) {
|
|
165
165
|
if (err && err.code === "ENOENT") {
|
|
166
|
-
|
|
167
|
-
|
|
166
|
+
fail({
|
|
167
|
+
code: "BAD_USAGE",
|
|
168
168
|
message: `File not found: ${err.absPath || err.path || "<unknown>"}`,
|
|
169
|
-
field: err.field || "manifest",
|
|
170
|
-
...(err.absPath || err.path ? { path: err.absPath || err.path } : {}),
|
|
171
169
|
hint: `Paths in manifest.${err.field || "files[].path"} are resolved relative to the manifest file's directory (${baseDir}).`,
|
|
172
|
-
|
|
170
|
+
details: {
|
|
171
|
+
field: err.field || "manifest",
|
|
172
|
+
...(err.absPath || err.path ? { path: err.absPath || err.path } : {}),
|
|
173
|
+
},
|
|
174
|
+
});
|
|
173
175
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
+
fail({
|
|
177
|
+
code: "BAD_USAGE",
|
|
176
178
|
message: err && err.message ? err.message : String(err),
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
179
|
+
details: {
|
|
180
|
+
...(err && err.field ? { field: err.field } : {}),
|
|
181
|
+
...(err && (err.absPath || err.path) ? { path: err.absPath || err.path } : {}),
|
|
182
|
+
...(err && err.code ? { syscall_code: err.code } : {}),
|
|
183
|
+
},
|
|
184
|
+
});
|
|
181
185
|
}
|
|
182
186
|
}
|
|
183
187
|
|
|
184
|
-
return
|
|
188
|
+
return manifest;
|
|
185
189
|
}
|
|
186
190
|
|
|
187
191
|
export async function run(args) {
|
|
@@ -210,25 +214,20 @@ export async function run(args) {
|
|
|
210
214
|
if (args[i] === "--project" && args[i + 1]) opts.project = args[++i];
|
|
211
215
|
}
|
|
212
216
|
|
|
213
|
-
const
|
|
214
|
-
if (manifestResult.error) {
|
|
215
|
-
console.error(JSON.stringify(manifestResult.error));
|
|
216
|
-
process.exit(1);
|
|
217
|
-
}
|
|
218
|
-
const manifest = manifestResult.manifest;
|
|
217
|
+
const manifest = await loadManifest(opts);
|
|
219
218
|
|
|
220
219
|
// If both sources set project_id and they disagree, refuse to deploy rather
|
|
221
220
|
// than silently shipping to the wrong target.
|
|
222
221
|
if (opts.project && manifest.project_id && opts.project !== manifest.project_id) {
|
|
223
|
-
|
|
224
|
-
|
|
222
|
+
fail({
|
|
223
|
+
code: "BAD_USAGE",
|
|
225
224
|
message: `project_id conflict: manifest.project_id=${manifest.project_id} but --project=${opts.project}`,
|
|
226
|
-
manifest_project_id: manifest.project_id,
|
|
227
|
-
flag_project_id: opts.project,
|
|
228
225
|
hint: "Remove one of them or make them match. The --project flag and manifest.project_id must agree (or only one of them must be set).",
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
226
|
+
details: {
|
|
227
|
+
manifest_project_id: manifest.project_id,
|
|
228
|
+
flag_project_id: opts.project,
|
|
229
|
+
},
|
|
230
|
+
});
|
|
232
231
|
}
|
|
233
232
|
|
|
234
233
|
if (opts.project) manifest.project_id = opts.project;
|