run402 1.24.0 → 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.
Files changed (3) hide show
  1. package/cli.mjs +6 -0
  2. package/lib/ai.mjs +131 -0
  3. package/package.json +1 -1
package/cli.mjs CHANGED
@@ -33,6 +33,7 @@ Commands:
33
33
  subdomains Manage custom subdomains (claim, list, delete)
34
34
  domains Manage custom domains (add, list, status, delete)
35
35
  apps Browse and manage the app marketplace
36
+ ai AI translation and moderation tools
36
37
  image Generate AI images via x402 or MPP micropayments
37
38
  email Send template-based emails from your project
38
39
  message Send messages to Run402 developers
@@ -133,6 +134,11 @@ switch (cmd) {
133
134
  await run(sub, rest);
134
135
  break;
135
136
  }
137
+ case "ai": {
138
+ const { run } = await import("./lib/ai.mjs");
139
+ await run(sub, rest);
140
+ break;
141
+ }
136
142
  case "image": {
137
143
  const { run } = await import("./lib/image.mjs");
138
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "run402",
3
- "version": "1.24.0",
3
+ "version": "1.25.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": {