run402 1.23.2 → 1.25.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/cli.mjs +12 -0
- package/lib/ai.mjs +131 -0
- package/lib/domains.mjs +105 -0
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -31,7 +31,9 @@ Commands:
|
|
|
31
31
|
storage Manage file storage (upload, download, list, delete)
|
|
32
32
|
sites Deploy static sites
|
|
33
33
|
subdomains Manage custom subdomains (claim, list, delete)
|
|
34
|
+
domains Manage custom domains (add, list, status, delete)
|
|
34
35
|
apps Browse and manage the app marketplace
|
|
36
|
+
ai AI translation and moderation tools
|
|
35
37
|
image Generate AI images via x402 or MPP micropayments
|
|
36
38
|
email Send template-based emails from your project
|
|
37
39
|
message Send messages to Run402 developers
|
|
@@ -122,11 +124,21 @@ switch (cmd) {
|
|
|
122
124
|
await run(sub, rest);
|
|
123
125
|
break;
|
|
124
126
|
}
|
|
127
|
+
case "domains": {
|
|
128
|
+
const { run } = await import("./lib/domains.mjs");
|
|
129
|
+
await run(sub, rest);
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
125
132
|
case "apps": {
|
|
126
133
|
const { run } = await import("./lib/apps.mjs");
|
|
127
134
|
await run(sub, rest);
|
|
128
135
|
break;
|
|
129
136
|
}
|
|
137
|
+
case "ai": {
|
|
138
|
+
const { run } = await import("./lib/ai.mjs");
|
|
139
|
+
await run(sub, rest);
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
130
142
|
case "image": {
|
|
131
143
|
const { run } = await import("./lib/image.mjs");
|
|
132
144
|
await run(sub, rest);
|
package/lib/ai.mjs
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { findProject, resolveProjectId, API } from "./config.mjs";
|
|
2
|
+
|
|
3
|
+
const HELP = `run402 ai — AI translation and moderation tools
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
run402 ai <subcommand> [args...]
|
|
7
|
+
|
|
8
|
+
Subcommands:
|
|
9
|
+
translate <project_id> <text> --to <lang> [--from <lang>] [--context <hint>]
|
|
10
|
+
moderate <project_id> <text>
|
|
11
|
+
usage <project_id>
|
|
12
|
+
|
|
13
|
+
Examples:
|
|
14
|
+
run402 ai translate proj-001 "Hello world" --to es
|
|
15
|
+
run402 ai translate proj-001 "Hello" --to ja --from en --context "formal business email"
|
|
16
|
+
run402 ai moderate proj-001 "content to check"
|
|
17
|
+
run402 ai usage proj-001
|
|
18
|
+
|
|
19
|
+
Notes:
|
|
20
|
+
- translate requires the AI Translation add-on on the project
|
|
21
|
+
- moderate is free for all projects
|
|
22
|
+
- usage shows translation word quota for the current billing period
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
function parseFlag(args, flag) {
|
|
26
|
+
for (let i = 0; i < args.length; i++) {
|
|
27
|
+
if (args[i] === flag && args[i + 1]) return args[i + 1];
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function translate(args) {
|
|
33
|
+
let projectOpt = null;
|
|
34
|
+
let text = null;
|
|
35
|
+
const positional = [];
|
|
36
|
+
let i = 0;
|
|
37
|
+
while (i < args.length) {
|
|
38
|
+
if (args[i] === "--project" && args[i + 1]) { projectOpt = args[++i]; }
|
|
39
|
+
else if (args[i] === "--to" || args[i] === "--from" || args[i] === "--context") { i++; }
|
|
40
|
+
else if (!args[i].startsWith("--")) { positional.push(args[i]); }
|
|
41
|
+
i++;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const projectId = resolveProjectId(projectOpt || positional[0]);
|
|
45
|
+
const p = findProject(projectId);
|
|
46
|
+
text = positional[1] || null;
|
|
47
|
+
|
|
48
|
+
const to = parseFlag(args, "--to");
|
|
49
|
+
const from = parseFlag(args, "--from");
|
|
50
|
+
const context = parseFlag(args, "--context");
|
|
51
|
+
|
|
52
|
+
if (!text) { console.error(JSON.stringify({ status: "error", message: "Text required. Usage: run402 ai translate <project_id> <text> --to <lang>" })); process.exit(1); }
|
|
53
|
+
if (!to) { console.error(JSON.stringify({ status: "error", message: "--to <lang> is required" })); process.exit(1); }
|
|
54
|
+
|
|
55
|
+
const body = { text, to };
|
|
56
|
+
if (from) body.from = from;
|
|
57
|
+
if (context) body.context = context;
|
|
58
|
+
|
|
59
|
+
const res = await fetch(`${API}/ai/v1/translate`, {
|
|
60
|
+
method: "POST",
|
|
61
|
+
headers: { "Authorization": `Bearer ${p.service_key}`, "Content-Type": "application/json" },
|
|
62
|
+
body: JSON.stringify(body),
|
|
63
|
+
});
|
|
64
|
+
const data = await res.json();
|
|
65
|
+
if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
|
|
66
|
+
|
|
67
|
+
console.log(JSON.stringify({ status: "ok", text: data.text, from: data.from, to: data.to }));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function moderate(args) {
|
|
71
|
+
let projectOpt = null;
|
|
72
|
+
let text = null;
|
|
73
|
+
const positional = [];
|
|
74
|
+
let i = 0;
|
|
75
|
+
while (i < args.length) {
|
|
76
|
+
if (args[i] === "--project" && args[i + 1]) { projectOpt = args[++i]; }
|
|
77
|
+
else if (!args[i].startsWith("--")) { positional.push(args[i]); }
|
|
78
|
+
i++;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const projectId = resolveProjectId(projectOpt || positional[0]);
|
|
82
|
+
const p = findProject(projectId);
|
|
83
|
+
text = positional[1] || null;
|
|
84
|
+
|
|
85
|
+
if (!text) { console.error(JSON.stringify({ status: "error", message: "Text required. Usage: run402 ai moderate <project_id> <text>" })); process.exit(1); }
|
|
86
|
+
|
|
87
|
+
const res = await fetch(`${API}/ai/v1/moderate`, {
|
|
88
|
+
method: "POST",
|
|
89
|
+
headers: { "Authorization": `Bearer ${p.service_key}`, "Content-Type": "application/json" },
|
|
90
|
+
body: JSON.stringify({ text }),
|
|
91
|
+
});
|
|
92
|
+
const data = await res.json();
|
|
93
|
+
if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
|
|
94
|
+
|
|
95
|
+
console.log(JSON.stringify({ status: "ok", flagged: data.flagged, categories: data.categories, category_scores: data.category_scores }));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function usage(args) {
|
|
99
|
+
let projectOpt = null;
|
|
100
|
+
const positional = [];
|
|
101
|
+
let i = 0;
|
|
102
|
+
while (i < args.length) {
|
|
103
|
+
if (args[i] === "--project" && args[i + 1]) { projectOpt = args[++i]; }
|
|
104
|
+
else if (!args[i].startsWith("--")) { positional.push(args[i]); }
|
|
105
|
+
i++;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const projectId = resolveProjectId(projectOpt || positional[0]);
|
|
109
|
+
const p = findProject(projectId);
|
|
110
|
+
|
|
111
|
+
const res = await fetch(`${API}/ai/v1/usage`, {
|
|
112
|
+
headers: { "Authorization": `Bearer ${p.service_key}` },
|
|
113
|
+
});
|
|
114
|
+
const data = await res.json();
|
|
115
|
+
if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
|
|
116
|
+
|
|
117
|
+
console.log(JSON.stringify({ status: "ok", ...data }));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export async function run(sub, args) {
|
|
121
|
+
if (!sub || sub === '--help' || sub === '-h') { console.log(HELP); process.exit(0); }
|
|
122
|
+
switch (sub) {
|
|
123
|
+
case "translate": await translate(args); break;
|
|
124
|
+
case "moderate": await moderate(args); break;
|
|
125
|
+
case "usage": await usage(args); break;
|
|
126
|
+
default:
|
|
127
|
+
console.error(`Unknown subcommand: ${sub}\n`);
|
|
128
|
+
console.log(HELP);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
}
|
package/lib/domains.mjs
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { resolveProject, API } from "./config.mjs";
|
|
2
|
+
|
|
3
|
+
const HELP = `run402 domains — Manage custom domains
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
run402 domains <subcommand> [args...]
|
|
7
|
+
|
|
8
|
+
Subcommands:
|
|
9
|
+
add <domain> <subdomain_name> [--project <id>] Register a custom domain
|
|
10
|
+
list [<id>] List custom domains for a project
|
|
11
|
+
status <domain> [--project <id>] Check domain DNS/SSL status
|
|
12
|
+
delete <domain> [--project <id>] Release a custom domain
|
|
13
|
+
|
|
14
|
+
Examples:
|
|
15
|
+
run402 domains add example.com myapp
|
|
16
|
+
run402 domains add example.com myapp --project prj_123
|
|
17
|
+
run402 domains list
|
|
18
|
+
run402 domains status example.com
|
|
19
|
+
run402 domains delete example.com
|
|
20
|
+
|
|
21
|
+
Notes:
|
|
22
|
+
- After adding a domain, configure DNS as shown in the response
|
|
23
|
+
- Poll 'status' until the domain is active (DNS propagation ~60s)
|
|
24
|
+
- The domain must CNAME to domains.run402.com (or ALIAS for apex domains)
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
function parseProjectFlag(args) {
|
|
28
|
+
let project = null;
|
|
29
|
+
const rest = [];
|
|
30
|
+
for (let i = 0; i < args.length; i++) {
|
|
31
|
+
if (args[i] === "--project" && args[i + 1]) { project = args[++i]; }
|
|
32
|
+
else { rest.push(args[i]); }
|
|
33
|
+
}
|
|
34
|
+
return { project, rest };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function add(args) {
|
|
38
|
+
const { project, rest } = parseProjectFlag(args);
|
|
39
|
+
const domain = rest[0];
|
|
40
|
+
const subdomainName = rest[1];
|
|
41
|
+
if (!domain || !subdomainName) { console.error("Usage: run402 domains add <domain> <subdomain_name> [--project <id>]"); process.exit(1); }
|
|
42
|
+
const p = resolveProject(project);
|
|
43
|
+
const res = await fetch(`${API}/domains/v1`, {
|
|
44
|
+
method: "POST",
|
|
45
|
+
headers: { "Authorization": `Bearer ${p.service_key}`, "Content-Type": "application/json" },
|
|
46
|
+
body: JSON.stringify({ domain, subdomain_name: subdomainName }),
|
|
47
|
+
});
|
|
48
|
+
const data = await res.json();
|
|
49
|
+
if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
|
|
50
|
+
console.log(JSON.stringify(data, null, 2));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function list(projectId) {
|
|
54
|
+
const p = resolveProject(projectId);
|
|
55
|
+
const res = await fetch(`${API}/domains/v1`, {
|
|
56
|
+
headers: { "Authorization": `Bearer ${p.service_key}` },
|
|
57
|
+
});
|
|
58
|
+
const data = await res.json();
|
|
59
|
+
if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
|
|
60
|
+
console.log(JSON.stringify(data, null, 2));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function status(args) {
|
|
64
|
+
const { project, rest } = parseProjectFlag(args);
|
|
65
|
+
const domain = rest[0];
|
|
66
|
+
if (!domain) { console.error("Usage: run402 domains status <domain> [--project <id>]"); process.exit(1); }
|
|
67
|
+
const p = resolveProject(project);
|
|
68
|
+
const res = await fetch(`${API}/domains/v1/${encodeURIComponent(domain)}`, {
|
|
69
|
+
headers: { "Authorization": `Bearer ${p.service_key}` },
|
|
70
|
+
});
|
|
71
|
+
const data = await res.json();
|
|
72
|
+
if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
|
|
73
|
+
console.log(JSON.stringify(data, null, 2));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function deleteDomain(args) {
|
|
77
|
+
const { project, rest } = parseProjectFlag(args);
|
|
78
|
+
const domain = rest[0];
|
|
79
|
+
if (!domain) { console.error("Usage: run402 domains delete <domain> [--project <id>]"); process.exit(1); }
|
|
80
|
+
const p = resolveProject(project);
|
|
81
|
+
const res = await fetch(`${API}/domains/v1/${encodeURIComponent(domain)}`, {
|
|
82
|
+
method: "DELETE",
|
|
83
|
+
headers: { "Authorization": `Bearer ${p.service_key}` },
|
|
84
|
+
});
|
|
85
|
+
if (res.status === 204 || res.ok) {
|
|
86
|
+
console.log(JSON.stringify({ status: "ok", message: `Domain '${domain}' released.` }));
|
|
87
|
+
} else {
|
|
88
|
+
const data = await res.json().catch(() => ({}));
|
|
89
|
+
console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export async function run(sub, args) {
|
|
94
|
+
if (!sub || sub === '--help' || sub === '-h') { console.log(HELP); process.exit(0); }
|
|
95
|
+
switch (sub) {
|
|
96
|
+
case "add": await add(args); break;
|
|
97
|
+
case "list": await list(args[0]); break;
|
|
98
|
+
case "status": await status(args); break;
|
|
99
|
+
case "delete": await deleteDomain(args); break;
|
|
100
|
+
default:
|
|
101
|
+
console.error(`Unknown subcommand: ${sub}\n`);
|
|
102
|
+
console.log(HELP);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
}
|
package/package.json
CHANGED