@4ort/cli 0.7.0 → 0.8.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 (51) hide show
  1. package/README.md +19 -7
  2. package/dist/api-client.d.ts +9 -1
  3. package/dist/api-client.js +9 -2
  4. package/dist/api-client.js.map +1 -1
  5. package/dist/commands/docs.d.ts +17 -0
  6. package/dist/commands/docs.js +90 -0
  7. package/dist/commands/docs.js.map +1 -0
  8. package/dist/commands/domain.d.ts +20 -0
  9. package/dist/commands/domain.js +179 -0
  10. package/dist/commands/domain.js.map +1 -0
  11. package/dist/commands/domains.d.ts +6 -0
  12. package/dist/commands/domains.js +91 -0
  13. package/dist/commands/domains.js.map +1 -0
  14. package/dist/commands/login-link.d.ts +1 -0
  15. package/dist/commands/login-link.js +18 -0
  16. package/dist/commands/login-link.js.map +1 -0
  17. package/dist/commands/mov.d.ts +1 -0
  18. package/dist/commands/mov.js +40 -4
  19. package/dist/commands/mov.js.map +1 -1
  20. package/dist/commands/register.d.ts +1 -0
  21. package/dist/commands/register.js +1 -1
  22. package/dist/commands/register.js.map +1 -1
  23. package/dist/commands/repo.d.ts +9 -0
  24. package/dist/commands/repo.js +73 -0
  25. package/dist/commands/repo.js.map +1 -0
  26. package/dist/commands/sheets.d.ts +17 -0
  27. package/dist/commands/sheets.js +90 -0
  28. package/dist/commands/sheets.js.map +1 -0
  29. package/dist/gitea-client.d.ts +41 -0
  30. package/dist/gitea-client.js +79 -0
  31. package/dist/gitea-client.js.map +1 -0
  32. package/dist/gitea-client.test.d.ts +1 -0
  33. package/dist/gitea-client.test.js +40 -0
  34. package/dist/gitea-client.test.js.map +1 -0
  35. package/dist/index.js +146 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/mcp-server.js +204 -2
  38. package/dist/mcp-server.js.map +1 -1
  39. package/dist/namesilo-client.d.ts +31 -0
  40. package/dist/namesilo-client.js +85 -0
  41. package/dist/namesilo-client.js.map +1 -0
  42. package/dist/namesilo-client.test.d.ts +1 -0
  43. package/dist/namesilo-client.test.js +58 -0
  44. package/dist/namesilo-client.test.js.map +1 -0
  45. package/dist/office-client.d.ts +76 -0
  46. package/dist/office-client.js +110 -0
  47. package/dist/office-client.js.map +1 -0
  48. package/dist/office-client.test.d.ts +1 -0
  49. package/dist/office-client.test.js +53 -0
  50. package/dist/office-client.test.js.map +1 -0
  51. package/package.json +1 -1
package/README.md CHANGED
@@ -12,13 +12,25 @@ One install. One identity. Every product.
12
12
 
13
13
  ## Products
14
14
 
15
- | Namespace | What it does |
16
- | -------------------------- | ---------------------------------------------- |
17
- | `4ort register / push / …` | 4ort.net subdomain hosting + email + agents |
18
- | `4ort mail` | Manage your `@4ort.net` inbox from the CLI |
19
- | `4ort provision` | Auto-provision third-party service accounts |
20
- | `4ort kg` | 4ort.xyz knowledge graph (Popularity Graph) |
21
- | `4ort config` | Inspect ~/.4ort/config.json |
15
+ | Namespace | What it does |
16
+ | ------------------------------------------ | ------------------------------------------------------------ |
17
+ | `4ort register / push / list / delete / whoami / recover` | 4ort.net subdomain hosting + agent identity |
18
+ | `4ort mail …` | Manage your `@4ort.net` inbox from the CLI |
19
+ | `4ort provision …` | Auto-provision Tier-A third-party service accounts (incl. `video` and `gitea`) |
20
+ | `4ort domains …` | Map custom domains onto your 4ort.net site (add → verify → live w/ HTTPS) |
21
+ | `4ort domain …` | Registrar ops on YOUR NameSilo account — check/buy/dns (BYO key, never bundled) |
22
+ | `4ort docs …` / `4ort sheets …` | Documents (markdown) + spreadsheets (CSV) on docs/sheets.4ort.net |
23
+ | `4ort repo …` | Code repos on 4ort.dev (the Launchpad) — create/list/delete/push |
24
+ | `4ort kg …` | 4ort.xyz knowledge graph (search/ask/entity/match/trending…) |
25
+ | `4ort search …` | Smart router (semantic + entity match) |
26
+ | `4ort web get …` | Polite web reader (SSRF-hardened, via 4ort.xyz public/fetch) |
27
+ | `4ort mov login / publish / render / list / channels / delete` | PeerTube publishing + dir→video-factory render → 4ort.mov — **see `docs/MOV.md`** |
28
+ | `4ort mcp stdio / serve` | Run the CLI itself as an MCP server — **see `docs/MCP.md`** |
29
+ | `4ort vault login / status / logout` + `4ort run …` | 🔑 Secrets layer (1Password-`op run` model) — **see `docs/VAULT.md`** |
30
+ | `4ort config` | Inspect `~/.4ort/config.json` |
31
+
32
+ > **Galaxy property-note:** `~/ai/4ort-galaxy/property-notes/4ort-cli.md` covers
33
+ > this CLI's role across the whole galaxy + the four identity types it juggles.
22
34
 
23
35
  ---
24
36
 
@@ -1,3 +1,11 @@
1
1
  import { Config } from "./config.js";
2
2
  export declare function apiRequest(config: Config, method: string, path: string, body?: any): Promise<any>;
3
- export declare function registerAgent(server: string, name: string, metadata?: Record<string, any>): Promise<any>;
3
+ export declare function registerAgent(server: string, name: string, metadata?: Record<string, any>, backupEmail?: string): Promise<any>;
4
+ /**
5
+ * Mint a single-use web-login link for the human behind this agent.
6
+ * Authenticated with the agent's API key (via apiRequest).
7
+ */
8
+ export declare function loginLink(config: Config): Promise<{
9
+ url: string;
10
+ expiresInSeconds: number;
11
+ }>;
@@ -18,11 +18,11 @@ export async function apiRequest(config, method, path, body) {
18
18
  }
19
19
  return data;
20
20
  }
21
- export async function registerAgent(server, name, metadata) {
21
+ export async function registerAgent(server, name, metadata, backupEmail) {
22
22
  const res = await fetch(`${server}/api/register`, {
23
23
  method: "POST",
24
24
  headers: { "Content-Type": "application/json" },
25
- body: JSON.stringify({ name, metadata }),
25
+ body: JSON.stringify({ name, metadata, backupEmail }),
26
26
  });
27
27
  const data = await res.json();
28
28
  if (!res.ok) {
@@ -30,4 +30,11 @@ export async function registerAgent(server, name, metadata) {
30
30
  }
31
31
  return data;
32
32
  }
33
+ /**
34
+ * Mint a single-use web-login link for the human behind this agent.
35
+ * Authenticated with the agent's API key (via apiRequest).
36
+ */
37
+ export async function loginLink(config) {
38
+ return apiRequest(config, "POST", "/api/login-link");
39
+ }
33
40
  //# sourceMappingURL=api-client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,MAAc,EACd,MAAc,EACd,IAAY,EACZ,IAAU;IAEV,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC;IACtC,MAAM,OAAO,GAA2B;QACtC,aAAa,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE;KACzC,CAAC;IAEF,MAAM,IAAI,GAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IAE9C,IAAI,IAAI,YAAY,QAAQ,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;SAAM,IAAI,IAAI,EAAE,CAAC;QAChB,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;QAC7C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAE9B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAc,EACd,IAAY,EACZ,QAA8B;IAE9B,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,eAAe,EAAE;QAChD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;KACzC,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAE9B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,MAAc,EACd,MAAc,EACd,IAAY,EACZ,IAAU;IAEV,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC;IACtC,MAAM,OAAO,GAA2B;QACtC,aAAa,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE;KACzC,CAAC;IAEF,MAAM,IAAI,GAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IAE9C,IAAI,IAAI,YAAY,QAAQ,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;SAAM,IAAI,IAAI,EAAE,CAAC;QAChB,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;QAC7C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAE9B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAc,EACd,IAAY,EACZ,QAA8B,EAC9B,WAAoB;IAEpB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,eAAe,EAAE;QAChD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;KACtD,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAE9B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAc;IAC5C,OAAO,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,iBAAiB,CAAC,CAAC;AACvD,CAAC"}
@@ -0,0 +1,17 @@
1
+ export declare function docsList(options: {
2
+ json?: boolean;
3
+ }): Promise<void>;
4
+ export declare function docsRead(id: string, options: {
5
+ json?: boolean;
6
+ }): Promise<void>;
7
+ export declare function docsCreate(options: {
8
+ title?: string;
9
+ file?: string;
10
+ content?: string;
11
+ }): Promise<void>;
12
+ export declare function docsUpdate(id: string, options: {
13
+ title?: string;
14
+ file?: string;
15
+ content?: string;
16
+ }): Promise<void>;
17
+ export declare function docsDelete(id: string): Promise<void>;
@@ -0,0 +1,90 @@
1
+ /**
2
+ * `4ort docs` — manage documents on docs.4ort.net.
3
+ * Content comes from --file, --content, or piped stdin (in that order).
4
+ * `read` prints raw markdown so output pipes cleanly into other tools.
5
+ */
6
+ import { requireConfig } from "../config.js";
7
+ import { docsClient, resolveContentInput } from "../office-client.js";
8
+ export async function docsList(options) {
9
+ const client = docsClient(requireConfig());
10
+ try {
11
+ const { docs } = await client.list();
12
+ if (options.json) {
13
+ console.log(JSON.stringify(docs, null, 2));
14
+ return;
15
+ }
16
+ if (docs.length === 0) {
17
+ console.log("No docs yet. Create one: 4ort docs create --title 'My Doc' --file notes.md");
18
+ return;
19
+ }
20
+ for (const d of docs) {
21
+ console.log(`[${d.id}] ${d.title} (updated ${d.updatedAt})`);
22
+ }
23
+ }
24
+ catch (err) {
25
+ console.error(`Failed: ${err.message}`);
26
+ process.exit(1);
27
+ }
28
+ }
29
+ export async function docsRead(id, options) {
30
+ const client = docsClient(requireConfig());
31
+ try {
32
+ const doc = await client.read(id);
33
+ if (options.json) {
34
+ console.log(JSON.stringify(doc, null, 2));
35
+ return;
36
+ }
37
+ console.log(doc.markdown);
38
+ }
39
+ catch (err) {
40
+ console.error(`Failed: ${err.message}`);
41
+ process.exit(1);
42
+ }
43
+ }
44
+ export async function docsCreate(options) {
45
+ const client = docsClient(requireConfig());
46
+ const markdown = resolveContentInput(options);
47
+ if (markdown === null) {
48
+ console.error("No content. Use --file <path>, --content <text>, or pipe markdown on stdin.");
49
+ process.exit(1);
50
+ }
51
+ try {
52
+ const doc = await client.create(markdown, options.title);
53
+ console.log(`Created [${doc.id}] ${doc.title}`);
54
+ }
55
+ catch (err) {
56
+ console.error(`Failed: ${err.message}`);
57
+ process.exit(1);
58
+ }
59
+ }
60
+ export async function docsUpdate(id, options) {
61
+ const client = docsClient(requireConfig());
62
+ const markdown = resolveContentInput(options);
63
+ if (markdown === null && !options.title) {
64
+ console.error("Nothing to update. Pass --title and/or content (--file, --content, or stdin).");
65
+ process.exit(1);
66
+ }
67
+ try {
68
+ const doc = await client.update(id, {
69
+ ...(markdown !== null ? { markdown } : {}),
70
+ ...(options.title ? { title: options.title } : {}),
71
+ });
72
+ console.log(`Updated [${doc.id}] ${doc.title} (${doc.updatedAt})`);
73
+ }
74
+ catch (err) {
75
+ console.error(`Failed: ${err.message}`);
76
+ process.exit(1);
77
+ }
78
+ }
79
+ export async function docsDelete(id) {
80
+ const client = docsClient(requireConfig());
81
+ try {
82
+ await client.remove(id);
83
+ console.log(`Deleted ${id}`);
84
+ }
85
+ catch (err) {
86
+ console.error(`Failed: ${err.message}`);
87
+ process.exit(1);
88
+ }
89
+ }
90
+ //# sourceMappingURL=docs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs.js","sourceRoot":"","sources":["../../src/commands/docs.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAEtE,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAA2B;IACxD,MAAM,MAAM,GAAG,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;IAC3C,IAAI,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACrC,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC3C,OAAO;QACT,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,CAAC,GAAG,CAAC,4EAA4E,CAAC,CAAC;YAC1F,OAAO;QACT,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,KAAK,cAAc,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,EAAU,EAAE,OAA2B;IACpE,MAAM,MAAM,GAAG,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;IAC3C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClC,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC1C,OAAO;QACT,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAA4D;IAC3F,MAAM,MAAM,GAAG,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC9C,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,OAAO,CAAC,KAAK,CAAC,6EAA6E,CAAC,CAAC;QAC7F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;IAClD,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,EAAU,EAAE,OAA4D;IACvG,MAAM,MAAM,GAAG,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC9C,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACxC,OAAO,CAAC,KAAK,CAAC,+EAA+E,CAAC,CAAC;QAC/F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE;YAClC,GAAG,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1C,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACnD,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,KAAK,MAAM,GAAG,CAAC,SAAS,GAAG,CAAC,CAAC;IACtE,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,EAAU;IACzC,MAAM,MAAM,GAAG,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;IAC3C,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
@@ -0,0 +1,20 @@
1
+ export declare function domainCheck(domains: string[], options: {
2
+ json?: boolean;
3
+ }): Promise<void>;
4
+ export declare function domainBuy(domain: string, options: {
5
+ years?: string;
6
+ yes?: boolean;
7
+ autoRenew?: boolean;
8
+ privacy?: boolean;
9
+ json?: boolean;
10
+ }): Promise<void>;
11
+ export declare function domainList(options: {
12
+ json?: boolean;
13
+ }): Promise<void>;
14
+ export declare function domainBalance(): Promise<void>;
15
+ export declare function domainDnsList(domain: string, options: {
16
+ json?: boolean;
17
+ }): Promise<void>;
18
+ export declare function domainDnsAdd(domain: string, type: string, host: string, value: string, options: {
19
+ ttl?: string;
20
+ }): Promise<void>;
@@ -0,0 +1,179 @@
1
+ /**
2
+ * `4ort domain` — registrar operations on the user's OWN NameSilo account.
3
+ * BYO key: NAMESILO_KEY env or the 4ort.ai vault (`4ort://namesilo`).
4
+ *
5
+ * `buy` spends real money, so it is gated three ways: live availability
6
+ * re-check, account balance check, and an explicit --yes flag. Without
7
+ * --yes it prints the quote and exits non-zero.
8
+ */
9
+ import { namesilo, normalizeDomainArg, resolveNamesiloKey } from "../namesilo-client.js";
10
+ const NO_KEY_HELP = [
11
+ "No NameSilo key found. Provide one of:",
12
+ " NAMESILO_KEY=<key> 4ort domain ... # env var",
13
+ " 4ort vault login, then vault a secret named 'namesilo' in the 4ort.ai Secrets panel",
14
+ "(NameSilo is BYO-account — sign up at namesilo.com, fund it, mint an API key.)",
15
+ ].join("\n");
16
+ async function requireKey() {
17
+ const key = await resolveNamesiloKey();
18
+ if (!key) {
19
+ console.error(NO_KEY_HELP);
20
+ process.exit(1);
21
+ }
22
+ return key;
23
+ }
24
+ /** NameSilo's `available.domain` comes back as object | array | string — normalize. */
25
+ function asArray(value) {
26
+ if (value === undefined || value === null)
27
+ return [];
28
+ return Array.isArray(value) ? value : [value];
29
+ }
30
+ function availabilityRows(reply) {
31
+ const avail = asArray(reply.available?.domain).map((d) => ({
32
+ domain: typeof d === "string" ? d : d.domain,
33
+ available: true,
34
+ price: typeof d === "object" ? d.price : undefined,
35
+ }));
36
+ const taken = asArray(reply.unavailable?.domain).map((d) => ({
37
+ domain: typeof d === "string" ? d : d.domain,
38
+ available: false,
39
+ }));
40
+ return [...avail, ...taken];
41
+ }
42
+ export async function domainCheck(domains, options) {
43
+ const key = await requireKey();
44
+ const cleaned = domains.map(normalizeDomainArg).filter(Boolean);
45
+ if (cleaned.length === 0) {
46
+ console.error("Usage: 4ort domain check <domain> [more...]");
47
+ process.exit(1);
48
+ }
49
+ const r = await namesilo.check(key, cleaned);
50
+ if (!r.ok) {
51
+ console.error(`NameSilo error ${r.code}: ${r.detail || "unknown"}`);
52
+ process.exit(1);
53
+ }
54
+ const rows = availabilityRows(r.reply);
55
+ if (options.json) {
56
+ console.log(JSON.stringify(rows, null, 2));
57
+ return;
58
+ }
59
+ for (const row of rows) {
60
+ console.log(row.available ? `✓ ${row.domain} available${row.price ? ` $${row.price}/yr` : ""}` : `✗ ${row.domain} taken`);
61
+ }
62
+ }
63
+ export async function domainBuy(domain, options) {
64
+ const key = await requireKey();
65
+ const name = normalizeDomainArg(domain);
66
+ const years = Math.max(1, parseInt(options.years || "1", 10) || 1);
67
+ // 1. Re-check availability + authoritative price right before buying.
68
+ const check = await namesilo.check(key, [name]);
69
+ if (!check.ok) {
70
+ console.error(`NameSilo error ${check.code}: ${check.detail || "availability check failed"}`);
71
+ process.exit(1);
72
+ }
73
+ const row = availabilityRows(check.reply).find((r) => r.domain === name);
74
+ if (!row?.available) {
75
+ console.error(`${name} is not available to register.`);
76
+ process.exit(1);
77
+ }
78
+ const pricePerYear = row.price ? parseFloat(row.price) : undefined;
79
+ const estimate = pricePerYear !== undefined ? (pricePerYear * years).toFixed(2) : undefined;
80
+ // 2. Balance check — never attempt a purchase that will bounce.
81
+ const bal = await namesilo.balance(key);
82
+ const balance = bal.ok ? parseFloat(String(bal.reply.balance ?? "0")) : undefined;
83
+ if (balance !== undefined && pricePerYear !== undefined && balance < pricePerYear * years) {
84
+ console.error(`Insufficient NameSilo balance: $${balance.toFixed(2)} on hand, ~$${estimate} needed. Add funds at namesilo.com first.`);
85
+ process.exit(1);
86
+ }
87
+ // 3. Explicit consent — this spends real money.
88
+ if (!options.yes) {
89
+ console.log(`Quote: ${name} × ${years} year${years > 1 ? "s" : ""}${estimate ? ` ≈ $${estimate}` : ""}`);
90
+ if (balance !== undefined)
91
+ console.log(`NameSilo balance: $${balance.toFixed(2)}`);
92
+ console.log(`\nThis will spend real money from YOUR NameSilo account.`);
93
+ console.log(`To proceed: 4ort domain buy ${name} --years ${years} --yes`);
94
+ process.exit(1);
95
+ }
96
+ const r = await namesilo.register(key, name, {
97
+ years,
98
+ autoRenew: options.autoRenew,
99
+ privacy: options.privacy,
100
+ });
101
+ if (!r.ok) {
102
+ console.error(`Registration failed (code ${r.code}): ${r.detail || "unknown"}`);
103
+ process.exit(1);
104
+ }
105
+ if (options.json) {
106
+ console.log(JSON.stringify(r.reply, null, 2));
107
+ return;
108
+ }
109
+ console.log(`Registered ${name} for ${years} year${years > 1 ? "s" : ""}. ${r.detail || ""}`);
110
+ console.log(`Next: point it at your site — 4ort domains add ${name} (prints the DNS records),`);
111
+ console.log(`then: 4ort domain dns add ${name} A @ <ip> and 4ort domain dns add ${name} TXT <host> <token>`);
112
+ }
113
+ export async function domainList(options) {
114
+ const key = await requireKey();
115
+ const r = await namesilo.listDomains(key);
116
+ if (!r.ok) {
117
+ console.error(`NameSilo error ${r.code}: ${r.detail || "unknown"}`);
118
+ process.exit(1);
119
+ }
120
+ const domains = asArray(r.reply.domains?.domain).map((d) => (typeof d === "string" ? d : d.domain));
121
+ if (options.json) {
122
+ console.log(JSON.stringify(domains, null, 2));
123
+ return;
124
+ }
125
+ if (domains.length === 0) {
126
+ console.log("No domains in this NameSilo account.");
127
+ return;
128
+ }
129
+ for (const d of domains)
130
+ console.log(d);
131
+ }
132
+ export async function domainBalance() {
133
+ const key = await requireKey();
134
+ const r = await namesilo.balance(key);
135
+ if (!r.ok) {
136
+ console.error(`NameSilo error ${r.code}: ${r.detail || "unknown"}`);
137
+ process.exit(1);
138
+ }
139
+ console.log(`$${r.reply.balance}`);
140
+ }
141
+ export async function domainDnsList(domain, options) {
142
+ const key = await requireKey();
143
+ const name = normalizeDomainArg(domain);
144
+ const r = await namesilo.dnsList(key, name);
145
+ if (!r.ok) {
146
+ console.error(`NameSilo error ${r.code}: ${r.detail || "unknown"}`);
147
+ process.exit(1);
148
+ }
149
+ const records = asArray(r.reply.resource_record);
150
+ if (options.json) {
151
+ console.log(JSON.stringify(records, null, 2));
152
+ return;
153
+ }
154
+ if (records.length === 0) {
155
+ console.log(`No DNS records on ${name}.`);
156
+ return;
157
+ }
158
+ for (const rec of records) {
159
+ console.log(`${String(rec.type).padEnd(6)} ${String(rec.host).padEnd(40)} ${rec.value} (ttl ${rec.ttl})`);
160
+ }
161
+ }
162
+ export async function domainDnsAdd(domain, type, host, value, options) {
163
+ const key = await requireKey();
164
+ const name = normalizeDomainArg(domain);
165
+ // NameSilo uses "" for the apex; accept "@" as the conventional spelling.
166
+ const rrhost = host === "@" ? "" : host;
167
+ const r = await namesilo.dnsAdd(key, name, {
168
+ type,
169
+ host: rrhost,
170
+ value,
171
+ ttl: options.ttl ? parseInt(options.ttl, 10) : undefined,
172
+ });
173
+ if (!r.ok) {
174
+ console.error(`NameSilo error ${r.code}: ${r.detail || "unknown"}`);
175
+ process.exit(1);
176
+ }
177
+ console.log(`Added ${type.toUpperCase()} ${host} -> ${value} on ${name} (record id ${r.reply.record_id ?? "?"})`);
178
+ }
179
+ //# sourceMappingURL=domain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain.js","sourceRoot":"","sources":["../../src/commands/domain.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export declare function domainsList(subdomain: string | undefined, options: {
2
+ json?: boolean;
3
+ }): Promise<void>;
4
+ export declare function domainsAdd(domain: string, subdomain: string | undefined): Promise<void>;
5
+ export declare function domainsVerify(domain: string, subdomain: string | undefined): Promise<void>;
6
+ export declare function domainsRemove(domain: string, subdomain: string | undefined): Promise<void>;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * `4ort domains` — map custom domains onto a 4ort.net-hosted site.
3
+ * Wraps /api/sites/:subdomain/domains (add → DNS instructions → verify →
4
+ * live with auto-HTTPS). The subdomain defaults to your agent name, which
5
+ * is the common single-site case.
6
+ */
7
+ import { apiRequest } from "../api-client.js";
8
+ import { requireConfig } from "../config.js";
9
+ function siteFor(config, subdomain) {
10
+ const site = subdomain || config.agentName;
11
+ if (!site) {
12
+ console.error("No subdomain given and no agent name in config. Pass one: 4ort domains list <subdomain>");
13
+ process.exit(1);
14
+ }
15
+ return site;
16
+ }
17
+ function printDns(dns) {
18
+ console.log(` Set these DNS records:`);
19
+ console.log(` ${dns.a_record.type.padEnd(4)} ${dns.a_record.host} -> ${dns.a_record.value}`);
20
+ console.log(` ${dns.txt_record.type.padEnd(4)} ${dns.txt_record.host} -> ${dns.txt_record.value}`);
21
+ console.log(` ${dns.note}`);
22
+ }
23
+ export async function domainsList(subdomain, options) {
24
+ const config = requireConfig();
25
+ const site = siteFor(config, subdomain);
26
+ try {
27
+ const data = await apiRequest(config, "GET", `/api/sites/${encodeURIComponent(site)}/domains`);
28
+ if (options.json) {
29
+ console.log(JSON.stringify(data, null, 2));
30
+ return;
31
+ }
32
+ if (data.domains.length === 0) {
33
+ console.log(`No custom domains on ${site}.4ort.net. Add one: 4ort domains add example.com`);
34
+ return;
35
+ }
36
+ for (const d of data.domains) {
37
+ console.log(`${d.verified ? "✓" : "…"} ${d.domain} (${d.verified ? `verified ${d.verified_at}` : "pending verification"})`);
38
+ if (!d.verified && d.dns)
39
+ printDns(d.dns);
40
+ }
41
+ }
42
+ catch (err) {
43
+ console.error(`Failed: ${err.message}`);
44
+ process.exit(1);
45
+ }
46
+ }
47
+ export async function domainsAdd(domain, subdomain) {
48
+ const config = requireConfig();
49
+ const site = siteFor(config, subdomain);
50
+ try {
51
+ const data = await apiRequest(config, "POST", `/api/sites/${encodeURIComponent(site)}/domains`, { domain });
52
+ if (data.verified) {
53
+ console.log(`${data.domain} is already verified and mapped to ${site}.4ort.net.`);
54
+ return;
55
+ }
56
+ console.log(`${data.domain} added to ${site}.4ort.net — pending verification.`);
57
+ printDns(data.dns);
58
+ console.log(`\nThen run: 4ort domains verify ${data.domain}${subdomain ? ` ${site}` : ""}`);
59
+ }
60
+ catch (err) {
61
+ console.error(`Failed: ${err.message}`);
62
+ process.exit(1);
63
+ }
64
+ }
65
+ export async function domainsVerify(domain, subdomain) {
66
+ const config = requireConfig();
67
+ const site = siteFor(config, subdomain);
68
+ try {
69
+ const data = await apiRequest(config, "POST", `/api/sites/${encodeURIComponent(site)}/domains/${encodeURIComponent(domain)}/verify`);
70
+ console.log(data.message || `${data.domain} verified.`);
71
+ }
72
+ catch (err) {
73
+ // The server's 422 includes the expected TXT record — apiRequest surfaces
74
+ // only error text, which already explains propagation. Keep it simple.
75
+ console.error(`Not verified yet: ${err.message}`);
76
+ process.exit(1);
77
+ }
78
+ }
79
+ export async function domainsRemove(domain, subdomain) {
80
+ const config = requireConfig();
81
+ const site = siteFor(config, subdomain);
82
+ try {
83
+ const data = await apiRequest(config, "DELETE", `/api/sites/${encodeURIComponent(site)}/domains/${encodeURIComponent(domain)}`);
84
+ console.log(data.message);
85
+ }
86
+ catch (err) {
87
+ console.error(`Failed: ${err.message}`);
88
+ process.exit(1);
89
+ }
90
+ }
91
+ //# sourceMappingURL=domains.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domains.js","sourceRoot":"","sources":["../../src/commands/domains.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function loginLinkCommand(): Promise<void>;
@@ -0,0 +1,18 @@
1
+ import { requireConfig } from "../config.js";
2
+ import { loginLink } from "../api-client.js";
3
+ export async function loginLinkCommand() {
4
+ const config = requireConfig();
5
+ try {
6
+ const result = await loginLink(config);
7
+ const mins = Math.round(result.expiresInSeconds / 60);
8
+ console.log(`\nWeb login link for ${config.agentName}:\n`);
9
+ console.log(` ${result.url}\n`);
10
+ console.log(`Hand this to your human — clicking it logs them into 4ort.ai.`);
11
+ console.log(`Single-use, expires in ${mins} min. Re-run anytime for a fresh one.`);
12
+ }
13
+ catch (err) {
14
+ console.error(`Failed to mint login link: ${err.message}`);
15
+ process.exit(1);
16
+ }
17
+ }
18
+ //# sourceMappingURL=login-link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login-link.js","sourceRoot":"","sources":["../../src/commands/login-link.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAE/B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,gBAAgB,GAAG,EAAE,CAAC,CAAC;QACtD,OAAO,CAAC,GAAG,CAAC,wBAAwB,MAAM,CAAC,SAAS,KAAK,CAAC,CAAC;QAC3D,OAAO,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,+DAA+D,CAAC,CAAC;QAC7E,OAAO,CAAC,GAAG,CAAC,0BAA0B,IAAI,uCAAuC,CAAC,CAAC;IACrF,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,8BAA8B,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAC3D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
@@ -2,6 +2,7 @@ export declare function movLogin(options: {
2
2
  server?: string;
3
3
  username?: string;
4
4
  password?: string;
5
+ provisioned?: boolean;
5
6
  }): Promise<void>;
6
7
  export declare function movPublish(file: string, options: {
7
8
  title?: string;
@@ -13,10 +13,26 @@ import { saveMovConfig, optionalMovConfig } from "../config.js";
13
13
  import { getOAuthClient, getToken, uploadVideo, listMyChannels, createChannel, listMyVideos, deleteVideo, resolveChannelId, privacyToEnum, buildWatchUrl, } from "../peertube-client.js";
14
14
  const DEFAULT_SERVER = "https://4ort.mov";
15
15
  export async function movLogin(options) {
16
- const server = options.server || DEFAULT_SERVER;
16
+ let server = options.server || DEFAULT_SERVER;
17
17
  // Non-interactive (agents/CI): flags or env. Falls back to prompts for humans.
18
18
  let username = options.username || process.env.FORT_MOV_USERNAME;
19
19
  let password = options.password || process.env.FORT_MOV_PASSWORD;
20
+ // --provisioned: pull the account minted by `4ort provision run video`.
21
+ if (options.provisioned && (!username || !password)) {
22
+ const { requireConfig } = await import("../config.js");
23
+ const { apiRequest } = await import("../api-client.js");
24
+ const data = await apiRequest(requireConfig(), "GET", "/api/provision/credentials/video").catch((e) => {
25
+ console.error(`No provisioned video account: ${e.message}`);
26
+ console.error("Run: 4ort provision run video");
27
+ process.exit(1);
28
+ });
29
+ const byType = Object.fromEntries(data.credentials.map((c) => [c.type, c]));
30
+ username = username || byType.peertube_username?.value;
31
+ password = password || byType.peertube_password?.value;
32
+ if (!options.server && byType.peertube_username?.metadata?.server) {
33
+ server = byType.peertube_username.metadata.server;
34
+ }
35
+ }
20
36
  let rl;
21
37
  try {
22
38
  if (!username || !password) {
@@ -146,10 +162,30 @@ async function loadComposition(p) {
146
162
  return { type: "tar", data };
147
163
  }
148
164
  export async function movRender(file, options) {
149
- const server = options.server || process.env.FORT_FACTORY_URL || "https://factory.4ort.mov";
150
- const key = options.key || process.env.FORT_FACTORY_KEY;
165
+ let server = options.server || process.env.FORT_FACTORY_URL || "https://factory.4ort.mov";
166
+ let key = options.key || process.env.FORT_FACTORY_KEY;
167
+ if (!key) {
168
+ // Fall back to the factory token minted by `4ort provision run video`.
169
+ try {
170
+ const { loadConfig } = await import("../config.js");
171
+ const { apiRequest } = await import("../api-client.js");
172
+ const config = loadConfig();
173
+ if (config?.apiKey) {
174
+ const data = await apiRequest(config, "GET", "/api/provision/credentials/video");
175
+ const cred = data.credentials.find((c) => c.type === "factory_key");
176
+ if (cred) {
177
+ key = cred.value;
178
+ if (!options.server && !process.env.FORT_FACTORY_URL && cred.metadata?.server)
179
+ server = cred.metadata.server;
180
+ }
181
+ }
182
+ }
183
+ catch {
184
+ // fall through to the error below
185
+ }
186
+ }
151
187
  if (!key) {
152
- console.error("set --key or FORT_FACTORY_KEY (the video-factory bearer token)");
188
+ console.error("No factory token. Set --key / FORT_FACTORY_KEY, or run: 4ort provision run video");
153
189
  process.exit(1);
154
190
  }
155
191
  const composition = await loadComposition(file);