@4ort/cli 0.4.0 → 0.5.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 (73) hide show
  1. package/README.md +286 -0
  2. package/dist/commands/kg/agent-context.d.ts +3 -0
  3. package/dist/commands/kg/agent-context.js +54 -0
  4. package/dist/commands/kg/agent-context.js.map +1 -0
  5. package/dist/commands/kg/article-context.d.ts +3 -0
  6. package/dist/commands/kg/article-context.js +51 -0
  7. package/dist/commands/kg/article-context.js.map +1 -0
  8. package/dist/commands/kg/ask.d.ts +5 -0
  9. package/dist/commands/kg/ask.js +121 -0
  10. package/dist/commands/kg/ask.js.map +1 -0
  11. package/dist/commands/kg/auth.d.ts +9 -0
  12. package/dist/commands/kg/auth.js +95 -0
  13. package/dist/commands/kg/auth.js.map +1 -0
  14. package/dist/commands/kg/entity.d.ts +4 -0
  15. package/dist/commands/kg/entity.js +33 -0
  16. package/dist/commands/kg/entity.js.map +1 -0
  17. package/dist/commands/kg/match.d.ts +5 -0
  18. package/dist/commands/kg/match.js +44 -0
  19. package/dist/commands/kg/match.js.map +1 -0
  20. package/dist/commands/kg/popularity.d.ts +3 -0
  21. package/dist/commands/kg/popularity.js +42 -0
  22. package/dist/commands/kg/popularity.js.map +1 -0
  23. package/dist/commands/kg/search.d.ts +4 -0
  24. package/dist/commands/kg/search.js +34 -0
  25. package/dist/commands/kg/search.js.map +1 -0
  26. package/dist/commands/kg/trending.d.ts +6 -0
  27. package/dist/commands/kg/trending.js +35 -0
  28. package/dist/commands/kg/trending.js.map +1 -0
  29. package/dist/commands/mcp.d.ts +5 -0
  30. package/dist/commands/mcp.js +24 -0
  31. package/dist/commands/mcp.js.map +1 -0
  32. package/dist/commands/run.d.ts +10 -0
  33. package/dist/commands/run.js +74 -0
  34. package/dist/commands/run.js.map +1 -0
  35. package/dist/commands/search.d.ts +7 -0
  36. package/dist/commands/search.js +105 -0
  37. package/dist/commands/search.js.map +1 -0
  38. package/dist/commands/vault.d.ts +7 -0
  39. package/dist/commands/vault.js +62 -0
  40. package/dist/commands/vault.js.map +1 -0
  41. package/dist/commands/web.d.ts +6 -0
  42. package/dist/commands/web.js +44 -0
  43. package/dist/commands/web.js.map +1 -0
  44. package/dist/config.d.ts +25 -0
  45. package/dist/config.js +80 -1
  46. package/dist/config.js.map +1 -1
  47. package/dist/index.js +167 -37
  48. package/dist/index.js.map +1 -1
  49. package/dist/kg-client.d.ts +42 -0
  50. package/dist/kg-client.js +125 -0
  51. package/dist/kg-client.js.map +1 -0
  52. package/dist/kg-output.d.ts +33 -0
  53. package/dist/kg-output.js +132 -0
  54. package/dist/kg-output.js.map +1 -0
  55. package/dist/mcp-server.d.ts +5 -0
  56. package/dist/mcp-server.js +363 -0
  57. package/dist/mcp-server.js.map +1 -0
  58. package/dist/web-client.d.ts +20 -0
  59. package/dist/web-client.js +335 -0
  60. package/dist/web-client.js.map +1 -0
  61. package/package.json +20 -4
  62. package/src/api-client.ts +0 -51
  63. package/src/commands/delete.ts +0 -15
  64. package/src/commands/list.ts +0 -25
  65. package/src/commands/mail.ts +0 -79
  66. package/src/commands/provision.ts +0 -106
  67. package/src/commands/push.ts +0 -63
  68. package/src/commands/recover.ts +0 -37
  69. package/src/commands/register.ts +0 -40
  70. package/src/commands/whoami.ts +0 -20
  71. package/src/config.ts +0 -40
  72. package/src/index.ts +0 -114
  73. package/tsconfig.json +0 -8
@@ -0,0 +1,335 @@
1
+ /**
2
+ * Public-internet reader for `4ort web get`.
3
+ *
4
+ * Tiered, cheapest tier first:
5
+ * Tier 0 — if the URL is one of ours (4ort.xyz/entity/<slug>), return our own
6
+ * structured entity instead of scraping the HTML.
7
+ * Tier 2 — live fetch (undici global fetch, proxy-aware via kg-client's
8
+ * EnvHttpProxyAgent) + Mozilla Readability ("Reader Mode") extraction.
9
+ * (Tier 1 — full text out of our Common Crawl index via a future server
10
+ * /page-text endpoint — TODO once that ships.)
11
+ *
12
+ * No JS execution, no cookies, no login. On a JS-wall / 403 / paywall / binary
13
+ * body / parse failure we return { ok: false, ... } *with the URL* so the calling
14
+ * agent isn't dead-ended — it can fall back to the link or pick another source.
15
+ */
16
+ import { Readability } from "@mozilla/readability";
17
+ import { parseHTML } from "linkedom";
18
+ import { loadConfig } from "./config.js";
19
+ import { kgRequest } from "./kg-client.js";
20
+ const UA = "4ort-cli/0.5.0 (+https://4ort.xyz)";
21
+ const FETCH_TIMEOUT_MS = 15_000;
22
+ const MAX_BODY_BYTES = 5 * 1024 * 1024; // 5 MB hard cap on what we'll download
23
+ const DEFAULT_MAX_CHARS = 50_000;
24
+ // ── Tier 0: our own pages ───────────────────────────────────────────────────
25
+ function looksLikeOurEntity(u) {
26
+ if (!/(^|\.)4ort\.xyz$/i.test(u.hostname))
27
+ return null;
28
+ const m = u.pathname.match(/^\/entity\/([^/]+)\/?$/);
29
+ return m ? decodeURIComponent(m[1]) : null;
30
+ }
31
+ async function readOurEntity(requested, slug) {
32
+ const kg = loadConfig()?.kg;
33
+ if (!kg?.apiKey)
34
+ return null; // no KG key → just scrape the SSR HTML normally
35
+ try {
36
+ const data = await kgRequest(kg, `/api/v1/entities/${encodeURIComponent(slug)}`, {
37
+ query: { detail: "full" },
38
+ });
39
+ return {
40
+ ok: true,
41
+ url: requested,
42
+ final_url: requested,
43
+ source: "4ort.xyz",
44
+ title: data?.name,
45
+ entity: data,
46
+ };
47
+ }
48
+ catch {
49
+ return null; // any failure → fall through to a normal fetch
50
+ }
51
+ }
52
+ // ── Tier 2: live fetch + Readability ────────────────────────────────────────
53
+ /** Read a Response body into bytes, hard-capped at `cap`. Cancels the stream once full. */
54
+ async function readCapped(res, cap) {
55
+ if (!res.body) {
56
+ const ab = await res.arrayBuffer();
57
+ const u = new Uint8Array(ab);
58
+ return { bytes: u.length > cap ? u.slice(0, cap) : u, truncatedBody: u.length > cap };
59
+ }
60
+ const reader = res.body.getReader();
61
+ const chunks = [];
62
+ let total = 0;
63
+ let truncatedBody = false;
64
+ while (true) {
65
+ const { value, done } = await reader.read();
66
+ if (done)
67
+ break;
68
+ if (value && value.length) {
69
+ chunks.push(value);
70
+ total += value.length;
71
+ if (total >= cap) {
72
+ truncatedBody = true;
73
+ try {
74
+ await reader.cancel();
75
+ }
76
+ catch { /* ignore */ }
77
+ break;
78
+ }
79
+ }
80
+ }
81
+ const out = new Uint8Array(Math.min(total, cap));
82
+ let off = 0;
83
+ for (const c of chunks) {
84
+ if (off >= cap)
85
+ break;
86
+ const take = Math.min(c.length, cap - off);
87
+ out.set(c.subarray(0, take), off);
88
+ off += take;
89
+ }
90
+ return { bytes: out, truncatedBody };
91
+ }
92
+ /** Crude tag-strip fallback when Readability extracts nothing usable. */
93
+ function crudeStrip(document, rawHtml) {
94
+ try {
95
+ for (const sel of ["script", "style", "noscript", "nav", "header", "footer", "aside", "form", "iframe", "svg"]) {
96
+ for (const el of Array.from(document.querySelectorAll(sel))) {
97
+ try {
98
+ el.remove();
99
+ }
100
+ catch { /* ignore */ }
101
+ }
102
+ }
103
+ const body = document.querySelector("body") || document.documentElement;
104
+ const text = (body?.textContent || "")
105
+ .replace(/[ \t]+/g, " ")
106
+ .replace(/\n[ \t]*\n[ \t]*\n+/g, "\n\n")
107
+ .trim();
108
+ if (text)
109
+ return text;
110
+ }
111
+ catch { /* fall through */ }
112
+ return rawHtml
113
+ .replace(/<script[\s\S]*?<\/script>/gi, "")
114
+ .replace(/<style[\s\S]*?<\/style>/gi, "")
115
+ .replace(/<[^>]+>/g, " ")
116
+ .replace(/\s+/g, " ")
117
+ .trim();
118
+ }
119
+ function wordCount(s) {
120
+ return s.split(/\s+/).filter(Boolean).length;
121
+ }
122
+ // Anti-bot / JS-challenge interstitials extract as short "content" that isn't.
123
+ // If a page comes back thin AND matches one of these, treat it as blocked so the
124
+ // agent gets an honest signal instead of a "verify you're a robot" page as text.
125
+ const BOT_WALL_RE = /verify (you('| a)re|that you('| a)re) (not )?a (human|robot)|enable javascript|javascript is (disabled|required)|captcha|checking your browser|cf-browser-verification|having trouble accessing|access denied|are you a (human|robot)|please complete the security check|unusual traffic|to continue, (please )?(enable|verify)/i;
126
+ function looksLikeBotWall(text) {
127
+ return wordCount(text) < 120 && BOT_WALL_RE.test(text);
128
+ }
129
+ async function liveFetch(requested, maxChars) {
130
+ const ctrl = new AbortController();
131
+ const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
132
+ let res;
133
+ try {
134
+ res = await fetch(requested, {
135
+ redirect: "follow",
136
+ signal: ctrl.signal,
137
+ headers: {
138
+ "User-Agent": UA,
139
+ Accept: "text/html,application/xhtml+xml,text/plain,application/json;q=0.9,*/*;q=0.8",
140
+ "Accept-Encoding": "gzip, deflate, br",
141
+ },
142
+ });
143
+ }
144
+ catch (e) {
145
+ clearTimeout(timer);
146
+ const detail = e?.cause?.code || e?.cause?.message || e?.message || String(e);
147
+ const reason = e?.name === "AbortError" ? `timeout after ${FETCH_TIMEOUT_MS}ms` : `fetch failed: ${detail}`;
148
+ return { ok: false, url: requested, source: "live", reason };
149
+ }
150
+ clearTimeout(timer);
151
+ const finalUrl = res.url || requested;
152
+ const status = res.status;
153
+ const ct = (res.headers.get("content-type") || "").toLowerCase();
154
+ if (!res.ok) {
155
+ try {
156
+ await res.arrayBuffer();
157
+ }
158
+ catch { /* drain */ }
159
+ const blocked = status === 401 || status === 403 || status === 429 || status === 451 || status === 503;
160
+ const reason = blocked
161
+ ? `HTTP ${status} — blocked; agent should try the URL directly or pick another source`
162
+ : `HTTP ${status}`;
163
+ return { ok: false, url: requested, final_url: finalUrl, source: "live", status, content_type: ct || undefined, reason };
164
+ }
165
+ const clen = parseInt(res.headers.get("content-length") || "", 10);
166
+ if (Number.isFinite(clen) && clen > MAX_BODY_BYTES) {
167
+ try {
168
+ await res.arrayBuffer();
169
+ }
170
+ catch { /* drain */ }
171
+ return {
172
+ ok: false, url: requested, final_url: finalUrl, source: "live", status,
173
+ content_type: ct || undefined, size: clen,
174
+ reason: `body too large (${clen} bytes) — fetch the URL directly`,
175
+ };
176
+ }
177
+ const { bytes, truncatedBody } = await readCapped(res, MAX_BODY_BYTES);
178
+ // PDF — out of scope for v1.
179
+ if (ct.includes("application/pdf") || finalUrl.toLowerCase().split("?")[0].endsWith(".pdf")) {
180
+ return {
181
+ ok: false, url: requested, final_url: finalUrl, source: "live", status,
182
+ content_type: ct || "application/pdf", size: bytes.length,
183
+ reason: "PDF not supported yet — fetch the URL directly",
184
+ };
185
+ }
186
+ const isHtml = ct.includes("html") || ct.includes("xhtml") || (!ct && /<html[\s>]/i.test(new TextDecoder().decode(bytes.subarray(0, 2048))));
187
+ const isText = isHtml || ct.includes("text/") || ct.includes("json") || ct.includes("xml") || ct.includes("+xml") || !ct;
188
+ if (!isText) {
189
+ return {
190
+ ok: false, url: requested, final_url: finalUrl, source: "live", status,
191
+ content_type: ct, size: bytes.length,
192
+ reason: `binary content (${ct}) — fetch the URL directly`,
193
+ };
194
+ }
195
+ const bodyText = new TextDecoder("utf-8").decode(bytes);
196
+ // Many JSON APIs serve their payload with a lying Content-Type (text/html,
197
+ // text/plain). If the body clearly *is* JSON, return it raw — don't run a
198
+ // DOM parser over it. (VIAF AutoSuggest, some MediaWiki APIs, etc.)
199
+ const trimmed = bodyText.trimStart();
200
+ const looksJson = (trimmed.startsWith("{") && bodyText.trimEnd().endsWith("}"))
201
+ || (trimmed.startsWith("[") && bodyText.trimEnd().endsWith("]"));
202
+ if (looksJson && !ct.includes("json")) {
203
+ let text = bodyText.trim();
204
+ let truncated = truncatedBody;
205
+ if (text.length > maxChars) {
206
+ text = text.slice(0, maxChars).trimEnd();
207
+ truncated = true;
208
+ }
209
+ return {
210
+ ok: true, url: requested, final_url: finalUrl, source: "live", status,
211
+ content_type: "application/json", word_count: wordCount(text), text, truncated,
212
+ };
213
+ }
214
+ if (isHtml) {
215
+ try {
216
+ const { document } = parseHTML(bodyText);
217
+ let title;
218
+ let byline = null;
219
+ let excerpt;
220
+ let text = "";
221
+ let note;
222
+ try {
223
+ const article = new Readability(document, { charThreshold: 100 }).parse();
224
+ if (article && article.textContent && article.textContent.trim()) {
225
+ title = article.title || undefined;
226
+ byline = article.byline ?? null;
227
+ excerpt = article.excerpt || undefined;
228
+ text = article.textContent.replace(/\n{3,}/g, "\n\n").trim();
229
+ }
230
+ }
231
+ catch { /* fall through to crude strip */ }
232
+ if (!text) {
233
+ text = crudeStrip(document, bodyText);
234
+ title = title || (document.querySelector("title")?.textContent || "").trim() || undefined;
235
+ note = "readability found no article body — returned crude page text";
236
+ }
237
+ let truncated = truncatedBody;
238
+ if (text.length > maxChars) {
239
+ text = text.slice(0, maxChars).trimEnd();
240
+ truncated = true;
241
+ }
242
+ if (!text.trim()) {
243
+ return {
244
+ ok: false, url: requested, final_url: finalUrl, source: "live", status,
245
+ content_type: ct || "text/html",
246
+ reason: "no extractable text (likely a JS-only page) — fetch the URL directly",
247
+ };
248
+ }
249
+ if (looksLikeBotWall(text)) {
250
+ return {
251
+ ok: false, url: requested, final_url: finalUrl, source: "live", status,
252
+ content_type: ct || "text/html",
253
+ reason: "bot wall / JS challenge — fetch the URL directly or pick another source",
254
+ };
255
+ }
256
+ return {
257
+ ok: true, url: requested, final_url: finalUrl, source: "live", status,
258
+ content_type: ct || "text/html",
259
+ title, byline, excerpt, word_count: wordCount(text), text, truncated,
260
+ reason: note,
261
+ };
262
+ }
263
+ catch (e) {
264
+ return {
265
+ ok: false, url: requested, final_url: finalUrl, source: "live", status,
266
+ content_type: ct, reason: `HTML parse failed: ${e?.message || e}`,
267
+ };
268
+ }
269
+ }
270
+ // text/plain, JSON, XML, markdown — return raw, capped.
271
+ let text = bodyText.replace(/\r\n/g, "\n").trim();
272
+ let truncated = truncatedBody;
273
+ if (text.length > maxChars) {
274
+ text = text.slice(0, maxChars).trimEnd();
275
+ truncated = true;
276
+ }
277
+ return {
278
+ ok: true, url: requested, final_url: finalUrl, source: "live", status,
279
+ content_type: ct || "text/plain", word_count: wordCount(text), text, truncated,
280
+ };
281
+ }
282
+ // ── SSRF guard ──────────────────────────────────────────────────────────────
283
+ // `web_get` is exposed publicly (via `4ort mcp serve`), so refuse to fetch
284
+ // loopback / RFC1918 / link-local / cloud-metadata addresses. v1: literal-host
285
+ // check only — a hardened public deploy should also check the *resolved* IP
286
+ // (DNS-rebinding) and rate-limit. Blocks some legit CLI dev use of localhost —
287
+ // that's the right default for a web-fetcher tool.
288
+ function isInternalHost(hostname) {
289
+ const h = hostname.replace(/^\[|\]$/g, "").toLowerCase();
290
+ if (["localhost", "metadata", "metadata.google.internal", "instance-data", "0.0.0.0"].includes(h))
291
+ return true;
292
+ if (h === "::1" || h.startsWith("fe80:") || h.startsWith("fc") || h.startsWith("fd"))
293
+ return true; // IPv6 loopback / link-local / ULA
294
+ if (/^\d{1,3}(\.\d{1,3}){3}$/.test(h)) {
295
+ const [a, b] = h.split(".").map(Number);
296
+ if (a === 127 || a === 10 || a === 0)
297
+ return true;
298
+ if (a === 169 && b === 254)
299
+ return true; // link-local / cloud metadata
300
+ if (a === 172 && b >= 16 && b <= 31)
301
+ return true; // RFC1918
302
+ if (a === 192 && b === 168)
303
+ return true; // RFC1918
304
+ if (a === 100 && b >= 64 && b <= 127)
305
+ return true; // CGNAT
306
+ }
307
+ return false;
308
+ }
309
+ // ── Public entry ────────────────────────────────────────────────────────────
310
+ export async function webRead(rawUrl, opts = {}) {
311
+ let u;
312
+ try {
313
+ u = new URL(rawUrl);
314
+ }
315
+ catch {
316
+ return { ok: false, url: rawUrl, source: "live", reason: "invalid URL" };
317
+ }
318
+ if (u.protocol !== "http:" && u.protocol !== "https:") {
319
+ return { ok: false, url: rawUrl, source: "live", reason: `unsupported scheme: ${u.protocol}` };
320
+ }
321
+ if (isInternalHost(u.hostname)) {
322
+ return { ok: false, url: rawUrl, source: "live", reason: "refusing to fetch an internal / loopback / link-local / metadata address" };
323
+ }
324
+ const maxChars = opts.maxChars && opts.maxChars > 0 ? opts.maxChars : DEFAULT_MAX_CHARS;
325
+ // Tier 0 — our own entity pages: serve the structured record, skip scraping.
326
+ const slug = looksLikeOurEntity(u);
327
+ if (slug) {
328
+ const ours = await readOurEntity(rawUrl, slug);
329
+ if (ours)
330
+ return ours;
331
+ }
332
+ // Tier 2 — live fetch + Readability.
333
+ return liveFetch(rawUrl, maxChars);
334
+ }
335
+ //# sourceMappingURL=web-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-client.js","sourceRoot":"","sources":["../src/web-client.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,21 +1,37 @@
1
1
  {
2
2
  "name": "@4ort/cli",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
+ "description": "Unified CLI for the 4ort ecosystem — 4ort.net hosting, knowledge graph, and more",
4
5
  "type": "module",
5
6
  "bin": {
6
7
  "4ort": "./dist/index.js"
7
8
  },
9
+ "files": [
10
+ "dist",
11
+ "README.md"
12
+ ],
8
13
  "scripts": {
9
14
  "dev": "tsx src/index.ts",
10
15
  "build": "tsc",
11
- "start": "node dist/index.js"
16
+ "start": "node dist/index.js",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "engines": {
20
+ "node": ">=18"
12
21
  },
13
22
  "dependencies": {
23
+ "@modelcontextprotocol/sdk": "^1.29.0",
24
+ "@mozilla/readability": "^0.6.0",
14
25
  "commander": "^12.0.0",
15
- "tar": "^7.0.0"
26
+ "linkedom": "^0.18.12",
27
+ "tar": "^7.0.0",
28
+ "undici": "^6.25.0"
16
29
  },
17
30
  "devDependencies": {
31
+ "@types/node": "^25.6.0",
18
32
  "tsx": "^4.0.0",
19
33
  "typescript": "^5.0.0"
20
- }
34
+ },
35
+ "license": "UNLICENSED",
36
+ "private": false
21
37
  }
package/src/api-client.ts DELETED
@@ -1,51 +0,0 @@
1
- import { Config } from "./config.js";
2
-
3
- export async function apiRequest(
4
- config: Config,
5
- method: string,
6
- path: string,
7
- body?: any,
8
- ): Promise<any> {
9
- const url = `${config.server}${path}`;
10
- const headers: Record<string, string> = {
11
- Authorization: `Bearer ${config.apiKey}`,
12
- };
13
-
14
- const init: RequestInit = { method, headers };
15
-
16
- if (body instanceof FormData) {
17
- init.body = body;
18
- } else if (body) {
19
- headers["Content-Type"] = "application/json";
20
- init.body = JSON.stringify(body);
21
- }
22
-
23
- const res = await fetch(url, init);
24
- const data = await res.json();
25
-
26
- if (!res.ok) {
27
- throw new Error(data.error || `HTTP ${res.status}`);
28
- }
29
-
30
- return data;
31
- }
32
-
33
- export async function registerAgent(
34
- server: string,
35
- name: string,
36
- metadata?: Record<string, any>,
37
- ): Promise<any> {
38
- const res = await fetch(`${server}/api/register`, {
39
- method: "POST",
40
- headers: { "Content-Type": "application/json" },
41
- body: JSON.stringify({ name, metadata }),
42
- });
43
-
44
- const data = await res.json();
45
-
46
- if (!res.ok) {
47
- throw new Error(data.error || `HTTP ${res.status}`);
48
- }
49
-
50
- return data;
51
- }
@@ -1,15 +0,0 @@
1
- import { apiRequest } from "../api-client.js";
2
- import { requireConfig } from "../config.js";
3
-
4
- export async function deleteSite(subdomain?: string) {
5
- const config = requireConfig();
6
- const name = subdomain || config.agentName;
7
-
8
- try {
9
- const data = await apiRequest(config, "DELETE", `/api/sites/${name}`);
10
- console.log(data.message);
11
- } catch (err: any) {
12
- console.error(`Delete failed: ${err.message}`);
13
- process.exit(1);
14
- }
15
- }
@@ -1,25 +0,0 @@
1
- import { apiRequest } from "../api-client.js";
2
- import { requireConfig } from "../config.js";
3
-
4
- export async function list() {
5
- const config = requireConfig();
6
-
7
- try {
8
- const data = await apiRequest(config, "GET", "/api/sites");
9
-
10
- if (data.sites.length === 0) {
11
- console.log("No sites yet. Run: 4ort push .");
12
- return;
13
- }
14
-
15
- console.log("Your sites:\n");
16
- for (const site of data.sites) {
17
- console.log(` ${site.subdomain}.4ort.net`);
18
- console.log(` Files: ${site.file_count} | Size: ${(site.size_bytes / 1024).toFixed(1)} KB`);
19
- console.log(` Updated: ${site.updated_at}\n`);
20
- }
21
- } catch (err: any) {
22
- console.error(`Failed: ${err.message}`);
23
- process.exit(1);
24
- }
25
- }
@@ -1,79 +0,0 @@
1
- import { apiRequest } from "../api-client.js";
2
- import { requireConfig } from "../config.js";
3
-
4
- export async function checkMail(options: { unread?: boolean }) {
5
- const config = requireConfig();
6
-
7
- try {
8
- const query = options.unread ? "?unread=true" : "";
9
- const data = await apiRequest(config, "GET", `/api/mail${query}`);
10
-
11
- console.log(`Inbox: ${data.inbox}`);
12
- console.log(`Unread: ${data.unread}\n`);
13
-
14
- if (data.messages.length === 0) {
15
- console.log("No messages.");
16
- return;
17
- }
18
-
19
- for (const msg of data.messages) {
20
- const marker = msg.read ? " " : "*";
21
- console.log(`${marker} [${msg.id}] ${msg.from} — ${msg.subject}`);
22
- console.log(` ${msg.date}`);
23
- }
24
- } catch (err: any) {
25
- console.error(`Failed: ${err.message}`);
26
- process.exit(1);
27
- }
28
- }
29
-
30
- export async function readMail(id: string) {
31
- const config = requireConfig();
32
-
33
- try {
34
- const data = await apiRequest(config, "GET", `/api/mail/${id}`);
35
-
36
- console.log(`From: ${data.from}`);
37
- console.log(`Subject: ${data.subject}`);
38
- console.log(`Date: ${data.date}`);
39
- console.log(`---`);
40
- console.log(data.body);
41
- } catch (err: any) {
42
- console.error(`Failed: ${err.message}`);
43
- process.exit(1);
44
- }
45
- }
46
-
47
- export async function sendMail(to: string, options: { subject?: string; body?: string }) {
48
- const config = requireConfig();
49
-
50
- if (!options.body) {
51
- console.error("--body is required");
52
- process.exit(1);
53
- }
54
-
55
- try {
56
- const data = await apiRequest(config, "POST", "/api/mail/send", {
57
- to,
58
- subject: options.subject || "(no subject)",
59
- body: options.body,
60
- });
61
-
62
- console.log(data.message);
63
- } catch (err: any) {
64
- console.error(`Failed: ${err.message}`);
65
- process.exit(1);
66
- }
67
- }
68
-
69
- export async function deleteMail(id: string) {
70
- const config = requireConfig();
71
-
72
- try {
73
- const data = await apiRequest(config, "DELETE", `/api/mail/${id}`);
74
- console.log(data.message);
75
- } catch (err: any) {
76
- console.error(`Failed: ${err.message}`);
77
- process.exit(1);
78
- }
79
- }