@oracle-agent/oracle 0.4.2 → 0.6.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 (129) hide show
  1. package/README.md +76 -18
  2. package/SETUP.md +13 -0
  3. package/bin/oracle-data-mcp.mjs +1 -1
  4. package/bin/oracle.mjs +33 -0
  5. package/docs/cli.md +47 -0
  6. package/docs/connectors.md +13 -0
  7. package/package.json +6 -2
  8. package/profiles/oracle/SOUL.md +10 -4
  9. package/profiles/robinhood-agent/SOUL.md +5 -3
  10. package/protocols/templates/safe-erc20/README.md +9 -0
  11. package/public/agent-connect/app.js +16 -13
  12. package/public/oracle-splash/assets/hero/orb.avif +0 -0
  13. package/public/oracle-splash/assets/hero/orb.webm +0 -0
  14. package/public/oracle-splash/assets/llms/claude.svg +1 -0
  15. package/public/oracle-splash/assets/llms/codex.svg +1 -0
  16. package/public/oracle-splash/assets/llms/cursor.svg +17 -0
  17. package/public/oracle-splash/assets/llms/deepseek.svg +1 -0
  18. package/public/oracle-splash/assets/llms/gemini.svg +1 -0
  19. package/public/oracle-splash/assets/llms/grok.svg +8 -0
  20. package/public/oracle-splash/assets/llms/hermes.png +0 -0
  21. package/public/oracle-splash/assets/llms/kimi.svg +1 -0
  22. package/public/oracle-splash/assets/llms/manus.svg +18 -0
  23. package/public/oracle-splash/assets/llms/openclaw.svg +22 -0
  24. package/public/oracle-splash/assets/llms/perplexity.svg +1 -0
  25. package/public/oracle-splash/assets/llms/qwen.svg +1 -0
  26. package/public/oracle-splash/assets/llms/windsurf.svg +1 -0
  27. package/public/oracle-splash/assets/protocols/abcdex.png +0 -0
  28. package/public/oracle-splash/assets/protocols/across.png +0 -0
  29. package/public/oracle-splash/assets/protocols/aerodrome.png +0 -0
  30. package/public/oracle-splash/assets/protocols/balancer.png +0 -0
  31. package/public/oracle-splash/assets/protocols/cowswap.png +0 -0
  32. package/public/oracle-splash/assets/protocols/curve.png +0 -0
  33. package/public/oracle-splash/assets/protocols/dreamcash.png +0 -0
  34. package/public/oracle-splash/assets/protocols/felix.png +0 -0
  35. package/public/oracle-splash/assets/protocols/gmx.png +0 -0
  36. package/public/oracle-splash/assets/protocols/hyena.png +0 -0
  37. package/public/oracle-splash/assets/protocols/hyperbeat.png +0 -0
  38. package/public/oracle-splash/assets/protocols/hyperliquid.png +0 -0
  39. package/public/oracle-splash/assets/protocols/hyperswap.png +0 -0
  40. package/public/oracle-splash/assets/protocols/jupiter.png +0 -0
  41. package/public/oracle-splash/assets/protocols/kinetiq.png +0 -0
  42. package/public/oracle-splash/assets/protocols/lfj.png +0 -0
  43. package/public/oracle-splash/assets/protocols/lifi.png +0 -0
  44. package/public/oracle-splash/assets/protocols/magic-eden.png +0 -0
  45. package/public/oracle-splash/assets/protocols/morpho.png +0 -0
  46. package/public/oracle-splash/assets/protocols/odos.png +0 -0
  47. package/public/oracle-splash/assets/protocols/oneinch.png +0 -0
  48. package/public/oracle-splash/assets/protocols/opensea.png +0 -0
  49. package/public/oracle-splash/assets/protocols/pancakeswap.png +0 -0
  50. package/public/oracle-splash/assets/protocols/paragon.png +0 -0
  51. package/public/oracle-splash/assets/protocols/paraswap.png +0 -0
  52. package/public/oracle-splash/assets/protocols/pendle.png +0 -0
  53. package/public/oracle-splash/assets/protocols/polymarket.png +0 -0
  54. package/public/oracle-splash/assets/protocols/quickswap.png +0 -0
  55. package/public/oracle-splash/assets/protocols/relay.png +0 -0
  56. package/public/oracle-splash/assets/protocols/stargate.png +0 -0
  57. package/public/oracle-splash/assets/protocols/tradexyz.png +0 -0
  58. package/public/oracle-splash/assets/protocols/uniswap.png +0 -0
  59. package/public/oracle-splash/assets/protocols/velodrome.png +0 -0
  60. package/public/oracle-splash/assets/protocols/ventuals.png +0 -0
  61. package/public/oracle-splash/assets/wordmarks/across.svg +4 -0
  62. package/public/oracle-splash/assets/wordmarks/aerodrome.svg +23 -0
  63. package/public/oracle-splash/assets/wordmarks/balancer.svg +3 -0
  64. package/public/oracle-splash/assets/wordmarks/cowswap.svg +1 -0
  65. package/public/oracle-splash/assets/wordmarks/gmx.svg +13 -0
  66. package/public/oracle-splash/assets/wordmarks/hyena.svg +7 -0
  67. package/public/oracle-splash/assets/wordmarks/hyperliquid.svg +21 -0
  68. package/public/oracle-splash/assets/wordmarks/hyperswap.svg +13 -0
  69. package/public/oracle-splash/assets/wordmarks/jupiter.svg +35 -0
  70. package/public/oracle-splash/assets/wordmarks/lifi.png +0 -0
  71. package/public/oracle-splash/assets/wordmarks/magic-eden.svg +1 -0
  72. package/public/oracle-splash/assets/wordmarks/morpho.svg +1 -0
  73. package/public/oracle-splash/assets/wordmarks/odos.svg +3 -0
  74. package/public/oracle-splash/assets/wordmarks/oneinch.svg +6 -0
  75. package/public/oracle-splash/assets/wordmarks/opensea.svg +1 -0
  76. package/public/oracle-splash/assets/wordmarks/outcome.svg +16 -0
  77. package/public/oracle-splash/assets/wordmarks/pancakeswap.svg +18 -0
  78. package/public/oracle-splash/assets/wordmarks/paraswap.svg +16 -0
  79. package/public/oracle-splash/assets/wordmarks/pendle.png +0 -0
  80. package/public/oracle-splash/assets/wordmarks/polymarket.png +0 -0
  81. package/public/oracle-splash/assets/wordmarks/quickswap.png +0 -0
  82. package/public/oracle-splash/assets/wordmarks/relay.svg +8 -0
  83. package/public/oracle-splash/assets/wordmarks/stargate.svg +15 -0
  84. package/public/oracle-splash/assets/wordmarks/uniswap.svg +18 -0
  85. package/public/oracle-splash/assets/wordmarks/velodrome.svg +84 -0
  86. package/public/oracle-splash/assets/wordmarks/xyz.svg +12 -0
  87. package/public/oracle-splash/index.html +784 -498
  88. package/scripts/check-doc-drift.mjs +111 -0
  89. package/skills/oracle-chat/SKILL.md +61 -0
  90. package/skills/oracle-chat/chain.SKILL.md +31 -0
  91. package/skills/oracle-chat/setup.SKILL.md +37 -0
  92. package/skins/oracle.yaml +52 -0
  93. package/src/cli/chain-catalog.mjs +215 -0
  94. package/src/cli/chain-state.mjs +74 -0
  95. package/src/cli/commands/chain.mjs +143 -0
  96. package/src/cli/commands/chat.mjs +268 -0
  97. package/src/cli/commands/credential.mjs +9 -0
  98. package/src/cli/commands/data-mcp.mjs +18 -0
  99. package/src/cli/commands/data.mjs +73 -0
  100. package/src/cli/commands/doctor.mjs +131 -0
  101. package/src/cli/commands/help.mjs +7 -0
  102. package/src/cli/commands/init.mjs +27 -0
  103. package/src/cli/commands/mcp.mjs +142 -0
  104. package/src/cli/commands/model.mjs +37 -0
  105. package/src/cli/commands/prepare.mjs +12 -0
  106. package/src/cli/commands/public.mjs +20 -0
  107. package/src/cli/commands/route.mjs +12 -0
  108. package/src/cli/commands/runner.mjs +9 -0
  109. package/src/cli/commands/scan.mjs +12 -0
  110. package/src/cli/commands/setup.mjs +283 -0
  111. package/src/cli/commands/sign.mjs +21 -0
  112. package/src/cli/commands/signer.mjs +9 -0
  113. package/src/cli/commands/upgrade.mjs +12 -0
  114. package/src/cli/commands/vault.mjs +9 -0
  115. package/src/cli/commands/version.mjs +22 -0
  116. package/src/cli/first-run.mjs +23 -0
  117. package/src/cli/kernel.mjs +237 -0
  118. package/src/cli/mcp-targets/chatgpt.mjs +51 -0
  119. package/src/cli/mcp-targets/claude-code.mjs +39 -0
  120. package/src/cli/mcp-targets/claude-desktop.mjs +24 -0
  121. package/src/cli/mcp-targets/codex.mjs +41 -0
  122. package/src/cli/mcp-targets/shared.mjs +61 -0
  123. package/src/cli/messaging-platforms.mjs +209 -0
  124. package/src/cli/operator-dispatch.mjs +258 -0
  125. package/src/cli/oracle-harness.py +148 -0
  126. package/src/cli/paths.mjs +83 -0
  127. package/src/cli/setup-state.mjs +168 -0
  128. package/src/cli/spawn-child.mjs +43 -0
  129. package/src/public-api/connect-agent.mjs +29 -10
@@ -0,0 +1,142 @@
1
+ import { parseArgs } from "node:util";
2
+ import { printOperatorNotInstalled } from "../operator-dispatch.mjs";
3
+ import { installClaudeCode } from "../mcp-targets/claude-code.mjs";
4
+ import { installClaudeDesktop } from "../mcp-targets/claude-desktop.mjs";
5
+ import { installCodex } from "../mcp-targets/codex.mjs";
6
+ import { installChatgpt } from "../mcp-targets/chatgpt.mjs";
7
+ import { buildServerSpecs } from "../mcp-targets/shared.mjs";
8
+
9
+ function usage() {
10
+ process.stderr.write(
11
+ "usage: oracle mcp <install|print> [target] [flags]\n" +
12
+ " targets: claude-code | claude-desktop | codex | chatgpt | generic\n" +
13
+ " flags: --with-control --project --print --json\n",
14
+ );
15
+ }
16
+
17
+ function emitResult(result, { json = false } = {}) {
18
+ if (result.print && !result.instructions) {
19
+ process.stdout.write(JSON.stringify(result.print, null, 2) + "\n");
20
+ } else if (result.printText) {
21
+ process.stdout.write(result.printText);
22
+ }
23
+ if (result.instructions) {
24
+ process.stdout.write(result.instructions + "\n");
25
+ if (result.print && json) process.stdout.write(JSON.stringify(result.print, null, 2) + "\n");
26
+ }
27
+ if (!result.print && !result.printText && !result.instructions) {
28
+ const line = {
29
+ status: result.status,
30
+ path: result.path || null,
31
+ backup: result.backup || null,
32
+ method: result.method || null,
33
+ };
34
+ process.stdout.write(
35
+ (json ? JSON.stringify(line, null, 2) : `${line.status}${line.path ? " " + line.path : ""}`) + "\n",
36
+ );
37
+ }
38
+ }
39
+
40
+ export default {
41
+ name: "mcp",
42
+ summary: "wire Oracle into AI clients",
43
+ group: "read",
44
+ usage: "oracle mcp install <target> | oracle mcp print [--target <t>]",
45
+ async run(ctx) {
46
+ const verb = ctx.argv[0];
47
+ if (!verb || verb === "--help" || verb === "-h") {
48
+ usage();
49
+ return verb ? 0 : 1;
50
+ }
51
+
52
+ let values, positionals;
53
+ try {
54
+ ({ values, positionals } = parseArgs({
55
+ args: ctx.argv.slice(1),
56
+ options: {
57
+ "with-control": { type: "boolean", default: false },
58
+ project: { type: "boolean", default: false },
59
+ print: { type: "boolean", default: false },
60
+ json: { type: "boolean", default: false },
61
+ target: { type: "string" },
62
+ },
63
+ allowPositionals: true,
64
+ strict: false,
65
+ }));
66
+ } catch (err) {
67
+ process.stderr.write(`oracle mcp: ${err.message}\n`);
68
+ usage();
69
+ return 1;
70
+ }
71
+
72
+ const withControl = !!values["with-control"];
73
+ const project = !!values.project;
74
+ const json = !!values.json;
75
+ const operator = ctx.resolveOperator ? ctx.resolveOperator() : { ok: false };
76
+
77
+ if (withControl && (!operator || !operator.ok)) {
78
+ printOperatorNotInstalled(process.stderr);
79
+ return 3;
80
+ }
81
+
82
+ const opts = { oracleRoot: ctx.root, operator, withControl, project, cwd: process.cwd() };
83
+
84
+ if (verb === "print") {
85
+ const target = values.target || positionals[0] || "claude-code";
86
+ if (target === "generic" || target === "claude-code") {
87
+ const built = buildServerSpecs(opts);
88
+ if (!built.ok) { printOperatorNotInstalled(process.stderr); return 3; }
89
+ process.stdout.write(JSON.stringify({ mcpServers: built.specs }, null, 2) + "\n");
90
+ return 0;
91
+ }
92
+ if (target === "chatgpt") {
93
+ const r = await installChatgpt({ printOnly: true });
94
+ emitResult(r, { json });
95
+ return 0;
96
+ }
97
+ if (target === "codex") {
98
+ emitResult(await installCodex({ ...opts, printOnly: true }), { json });
99
+ return 0;
100
+ }
101
+ if (target === "claude-desktop") {
102
+ emitResult(await installClaudeDesktop({ ...opts, printOnly: true }), { json });
103
+ return 0;
104
+ }
105
+ process.stderr.write(`oracle mcp: unknown target '${target}'\n`);
106
+ return 1;
107
+ }
108
+
109
+ if (verb === "install") {
110
+ const target = positionals[0] || values.target;
111
+ if (!target) { usage(); return 1; }
112
+ const printOnly = !!values.print;
113
+ let result;
114
+ if (target === "claude-code") result = await installClaudeCode({ ...opts, printOnly });
115
+ else if (target === "claude-desktop") result = await installClaudeDesktop({ ...opts, printOnly });
116
+ else if (target === "codex") result = await installCodex({ ...opts, printOnly });
117
+ else if (target === "chatgpt") result = await installChatgpt({ printOnly });
118
+ else if (target === "generic") {
119
+ const built = buildServerSpecs(opts);
120
+ if (!built.ok) { printOperatorNotInstalled(process.stderr); return 3; }
121
+ process.stdout.write(JSON.stringify({ mcpServers: built.specs }, null, 2) + "\n");
122
+ return 0;
123
+ } else {
124
+ process.stderr.write(`oracle mcp: unknown target '${target}'\n`);
125
+ return 1;
126
+ }
127
+ if (!result.ok) {
128
+ if (result.code === 3) printOperatorNotInstalled(process.stderr);
129
+ else process.stderr.write(`oracle mcp: ${result.reason || "failed"}\n`);
130
+ return result.code || 1;
131
+ }
132
+ emitResult(result, { json });
133
+ if (result.status && String(result.status).startsWith("printed") && result.print) {
134
+ process.stdout.write(JSON.stringify(result.print, null, 2) + "\n");
135
+ }
136
+ return 0;
137
+ }
138
+
139
+ usage();
140
+ return 1;
141
+ },
142
+ };
@@ -0,0 +1,37 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { spawnChild } from "../spawn-child.mjs";
4
+
5
+ function hermesBin() {
6
+ if (process.env.ORACLE_HERMES_BIN) return process.env.ORACLE_HERMES_BIN;
7
+ for (const dir of (process.env.PATH || "").split(path.delimiter)) {
8
+ const candidate = path.join(dir, "hermes");
9
+ if (fs.existsSync(candidate)) return candidate;
10
+ }
11
+ return null;
12
+ }
13
+
14
+ export default {
15
+ name: "model",
16
+ summary: "choose the oracle profile's model/provider",
17
+ group: "read",
18
+ usage: [
19
+ "oracle model — interactive provider/model picker for the oracle profile",
20
+ "",
21
+ "inside chat: /model",
22
+ "one oracle persona stays loaded when the model changes.",
23
+ ].join("\n"),
24
+ async run(ctx) {
25
+ const hermes = hermesBin();
26
+ if (!hermes) {
27
+ process.stderr.write("oracle model needs hermes on PATH\n");
28
+ return 1;
29
+ }
30
+ return spawnChild(
31
+ hermes,
32
+ ["-p", "oracle", "model", ...ctx.argv],
33
+ { stdio: "inherit", env: process.env },
34
+ "oracle-model",
35
+ );
36
+ },
37
+ };
@@ -0,0 +1,12 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import path from "node:path";
3
+ export default {
4
+ name: "prepare", summary: "build an unsigned swap (alias of route prepare)", group: "read",
5
+ usage: "oracle prepare <chain> <tokenIn> <tokenOut> <taker> [amt]",
6
+ async run(ctx) {
7
+ const bin = path.join(ctx.root, "bin", "oracle-route.mjs");
8
+ const r = spawnSync(process.execPath, [bin, "prepare", ...ctx.argv], { stdio: "inherit" });
9
+ if (r.error) { process.stderr.write(`oracle: failed to spawn oracle-route: ${r.error.message}\n`); return 1; }
10
+ return typeof r.status === "number" ? r.status : 1;
11
+ },
12
+ };
@@ -0,0 +1,20 @@
1
+ import path from "node:path";
2
+ import { parseArgs } from "node:util";
3
+ import { spawnChild } from "../spawn-child.mjs";
4
+ export default {
5
+ name: "public", summary: "secret-free public HTTP surface", group: "read",
6
+ usage: "oracle public serve [--port N]",
7
+ async run(ctx) {
8
+ const verb = ctx.argv[0] || "serve";
9
+ if (verb !== "serve") { process.stderr.write("usage: oracle public serve [--port N]\n"); return 1; }
10
+ let port = null;
11
+ try {
12
+ const { values } = parseArgs({ args: ctx.argv.slice(1), options: { port: { type: "string" } }, allowPositionals: true, strict: false });
13
+ port = values.port || null;
14
+ } catch {}
15
+ const env = { ...process.env };
16
+ if (port) env.ORACLE_PUBLIC_PORT = String(port);
17
+ const bin = path.join(ctx.root, "bin", "oracle-public-server.mjs");
18
+ return spawnChild(process.execPath, [bin], { stdio: "inherit", env }, "oracle-public-server");
19
+ },
20
+ };
@@ -0,0 +1,12 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import path from "node:path";
3
+ export default {
4
+ name: "route", summary: "swap/bridge prepare helpers", group: "read",
5
+ usage: "oracle route <swap|bridge|prepare|prepare-bridge> ...",
6
+ async run(ctx) {
7
+ const bin = path.join(ctx.root, "bin", "oracle-route.mjs");
8
+ const r = spawnSync(process.execPath, [bin, ...ctx.argv], { stdio: "inherit" });
9
+ if (r.error) { process.stderr.write(`oracle: failed to spawn oracle-route: ${r.error.message}\n`); return 1; }
10
+ return typeof r.status === "number" ? r.status : 1;
11
+ },
12
+ };
@@ -0,0 +1,9 @@
1
+ export default {
2
+ name: "runner",
3
+ summary: "action runner daemon",
4
+ group: "sign",
5
+ usage: "oracle runner [--interval-ms N ...]",
6
+ async run(ctx) {
7
+ return ctx.dispatchOperator("oracle-runner", ctx.argv, { appendHintOnError: true });
8
+ },
9
+ };
@@ -0,0 +1,12 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import path from "node:path";
3
+ export default {
4
+ name: "scan", summary: "chain scanner", group: "read",
5
+ usage: "oracle scan <chains|head|token|pools|risk|quote|sell> ...",
6
+ async run(ctx) {
7
+ const bin = path.join(ctx.root, "bin", "oracle-scan.mjs");
8
+ const r = spawnSync(process.execPath, [bin, ...ctx.argv], { stdio: "inherit" });
9
+ if (r.error) { process.stderr.write(`oracle: failed to spawn oracle-scan: ${r.error.message}\n`); return 1; }
10
+ return typeof r.status === "number" ? r.status : 1;
11
+ },
12
+ };
@@ -0,0 +1,283 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import readline from "node:readline/promises";
5
+ import { stdin as input, stdout as output } from "node:process";
6
+ import {
7
+ findMessagingPlatform,
8
+ listMessagingPlatforms,
9
+ renderSetupMenu,
10
+ } from "../messaging-platforms.mjs";
11
+ import {
12
+ platformStatus,
13
+ renderStatus,
14
+ setPlatformToken,
15
+ touchSetupState,
16
+ } from "../setup-state.mjs";
17
+ import { hermesRoot } from "../paths.mjs";
18
+
19
+ function which(bin) {
20
+ for (const dir of (process.env.PATH || "").split(path.delimiter)) {
21
+ const c = path.join(dir, bin);
22
+ try {
23
+ if (fs.existsSync(c)) return c;
24
+ } catch {}
25
+ }
26
+ return null;
27
+ }
28
+
29
+ function usage() {
30
+ return [
31
+ "oracle setup — wire messaging + local agent surface",
32
+ "",
33
+ " oracle setup menu + platform status",
34
+ " oracle setup status same, machine-friendly with --json",
35
+ " oracle setup messaging open hermes gateway setup wizard",
36
+ " oracle setup telegram set TELEGRAM_BOT_TOKEN for profile",
37
+ " oracle setup discord set DISCORD_BOT_TOKEN for profile",
38
+ " oracle setup slack set slack tokens",
39
+ " oracle setup <platform> any supported hermes platform",
40
+ " oracle setup gateway [status|restart|start|stop]",
41
+ " oracle setup profile [name] default: oracle",
42
+ "",
43
+ "chat:",
44
+ " /setup",
45
+ " /setup telegram",
46
+ " /setup status",
47
+ "",
48
+ "tokens stay local in ~/.hermes/profiles/<profile>/.env",
49
+ "oracle never prints secrets.",
50
+ ].join("\n");
51
+ }
52
+
53
+ function profileFromArgs(args) {
54
+ const i = args.indexOf("--profile");
55
+ if (i >= 0 && args[i + 1]) return args[i + 1];
56
+ const j = args.indexOf("-p");
57
+ if (j >= 0 && args[j + 1]) return args[j + 1];
58
+ return process.env.ORACLE_PROFILE || "oracle";
59
+ }
60
+
61
+ function stripProfileArgs(args) {
62
+ const out = [];
63
+ for (let i = 0; i < args.length; i++) {
64
+ if (args[i] === "--profile" || args[i] === "-p") {
65
+ i += 1;
66
+ continue;
67
+ }
68
+ out.push(args[i]);
69
+ }
70
+ return out;
71
+ }
72
+
73
+ function runHermes(argv, { inherit = true, env = process.env } = {}) {
74
+ const hermes = which("hermes");
75
+ if (!hermes) {
76
+ process.stderr.write(
77
+ "oracle setup: hermes not on PATH\ninstall hermes, then re-run\n",
78
+ );
79
+ return 1;
80
+ }
81
+ const r = spawnSync(hermes, argv, {
82
+ stdio: inherit ? "inherit" : "pipe",
83
+ encoding: "utf8",
84
+ env,
85
+ });
86
+ if (r.error) {
87
+ process.stderr.write(`oracle setup: failed to run hermes: ${r.error.message}\n`);
88
+ return 1;
89
+ }
90
+ if (!inherit) {
91
+ if (r.stdout) process.stdout.write(r.stdout);
92
+ if (r.stderr) process.stderr.write(r.stderr);
93
+ }
94
+ return typeof r.status === "number" ? r.status : 1;
95
+ }
96
+
97
+ async function promptSecret(label) {
98
+ if (!process.stdin.isTTY) {
99
+ const err = new Error("interactive token entry requires a tty");
100
+ err.code = "NO_TTY";
101
+ throw err;
102
+ }
103
+ const rl = readline.createInterface({ input, output });
104
+ try {
105
+ // readline cannot hide input portably without raw mode; still avoid echo by
106
+ // asking user to paste and not logging it ourselves.
107
+ const value = (await rl.question(`${label}: `)).trim();
108
+ return value;
109
+ } finally {
110
+ rl.close();
111
+ }
112
+ }
113
+
114
+ async function configurePlatform(profile, platformKey, inlineArgs = []) {
115
+ const platform = findMessagingPlatform(platformKey);
116
+ if (!platform) {
117
+ process.stderr.write(`oracle setup: unknown platform '${platformKey}'\n`);
118
+ process.stderr.write(
119
+ `known: ${listMessagingPlatforms()
120
+ .map((p) => p.key)
121
+ .join(", ")}\n`,
122
+ );
123
+ return 1;
124
+ }
125
+
126
+ // Platforms without simple token keys go through Hermes interactive setup.
127
+ if (!platform.required.length) {
128
+ process.stdout.write(
129
+ `opening hermes setup for ${platform.key} (interactive)\n`,
130
+ );
131
+ touchSetupState({ lastPlatform: platform.key, profile });
132
+ return runHermes(["-p", profile, ...(platform.hermes || ["gateway", "setup"])]);
133
+ }
134
+
135
+ const flags = {};
136
+ const positionals = [];
137
+ for (let i = 0; i < inlineArgs.length; i++) {
138
+ const a = inlineArgs[i];
139
+ if (a === "--token" && inlineArgs[i + 1]) {
140
+ flags.token = inlineArgs[++i];
141
+ continue;
142
+ }
143
+ if (a === "--allowed-users" && inlineArgs[i + 1]) {
144
+ flags.allowedUsers = inlineArgs[++i];
145
+ continue;
146
+ }
147
+ if (a === "--app-token" && inlineArgs[i + 1]) {
148
+ flags.appToken = inlineArgs[++i];
149
+ continue;
150
+ }
151
+ if (a.startsWith("-")) {
152
+ process.stderr.write(`oracle setup: unknown flag ${a}\n`);
153
+ return 1;
154
+ }
155
+ positionals.push(a);
156
+ }
157
+
158
+ let token = flags.token || positionals[0] || "";
159
+ if (!token) {
160
+ try {
161
+ token = await promptSecret(`${platform.key} bot token`);
162
+ } catch (err) {
163
+ if (err?.code === "NO_TTY") {
164
+ process.stderr.write(
165
+ `oracle setup ${platform.key}: pass --token <value> in non-interactive mode\n`,
166
+ );
167
+ return 1;
168
+ }
169
+ throw err;
170
+ }
171
+ }
172
+ if (!token) {
173
+ process.stderr.write("aborted: empty token\n");
174
+ return 1;
175
+ }
176
+
177
+ const extra = {};
178
+ if (flags.allowedUsers) {
179
+ if (platform.key === "telegram") extra.TELEGRAM_ALLOWED_USERS = flags.allowedUsers;
180
+ if (platform.key === "discord") extra.DISCORD_ALLOWED_USERS = flags.allowedUsers;
181
+ if (platform.key === "slack") extra.SLACK_ALLOWED_USERS = flags.allowedUsers;
182
+ }
183
+ if (flags.appToken && platform.key === "slack") {
184
+ extra.SLACK_APP_TOKEN = flags.appToken;
185
+ }
186
+
187
+ // Slack needs two tokens.
188
+ if (platform.key === "slack" && !extra.SLACK_APP_TOKEN) {
189
+ let app = flags.appToken || positionals[1] || "";
190
+ if (!app && process.stdin.isTTY) {
191
+ app = await promptSecret("slack app token (xapp-...)");
192
+ }
193
+ if (!app) {
194
+ process.stderr.write("slack needs bot token + app token\n");
195
+ return 1;
196
+ }
197
+ extra.SLACK_APP_TOKEN = app;
198
+ }
199
+
200
+ try {
201
+ const result = setPlatformToken(profile, platform.key, token, extra);
202
+ touchSetupState({ lastPlatform: platform.key, profile, ready: true });
203
+ process.stdout.write(
204
+ `saved ${platform.key} credentials for profile '${profile}'\n` +
205
+ `path: ${result.path}\n` +
206
+ `next: oracle setup gateway restart\n`,
207
+ );
208
+ return 0;
209
+ } catch (err) {
210
+ process.stderr.write(`oracle setup: ${err.message}\n`);
211
+ return 1;
212
+ }
213
+ }
214
+
215
+ export default {
216
+ name: "setup",
217
+ summary: "configure telegram/discord/slack and other hermes messaging platforms",
218
+ group: "read",
219
+ usage: usage(),
220
+ async run(ctx) {
221
+ const args = [...ctx.argv];
222
+ const json = args.includes("--json");
223
+ const profile = profileFromArgs(args);
224
+ const clean = stripProfileArgs(args).filter((a) => a !== "--json");
225
+ const verb = (clean[0] || "menu").toLowerCase();
226
+ const rest = clean.slice(1);
227
+
228
+ if (["-h", "--help", "help"].includes(verb)) {
229
+ process.stdout.write(usage() + "\n");
230
+ return 0;
231
+ }
232
+
233
+ if (["menu", "list", "ls"].includes(verb)) {
234
+ if (json) {
235
+ process.stdout.write(JSON.stringify(platformStatus(profile), null, 2) + "\n");
236
+ } else {
237
+ process.stdout.write(renderStatus(profile));
238
+ }
239
+ return 0;
240
+ }
241
+
242
+ if (["status", "show"].includes(verb)) {
243
+ const st = platformStatus(profile);
244
+ if (json) {
245
+ process.stdout.write(JSON.stringify(st, null, 2) + "\n");
246
+ } else {
247
+ process.stdout.write(renderStatus(profile));
248
+ process.stdout.write(`hermes root: ${hermesRoot()}\n`);
249
+ }
250
+ return 0;
251
+ }
252
+
253
+ if (["messaging", "gateway-setup", "platforms"].includes(verb)) {
254
+ touchSetupState({ lastAction: "messaging", profile });
255
+ process.stdout.write("opening hermes gateway setup wizard...\n");
256
+ return runHermes(["-p", profile, "gateway", "setup"]);
257
+ }
258
+
259
+ if (verb === "gateway") {
260
+ const action = (rest[0] || "status").toLowerCase();
261
+ if (!["status", "start", "stop", "restart", "run", "install"].includes(action)) {
262
+ process.stderr.write("usage: oracle setup gateway [status|start|stop|restart|run|install]\n");
263
+ return 1;
264
+ }
265
+ return runHermes(["-p", profile, "gateway", action]);
266
+ }
267
+
268
+ if (verb === "profile") {
269
+ const name = rest[0] || profile;
270
+ process.stdout.write(`profile: ${name}\nhermes: ${hermesRoot()}/profiles/${name}\n`);
271
+ process.stdout.write(renderStatus(name));
272
+ return 0;
273
+ }
274
+
275
+ // oracle setup telegram|discord|...
276
+ if (findMessagingPlatform(verb)) {
277
+ return configurePlatform(profile, verb, rest);
278
+ }
279
+
280
+ process.stderr.write(`oracle setup: unknown verb '${verb}'\n${usage()}\n`);
281
+ return 1;
282
+ },
283
+ };
@@ -0,0 +1,21 @@
1
+ export default {
2
+ name: "sign",
3
+ summary: "provision / check the local signer (opt-in)",
4
+ group: "sign",
5
+ usage: "oracle sign <init|doctor> ...",
6
+ async run(ctx) {
7
+ const verb = ctx.argv[0];
8
+ if (verb === "init") {
9
+ return ctx.dispatchOperator("oracle-agentic-init", ctx.argv.slice(1), { appendHintOnError: true });
10
+ }
11
+ if (verb === "doctor") {
12
+ return ctx.dispatchOperator("oracle-agentic-doctor", ctx.argv.slice(1), { appendHintOnError: true });
13
+ }
14
+ process.stderr.write(
15
+ "usage: oracle sign <init|doctor> ...\n" +
16
+ " oracle sign init provision keys + policy (interactive)\n" +
17
+ " oracle sign doctor check local signer readiness\n",
18
+ );
19
+ return 1;
20
+ },
21
+ };
@@ -0,0 +1,9 @@
1
+ export default {
2
+ name: "signer",
3
+ summary: "loopback signer daemon",
4
+ group: "sign",
5
+ usage: "oracle signer [--port N ...]",
6
+ async run(ctx) {
7
+ return ctx.dispatchOperator("oracle-signer", ctx.argv, { appendHintOnError: true });
8
+ },
9
+ };
@@ -0,0 +1,12 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import path from "node:path";
3
+ export default {
4
+ name: "upgrade", summary: "upgrade installed Hermes lanes", group: "read",
5
+ usage: "oracle upgrade [--apply ...]",
6
+ async run(ctx) {
7
+ const bin = path.join(ctx.root, "bin", "oracle-upgrade.mjs");
8
+ const r = spawnSync(process.execPath, [bin, ...ctx.argv], { stdio: "inherit" });
9
+ if (r.error) { process.stderr.write(`oracle: failed to spawn oracle-upgrade: ${r.error.message}\n`); return 1; }
10
+ return typeof r.status === "number" ? r.status : 1;
11
+ },
12
+ };
@@ -0,0 +1,9 @@
1
+ export default {
2
+ name: "vault",
3
+ summary: "encrypt local key files at rest",
4
+ group: "sign",
5
+ usage: "oracle vault <encrypt|rekey|inspect|decrypt> ...",
6
+ async run(ctx) {
7
+ return ctx.dispatchOperator("oracle-vault", ctx.argv, { appendHintOnError: true });
8
+ },
9
+ };
@@ -0,0 +1,22 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { resolveOperator } from "../operator-dispatch.mjs";
4
+
5
+ export default {
6
+ name: "version",
7
+ summary: "print oracle (and operator, if present) version",
8
+ group: "meta",
9
+ usage: "oracle version",
10
+ async run(ctx) {
11
+ let version = "0.0.0";
12
+ try {
13
+ const pkg = JSON.parse(fs.readFileSync(path.join(ctx.root, "package.json"), "utf8"));
14
+ version = pkg.version || version;
15
+ } catch {}
16
+ let line = `oracle ${version}`;
17
+ const op = (ctx.resolveOperator || resolveOperator)();
18
+ if (op.ok) line += ` operator ${op.version}`;
19
+ process.stdout.write(line + "\n");
20
+ return 0;
21
+ },
22
+ };
@@ -0,0 +1,23 @@
1
+ export const NEXT_STEPS_AFTER_INIT = [
2
+ "oracle data serve # loopback read/prepare plane on 127.0.0.1:8787",
3
+ "oracle doctor # verify",
4
+ "oracle chat # one lowercase oracle surface",
5
+ "oracle chain list # pick hyperliquid/base/solana/...",
6
+ "oracle setup # telegram/discord/slack + hermes messaging",
7
+ "oracle mcp install claude-code",
8
+ "# optional, explicit second step — signing stays a separate local package:",
9
+ "npm i -g @oracle-agent/operator",
10
+ "oracle sign init",
11
+ ];
12
+
13
+ export function renderNextSteps() {
14
+ return [
15
+ "",
16
+ "Next:",
17
+ ...NEXT_STEPS_AFTER_INIT.map((l) => " " + l),
18
+ "",
19
+ ].join("\n");
20
+ }
21
+
22
+ export const DOCTOR_SIGNING_OPTIONAL =
23
+ "signing: not installed (optional) — npm i -g @oracle-agent/operator";