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/projects.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { readFileSync } from "fs";
|
|
2
2
|
import { findProject, loadKeyStore, API, allowanceAuthHeaders, resolveProjectId, getActiveProjectId } from "./config.mjs";
|
|
3
3
|
import { getSdk } from "./sdk.mjs";
|
|
4
|
-
import { reportSdkError } from "./sdk-errors.mjs";
|
|
4
|
+
import { reportSdkError, fail, parseFlagJson } from "./sdk-errors.mjs";
|
|
5
|
+
import { assertKnownFlags, failBadProjectId, hasHelp, normalizeArgv, positionalArgs } from "./argparse.mjs";
|
|
5
6
|
|
|
6
7
|
const HELP = `run402 projects — Manage your deployed Run402 projects
|
|
7
8
|
|
|
@@ -33,17 +34,17 @@ Examples:
|
|
|
33
34
|
run402 projects provision --tier hobby --name my-app
|
|
34
35
|
run402 projects use prj_abc123
|
|
35
36
|
run402 projects list
|
|
36
|
-
run402 projects info
|
|
37
|
-
run402 projects sql
|
|
38
|
-
run402 projects sql
|
|
39
|
-
run402 projects sql
|
|
40
|
-
run402 projects rest
|
|
41
|
-
run402 projects usage
|
|
42
|
-
run402 projects schema
|
|
43
|
-
run402 projects apply-expose
|
|
44
|
-
run402 projects get-expose
|
|
45
|
-
run402 projects keys
|
|
46
|
-
run402 projects delete
|
|
37
|
+
run402 projects info prj_abc123
|
|
38
|
+
run402 projects sql prj_abc123 "SELECT * FROM users LIMIT 5"
|
|
39
|
+
run402 projects sql prj_abc123 "SELECT * FROM users WHERE id = $1" --params '[42]'
|
|
40
|
+
run402 projects sql prj_abc123 --file setup.sql
|
|
41
|
+
run402 projects rest prj_abc123 users "limit=10&select=id,name"
|
|
42
|
+
run402 projects usage prj_abc123
|
|
43
|
+
run402 projects schema prj_abc123
|
|
44
|
+
run402 projects apply-expose prj_abc123 --file manifest.json
|
|
45
|
+
run402 projects get-expose prj_abc123
|
|
46
|
+
run402 projects keys prj_abc123
|
|
47
|
+
run402 projects delete prj_abc123 --confirm
|
|
47
48
|
|
|
48
49
|
Notes:
|
|
49
50
|
- <id> is the project_id shown in 'run402 projects list' (prefix: 'prj_')
|
|
@@ -103,9 +104,9 @@ Options:
|
|
|
103
104
|
--params '<json>' JSON array of parameters for a parameterized query
|
|
104
105
|
|
|
105
106
|
Examples:
|
|
106
|
-
run402 projects sql
|
|
107
|
-
run402 projects sql
|
|
108
|
-
run402 projects sql
|
|
107
|
+
run402 projects sql prj_abc123 "SELECT * FROM users LIMIT 5"
|
|
108
|
+
run402 projects sql prj_abc123 "SELECT * FROM users WHERE id = $1" --params '[42]'
|
|
109
|
+
run402 projects sql prj_abc123 --file setup.sql
|
|
109
110
|
`,
|
|
110
111
|
};
|
|
111
112
|
|
|
@@ -153,12 +154,21 @@ async function applyExpose(projectId, args = []) {
|
|
|
153
154
|
}
|
|
154
155
|
const raw = file ? readFileSync(file, "utf-8") : inline;
|
|
155
156
|
if (!raw) {
|
|
156
|
-
|
|
157
|
-
|
|
157
|
+
fail({
|
|
158
|
+
code: "BAD_USAGE",
|
|
159
|
+
message: "Missing manifest.",
|
|
160
|
+
hint: "Provide inline JSON or use --file <path>",
|
|
161
|
+
});
|
|
158
162
|
}
|
|
159
163
|
let manifest;
|
|
160
164
|
try { manifest = JSON.parse(raw); }
|
|
161
|
-
catch
|
|
165
|
+
catch (err) {
|
|
166
|
+
fail({
|
|
167
|
+
code: "BAD_USAGE",
|
|
168
|
+
message: "Invalid JSON for manifest",
|
|
169
|
+
details: { parse_error: err.message },
|
|
170
|
+
});
|
|
171
|
+
}
|
|
162
172
|
const res = await fetch(`${API}/projects/v1/admin/${projectId}/expose`, {
|
|
163
173
|
method: "POST",
|
|
164
174
|
headers: { "Authorization": `Bearer ${p.service_key}`, "Content-Type": "application/json" },
|
|
@@ -222,11 +232,22 @@ async function sqlCmd(projectId, args = []) {
|
|
|
222
232
|
else if (!query && !args[i].startsWith("--")) { query = args[i]; }
|
|
223
233
|
}
|
|
224
234
|
const sql = file ? readFileSync(file, "utf-8") : query;
|
|
225
|
-
if (!sql) {
|
|
235
|
+
if (!sql) {
|
|
236
|
+
fail({
|
|
237
|
+
code: "BAD_USAGE",
|
|
238
|
+
message: "Missing SQL query.",
|
|
239
|
+
hint: "Provide inline or use --file <path>",
|
|
240
|
+
});
|
|
241
|
+
}
|
|
226
242
|
let params;
|
|
227
243
|
if (paramsRaw) {
|
|
228
|
-
|
|
229
|
-
if (!Array.isArray(params)) {
|
|
244
|
+
params = parseFlagJson("--params", paramsRaw);
|
|
245
|
+
if (!Array.isArray(params)) {
|
|
246
|
+
fail({
|
|
247
|
+
code: "BAD_USAGE",
|
|
248
|
+
message: "--params must be a JSON array, e.g. '[42, \"hello\"]'",
|
|
249
|
+
});
|
|
250
|
+
}
|
|
230
251
|
}
|
|
231
252
|
const useParams = params && params.length > 0;
|
|
232
253
|
const headers = { "Authorization": `Bearer ${p.service_key}`, "Content-Type": useParams ? "application/json" : "text/plain" };
|
|
@@ -264,7 +285,13 @@ async function schema(projectId) {
|
|
|
264
285
|
}
|
|
265
286
|
|
|
266
287
|
async function use(projectId) {
|
|
267
|
-
if (!projectId) {
|
|
288
|
+
if (!projectId) {
|
|
289
|
+
fail({
|
|
290
|
+
code: "BAD_USAGE",
|
|
291
|
+
message: "Missing <project_id>.",
|
|
292
|
+
hint: "run402 projects use <project_id>",
|
|
293
|
+
});
|
|
294
|
+
}
|
|
268
295
|
try {
|
|
269
296
|
await getSdk().projects.use(projectId);
|
|
270
297
|
console.log(JSON.stringify({ status: "ok", active_project_id: projectId }));
|
|
@@ -274,7 +301,13 @@ async function use(projectId) {
|
|
|
274
301
|
}
|
|
275
302
|
|
|
276
303
|
async function pin(projectId) {
|
|
277
|
-
if (!projectId) {
|
|
304
|
+
if (!projectId) {
|
|
305
|
+
fail({
|
|
306
|
+
code: "BAD_USAGE",
|
|
307
|
+
message: "Missing <project_id>.",
|
|
308
|
+
hint: "run402 projects pin <project_id>",
|
|
309
|
+
});
|
|
310
|
+
}
|
|
278
311
|
try {
|
|
279
312
|
const data = await getSdk().projects.pin(projectId);
|
|
280
313
|
console.log(JSON.stringify(data, null, 2));
|
|
@@ -284,7 +317,13 @@ async function pin(projectId) {
|
|
|
284
317
|
}
|
|
285
318
|
|
|
286
319
|
async function promoteUser(projectId, email) {
|
|
287
|
-
if (!email) {
|
|
320
|
+
if (!email) {
|
|
321
|
+
fail({
|
|
322
|
+
code: "BAD_USAGE",
|
|
323
|
+
message: "Missing <email>.",
|
|
324
|
+
hint: "run402 projects promote-user <project_id> <email>",
|
|
325
|
+
});
|
|
326
|
+
}
|
|
288
327
|
const p = findProject(projectId);
|
|
289
328
|
const res = await fetch(`${API}/projects/v1/admin/${projectId}/promote-user`, {
|
|
290
329
|
method: "POST",
|
|
@@ -297,7 +336,13 @@ async function promoteUser(projectId, email) {
|
|
|
297
336
|
}
|
|
298
337
|
|
|
299
338
|
async function demoteUser(projectId, email) {
|
|
300
|
-
if (!email) {
|
|
339
|
+
if (!email) {
|
|
340
|
+
fail({
|
|
341
|
+
code: "BAD_USAGE",
|
|
342
|
+
message: "Missing <email>.",
|
|
343
|
+
hint: "run402 projects demote-user <project_id> <email>",
|
|
344
|
+
});
|
|
345
|
+
}
|
|
301
346
|
const p = findProject(projectId);
|
|
302
347
|
const res = await fetch(`${API}/projects/v1/admin/${projectId}/demote-user`, {
|
|
303
348
|
method: "POST",
|
|
@@ -329,42 +374,72 @@ async function deleteProject(projectId, args = []) {
|
|
|
329
374
|
}
|
|
330
375
|
|
|
331
376
|
// Resolve a positional project_id argument with active-project fallback (GH-102).
|
|
332
|
-
//
|
|
333
|
-
//
|
|
334
|
-
|
|
335
|
-
// project_id and return args.slice(1) as remaining positionals.
|
|
336
|
-
function resolvePositionalProject(args) {
|
|
377
|
+
// Callers can tighten the legacy shorthand when a bare non-prj positional is
|
|
378
|
+
// more likely a mistyped project id than an argument for the active project.
|
|
379
|
+
function resolvePositionalProject(args, opts = {}) {
|
|
337
380
|
const first = Array.isArray(args) ? args[0] : undefined;
|
|
338
381
|
if (typeof first === "string" && first.startsWith("prj_")) {
|
|
339
382
|
return { projectId: first, rest: args.slice(1) };
|
|
340
383
|
}
|
|
384
|
+
if (
|
|
385
|
+
typeof first === "string" &&
|
|
386
|
+
first.length > 0 &&
|
|
387
|
+
!first.startsWith("-") &&
|
|
388
|
+
Array.isArray(opts.rejectBareFirstWhenFlagPresent) &&
|
|
389
|
+
opts.rejectBareFirstWhenFlagPresent.some((flag) => args.includes(flag))
|
|
390
|
+
) {
|
|
391
|
+
failBadProjectId(first);
|
|
392
|
+
}
|
|
393
|
+
if (typeof first === "string" && first.length > 0 && !first.startsWith("-") && opts.rejectBareFirst) {
|
|
394
|
+
failBadProjectId(first);
|
|
395
|
+
}
|
|
396
|
+
if (typeof first === "string" && first.length > 0 && !first.startsWith("-") && opts.maxBarePositionals !== undefined) {
|
|
397
|
+
const bare = positionalArgs(args, opts.valueFlags ?? []);
|
|
398
|
+
if (bare.length > opts.maxBarePositionals) {
|
|
399
|
+
failBadProjectId(first);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
341
402
|
return { projectId: resolveProjectId(null), rest: Array.isArray(args) ? args : [] };
|
|
342
403
|
}
|
|
343
404
|
|
|
405
|
+
const FLAGS_BY_SUB = {
|
|
406
|
+
provision: { known: ["--tier", "--name"], values: ["--tier", "--name"] },
|
|
407
|
+
sql: { known: ["--file", "--params"], values: ["--file", "--params"] },
|
|
408
|
+
"apply-expose": { known: ["--file"], values: ["--file"] },
|
|
409
|
+
delete: { known: ["--confirm"], values: [] },
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
function validateFlags(sub, args) {
|
|
413
|
+
const spec = FLAGS_BY_SUB[sub] ?? { known: [], values: [] };
|
|
414
|
+
assertKnownFlags(args, [...spec.known, "--help", "-h"], spec.values);
|
|
415
|
+
}
|
|
416
|
+
|
|
344
417
|
export async function run(sub, args) {
|
|
345
418
|
if (!sub || sub === '--help' || sub === '-h') {
|
|
346
419
|
console.log(HELP);
|
|
347
420
|
process.exit(0);
|
|
348
421
|
}
|
|
349
|
-
|
|
422
|
+
args = normalizeArgv(args);
|
|
423
|
+
if (Array.isArray(args) && hasHelp(args)) {
|
|
350
424
|
console.log(SUB_HELP[sub] || HELP);
|
|
351
425
|
process.exit(0);
|
|
352
426
|
}
|
|
427
|
+
validateFlags(sub, args);
|
|
353
428
|
switch (sub) {
|
|
354
429
|
case "quote": await quote(); break;
|
|
355
430
|
case "provision": await provision(args); break;
|
|
356
431
|
case "use": await use(args[0]); break;
|
|
357
432
|
case "list": await list(); break;
|
|
358
|
-
case "info": { const { projectId } = resolvePositionalProject(args); await info(projectId); break; }
|
|
359
|
-
case "keys": { const { projectId } = resolvePositionalProject(args); await keys(projectId); break; }
|
|
360
|
-
case "sql": { const { projectId, rest } = resolvePositionalProject(args); await sqlCmd(projectId, rest); break; }
|
|
433
|
+
case "info": { const { projectId } = resolvePositionalProject(args, { rejectBareFirst: true }); await info(projectId); break; }
|
|
434
|
+
case "keys": { const { projectId } = resolvePositionalProject(args, { rejectBareFirst: true }); await keys(projectId); break; }
|
|
435
|
+
case "sql": { const { projectId, rest } = resolvePositionalProject(args, { maxBarePositionals: 1, valueFlags: FLAGS_BY_SUB.sql.values, rejectBareFirstWhenFlagPresent: ["--file"] }); await sqlCmd(projectId, rest); break; }
|
|
361
436
|
case "rest": { const { projectId, rest: restArgs } = resolvePositionalProject(args); await rest(projectId, restArgs[0], restArgs[1]); break; }
|
|
362
|
-
case "usage": { const { projectId } = resolvePositionalProject(args); await usage(projectId); break; }
|
|
363
|
-
case "schema": { const { projectId } = resolvePositionalProject(args); await schema(projectId); break; }
|
|
364
|
-
case "apply-expose": { const { projectId, rest } = resolvePositionalProject(args); await applyExpose(projectId, rest); break; }
|
|
365
|
-
case "get-expose": { const { projectId } = resolvePositionalProject(args); await getExpose(projectId); break; }
|
|
366
|
-
case "delete": { const { projectId, rest } = resolvePositionalProject(args); await deleteProject(projectId, rest); break; }
|
|
367
|
-
case "pin": { const { projectId } = resolvePositionalProject(args); await pin(projectId); break; }
|
|
437
|
+
case "usage": { const { projectId } = resolvePositionalProject(args, { rejectBareFirst: true }); await usage(projectId); break; }
|
|
438
|
+
case "schema": { const { projectId } = resolvePositionalProject(args, { rejectBareFirst: true }); await schema(projectId); break; }
|
|
439
|
+
case "apply-expose": { const { projectId, rest } = resolvePositionalProject(args, { maxBarePositionals: 1, valueFlags: FLAGS_BY_SUB["apply-expose"].values, rejectBareFirstWhenFlagPresent: ["--file"] }); await applyExpose(projectId, rest); break; }
|
|
440
|
+
case "get-expose": { const { projectId } = resolvePositionalProject(args, { rejectBareFirst: true }); await getExpose(projectId); break; }
|
|
441
|
+
case "delete": { const { projectId, rest } = resolvePositionalProject(args, { rejectBareFirst: true }); await deleteProject(projectId, rest); break; }
|
|
442
|
+
case "pin": { const { projectId } = resolvePositionalProject(args, { rejectBareFirst: true }); await pin(projectId); break; }
|
|
368
443
|
case "promote-user": { const { projectId, rest } = resolvePositionalProject(args); await promoteUser(projectId, rest[0]); break; }
|
|
369
444
|
case "demote-user": { const { projectId, rest } = resolvePositionalProject(args); await demoteUser(projectId, rest[0]); break; }
|
|
370
445
|
default:
|
package/lib/sdk-errors.mjs
CHANGED
|
@@ -1,25 +1,81 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CLI-side SDK error translator.
|
|
3
3
|
*
|
|
4
|
-
* Maps SDK `Run402Error` subclasses into the CLI's
|
|
5
|
-
*
|
|
6
|
-
* Preserves specific behaviors:
|
|
7
|
-
* - `ProjectNotFound` →
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* Maps SDK `Run402Error` subclasses into the CLI's canonical error envelope:
|
|
5
|
+
* `{status: "error", code, message, retryable, safe_to_retry, ...}` on stderr,
|
|
6
|
+
* `process.exit(1)`. Preserves specific behaviors:
|
|
7
|
+
* - `ProjectNotFound` → canonical envelope with `code: "PROJECT_NOT_FOUND"`
|
|
8
|
+
* and `details.source: "local_registry"` so callers can distinguish the
|
|
9
|
+
* local-registry miss from a gateway 404.
|
|
10
10
|
* - HTML / non-JSON error bodies → `body_preview` field (first 500 chars),
|
|
11
11
|
* matching GH-84 behavior.
|
|
12
12
|
* - Network errors → `{status: "error", message: "..."}`.
|
|
13
|
+
*
|
|
14
|
+
* For client-side validation failures (missing flags, bad JSON, no-op
|
|
15
|
+
* environments), use `fail()` instead — `reportSdkError` is strictly for
|
|
16
|
+
* thrown `Run402Error` instances.
|
|
13
17
|
*/
|
|
14
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Canonical client-side failure emitter.
|
|
21
|
+
*
|
|
22
|
+
* Writes a single JSON envelope to stderr and exits with `exit_code` (default 1).
|
|
23
|
+
* The envelope shape matches the gateway's structured error contract so callers
|
|
24
|
+
* branching on `code` / `retryable` / `safe_to_retry` work uniformly across
|
|
25
|
+
* client-side validation errors and SDK-thrown errors.
|
|
26
|
+
*
|
|
27
|
+
* `code` defaults to "BAD_USAGE" so the helper is safe to call without one.
|
|
28
|
+
* `retryable: false` and `safe_to_retry: true` are sane defaults for client-side
|
|
29
|
+
* validation: the call wasn't sent, so retrying is safe and won't help unless
|
|
30
|
+
* the user fixes input.
|
|
31
|
+
*/
|
|
32
|
+
export function fail({ message, code, hint, details, next_actions, retryable = false, safe_to_retry = true, exit_code = 1 } = {}) {
|
|
33
|
+
const envelope = {
|
|
34
|
+
status: "error",
|
|
35
|
+
code: code ?? "BAD_USAGE",
|
|
36
|
+
message,
|
|
37
|
+
retryable,
|
|
38
|
+
safe_to_retry,
|
|
39
|
+
};
|
|
40
|
+
if (hint !== undefined) envelope.hint = hint;
|
|
41
|
+
if (details !== undefined) envelope.details = details;
|
|
42
|
+
envelope.next_actions = Array.isArray(next_actions) ? next_actions : [];
|
|
43
|
+
envelope.trace_id = null;
|
|
44
|
+
console.error(JSON.stringify(envelope));
|
|
45
|
+
process.exit(exit_code);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Parse a JSON-bearing CLI flag value, naming the flag in the failure envelope.
|
|
50
|
+
*
|
|
51
|
+
* Wraps `JSON.parse` so the failure says which flag was bad and includes a
|
|
52
|
+
* truncated value preview, instead of leaking a raw V8 `JSON.parse` message
|
|
53
|
+
* that doesn't tell the caller which flag failed.
|
|
54
|
+
*/
|
|
55
|
+
export function parseFlagJson(name, value) {
|
|
56
|
+
try {
|
|
57
|
+
return JSON.parse(value);
|
|
58
|
+
} catch (e) {
|
|
59
|
+
fail({
|
|
60
|
+
code: "BAD_JSON_FLAG",
|
|
61
|
+
message: `${name} value is not valid JSON`,
|
|
62
|
+
details: { flag: name, value_preview: String(value).slice(0, 32), parse_error: e.message },
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
15
67
|
export function reportSdkError(err) {
|
|
16
68
|
if (err?.name === "ProjectNotFound") {
|
|
17
69
|
const id = err.projectId || "";
|
|
18
70
|
const hint = id && !String(id).startsWith("prj_")
|
|
19
|
-
? `
|
|
20
|
-
:
|
|
21
|
-
|
|
22
|
-
|
|
71
|
+
? `project IDs start with "prj_". Check that the argument order is <project_id> <name>.`
|
|
72
|
+
: undefined;
|
|
73
|
+
fail({
|
|
74
|
+
code: "PROJECT_NOT_FOUND",
|
|
75
|
+
message: `Project ${id} not found in local registry.`,
|
|
76
|
+
hint,
|
|
77
|
+
details: { project_id: id, source: "local_registry" },
|
|
78
|
+
});
|
|
23
79
|
}
|
|
24
80
|
|
|
25
81
|
const payload = { status: "error" };
|
package/lib/secrets.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from "fs";
|
|
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 secrets — Manage project secrets
|
|
6
6
|
|
|
@@ -13,10 +13,10 @@ Subcommands:
|
|
|
13
13
|
delete <id> <key> Delete a secret from a project
|
|
14
14
|
|
|
15
15
|
Examples:
|
|
16
|
-
run402 secrets set
|
|
17
|
-
run402 secrets set
|
|
18
|
-
run402 secrets list
|
|
19
|
-
run402 secrets delete
|
|
16
|
+
run402 secrets set prj_abc123 STRIPE_KEY sk-1234
|
|
17
|
+
run402 secrets set prj_abc123 TLS_CERT --file cert.pem
|
|
18
|
+
run402 secrets list prj_abc123
|
|
19
|
+
run402 secrets delete prj_abc123 STRIPE_KEY
|
|
20
20
|
|
|
21
21
|
Notes:
|
|
22
22
|
- Secrets are injected as process.env in serverless functions
|
|
@@ -43,8 +43,8 @@ Notes:
|
|
|
43
43
|
- Values are write-only; 'list' returns a value_hash for verification
|
|
44
44
|
|
|
45
45
|
Examples:
|
|
46
|
-
run402 secrets set
|
|
47
|
-
run402 secrets set
|
|
46
|
+
run402 secrets set prj_abc123 STRIPE_KEY sk-1234
|
|
47
|
+
run402 secrets set prj_abc123 TLS_CERT --file cert.pem
|
|
48
48
|
`,
|
|
49
49
|
};
|
|
50
50
|
|
|
@@ -56,7 +56,13 @@ async function set(projectId, key, args = []) {
|
|
|
56
56
|
else if (!value && !args[i].startsWith("--")) { value = args[i]; }
|
|
57
57
|
}
|
|
58
58
|
const val = file ? readFileSync(file, "utf-8") : value;
|
|
59
|
-
if (!val) {
|
|
59
|
+
if (!val) {
|
|
60
|
+
fail({
|
|
61
|
+
code: "BAD_USAGE",
|
|
62
|
+
message: "Missing secret value.",
|
|
63
|
+
hint: "Provide inline or use --file <path>",
|
|
64
|
+
});
|
|
65
|
+
}
|
|
60
66
|
try {
|
|
61
67
|
await getSdk().secrets.set(projectId, key, val);
|
|
62
68
|
console.log(JSON.stringify({ status: "ok", message: `Secret '${key}' set for project ${projectId}.` }));
|
package/lib/sender-domain.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 sender-domain — Manage custom email sender domain
|
|
6
6
|
|
|
@@ -39,8 +39,11 @@ async function register(args) {
|
|
|
39
39
|
const projectId = resolveProjectId(projectOpt);
|
|
40
40
|
|
|
41
41
|
if (!domain) {
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
fail({
|
|
43
|
+
code: "BAD_USAGE",
|
|
44
|
+
message: "Missing domain.",
|
|
45
|
+
hint: "run402 sender-domain register <domain>",
|
|
46
|
+
});
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
try {
|
|
@@ -81,8 +84,11 @@ async function inboundToggle(action, args) {
|
|
|
81
84
|
const projectId = resolveProjectId(projectOpt);
|
|
82
85
|
|
|
83
86
|
if (!domain) {
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
fail({
|
|
88
|
+
code: "BAD_USAGE",
|
|
89
|
+
message: "Missing domain.",
|
|
90
|
+
hint: `run402 sender-domain inbound-${action} <domain>`,
|
|
91
|
+
});
|
|
86
92
|
}
|
|
87
93
|
|
|
88
94
|
try {
|
package/lib/sites.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { fileSetFromDir } from "#sdk/node";
|
|
|
5
5
|
import { allowanceAuthHeaders, resolveProjectId, updateProject } from "./config.mjs";
|
|
6
6
|
import { resolveFilePathsInManifest } from "./manifest.mjs";
|
|
7
7
|
import { getSdk } from "./sdk.mjs";
|
|
8
|
-
import { reportSdkError } from "./sdk-errors.mjs";
|
|
8
|
+
import { reportSdkError, fail } from "./sdk-errors.mjs";
|
|
9
9
|
|
|
10
10
|
const SMALL_DIR_THRESHOLD = 5;
|
|
11
11
|
|
|
@@ -244,17 +244,19 @@ async function deployDir(args) {
|
|
|
244
244
|
if (args[i] === "--dry-run") { opts.dryRun = true; continue; }
|
|
245
245
|
if (args[i] === "--confirm-prune") { opts.confirmPrune = true; continue; }
|
|
246
246
|
if (args[i] === "--inherit") {
|
|
247
|
-
|
|
248
|
-
|
|
247
|
+
fail({
|
|
248
|
+
code: "BAD_USAGE",
|
|
249
249
|
message: "--inherit is removed; the SDK now uploads only changed files automatically.",
|
|
250
|
-
})
|
|
251
|
-
process.exit(1);
|
|
250
|
+
});
|
|
252
251
|
}
|
|
253
252
|
if (!args[i].startsWith("-") && opts.dir === null) { opts.dir = args[i]; continue; }
|
|
254
253
|
}
|
|
255
254
|
if (!opts.dir) {
|
|
256
|
-
|
|
257
|
-
|
|
255
|
+
fail({
|
|
256
|
+
code: "BAD_USAGE",
|
|
257
|
+
message: "Missing <path>.",
|
|
258
|
+
hint: "run402 sites deploy-dir <path> --project <id>",
|
|
259
|
+
});
|
|
258
260
|
}
|
|
259
261
|
const projectId = resolveProjectId(opts.project);
|
|
260
262
|
|
package/lib/status.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readAllowance, loadKeyStore, getActiveProjectId, API } from "./config.mjs";
|
|
2
2
|
import { getAllowanceAuthHeaders } from "../core-dist/allowance-auth.js";
|
|
3
|
+
import { assertKnownFlags, hasHelp, normalizeArgv } from "./argparse.mjs";
|
|
3
4
|
|
|
4
5
|
const HELP = `run402 status — Show full account state in one shot
|
|
5
6
|
|
|
@@ -75,11 +76,13 @@ function normalizeProject(raw) {
|
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
export async function run(args = []) {
|
|
78
|
-
|
|
79
|
+
args = normalizeArgv(args);
|
|
80
|
+
if (hasHelp(args)) { console.log(HELP); process.exit(0); }
|
|
81
|
+
assertKnownFlags(args, ["--help", "-h"]);
|
|
79
82
|
const allowance = readAllowance();
|
|
80
83
|
if (!allowance) {
|
|
81
84
|
console.log(JSON.stringify({ status: "no_allowance", message: "No agent allowance found. Run: run402 init" }));
|
|
82
|
-
|
|
85
|
+
process.exit(1);
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
const wallet = allowance.address.toLowerCase();
|
package/lib/subdomains.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolveProject, 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 subdomains — Manage custom subdomains
|
|
6
6
|
|
|
@@ -17,7 +17,7 @@ Legacy syntax 'claim <deployment_id> <name>' is still supported.
|
|
|
17
17
|
|
|
18
18
|
Examples:
|
|
19
19
|
run402 subdomains claim myapp
|
|
20
|
-
run402 subdomains claim myapp --deployment dpl_abc123 --project
|
|
20
|
+
run402 subdomains claim myapp --deployment dpl_abc123 --project prj_abc123
|
|
21
21
|
run402 subdomains delete myapp --confirm
|
|
22
22
|
run402 subdomains list
|
|
23
23
|
|
|
@@ -47,7 +47,7 @@ Notes:
|
|
|
47
47
|
|
|
48
48
|
Examples:
|
|
49
49
|
run402 subdomains claim myapp
|
|
50
|
-
run402 subdomains claim myapp --deployment dpl_abc123 --project
|
|
50
|
+
run402 subdomains claim myapp --deployment dpl_abc123 --project prj_abc123
|
|
51
51
|
`,
|
|
52
52
|
};
|
|
53
53
|
|
|
@@ -64,11 +64,25 @@ async function claim(positionalArgs, flagArgs) {
|
|
|
64
64
|
} else if (positionalArgs.length === 1) {
|
|
65
65
|
name = positionalArgs[0];
|
|
66
66
|
}
|
|
67
|
-
if (!name) {
|
|
67
|
+
if (!name) {
|
|
68
|
+
fail({
|
|
69
|
+
code: "BAD_USAGE",
|
|
70
|
+
message: "Missing <name>.",
|
|
71
|
+
hint: "run402 subdomains claim <name> [--project <id>] [--deployment <id>]",
|
|
72
|
+
});
|
|
73
|
+
}
|
|
68
74
|
const projectId = resolveProjectId(opts.project);
|
|
69
75
|
const p = resolveProject(opts.project);
|
|
70
76
|
deploymentId = opts.deployment || deploymentId || p.last_deployment_id;
|
|
71
|
-
if (!deploymentId) {
|
|
77
|
+
if (!deploymentId) {
|
|
78
|
+
fail({
|
|
79
|
+
code: "NO_DEPLOYMENT",
|
|
80
|
+
message: "no deployment_id specified and no recent deployment found.",
|
|
81
|
+
hint: "Deploy a site first or pass --deployment <id>.",
|
|
82
|
+
details: { project_id: projectId },
|
|
83
|
+
next_actions: [{ action: "deploy_site_first" }],
|
|
84
|
+
});
|
|
85
|
+
}
|
|
72
86
|
try {
|
|
73
87
|
const data = await getSdk().subdomains.claim(name, deploymentId, { projectId });
|
|
74
88
|
console.log(JSON.stringify(data, null, 2));
|
|
@@ -86,17 +100,18 @@ async function deleteSubdomain(allArgs) {
|
|
|
86
100
|
else if (!argList[i].startsWith("--") && !name) { name = argList[i]; }
|
|
87
101
|
}
|
|
88
102
|
if (!name) {
|
|
89
|
-
|
|
90
|
-
|
|
103
|
+
fail({
|
|
104
|
+
code: "BAD_USAGE",
|
|
105
|
+
message: "Missing <name>.",
|
|
106
|
+
hint: "run402 subdomains delete <name> --confirm [--project <id>]",
|
|
107
|
+
});
|
|
91
108
|
}
|
|
92
109
|
if (!argList.includes("--confirm")) {
|
|
93
|
-
|
|
94
|
-
status: "error",
|
|
110
|
+
fail({
|
|
95
111
|
code: "CONFIRMATION_REQUIRED",
|
|
96
112
|
message: `Destructive: releasing subdomain '${name}' makes it available for any other project to claim. This is irreversible. Re-run with --confirm to proceed.`,
|
|
97
113
|
details: { name },
|
|
98
|
-
})
|
|
99
|
-
process.exit(1);
|
|
114
|
+
});
|
|
100
115
|
}
|
|
101
116
|
const projectId = resolveProjectId(opts.project);
|
|
102
117
|
try {
|
package/lib/tier.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getSdk } from "./sdk.mjs";
|
|
2
|
-
import { reportSdkError } from "./sdk-errors.mjs";
|
|
2
|
+
import { reportSdkError, fail } from "./sdk-errors.mjs";
|
|
3
3
|
|
|
4
4
|
const HELP = `run402 tier — Manage your Run402 tier subscription
|
|
5
5
|
|
|
@@ -34,7 +34,13 @@ async function status() {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
async function set(tierName) {
|
|
37
|
-
if (!tierName) {
|
|
37
|
+
if (!tierName) {
|
|
38
|
+
fail({
|
|
39
|
+
code: "BAD_USAGE",
|
|
40
|
+
message: "Missing <tier>.",
|
|
41
|
+
hint: "run402 tier set <prototype|hobby|team>",
|
|
42
|
+
});
|
|
43
|
+
}
|
|
38
44
|
try {
|
|
39
45
|
const data = await getSdk().tier.set(tierName);
|
|
40
46
|
console.log(JSON.stringify(data, null, 2));
|
package/lib/webhooks.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 email webhooks — Manage mailbox webhooks
|
|
6
6
|
|
|
@@ -62,8 +62,11 @@ async function get(args) {
|
|
|
62
62
|
}
|
|
63
63
|
const projectId = resolveProjectId(projectOpt);
|
|
64
64
|
if (!webhookId) {
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
fail({
|
|
66
|
+
code: "BAD_USAGE",
|
|
67
|
+
message: "Missing webhook_id.",
|
|
68
|
+
hint: "run402 email webhooks get <webhook_id>",
|
|
69
|
+
});
|
|
67
70
|
}
|
|
68
71
|
try {
|
|
69
72
|
const data = await getSdk().email.webhooks.get(projectId, webhookId);
|
|
@@ -82,8 +85,11 @@ async function del(args) {
|
|
|
82
85
|
}
|
|
83
86
|
const projectId = resolveProjectId(projectOpt);
|
|
84
87
|
if (!webhookId) {
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
fail({
|
|
89
|
+
code: "BAD_USAGE",
|
|
90
|
+
message: "Missing webhook_id.",
|
|
91
|
+
hint: "run402 email webhooks delete <webhook_id>",
|
|
92
|
+
});
|
|
87
93
|
}
|
|
88
94
|
try {
|
|
89
95
|
await getSdk().email.webhooks.delete(projectId, webhookId);
|
|
@@ -106,12 +112,14 @@ async function update(args) {
|
|
|
106
112
|
}
|
|
107
113
|
const projectId = resolveProjectId(projectOpt);
|
|
108
114
|
if (!webhookId) {
|
|
109
|
-
|
|
110
|
-
|
|
115
|
+
fail({
|
|
116
|
+
code: "BAD_USAGE",
|
|
117
|
+
message: "Missing webhook_id.",
|
|
118
|
+
hint: "run402 email webhooks update <webhook_id> [--url <url>] [--events <e1,e2>]",
|
|
119
|
+
});
|
|
111
120
|
}
|
|
112
121
|
if (!url && !eventsRaw) {
|
|
113
|
-
|
|
114
|
-
process.exit(1);
|
|
122
|
+
fail({ code: "BAD_USAGE", message: "Provide at least --url or --events" });
|
|
115
123
|
}
|
|
116
124
|
|
|
117
125
|
try {
|
|
@@ -132,12 +140,18 @@ async function register(args) {
|
|
|
132
140
|
const projectId = resolveProjectId(projectOpt);
|
|
133
141
|
|
|
134
142
|
if (!url) {
|
|
135
|
-
|
|
136
|
-
|
|
143
|
+
fail({
|
|
144
|
+
code: "BAD_USAGE",
|
|
145
|
+
message: "Missing --url.",
|
|
146
|
+
hint: "run402 email webhooks register --url <url> --events <e1,e2>",
|
|
147
|
+
});
|
|
137
148
|
}
|
|
138
149
|
if (!eventsRaw) {
|
|
139
|
-
|
|
140
|
-
|
|
150
|
+
fail({
|
|
151
|
+
code: "BAD_USAGE",
|
|
152
|
+
message: "Missing --events.",
|
|
153
|
+
hint: "Valid events: delivery, bounced, complained, reply_received",
|
|
154
|
+
});
|
|
141
155
|
}
|
|
142
156
|
|
|
143
157
|
const events = eventsRaw.split(",").map((e) => e.trim());
|