run402 1.54.0 → 1.54.1
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 +18 -4
- package/lib/allowance.mjs +53 -15
- package/lib/apps.mjs +4 -2
- package/lib/auth.mjs +22 -7
- package/lib/billing.mjs +33 -17
- package/lib/blob.mjs +3 -4
- 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 +15 -5
- package/lib/image.mjs +8 -2
- package/lib/message.mjs +4 -2
- package/lib/projects.mjs +55 -11
- package/lib/sdk-errors.mjs +66 -10
- package/lib/secrets.mjs +8 -2
- package/lib/sender-domain.mjs +11 -5
- package/lib/sites.mjs +9 -7
- package/lib/status.mjs +1 -1
- package/lib/subdomains.mjs +24 -9
- package/lib/tier.mjs +8 -2
- package/lib/webhooks.mjs +27 -13
- package/package.json +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
|
|
|
@@ -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
|
|
|
@@ -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/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
|
|
|
@@ -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)}`);
|
package/lib/blob.mjs
CHANGED
|
@@ -36,7 +36,7 @@ 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
40
|
|
|
41
41
|
const HELP = `run402 blob — Direct-to-S3 blob storage
|
|
42
42
|
|
|
@@ -182,9 +182,8 @@ Examples:
|
|
|
182
182
|
|
|
183
183
|
const UPLOAD_STATE_DIR = join(homedir(), ".run402", "uploads");
|
|
184
184
|
|
|
185
|
-
function die(msg,
|
|
186
|
-
|
|
187
|
-
process.exit(code);
|
|
185
|
+
function die(msg, exit_code = 1) {
|
|
186
|
+
fail({ code: "BAD_USAGE", message: msg, exit_code });
|
|
188
187
|
}
|
|
189
188
|
|
|
190
189
|
function parseArgs(args) {
|
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);
|