run402 1.63.0 → 1.64.0
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/README.md +4 -0
- package/lib/allowance.mjs +20 -25
- package/lib/auth.mjs +7 -16
- package/lib/billing.mjs +12 -10
- package/lib/blob.mjs +30 -54
- package/lib/contracts.mjs +11 -19
- package/lib/deploy-v2.mjs +4 -1
- package/lib/init.mjs +12 -19
- package/lib/projects.mjs +37 -48
- package/lib/status.mjs +8 -15
- package/package.json +1 -1
- package/sdk/dist/kernel.d.ts +6 -0
- package/sdk/dist/kernel.d.ts.map +1 -1
- package/sdk/dist/kernel.js +5 -1
- package/sdk/dist/kernel.js.map +1 -1
- package/sdk/dist/namespaces/billing.d.ts +10 -5
- package/sdk/dist/namespaces/billing.d.ts.map +1 -1
- package/sdk/dist/namespaces/billing.js +23 -9
- package/sdk/dist/namespaces/billing.js.map +1 -1
- package/sdk/dist/namespaces/blobs.d.ts +16 -1
- package/sdk/dist/namespaces/blobs.d.ts.map +1 -1
- package/sdk/dist/namespaces/blobs.js +61 -25
- package/sdk/dist/namespaces/blobs.js.map +1 -1
- package/sdk/dist/namespaces/blobs.types.d.ts +41 -0
- package/sdk/dist/namespaces/blobs.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/projects.d.ts +5 -3
- package/sdk/dist/namespaces/projects.d.ts.map +1 -1
- package/sdk/dist/namespaces/projects.js +31 -5
- package/sdk/dist/namespaces/projects.js.map +1 -1
- package/sdk/dist/namespaces/projects.types.d.ts +16 -0
- package/sdk/dist/namespaces/projects.types.d.ts.map +1 -1
- package/sdk/dist/scoped.d.ts +9 -3
- package/sdk/dist/scoped.d.ts.map +1 -1
- package/sdk/dist/scoped.js +14 -2
- package/sdk/dist/scoped.js.map +1 -1
package/README.md
CHANGED
|
@@ -125,6 +125,10 @@ import { db, adminDb, getUser, email, ai } from "@run402/functions";
|
|
|
125
125
|
|
|
126
126
|
`db(req)` is the caller-context client (RLS applies); `adminDb()` bypasses RLS for platform-authored writes.
|
|
127
127
|
|
|
128
|
+
### Same-origin web routes
|
|
129
|
+
|
|
130
|
+
`run402 deploy apply` accepts `routes.replace` as an array of route entries, not a path-keyed map. Use exact `/admin` plus final-wildcard `/admin/*` for a routed section root, and target a function deployed in the same release. Routed functions use Node 22 Fetch Request -> Response; `req.url` is the full public URL on managed subdomains, deployment hosts, and verified custom domains, so OAuth redirect URIs can be derived from `new URL(req.url).origin`. Direct `/functions/v1/:name` remains API-key protected. Runtime route failure codes to branch on: `ROUTE_MANIFEST_LOAD_FAILED` (manifest/propagation), `ROUTED_INVOKE_WORKER_SECRET_MISSING` (custom-domain Worker secret), `ROUTED_INVOKE_AUTH_FAILED` (internal invoke signature), `ROUTED_ROUTE_STALE` (selected route failed release revalidation), `ROUTE_METHOD_NOT_ALLOWED` (method mismatch), and `ROUTED_RESPONSE_TOO_LARGE` (body over 6 MiB).
|
|
131
|
+
|
|
128
132
|
### Secrets
|
|
129
133
|
|
|
130
134
|
```bash
|
package/lib/allowance.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readAllowance, saveAllowance, ALLOWANCE_FILE
|
|
1
|
+
import { readAllowance, saveAllowance, ALLOWANCE_FILE } from "./config.mjs";
|
|
2
2
|
import { getSdk } from "./sdk.mjs";
|
|
3
3
|
import { reportSdkError, fail } from "./sdk-errors.mjs";
|
|
4
4
|
|
|
@@ -173,14 +173,11 @@ async function fund() {
|
|
|
173
173
|
const client = createPublicClient({ chain: baseSepolia, transport: http() });
|
|
174
174
|
const before = await readUsdcBalance(client, USDC_SEPOLIA, w.address).catch(() => 0);
|
|
175
175
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
message: data?.message || "Faucet request failed",
|
|
182
|
-
details: { http: res.status, ...data },
|
|
183
|
-
});
|
|
176
|
+
let data;
|
|
177
|
+
try {
|
|
178
|
+
data = await getSdk().allowance.faucet(w.address);
|
|
179
|
+
} catch (err) {
|
|
180
|
+
reportSdkError(err);
|
|
184
181
|
}
|
|
185
182
|
|
|
186
183
|
const MAX_WAIT = 30;
|
|
@@ -228,11 +225,9 @@ async function balance() {
|
|
|
228
225
|
readUsdcBalance(mainnetClient, USDC_MAINNET, w.address).catch(() => null),
|
|
229
226
|
readUsdcBalance(sepoliaClient, USDC_SEPOLIA, w.address).catch(() => null),
|
|
230
227
|
readUsdcBalance(tempoClient, PATH_USD, w.address).catch(() => null),
|
|
231
|
-
|
|
228
|
+
getSdk().billing.checkBalance(w.address).catch(() => null),
|
|
232
229
|
]);
|
|
233
230
|
|
|
234
|
-
const billing = billingRes.ok ? await billingRes.json() : null;
|
|
235
|
-
|
|
236
231
|
console.log(JSON.stringify({
|
|
237
232
|
address: w.address,
|
|
238
233
|
rail: w.rail || "x402",
|
|
@@ -241,7 +236,7 @@ async function balance() {
|
|
|
241
236
|
"base-sepolia_usd_micros": sepoliaUsdc,
|
|
242
237
|
"tempo-moderato_pathusd_micros": tempoPathUsd,
|
|
243
238
|
},
|
|
244
|
-
run402:
|
|
239
|
+
run402: billingRes ? { balance_usd_micros: billingRes.available_usd_micros } : "no billing account",
|
|
245
240
|
}, null, 2));
|
|
246
241
|
}
|
|
247
242
|
|
|
@@ -274,14 +269,12 @@ async function checkout(args) {
|
|
|
274
269
|
hint: "e.g. --amount 5000000 for $5",
|
|
275
270
|
});
|
|
276
271
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
|
|
284
|
-
console.log(JSON.stringify(data, null, 2));
|
|
272
|
+
try {
|
|
273
|
+
const data = await getSdk().billing.createCheckout(w.address, amount);
|
|
274
|
+
console.log(JSON.stringify(data, null, 2));
|
|
275
|
+
} catch (err) {
|
|
276
|
+
reportSdkError(err);
|
|
277
|
+
}
|
|
285
278
|
}
|
|
286
279
|
|
|
287
280
|
async function history(args) {
|
|
@@ -297,10 +290,12 @@ async function history(args) {
|
|
|
297
290
|
for (let i = 0; i < args.length; i++) {
|
|
298
291
|
if (args[i] === "--limit" && args[i + 1]) limit = parseInt(args[++i], 10);
|
|
299
292
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
293
|
+
try {
|
|
294
|
+
const data = await getSdk().billing.history(w.address, limit);
|
|
295
|
+
console.log(JSON.stringify(data, null, 2));
|
|
296
|
+
} catch (err) {
|
|
297
|
+
reportSdkError(err);
|
|
298
|
+
}
|
|
304
299
|
}
|
|
305
300
|
|
|
306
301
|
export async function run(sub, args) {
|
package/lib/auth.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveProjectId } from "./config.mjs";
|
|
2
2
|
import { getSdk } from "./sdk.mjs";
|
|
3
3
|
import { reportSdkError, fail } from "./sdk-errors.mjs";
|
|
4
4
|
|
|
@@ -546,22 +546,13 @@ async function deletePasskey(args) {
|
|
|
546
546
|
}
|
|
547
547
|
|
|
548
548
|
async function providers(args) {
|
|
549
|
-
// `providers` isn't in the pilot SDK surface — keep the direct fetch.
|
|
550
549
|
const projectId = resolveProjectId(parseFlag(args, "--project"));
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
},
|
|
558
|
-
});
|
|
559
|
-
const data = await res.json();
|
|
560
|
-
if (!res.ok) {
|
|
561
|
-
console.error(JSON.stringify({ status: "error", http: res.status, ...data }));
|
|
562
|
-
process.exit(1);
|
|
563
|
-
}
|
|
564
|
-
console.log(JSON.stringify(data, null, 2));
|
|
550
|
+
try {
|
|
551
|
+
const data = await getSdk().auth.providers(projectId);
|
|
552
|
+
console.log(JSON.stringify(data, null, 2));
|
|
553
|
+
} catch (err) {
|
|
554
|
+
reportSdkError(err);
|
|
555
|
+
}
|
|
565
556
|
}
|
|
566
557
|
|
|
567
558
|
export async function run(sub, args) {
|
package/lib/billing.mjs
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { API } from "./config.mjs";
|
|
2
1
|
import { getSdk } from "./sdk.mjs";
|
|
3
2
|
import { reportSdkError, fail } from "./sdk-errors.mjs";
|
|
4
3
|
|
|
@@ -224,7 +223,6 @@ async function autoRecharge(args) {
|
|
|
224
223
|
}
|
|
225
224
|
|
|
226
225
|
async function balance(args) {
|
|
227
|
-
// Accepts email OR wallet — SDK only models wallet, so keep direct fetch.
|
|
228
226
|
const id = args[0];
|
|
229
227
|
if (!id) {
|
|
230
228
|
fail({
|
|
@@ -233,10 +231,12 @@ async function balance(args) {
|
|
|
233
231
|
hint: "run402 billing balance <email-or-wallet>",
|
|
234
232
|
});
|
|
235
233
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
234
|
+
try {
|
|
235
|
+
const data = await getSdk().billing.getAccount(id);
|
|
236
|
+
console.log(JSON.stringify(data, null, 2));
|
|
237
|
+
} catch (err) {
|
|
238
|
+
reportSdkError(err);
|
|
239
|
+
}
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
async function history(args) {
|
|
@@ -249,10 +249,12 @@ async function history(args) {
|
|
|
249
249
|
});
|
|
250
250
|
}
|
|
251
251
|
const limit = parseFlag(args, "--limit") || "50";
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
252
|
+
try {
|
|
253
|
+
const data = await getSdk().billing.getHistory(id, Number(limit));
|
|
254
|
+
console.log(JSON.stringify(data, null, 2));
|
|
255
|
+
} catch (err) {
|
|
256
|
+
reportSdkError(err);
|
|
257
|
+
}
|
|
256
258
|
}
|
|
257
259
|
|
|
258
260
|
export async function run(sub, args) {
|
package/lib/blob.mjs
CHANGED
|
@@ -34,7 +34,7 @@ import { basename, dirname, join, resolve as resolvePath } from "node:path";
|
|
|
34
34
|
import { homedir } from "node:os";
|
|
35
35
|
import { pipeline } from "node:stream/promises";
|
|
36
36
|
|
|
37
|
-
import {
|
|
37
|
+
import { resolveProjectId } from "./config.mjs";
|
|
38
38
|
import { getSdk } from "./sdk.mjs";
|
|
39
39
|
import { reportSdkError, fail } from "./sdk-errors.mjs";
|
|
40
40
|
import { assertKnownFlags, hasHelp, normalizeArgv, parseIntegerFlag } from "./argparse.mjs";
|
|
@@ -190,19 +190,6 @@ function die(msg, exit_code = 1) {
|
|
|
190
190
|
fail({ code: "BAD_USAGE", message: msg, exit_code });
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
function dieApiFailure(prefix, http, body) {
|
|
194
|
-
if (body && typeof body === "object" && !Array.isArray(body)) {
|
|
195
|
-
const envelope = { status: "error", http, ...body };
|
|
196
|
-
if (!envelope.message && envelope.error) envelope.message = envelope.error;
|
|
197
|
-
console.error(JSON.stringify(envelope));
|
|
198
|
-
process.exit(1);
|
|
199
|
-
}
|
|
200
|
-
fail({
|
|
201
|
-
message: `${prefix}: HTTP ${http}${typeof body === "string" && body ? `: ${body.slice(0, 500)}` : ""}`,
|
|
202
|
-
details: { http },
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
|
|
206
193
|
function parseArgs(rawArgs) {
|
|
207
194
|
const args = normalizeArgv(rawArgs);
|
|
208
195
|
const valueFlags = ["--project", "--key", "--content-type", "--concurrency", "--prefix", "--limit", "--output", "-o", "--ttl"];
|
|
@@ -305,7 +292,7 @@ function findResumableStateForFile(projectId, localPath, key) {
|
|
|
305
292
|
// put
|
|
306
293
|
// ---------------------------------------------------------------------------
|
|
307
294
|
|
|
308
|
-
async function putOne(
|
|
295
|
+
async function putOne(projectId, filePath, opts) {
|
|
309
296
|
const stat = statSync(filePath);
|
|
310
297
|
const size = stat.size;
|
|
311
298
|
const destKey = computeDestKey(filePath, opts.key);
|
|
@@ -317,15 +304,21 @@ async function putOne(project, filePath, opts) {
|
|
|
317
304
|
|
|
318
305
|
// Attempt to resume
|
|
319
306
|
let state = opts.resume
|
|
320
|
-
? findResumableStateForFile(
|
|
307
|
+
? findResumableStateForFile(projectId, absLocal, destKey)
|
|
321
308
|
: null;
|
|
322
309
|
let initRes;
|
|
323
310
|
if (state) {
|
|
324
311
|
// Re-poll the session; if it's still active, resume. Otherwise start fresh.
|
|
325
|
-
const poll = await
|
|
326
|
-
if (poll.status ===
|
|
312
|
+
const poll = await getSdk().blobs.getUploadSession(projectId, state.upload_id);
|
|
313
|
+
if (poll.status === "active") {
|
|
327
314
|
log(opts, { event: "resume", upload_id: state.upload_id, key: destKey });
|
|
328
|
-
initRes = {
|
|
315
|
+
initRes = {
|
|
316
|
+
upload_id: state.upload_id,
|
|
317
|
+
mode: poll.mode ?? state.mode,
|
|
318
|
+
parts: poll.parts ?? state.parts,
|
|
319
|
+
part_count: poll.part_count ?? state.part_count,
|
|
320
|
+
part_size_bytes: poll.part_size_bytes ?? state.part_size_bytes,
|
|
321
|
+
};
|
|
329
322
|
} else {
|
|
330
323
|
removeState(state.upload_id);
|
|
331
324
|
state = null;
|
|
@@ -333,7 +326,7 @@ async function putOne(project, filePath, opts) {
|
|
|
333
326
|
}
|
|
334
327
|
|
|
335
328
|
if (!state) {
|
|
336
|
-
|
|
329
|
+
initRes = await getSdk().blobs.initUploadSession(projectId, {
|
|
337
330
|
key: destKey,
|
|
338
331
|
size_bytes: size,
|
|
339
332
|
content_type: opts.contentType ?? guessContentType(destKey),
|
|
@@ -341,11 +334,9 @@ async function putOne(project, filePath, opts) {
|
|
|
341
334
|
immutable: opts.immutable,
|
|
342
335
|
sha256,
|
|
343
336
|
});
|
|
344
|
-
|
|
345
|
-
initRes = init.body;
|
|
346
|
-
saveState({
|
|
337
|
+
state = {
|
|
347
338
|
upload_id: initRes.upload_id,
|
|
348
|
-
project_id:
|
|
339
|
+
project_id: projectId,
|
|
349
340
|
local_path: absLocal,
|
|
350
341
|
key: destKey,
|
|
351
342
|
mode: initRes.mode,
|
|
@@ -355,8 +346,8 @@ async function putOne(project, filePath, opts) {
|
|
|
355
346
|
parts_done: {},
|
|
356
347
|
sha256,
|
|
357
348
|
started_at: new Date().toISOString(),
|
|
358
|
-
}
|
|
359
|
-
|
|
349
|
+
};
|
|
350
|
+
if (opts.resume) saveState(state);
|
|
360
351
|
}
|
|
361
352
|
|
|
362
353
|
// Upload parts with concurrency limit. For single-PUT mode part_count=1 and
|
|
@@ -378,7 +369,7 @@ async function putOne(project, filePath, opts) {
|
|
|
378
369
|
const { etag } = await putPart(filePath, part);
|
|
379
370
|
etags[part.part_number - 1] = { etag };
|
|
380
371
|
state.parts_done[String(part.part_number)] = { etag };
|
|
381
|
-
saveState(state);
|
|
372
|
+
if (opts.resume) saveState(state);
|
|
382
373
|
log(opts, { event: "part", upload_id: state.upload_id, part_number: part.part_number, etag });
|
|
383
374
|
});
|
|
384
375
|
|
|
@@ -386,12 +377,13 @@ async function putOne(project, filePath, opts) {
|
|
|
386
377
|
const body = initRes.mode === "multipart"
|
|
387
378
|
? { parts: etags.map((e, i) => ({ part_number: i + 1, etag: e.etag })) }
|
|
388
379
|
: {};
|
|
389
|
-
const
|
|
390
|
-
|
|
380
|
+
const result = await getSdk().blobs.completeUploadSession(projectId, state.upload_id, body, {
|
|
381
|
+
contentType: opts.contentType ?? guessContentType(destKey),
|
|
382
|
+
});
|
|
391
383
|
|
|
392
384
|
removeState(state.upload_id);
|
|
393
|
-
log(opts, { event: "done", ...
|
|
394
|
-
return
|
|
385
|
+
log(opts, { event: "done", ...result });
|
|
386
|
+
return result;
|
|
395
387
|
}
|
|
396
388
|
|
|
397
389
|
function computeDestKey(filePath, keyOpt) {
|
|
@@ -443,7 +435,7 @@ function isSettled(p) {
|
|
|
443
435
|
async function put(projectId, argv) {
|
|
444
436
|
const opts = parseArgs(argv);
|
|
445
437
|
opts.project = opts.project || projectId;
|
|
446
|
-
const
|
|
438
|
+
const resolvedId = resolveProjectId(opts.project);
|
|
447
439
|
|
|
448
440
|
if (opts.positional.length === 0) die("At least one file path is required");
|
|
449
441
|
if (opts.immutable && opts.positional.length > 1 && opts.key && !opts.key.endsWith("/")) {
|
|
@@ -453,8 +445,12 @@ async function put(projectId, argv) {
|
|
|
453
445
|
const results = [];
|
|
454
446
|
for (const filePath of opts.positional) {
|
|
455
447
|
if (!existsSync(filePath)) die(`File not found: ${filePath}`);
|
|
456
|
-
|
|
457
|
-
|
|
448
|
+
try {
|
|
449
|
+
const r = await putOne(resolvedId, filePath, opts);
|
|
450
|
+
results.push({ file: filePath, ...r });
|
|
451
|
+
} catch (err) {
|
|
452
|
+
reportSdkError(err);
|
|
453
|
+
}
|
|
458
454
|
}
|
|
459
455
|
if (!opts.json) console.log(JSON.stringify(results, null, 2));
|
|
460
456
|
}
|
|
@@ -576,11 +572,6 @@ async function sign(projectId, argv) {
|
|
|
576
572
|
// Shared helpers
|
|
577
573
|
// ---------------------------------------------------------------------------
|
|
578
574
|
|
|
579
|
-
function encodeKey(key) {
|
|
580
|
-
// Encode each path segment; preserve `/` as separator.
|
|
581
|
-
return key.split("/").map(encodeURIComponent).join("/");
|
|
582
|
-
}
|
|
583
|
-
|
|
584
575
|
function guessContentType(key) {
|
|
585
576
|
const ext = key.slice(key.lastIndexOf(".") + 1).toLowerCase();
|
|
586
577
|
const map = {
|
|
@@ -594,21 +585,6 @@ function guessContentType(key) {
|
|
|
594
585
|
return map[ext] ?? "application/octet-stream";
|
|
595
586
|
}
|
|
596
587
|
|
|
597
|
-
async function apiFetch(url, method, project, body) {
|
|
598
|
-
const res = await fetch(url, {
|
|
599
|
-
method,
|
|
600
|
-
headers: {
|
|
601
|
-
"content-type": "application/json",
|
|
602
|
-
apikey: project.anon_key,
|
|
603
|
-
Authorization: `Bearer ${project.anon_key}`,
|
|
604
|
-
},
|
|
605
|
-
body: body === null ? undefined : JSON.stringify(body ?? {}),
|
|
606
|
-
});
|
|
607
|
-
const txt = await res.text();
|
|
608
|
-
let parsed; try { parsed = txt ? JSON.parse(txt) : null; } catch { parsed = txt; }
|
|
609
|
-
return { status: res.status, body: parsed };
|
|
610
|
-
}
|
|
611
|
-
|
|
612
588
|
function log(opts, event) {
|
|
613
589
|
if (opts.json) console.log(JSON.stringify(event));
|
|
614
590
|
}
|
package/lib/contracts.mjs
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { findProject, API } from "./config.mjs";
|
|
2
1
|
import { getSdk } from "./sdk.mjs";
|
|
3
2
|
import { reportSdkError, fail, parseFlagJson } from "./sdk-errors.mjs";
|
|
4
3
|
|
|
@@ -138,7 +137,6 @@ function hasFlag(args, flag) {
|
|
|
138
137
|
}
|
|
139
138
|
|
|
140
139
|
async function provisionWallet(projectId, args) {
|
|
141
|
-
const p = findProject(projectId);
|
|
142
140
|
const chain = parseFlag(args, "--chain");
|
|
143
141
|
if (!chain) {
|
|
144
142
|
fail({
|
|
@@ -147,25 +145,19 @@ async function provisionWallet(projectId, args) {
|
|
|
147
145
|
});
|
|
148
146
|
}
|
|
149
147
|
const recovery = parseFlag(args, "--recovery");
|
|
150
|
-
// Soft default of one wallet — confirm if project already has one.
|
|
151
|
-
|
|
152
|
-
// a primary API call.
|
|
148
|
+
// Soft default of one wallet — confirm if project already has one.
|
|
149
|
+
let activeWallets = null;
|
|
153
150
|
try {
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
});
|
|
157
|
-
if (listRes.ok) {
|
|
158
|
-
const list = await listRes.json();
|
|
159
|
-
const active = (list.wallets || []).filter((w) => w.status === "active");
|
|
160
|
-
if (active.length >= 1 && !hasFlag(args, "--yes")) {
|
|
161
|
-
fail({
|
|
162
|
-
code: "CONFIRMATION_REQUIRED",
|
|
163
|
-
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.`,
|
|
164
|
-
details: { active_wallets: active.length },
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
}
|
|
151
|
+
const list = await getSdk().contracts.listWallets(projectId);
|
|
152
|
+
activeWallets = (list.wallets || []).filter((w) => w.status === "active").length;
|
|
168
153
|
} catch { /* best-effort */ }
|
|
154
|
+
if (activeWallets !== null && activeWallets >= 1 && !hasFlag(args, "--yes")) {
|
|
155
|
+
fail({
|
|
156
|
+
code: "CONFIRMATION_REQUIRED",
|
|
157
|
+
message: `This project already has ${activeWallets} active wallet(s). Adding another costs $0.04/day each ($1.20/month). Re-run with --yes to confirm.`,
|
|
158
|
+
details: { active_wallets: activeWallets },
|
|
159
|
+
});
|
|
160
|
+
}
|
|
169
161
|
|
|
170
162
|
try {
|
|
171
163
|
const data = await getSdk().contracts.provisionWallet(projectId, {
|
package/lib/deploy-v2.mjs
CHANGED
|
@@ -62,7 +62,7 @@ Complete static site + function + route manifest:
|
|
|
62
62
|
"replace": {
|
|
63
63
|
"api": {
|
|
64
64
|
"runtime": "node22",
|
|
65
|
-
"source": { "data": "
|
|
65
|
+
"source": { "data": "export default async function handler(req) { const url = new URL(req.url); return Response.json({ ok: true, path: url.pathname }); }" }
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
},
|
|
@@ -93,7 +93,10 @@ Patch examples (only the listed file changes):
|
|
|
93
93
|
Routes:
|
|
94
94
|
Omit routes or pass "routes": null to carry forward base routes.
|
|
95
95
|
Use "routes": { "replace": [] } to clear dynamic routes.
|
|
96
|
+
Route entries are array-based, not path-keyed maps. Use exact /admin plus final-wildcard /admin/* for a routed section root.
|
|
97
|
+
Routed functions use Node 22 Fetch Request -> Response. req.url is the full public URL on managed domains, deployment hosts, and verified custom domains.
|
|
96
98
|
Routes activate atomically with the release. Direct /functions/v1/:name remains API-key protected.
|
|
99
|
+
Runtime route failure codes: ROUTE_MANIFEST_LOAD_FAILED, ROUTED_INVOKE_WORKER_SECRET_MISSING, ROUTED_INVOKE_AUTH_FAILED, ROUTED_ROUTE_STALE, ROUTE_METHOD_NOT_ALLOWED, ROUTED_RESPONSE_TOO_LARGE.
|
|
97
100
|
`;
|
|
98
101
|
|
|
99
102
|
const RESUME_HELP = `run402 deploy resume — Resume a stuck deploy operation
|
package/lib/init.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { readAllowance, saveAllowance, loadKeyStore, CONFIG_DIR
|
|
2
|
-
import {
|
|
1
|
+
import { readAllowance, saveAllowance, loadKeyStore, CONFIG_DIR } from "./config.mjs";
|
|
2
|
+
import { getSdk } from "./sdk.mjs";
|
|
3
3
|
import { mkdirSync } from "fs";
|
|
4
4
|
|
|
5
5
|
const USDC_ABI = [{ name: "balanceOf", type: "function", stateMutability: "view", inputs: [{ name: "account", type: "address" }], outputs: [{ name: "", type: "uint256" }] }];
|
|
@@ -39,6 +39,11 @@ Run this once to get started, or again to check your setup.
|
|
|
39
39
|
|
|
40
40
|
function short(addr) { return addr.slice(0, 6) + "..." + addr.slice(-4); }
|
|
41
41
|
|
|
42
|
+
function errorMessage(err) {
|
|
43
|
+
if (err?.body && typeof err.body === "object") return err.body.message || err.body.error || err.message;
|
|
44
|
+
return err?.message || String(err);
|
|
45
|
+
}
|
|
46
|
+
|
|
42
47
|
export async function run(args = []) {
|
|
43
48
|
if (args.includes("--help") || args.includes("-h")) { console.log(HELP); process.exit(0); }
|
|
44
49
|
const jsonMode = args.includes("--json");
|
|
@@ -178,12 +183,8 @@ export async function run(args = []) {
|
|
|
178
183
|
|
|
179
184
|
if (balance === 0) {
|
|
180
185
|
line("Balance", "0 USDC — requesting faucet...");
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
headers: { "Content-Type": "application/json" },
|
|
184
|
-
body: JSON.stringify({ address: allowance.address }),
|
|
185
|
-
});
|
|
186
|
-
if (res.ok) {
|
|
186
|
+
try {
|
|
187
|
+
await getSdk().allowance.faucet(allowance.address);
|
|
187
188
|
// Poll for up to 30s
|
|
188
189
|
for (let i = 0; i < 30; i++) {
|
|
189
190
|
await new Promise(r => setTimeout(r, 1000));
|
|
@@ -200,10 +201,8 @@ export async function run(args = []) {
|
|
|
200
201
|
} else {
|
|
201
202
|
line("Balance", "faucet sent — not yet confirmed on-chain");
|
|
202
203
|
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const msg = data.error || data.message || `HTTP ${res.status}`;
|
|
206
|
-
line("Balance", `faucet failed: ${msg}`);
|
|
204
|
+
} catch (err) {
|
|
205
|
+
line("Balance", `faucet failed: ${errorMessage(err)}`);
|
|
207
206
|
}
|
|
208
207
|
} else {
|
|
209
208
|
line("Balance", `${(balance / 1e6).toFixed(2)} USDC`);
|
|
@@ -222,13 +221,7 @@ export async function run(args = []) {
|
|
|
222
221
|
const store = loadKeyStore();
|
|
223
222
|
let tierInfo = null;
|
|
224
223
|
try {
|
|
225
|
-
|
|
226
|
-
if (authHeaders) {
|
|
227
|
-
const res = await fetch(`${API}/tiers/v1/status`, {
|
|
228
|
-
headers: { ...authHeaders },
|
|
229
|
-
});
|
|
230
|
-
if (res.ok) tierInfo = await res.json();
|
|
231
|
-
}
|
|
224
|
+
tierInfo = await getSdk().tier.status();
|
|
232
225
|
} catch {}
|
|
233
226
|
|
|
234
227
|
if (tierInfo && tierInfo.tier && tierInfo.active) {
|
package/lib/projects.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from "fs";
|
|
2
|
-
import {
|
|
2
|
+
import { loadKeyStore, API, allowanceAuthHeaders, resolveProjectId, getActiveProjectId } from "./config.mjs";
|
|
3
3
|
import { getSdk } from "./sdk.mjs";
|
|
4
4
|
import { reportSdkError, fail, parseFlagJson } from "./sdk-errors.mjs";
|
|
5
5
|
import { assertKnownFlags, failBadProjectId, flagValue, hasHelp, normalizeArgv, positionalArgs, resolvePositionalProject, validateRegularFile } from "./argparse.mjs";
|
|
@@ -212,7 +212,6 @@ async function applyExpose(projectId, args = []) {
|
|
|
212
212
|
else if (!inline && !args[i].startsWith("--")) { inline = args[i]; }
|
|
213
213
|
}
|
|
214
214
|
if (file) validateRegularFile(file, "--file");
|
|
215
|
-
const p = findProject(projectId);
|
|
216
215
|
const raw = file ? readFileSync(file, "utf-8") : inline;
|
|
217
216
|
if (!raw) {
|
|
218
217
|
fail({
|
|
@@ -230,24 +229,21 @@ async function applyExpose(projectId, args = []) {
|
|
|
230
229
|
details: { parse_error: err.message },
|
|
231
230
|
});
|
|
232
231
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
|
|
240
|
-
console.log(JSON.stringify(data, null, 2));
|
|
232
|
+
try {
|
|
233
|
+
const data = await getSdk().projects.applyExpose(projectId, manifest);
|
|
234
|
+
console.log(JSON.stringify(data, null, 2));
|
|
235
|
+
} catch (err) {
|
|
236
|
+
reportSdkError(err);
|
|
237
|
+
}
|
|
241
238
|
}
|
|
242
239
|
|
|
243
240
|
async function getExpose(projectId) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
console.log(JSON.stringify(data, null, 2));
|
|
241
|
+
try {
|
|
242
|
+
const data = await getSdk().projects.getExpose(projectId);
|
|
243
|
+
console.log(JSON.stringify(data, null, 2));
|
|
244
|
+
} catch (err) {
|
|
245
|
+
reportSdkError(err);
|
|
246
|
+
}
|
|
251
247
|
}
|
|
252
248
|
|
|
253
249
|
async function list() {
|
|
@@ -292,7 +288,6 @@ async function sqlCmd(projectId, args = []) {
|
|
|
292
288
|
else if (!query && !args[i].startsWith("--")) { query = args[i]; }
|
|
293
289
|
}
|
|
294
290
|
if (file) validateRegularFile(file, "--file");
|
|
295
|
-
const p = findProject(projectId);
|
|
296
291
|
const sql = file ? readFileSync(file, "utf-8") : query;
|
|
297
292
|
if (!sql) {
|
|
298
293
|
fail({
|
|
@@ -311,13 +306,12 @@ async function sqlCmd(projectId, args = []) {
|
|
|
311
306
|
});
|
|
312
307
|
}
|
|
313
308
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
console.log(JSON.stringify(data, null, 2));
|
|
309
|
+
try {
|
|
310
|
+
const data = await getSdk().projects.sql(projectId, sql, params);
|
|
311
|
+
console.log(JSON.stringify(data, null, 2));
|
|
312
|
+
} catch (err) {
|
|
313
|
+
reportSdkError(err);
|
|
314
|
+
}
|
|
321
315
|
}
|
|
322
316
|
|
|
323
317
|
async function rest(projectId, table, queryParams) {
|
|
@@ -328,11 +322,12 @@ async function rest(projectId, table, queryParams) {
|
|
|
328
322
|
hint: "Run 'run402 projects schema <id>' to list tables.",
|
|
329
323
|
});
|
|
330
324
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
325
|
+
try {
|
|
326
|
+
const data = await getSdk().projects.rest(projectId, table, queryParams);
|
|
327
|
+
console.log(JSON.stringify(data, null, 2));
|
|
328
|
+
} catch (err) {
|
|
329
|
+
reportSdkError(err);
|
|
330
|
+
}
|
|
336
331
|
}
|
|
337
332
|
|
|
338
333
|
async function usage(projectId) {
|
|
@@ -420,15 +415,12 @@ async function promoteUser(projectId, email) {
|
|
|
420
415
|
hint: "run402 projects promote-user <project_id> <email>",
|
|
421
416
|
});
|
|
422
417
|
}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
}
|
|
429
|
-
const data = await res.json();
|
|
430
|
-
if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
|
|
431
|
-
console.log(JSON.stringify(data, null, 2));
|
|
418
|
+
try {
|
|
419
|
+
await getSdk().projects.promoteUser(projectId, email);
|
|
420
|
+
console.log(JSON.stringify({ status: "ok", email }));
|
|
421
|
+
} catch (err) {
|
|
422
|
+
reportSdkError(err);
|
|
423
|
+
}
|
|
432
424
|
}
|
|
433
425
|
|
|
434
426
|
async function demoteUser(projectId, email) {
|
|
@@ -439,15 +431,12 @@ async function demoteUser(projectId, email) {
|
|
|
439
431
|
hint: "run402 projects demote-user <project_id> <email>",
|
|
440
432
|
});
|
|
441
433
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
}
|
|
448
|
-
const data = await res.json();
|
|
449
|
-
if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
|
|
450
|
-
console.log(JSON.stringify(data, null, 2));
|
|
434
|
+
try {
|
|
435
|
+
await getSdk().projects.demoteUser(projectId, email);
|
|
436
|
+
console.log(JSON.stringify({ status: "ok", email }));
|
|
437
|
+
} catch (err) {
|
|
438
|
+
reportSdkError(err);
|
|
439
|
+
}
|
|
451
440
|
}
|
|
452
441
|
|
|
453
442
|
async function deleteProject(projectId, args = []) {
|