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/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;
|
package/lib/domains.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 domains — Manage custom domains
|
|
6
6
|
|
|
@@ -40,7 +40,13 @@ async function add(args) {
|
|
|
40
40
|
const { project, rest } = parseProjectFlag(args);
|
|
41
41
|
const domain = rest[0];
|
|
42
42
|
const subdomainName = rest[1];
|
|
43
|
-
if (!domain || !subdomainName) {
|
|
43
|
+
if (!domain || !subdomainName) {
|
|
44
|
+
fail({
|
|
45
|
+
code: "BAD_USAGE",
|
|
46
|
+
message: "Missing <domain> and/or <subdomain_name>.",
|
|
47
|
+
hint: "run402 domains add <domain> <subdomain_name> [--project <id>]",
|
|
48
|
+
});
|
|
49
|
+
}
|
|
44
50
|
const projectId = resolveProjectId(project);
|
|
45
51
|
try {
|
|
46
52
|
const data = await getSdk().domains.add(projectId, domain, subdomainName);
|
|
@@ -63,7 +69,13 @@ async function list(projectIdArg) {
|
|
|
63
69
|
async function status(args) {
|
|
64
70
|
const { project, rest } = parseProjectFlag(args);
|
|
65
71
|
const domain = rest[0];
|
|
66
|
-
if (!domain) {
|
|
72
|
+
if (!domain) {
|
|
73
|
+
fail({
|
|
74
|
+
code: "BAD_USAGE",
|
|
75
|
+
message: "Missing <domain>.",
|
|
76
|
+
hint: "run402 domains status <domain> [--project <id>]",
|
|
77
|
+
});
|
|
78
|
+
}
|
|
67
79
|
const projectId = resolveProjectId(project);
|
|
68
80
|
try {
|
|
69
81
|
const data = await getSdk().domains.status(projectId, domain);
|
|
@@ -76,15 +88,19 @@ async function status(args) {
|
|
|
76
88
|
async function deleteDomain(args) {
|
|
77
89
|
const { project, rest } = parseProjectFlag(args);
|
|
78
90
|
const domain = rest.find((a) => !a.startsWith("--"));
|
|
79
|
-
if (!domain) {
|
|
91
|
+
if (!domain) {
|
|
92
|
+
fail({
|
|
93
|
+
code: "BAD_USAGE",
|
|
94
|
+
message: "Missing <domain>.",
|
|
95
|
+
hint: "run402 domains delete <domain> --confirm [--project <id>]",
|
|
96
|
+
});
|
|
97
|
+
}
|
|
80
98
|
if (!Array.isArray(args) || !args.includes("--confirm")) {
|
|
81
|
-
|
|
82
|
-
status: "error",
|
|
99
|
+
fail({
|
|
83
100
|
code: "CONFIRMATION_REQUIRED",
|
|
84
101
|
message: `Destructive: releasing custom domain '${domain}' detaches it from this project and clears its DNS/SSL configuration. This is irreversible. Re-run with --confirm to proceed.`,
|
|
85
102
|
details: { domain },
|
|
86
|
-
})
|
|
87
|
-
process.exit(1);
|
|
103
|
+
});
|
|
88
104
|
}
|
|
89
105
|
const projectId = resolveProjectId(project);
|
|
90
106
|
try {
|
package/lib/email.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, parseFlagJson } from "./sdk-errors.mjs";
|
|
4
4
|
|
|
5
5
|
const HELP = `run402 email — Send emails from your project
|
|
6
6
|
|
|
@@ -130,14 +130,12 @@ function parseVars(args) {
|
|
|
130
130
|
for (let i = 0; i < args.length; i++) {
|
|
131
131
|
if (args[i] === "--vars" && args[i + 1]) {
|
|
132
132
|
const raw = args[++i];
|
|
133
|
-
|
|
134
|
-
try { parsed = JSON.parse(raw); } catch {
|
|
135
|
-
console.error(JSON.stringify({ status: "error", message: "Invalid JSON for --vars. Expected a JSON object, e.g. '{\"key\":\"value\"}'" }));
|
|
136
|
-
process.exit(1);
|
|
137
|
-
}
|
|
133
|
+
const parsed = parseFlagJson("--vars", raw);
|
|
138
134
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
139
|
-
|
|
140
|
-
|
|
135
|
+
fail({
|
|
136
|
+
code: "BAD_USAGE",
|
|
137
|
+
message: "--vars must be a JSON object, e.g. '{\"key\":\"value\"}'",
|
|
138
|
+
});
|
|
141
139
|
}
|
|
142
140
|
for (const [k, v] of Object.entries(parsed)) vars[k] = typeof v === "string" ? v : String(v);
|
|
143
141
|
}
|
|
@@ -161,8 +159,11 @@ async function create(args) {
|
|
|
161
159
|
}
|
|
162
160
|
const projectId = resolveProjectId(projectOpt);
|
|
163
161
|
if (!slug) {
|
|
164
|
-
|
|
165
|
-
|
|
162
|
+
fail({
|
|
163
|
+
code: "BAD_USAGE",
|
|
164
|
+
message: "Missing slug.",
|
|
165
|
+
hint: "run402 email create <slug>",
|
|
166
|
+
});
|
|
166
167
|
}
|
|
167
168
|
|
|
168
169
|
try {
|
|
@@ -184,8 +185,7 @@ async function send(args) {
|
|
|
184
185
|
const variables = parseVars(args);
|
|
185
186
|
|
|
186
187
|
if (!to) {
|
|
187
|
-
|
|
188
|
-
process.exit(1);
|
|
188
|
+
fail({ code: "BAD_USAGE", message: "Missing --to <email>" });
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
try {
|
|
@@ -228,8 +228,11 @@ async function get(args) {
|
|
|
228
228
|
}
|
|
229
229
|
const projectId = resolveProjectId(projectOpt);
|
|
230
230
|
if (!messageId) {
|
|
231
|
-
|
|
232
|
-
|
|
231
|
+
fail({
|
|
232
|
+
code: "BAD_USAGE",
|
|
233
|
+
message: "Missing message_id.",
|
|
234
|
+
hint: "run402 email get <message_id>",
|
|
235
|
+
});
|
|
233
236
|
}
|
|
234
237
|
try {
|
|
235
238
|
const data = await getSdk().email.get(projectId, messageId);
|
|
@@ -250,8 +253,11 @@ async function getRaw(args) {
|
|
|
250
253
|
}
|
|
251
254
|
const projectId = resolveProjectId(projectOpt);
|
|
252
255
|
if (!messageId) {
|
|
253
|
-
|
|
254
|
-
|
|
256
|
+
fail({
|
|
257
|
+
code: "BAD_USAGE",
|
|
258
|
+
message: "Missing message_id.",
|
|
259
|
+
hint: "run402 email get-raw <message_id> [--output <file>]",
|
|
260
|
+
});
|
|
255
261
|
}
|
|
256
262
|
|
|
257
263
|
try {
|
|
@@ -286,12 +292,17 @@ async function reply(args) {
|
|
|
286
292
|
const projectId = resolveProjectId(projectOpt);
|
|
287
293
|
|
|
288
294
|
if (!messageId) {
|
|
289
|
-
|
|
290
|
-
|
|
295
|
+
fail({
|
|
296
|
+
code: "BAD_USAGE",
|
|
297
|
+
message: "Missing message_id.",
|
|
298
|
+
hint: 'run402 email reply <message_id> --html "..."',
|
|
299
|
+
});
|
|
291
300
|
}
|
|
292
301
|
if (!html && !text) {
|
|
293
|
-
|
|
294
|
-
|
|
302
|
+
fail({
|
|
303
|
+
code: "BAD_USAGE",
|
|
304
|
+
message: "Provide --html and/or --text for the reply body",
|
|
305
|
+
});
|
|
295
306
|
}
|
|
296
307
|
|
|
297
308
|
try {
|
|
@@ -299,12 +310,11 @@ async function reply(args) {
|
|
|
299
310
|
const original = await getSdk().email.get(projectId, messageId);
|
|
300
311
|
const replyTo = original.from || original.from_address || original.sender || null;
|
|
301
312
|
if (!replyTo) {
|
|
302
|
-
|
|
303
|
-
|
|
313
|
+
fail({
|
|
314
|
+
code: "BAD_USAGE",
|
|
304
315
|
message: "Original message has no from address to reply to",
|
|
305
|
-
original_keys: Object.keys(original),
|
|
306
|
-
})
|
|
307
|
-
process.exit(1);
|
|
316
|
+
details: { original_keys: Object.keys(original) },
|
|
317
|
+
});
|
|
308
318
|
}
|
|
309
319
|
const origSubject = typeof original.subject === "string" ? original.subject : "";
|
|
310
320
|
const defaultSubject = origSubject && origSubject.toLowerCase().startsWith("re:")
|
|
@@ -339,11 +349,10 @@ async function deleteMailbox(args) {
|
|
|
339
349
|
const confirmed = args.includes("--confirm");
|
|
340
350
|
|
|
341
351
|
if (!confirmed) {
|
|
342
|
-
|
|
343
|
-
|
|
352
|
+
fail({
|
|
353
|
+
code: "CONFIRMATION_REQUIRED",
|
|
344
354
|
message: "Destructive: deleting a mailbox is irreversible (drops all messages and webhook subscriptions). Re-run with --confirm to proceed.",
|
|
345
|
-
})
|
|
346
|
-
process.exit(1);
|
|
355
|
+
});
|
|
347
356
|
}
|
|
348
357
|
|
|
349
358
|
try {
|
package/lib/functions.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "fs";
|
|
2
2
|
import { findProject, API } from "./config.mjs";
|
|
3
3
|
import { getSdk } from "./sdk.mjs";
|
|
4
|
-
import { reportSdkError } from "./sdk-errors.mjs";
|
|
4
|
+
import { reportSdkError, fail } from "./sdk-errors.mjs";
|
|
5
5
|
|
|
6
6
|
const HELP = `run402 functions — Manage serverless functions
|
|
7
7
|
|
|
@@ -152,7 +152,9 @@ async function deploy(projectId, name, args) {
|
|
|
152
152
|
if (args[i] === "--deps" && args[i + 1]) opts.deps = args[++i].split(",");
|
|
153
153
|
if (args[i] === "--schedule" && i + 1 < args.length) opts.schedule = args[++i];
|
|
154
154
|
}
|
|
155
|
-
if (!opts.file) {
|
|
155
|
+
if (!opts.file) {
|
|
156
|
+
fail({ code: "BAD_USAGE", message: "Missing --file <file>" });
|
|
157
|
+
}
|
|
156
158
|
const code = readFileSync(opts.file, "utf-8");
|
|
157
159
|
|
|
158
160
|
const deployOpts = { name, code };
|
|
@@ -212,7 +214,13 @@ async function logs(projectId, name, args) {
|
|
|
212
214
|
if (since !== undefined) {
|
|
213
215
|
const parsed = Number(since);
|
|
214
216
|
const ms = Number.isNaN(parsed) ? new Date(since).getTime() : parsed;
|
|
215
|
-
if (Number.isNaN(ms)) {
|
|
217
|
+
if (Number.isNaN(ms)) {
|
|
218
|
+
fail({
|
|
219
|
+
code: "BAD_USAGE",
|
|
220
|
+
message: `Invalid --since value: ${since}`,
|
|
221
|
+
details: { flag: "--since", value: since },
|
|
222
|
+
});
|
|
223
|
+
}
|
|
216
224
|
sinceIso = new Date(ms).toISOString();
|
|
217
225
|
}
|
|
218
226
|
|
|
@@ -282,8 +290,10 @@ async function update(projectId, name, args) {
|
|
|
282
290
|
if (memory !== undefined) updateOpts.memory = memory;
|
|
283
291
|
|
|
284
292
|
if (Object.keys(updateOpts).length === 0) {
|
|
285
|
-
|
|
286
|
-
|
|
293
|
+
fail({
|
|
294
|
+
code: "BAD_USAGE",
|
|
295
|
+
message: "Provide at least one of: --schedule, --schedule-remove, --timeout, --memory",
|
|
296
|
+
});
|
|
287
297
|
}
|
|
288
298
|
|
|
289
299
|
try {
|
package/lib/image.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { writeFileSync } 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 image — Generate AI images via x402 micropayments
|
|
6
6
|
|
|
@@ -49,7 +49,13 @@ export async function run(sub, args) {
|
|
|
49
49
|
i++;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
if (!opts.prompt) {
|
|
52
|
+
if (!opts.prompt) {
|
|
53
|
+
fail({
|
|
54
|
+
code: "BAD_USAGE",
|
|
55
|
+
message: "Prompt required.",
|
|
56
|
+
hint: 'run402 image generate "your prompt"',
|
|
57
|
+
});
|
|
58
|
+
}
|
|
53
59
|
|
|
54
60
|
try {
|
|
55
61
|
const data = await getSdk().ai.generateImage({ prompt: opts.prompt, aspect: opts.aspect });
|
package/lib/message.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 message — Send messages to Run402 developers
|
|
6
6
|
|
|
@@ -16,7 +16,9 @@ Examples:
|
|
|
16
16
|
`;
|
|
17
17
|
|
|
18
18
|
async function send(text) {
|
|
19
|
-
if (!text) {
|
|
19
|
+
if (!text) {
|
|
20
|
+
fail({ code: "BAD_USAGE", message: "Missing message text." });
|
|
21
|
+
}
|
|
20
22
|
// Preserve the aggressive early exit when no allowance is configured.
|
|
21
23
|
allowanceAuthHeaders("/message/v1");
|
|
22
24
|
|
package/lib/projects.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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
5
|
|
|
6
6
|
const HELP = `run402 projects — Manage your deployed Run402 projects
|
|
7
7
|
|
|
@@ -153,12 +153,21 @@ async function applyExpose(projectId, args = []) {
|
|
|
153
153
|
}
|
|
154
154
|
const raw = file ? readFileSync(file, "utf-8") : inline;
|
|
155
155
|
if (!raw) {
|
|
156
|
-
|
|
157
|
-
|
|
156
|
+
fail({
|
|
157
|
+
code: "BAD_USAGE",
|
|
158
|
+
message: "Missing manifest.",
|
|
159
|
+
hint: "Provide inline JSON or use --file <path>",
|
|
160
|
+
});
|
|
158
161
|
}
|
|
159
162
|
let manifest;
|
|
160
163
|
try { manifest = JSON.parse(raw); }
|
|
161
|
-
catch
|
|
164
|
+
catch (err) {
|
|
165
|
+
fail({
|
|
166
|
+
code: "BAD_USAGE",
|
|
167
|
+
message: "Invalid JSON for manifest",
|
|
168
|
+
details: { parse_error: err.message },
|
|
169
|
+
});
|
|
170
|
+
}
|
|
162
171
|
const res = await fetch(`${API}/projects/v1/admin/${projectId}/expose`, {
|
|
163
172
|
method: "POST",
|
|
164
173
|
headers: { "Authorization": `Bearer ${p.service_key}`, "Content-Type": "application/json" },
|
|
@@ -222,11 +231,22 @@ async function sqlCmd(projectId, args = []) {
|
|
|
222
231
|
else if (!query && !args[i].startsWith("--")) { query = args[i]; }
|
|
223
232
|
}
|
|
224
233
|
const sql = file ? readFileSync(file, "utf-8") : query;
|
|
225
|
-
if (!sql) {
|
|
234
|
+
if (!sql) {
|
|
235
|
+
fail({
|
|
236
|
+
code: "BAD_USAGE",
|
|
237
|
+
message: "Missing SQL query.",
|
|
238
|
+
hint: "Provide inline or use --file <path>",
|
|
239
|
+
});
|
|
240
|
+
}
|
|
226
241
|
let params;
|
|
227
242
|
if (paramsRaw) {
|
|
228
|
-
|
|
229
|
-
if (!Array.isArray(params)) {
|
|
243
|
+
params = parseFlagJson("--params", paramsRaw);
|
|
244
|
+
if (!Array.isArray(params)) {
|
|
245
|
+
fail({
|
|
246
|
+
code: "BAD_USAGE",
|
|
247
|
+
message: "--params must be a JSON array, e.g. '[42, \"hello\"]'",
|
|
248
|
+
});
|
|
249
|
+
}
|
|
230
250
|
}
|
|
231
251
|
const useParams = params && params.length > 0;
|
|
232
252
|
const headers = { "Authorization": `Bearer ${p.service_key}`, "Content-Type": useParams ? "application/json" : "text/plain" };
|
|
@@ -264,7 +284,13 @@ async function schema(projectId) {
|
|
|
264
284
|
}
|
|
265
285
|
|
|
266
286
|
async function use(projectId) {
|
|
267
|
-
if (!projectId) {
|
|
287
|
+
if (!projectId) {
|
|
288
|
+
fail({
|
|
289
|
+
code: "BAD_USAGE",
|
|
290
|
+
message: "Missing <project_id>.",
|
|
291
|
+
hint: "run402 projects use <project_id>",
|
|
292
|
+
});
|
|
293
|
+
}
|
|
268
294
|
try {
|
|
269
295
|
await getSdk().projects.use(projectId);
|
|
270
296
|
console.log(JSON.stringify({ status: "ok", active_project_id: projectId }));
|
|
@@ -274,7 +300,13 @@ async function use(projectId) {
|
|
|
274
300
|
}
|
|
275
301
|
|
|
276
302
|
async function pin(projectId) {
|
|
277
|
-
if (!projectId) {
|
|
303
|
+
if (!projectId) {
|
|
304
|
+
fail({
|
|
305
|
+
code: "BAD_USAGE",
|
|
306
|
+
message: "Missing <project_id>.",
|
|
307
|
+
hint: "run402 projects pin <project_id>",
|
|
308
|
+
});
|
|
309
|
+
}
|
|
278
310
|
try {
|
|
279
311
|
const data = await getSdk().projects.pin(projectId);
|
|
280
312
|
console.log(JSON.stringify(data, null, 2));
|
|
@@ -284,7 +316,13 @@ async function pin(projectId) {
|
|
|
284
316
|
}
|
|
285
317
|
|
|
286
318
|
async function promoteUser(projectId, email) {
|
|
287
|
-
if (!email) {
|
|
319
|
+
if (!email) {
|
|
320
|
+
fail({
|
|
321
|
+
code: "BAD_USAGE",
|
|
322
|
+
message: "Missing <email>.",
|
|
323
|
+
hint: "run402 projects promote-user <project_id> <email>",
|
|
324
|
+
});
|
|
325
|
+
}
|
|
288
326
|
const p = findProject(projectId);
|
|
289
327
|
const res = await fetch(`${API}/projects/v1/admin/${projectId}/promote-user`, {
|
|
290
328
|
method: "POST",
|
|
@@ -297,7 +335,13 @@ async function promoteUser(projectId, email) {
|
|
|
297
335
|
}
|
|
298
336
|
|
|
299
337
|
async function demoteUser(projectId, email) {
|
|
300
|
-
if (!email) {
|
|
338
|
+
if (!email) {
|
|
339
|
+
fail({
|
|
340
|
+
code: "BAD_USAGE",
|
|
341
|
+
message: "Missing <email>.",
|
|
342
|
+
hint: "run402 projects demote-user <project_id> <email>",
|
|
343
|
+
});
|
|
344
|
+
}
|
|
301
345
|
const p = findProject(projectId);
|
|
302
346
|
const res = await fetch(`${API}/projects/v1/admin/${projectId}/demote-user`, {
|
|
303
347
|
method: "POST",
|