run402 1.29.1 → 1.31.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/lib/email.mjs CHANGED
@@ -11,6 +11,8 @@ Subcommands:
11
11
  send --to <email> [mode flags] Send an email (template or raw HTML)
12
12
  list [--project <id>] List sent emails
13
13
  get <message_id> [--project <id>] Get a message with replies
14
+ get-raw <message_id> [--project <id>] [--output <file>]
15
+ Fetch raw RFC-822 bytes (inbound only)
14
16
 
15
17
  Send modes:
16
18
  Template: --template <name> --var key=value [--var ...]
@@ -32,6 +34,7 @@ Examples:
32
34
  --var project_name="My App" --var message="Deploy complete"
33
35
  run402 email list
34
36
  run402 email get msg_abc123
37
+ run402 email get-raw msg_abc123 --output reply.eml
35
38
 
36
39
  Notes:
37
40
  - One mailbox per project
@@ -246,6 +249,47 @@ async function get(args) {
246
249
  console.log(JSON.stringify(data, null, 2));
247
250
  }
248
251
 
252
+ async function getRaw(args) {
253
+ let messageId = null;
254
+ let projectOpt = null;
255
+ let outputFile = null;
256
+ for (let i = 0; i < args.length; i++) {
257
+ if (args[i] === "--project" && args[i + 1]) { projectOpt = args[++i]; }
258
+ else if (args[i] === "--output" && args[i + 1]) { outputFile = args[++i]; }
259
+ else if (!args[i].startsWith("--") && !messageId) { messageId = args[i]; }
260
+ }
261
+ const projectId = resolveProjectId(projectOpt);
262
+ const p = findProject(projectId);
263
+
264
+ if (!messageId) {
265
+ console.error(JSON.stringify({ status: "error", message: "Missing message_id. Usage: run402 email get-raw <message_id> [--output <file>]" }));
266
+ process.exit(1);
267
+ }
268
+
269
+ const mailboxId = await requireMailboxId(projectId, p.service_key);
270
+
271
+ const res = await fetch(`${API}/mailboxes/v1/${mailboxId}/messages/${messageId}/raw`, {
272
+ headers: { "Authorization": `Bearer ${p.service_key}` },
273
+ });
274
+ if (!res.ok) {
275
+ let errBody;
276
+ try { errBody = await res.json(); } catch { errBody = { error: await res.text().catch(() => "Unknown error") }; }
277
+ console.error(JSON.stringify({ status: "error", http: res.status, ...errBody }));
278
+ process.exit(1);
279
+ }
280
+
281
+ const buf = Buffer.from(await res.arrayBuffer());
282
+
283
+ if (outputFile) {
284
+ const { writeFileSync } = await import("node:fs");
285
+ writeFileSync(outputFile, buf);
286
+ console.log(JSON.stringify({ status: "ok", message_id: messageId, bytes: buf.length, output: outputFile }));
287
+ } else {
288
+ // Write raw bytes to stdout (no JSON wrapping — binary pipe-friendly)
289
+ process.stdout.write(buf);
290
+ }
291
+ }
292
+
249
293
  async function status(args) {
250
294
  const projectId = resolveProjectId(parseFlag(args, "--project"));
251
295
  const p = findProject(projectId);
@@ -277,6 +321,7 @@ export async function run(sub, args) {
277
321
  case "send": await send(args); break;
278
322
  case "list": await list(args); break;
279
323
  case "get": await get(args); break;
324
+ case "get-raw": await getRaw(args); break;
280
325
  default:
281
326
  console.error(`Unknown subcommand: ${sub}\n`);
282
327
  console.log(HELP);
@@ -9,11 +9,15 @@ Subcommands:
9
9
  register <domain> [--project <id>] Register a custom sender domain (returns DNS records)
10
10
  status [--project <id>] Check domain verification status
11
11
  remove [--project <id>] Remove custom sender domain
12
+ inbound-enable <domain> [--project <id>] Enable inbound email (requires DKIM-verified)
13
+ inbound-disable <domain> [--project <id>] Disable inbound email
12
14
 
13
15
  Examples:
14
16
  run402 sender-domain register kysigned.com
15
17
  run402 sender-domain status
16
18
  run402 sender-domain remove
19
+ run402 sender-domain inbound-enable kysigned.com
20
+ run402 sender-domain inbound-disable kysigned.com
17
21
  `;
18
22
 
19
23
  function parseFlag(args, flag) {
@@ -82,12 +86,43 @@ async function remove(args) {
82
86
  console.log(JSON.stringify(data));
83
87
  }
84
88
 
89
+ async function inboundToggle(action, args) {
90
+ let domain = null;
91
+ let projectOpt = null;
92
+ for (let i = 0; i < args.length; i++) {
93
+ if (args[i] === "--project" && args[i + 1]) { projectOpt = args[++i]; }
94
+ else if (!args[i].startsWith("--") && !domain) { domain = args[i]; }
95
+ }
96
+ const projectId = resolveProjectId(projectOpt);
97
+ const p = findProject(projectId);
98
+
99
+ if (!domain) {
100
+ console.error(JSON.stringify({ status: "error", message: `Missing domain. Usage: run402 sender-domain inbound-${action} <domain>` }));
101
+ process.exit(1);
102
+ }
103
+
104
+ const method = action === "enable" ? "POST" : "DELETE";
105
+ const res = await fetch(`${API}/email/v1/domains/inbound`, {
106
+ method,
107
+ headers: { apikey: p.service_key, "Content-Type": "application/json" },
108
+ body: JSON.stringify({ domain }),
109
+ });
110
+ const data = await res.json();
111
+ if (!res.ok) {
112
+ console.error(JSON.stringify({ status: "error", http: res.status, ...data }));
113
+ process.exit(1);
114
+ }
115
+ console.log(JSON.stringify(data, null, 2));
116
+ }
117
+
85
118
  export async function run(sub, args) {
86
119
  if (!sub || sub === "--help" || sub === "-h") { console.log(HELP); process.exit(0); }
87
120
  switch (sub) {
88
121
  case "register": await register(args); break;
89
122
  case "status": await status(args); break;
90
123
  case "remove": await remove(args); break;
124
+ case "inbound-enable": await inboundToggle("enable", args); break;
125
+ case "inbound-disable": await inboundToggle("disable", args); break;
91
126
  default:
92
127
  console.error(`Unknown subcommand: ${sub}\n`);
93
128
  console.log(HELP);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "run402",
3
- "version": "1.29.1",
3
+ "version": "1.31.0",
4
4
  "description": "CLI for Run402 — provision Postgres databases, deploy static sites, generate images, and manage wallets via x402 and MPP micropayments.",
5
5
  "type": "module",
6
6
  "bin": {