@oracle-agent/oracle 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/SETUP.md +1 -0
- package/bin/desk-server.mjs +2 -2
- package/docs/cli.md +2 -2
- package/package.json +1 -1
- package/profiles/oracle/SOUL.md +2 -2
- package/protocols/templates/safe-erc20/README.md +9 -0
- package/public/oracle-splash/assets/llms/codex-icon.svg +1 -0
- package/public/oracle-splash/assets/llms/gemini-icon.svg +1 -0
- package/public/oracle-splash/assets/wordmarks/across-icon.webp +0 -0
- package/public/oracle-splash/assets/wordmarks/hop-icon.webp +0 -0
- package/public/oracle-splash/assets/wordmarks/markets.svg +1 -0
- package/public/oracle-splash/assets/wordmarks/oneinch-icon.webp +0 -0
- package/public/oracle-splash/assets/wordmarks/opensea-icon.svg +17 -0
- package/public/oracle-splash/assets/wordmarks/satflow-icon.png +0 -0
- package/public/oracle-splash/assets/wordmarks/stargate-icon.webp +0 -0
- package/public/oracle-splash/assets/wordmarks/zerox-icon.webp +0 -0
- package/public/oracle-splash/favicon.ico +0 -0
- package/public/oracle-splash/favicon.svg +6 -0
- package/public/oracle-splash/index.html +153 -96
- package/scripts/check-doc-drift.mjs +1 -0
- package/skills/oracle-chat/setup.SKILL.md +2 -2
- package/skins/oracle.yaml +45 -41
- package/src/cli/commands/bootstrap.mjs +87 -0
- package/src/cli/commands/chat.mjs +140 -25
- package/src/cli/commands/doctor.mjs +5 -10
- package/src/cli/commands/model.mjs +19 -13
- package/src/cli/commands/setup.mjs +11 -19
- package/src/cli/first-run.mjs +2 -2
- package/src/cli/kernel.mjs +5 -4
- package/src/cli/messaging-platforms.mjs +1 -1
- package/src/cli/oracle-harness.py +226 -70
- package/src/cli/runtime.mjs +351 -0
- package/src/data/catalog.mjs +17 -2
- package/src/public-api/http.mjs +16 -1
- package/src/public-control/runtime-config.mjs +11 -1
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HERMES_PYPI,
|
|
3
|
+
installManagedHermes,
|
|
4
|
+
runtimeStatus,
|
|
5
|
+
runtimeVenvDir,
|
|
6
|
+
} from "../runtime.mjs";
|
|
7
|
+
|
|
8
|
+
function usage() {
|
|
9
|
+
return [
|
|
10
|
+
"oracle bootstrap - install the local agent runtime oracle chat needs",
|
|
11
|
+
"",
|
|
12
|
+
" oracle bootstrap install into ~/.config/oracle/runtime/venv",
|
|
13
|
+
" oracle bootstrap --upgrade upgrade the managed runtime",
|
|
14
|
+
" oracle bootstrap --status show what runtime oracle would use",
|
|
15
|
+
" oracle bootstrap --json machine-readable status",
|
|
16
|
+
"",
|
|
17
|
+
"notes:",
|
|
18
|
+
" a hermes on PATH always wins; bootstrap never touches system python",
|
|
19
|
+
" read/prepare commands (chain, scan, route, data) never need this",
|
|
20
|
+
].join("\n");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default {
|
|
24
|
+
name: "bootstrap",
|
|
25
|
+
summary: "install the local agent runtime for oracle chat",
|
|
26
|
+
group: "read",
|
|
27
|
+
usage: usage(),
|
|
28
|
+
async run(ctx) {
|
|
29
|
+
const args = ctx.argv;
|
|
30
|
+
if (args.includes("-h") || args.includes("--help")) {
|
|
31
|
+
process.stdout.write(usage() + "\n");
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const json = args.includes("--json");
|
|
36
|
+
const status = runtimeStatus();
|
|
37
|
+
|
|
38
|
+
if (args.includes("--status") || json) {
|
|
39
|
+
if (json) {
|
|
40
|
+
process.stdout.write(JSON.stringify(status, null, 2) + "\n");
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
process.stdout.write(
|
|
44
|
+
[
|
|
45
|
+
`hermes: ${status.hermes.ok ? `${status.hermes.bin} (${status.hermes.source})` : "not installed"}`,
|
|
46
|
+
`managed venv: ${status.managedVenv}`,
|
|
47
|
+
`managed built: ${status.managedInstalled ? "yes" : "no"}`,
|
|
48
|
+
`host python: ${status.hostPython ? `${status.hostPython.bin} (${status.hostPython.version})` : "none supported (need 3.11-3.13)"}`,
|
|
49
|
+
`uv: ${status.uv || "not installed (oracle will install it locally)"}`,
|
|
50
|
+
"",
|
|
51
|
+
].join("\n"),
|
|
52
|
+
);
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const upgrade = args.includes("--upgrade");
|
|
57
|
+
|
|
58
|
+
if (status.hermes.ok && status.hermes.source === "path" && !upgrade) {
|
|
59
|
+
process.stdout.write(
|
|
60
|
+
`hermes already on PATH: ${status.hermes.bin}\nnothing to do. run: oracle\n`,
|
|
61
|
+
);
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
process.stdout.write(
|
|
66
|
+
`installing ${HERMES_PYPI} into ${runtimeVenvDir()}\n` +
|
|
67
|
+
(status.hostPython
|
|
68
|
+
? `using python ${status.hostPython.version} (${status.hostPython.bin})\n`
|
|
69
|
+
: "provisioning an isolated python 3.13 with uv\n") +
|
|
70
|
+
"this takes a few minutes on first run\n\n",
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const r = installManagedHermes({ upgrade });
|
|
74
|
+
if (!r.ok) {
|
|
75
|
+
process.stderr.write(`oracle bootstrap failed: ${r.reason}\n`);
|
|
76
|
+
if (r.stderr) process.stderr.write(`${r.stderr}\n`);
|
|
77
|
+
return 1;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
process.stdout.write(
|
|
81
|
+
r.reused
|
|
82
|
+
? `runtime already present: ${r.bin}\n`
|
|
83
|
+
: `\nruntime ready: ${r.bin}\nrun: oracle\n`,
|
|
84
|
+
);
|
|
85
|
+
return 0;
|
|
86
|
+
},
|
|
87
|
+
};
|
|
@@ -4,13 +4,15 @@ import { spawnSync } from "node:child_process";
|
|
|
4
4
|
import { PACKAGE_ROOT, hermesRoot, ensureDir } from "../paths.mjs";
|
|
5
5
|
import { activeChainEnv } from "../chain-state.mjs";
|
|
6
6
|
import { spawnChild } from "../spawn-child.mjs";
|
|
7
|
+
import { installManagedHermes, resolveHermes } from "../runtime.mjs";
|
|
7
8
|
|
|
8
9
|
const PROFILE = "oracle";
|
|
9
10
|
const SKIN_NAME = "oracle";
|
|
10
11
|
|
|
11
12
|
function which(bin) {
|
|
12
|
-
if (bin === "hermes"
|
|
13
|
-
|
|
13
|
+
if (bin === "hermes") {
|
|
14
|
+
const r = resolveHermes();
|
|
15
|
+
return r.ok ? r.bin : null;
|
|
14
16
|
}
|
|
15
17
|
for (const dir of (process.env.PATH || "").split(path.delimiter)) {
|
|
16
18
|
const c = path.join(dir, bin);
|
|
@@ -21,6 +23,30 @@ function which(bin) {
|
|
|
21
23
|
return null;
|
|
22
24
|
}
|
|
23
25
|
|
|
26
|
+
/**
|
|
27
|
+
* First-run bootstrap. Only fires for a real interactive TTY so scripts,
|
|
28
|
+
* pipes, and CI never trigger a multi-minute install.
|
|
29
|
+
*/
|
|
30
|
+
function ensureRuntime() {
|
|
31
|
+
const found = resolveHermes();
|
|
32
|
+
if (found.ok) return found.bin;
|
|
33
|
+
if (process.env.ORACLE_NO_BOOTSTRAP === "1") return null;
|
|
34
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return null;
|
|
35
|
+
|
|
36
|
+
process.stdout.write(
|
|
37
|
+
"oracle needs a local agent runtime for chat (one-time setup).\n" +
|
|
38
|
+
"installing into ~/.config/oracle/runtime/venv, a few minutes.\n\n",
|
|
39
|
+
);
|
|
40
|
+
const r = installManagedHermes();
|
|
41
|
+
if (!r.ok) {
|
|
42
|
+
process.stderr.write(`oracle: runtime setup failed: ${r.reason}\n`);
|
|
43
|
+
if (r.stderr) process.stderr.write(`${r.stderr}\n`);
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
process.stdout.write("\nruntime ready.\n\n");
|
|
47
|
+
return r.bin;
|
|
48
|
+
}
|
|
49
|
+
|
|
24
50
|
function pythonShebang(file) {
|
|
25
51
|
try {
|
|
26
52
|
const first = fs.readFileSync(file, "utf8").split(/\r?\n/, 1)[0];
|
|
@@ -47,14 +73,43 @@ export function resolveHermesPython(hermes) {
|
|
|
47
73
|
return null;
|
|
48
74
|
}
|
|
49
75
|
|
|
50
|
-
function harnessSourcePath() {
|
|
76
|
+
export function harnessSourcePath() {
|
|
51
77
|
return path.join(PACKAGE_ROOT, "src", "cli", "oracle-harness.py");
|
|
52
78
|
}
|
|
53
79
|
|
|
80
|
+
export function oracleHarnessInvocation(hermes, args) {
|
|
81
|
+
const harness = harnessSourcePath();
|
|
82
|
+
const runtime = process.env.ORACLE_PLAIN_HARNESS === "0"
|
|
83
|
+
? null
|
|
84
|
+
: resolveHermesPython(hermes);
|
|
85
|
+
if (runtime && fs.existsSync(harness)) {
|
|
86
|
+
return {
|
|
87
|
+
command: runtime.command,
|
|
88
|
+
args: [...runtime.prefix, harness, ...args],
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return { command: hermes, args };
|
|
92
|
+
}
|
|
93
|
+
|
|
54
94
|
function skinSourcePath() {
|
|
55
95
|
return path.join(PACKAGE_ROOT, "skins", "oracle.yaml");
|
|
56
96
|
}
|
|
57
97
|
|
|
98
|
+
function ensureOracleProfile(profile = PROFILE) {
|
|
99
|
+
const src = path.join(PACKAGE_ROOT, "profiles", "oracle", "SOUL.md");
|
|
100
|
+
if (!fs.existsSync(src)) return { ok: false, reason: "oracle persona missing from package" };
|
|
101
|
+
const dest = path.join(hermesRoot(), "profiles", profile, "SOUL.md");
|
|
102
|
+
if (fs.existsSync(dest)) {
|
|
103
|
+
const current = fs.readFileSync(dest, "utf8");
|
|
104
|
+
if (!current.startsWith("You are Hermes Agent, an intelligent AI assistant created by Nous Research.")) {
|
|
105
|
+
return { ok: true, reused: true };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
ensureDir(path.dirname(dest));
|
|
109
|
+
fs.copyFileSync(src, dest);
|
|
110
|
+
return { ok: true, reused: false };
|
|
111
|
+
}
|
|
112
|
+
|
|
58
113
|
function ensureOracleSkin(profile = PROFILE) {
|
|
59
114
|
const src = skinSourcePath();
|
|
60
115
|
if (!fs.existsSync(src)) return { ok: false, reason: "skin missing from package" };
|
|
@@ -141,31 +196,78 @@ function setDisplaySkin(profile = PROFILE) {
|
|
|
141
196
|
return { ok: true };
|
|
142
197
|
}
|
|
143
198
|
|
|
199
|
+
function hasModelConfig(file) {
|
|
200
|
+
if (!fs.existsSync(file)) return false;
|
|
201
|
+
const text = fs.readFileSync(file, "utf8");
|
|
202
|
+
if (/^model:\s*\S+/m.test(text)) return true;
|
|
203
|
+
const block = text.match(/^model:\s*\n((?:[ \t]+.*(?:\n|$))*)/m)?.[1] || "";
|
|
204
|
+
return /^\s+(?:default|provider):\s*\S+/m.test(block);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function hasProviderCredentials(profile = PROFILE) {
|
|
208
|
+
const root = hermesRoot();
|
|
209
|
+
if (fs.existsSync(path.join(root, "auth.json"))) return true;
|
|
210
|
+
if (fs.existsSync(path.join(root, "profiles", profile, "auth.json"))) return true;
|
|
211
|
+
const keys = [
|
|
212
|
+
"OPENROUTER_API_KEY",
|
|
213
|
+
"ANTHROPIC_API_KEY",
|
|
214
|
+
"OPENAI_API_KEY",
|
|
215
|
+
"XAI_API_KEY",
|
|
216
|
+
"GOOGLE_API_KEY",
|
|
217
|
+
"GEMINI_API_KEY",
|
|
218
|
+
"DEEPSEEK_API_KEY",
|
|
219
|
+
"HF_TOKEN",
|
|
220
|
+
"GLM_API_KEY",
|
|
221
|
+
"KIMI_API_KEY",
|
|
222
|
+
"MINIMAX_API_KEY",
|
|
223
|
+
];
|
|
224
|
+
if (keys.some((key) => process.env[key])) return true;
|
|
225
|
+
for (const file of [path.join(root, ".env"), path.join(root, "profiles", profile, ".env")]) {
|
|
226
|
+
if (!fs.existsSync(file)) continue;
|
|
227
|
+
const text = fs.readFileSync(file, "utf8");
|
|
228
|
+
if (keys.some((key) => new RegExp(`^${key}=.+`, "m").test(text))) return true;
|
|
229
|
+
}
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function modelReady(profile = PROFILE) {
|
|
234
|
+
const root = hermesRoot();
|
|
235
|
+
return (
|
|
236
|
+
hasModelConfig(path.join(root, "config.yaml")) ||
|
|
237
|
+
hasModelConfig(path.join(root, "profiles", profile, "config.yaml")) ||
|
|
238
|
+
hasProviderCredentials(profile)
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
144
242
|
function usage() {
|
|
145
243
|
return [
|
|
146
|
-
"oracle chat —
|
|
244
|
+
"oracle chat — filled lowercase oracle chat",
|
|
147
245
|
"",
|
|
148
246
|
" oracle chat open interactive oracle chat",
|
|
149
247
|
" oracle chat -q \"...\" one-shot query",
|
|
150
|
-
" oracle chat --model <m>
|
|
151
|
-
" oracle chat --print-banner show
|
|
248
|
+
" oracle chat --model <m> use a model for this session",
|
|
249
|
+
" oracle chat --print-banner show filled oracle banner only",
|
|
152
250
|
"",
|
|
153
251
|
"inside chat:",
|
|
154
252
|
" /chain list/select chains",
|
|
155
253
|
" /setup messaging setup menu",
|
|
156
254
|
" /model switch models (still one oracle persona)",
|
|
157
255
|
"",
|
|
158
|
-
"brand:
|
|
256
|
+
"brand: filled lowercase oracle / classic boxed layout",
|
|
159
257
|
].join("\n");
|
|
160
258
|
}
|
|
161
259
|
|
|
162
260
|
function printBanner() {
|
|
163
261
|
const lines = [
|
|
164
262
|
"",
|
|
165
|
-
"
|
|
166
|
-
"
|
|
167
|
-
"
|
|
168
|
-
"
|
|
263
|
+
" ████",
|
|
264
|
+
" ░░███",
|
|
265
|
+
" ██████ ████████ ██████ ██████ ░███ ██████",
|
|
266
|
+
" ███░░███░░███░░███ ░░░░░███ ███░░███ ░███ ███░░███",
|
|
267
|
+
"░███ ░███ ░███ ░░░ ███████ ░███ ░░░ ░███ ░███████",
|
|
268
|
+
"░███ ░███ ░███ ███░░███ ░███ ███ ░███ ░███░░░",
|
|
269
|
+
"░░██████ █████ ░░████████░░██████ █████░░██████",
|
|
270
|
+
" ░░░░░░ ░░░░░ ░░░░░░░░ ░░░░░░ ░░░░░ ░░░░░░",
|
|
169
271
|
"",
|
|
170
272
|
];
|
|
171
273
|
process.stdout.write(lines.join("\n") + "\n");
|
|
@@ -187,23 +289,41 @@ export default {
|
|
|
187
289
|
return 0;
|
|
188
290
|
}
|
|
189
291
|
|
|
190
|
-
const hermes = which("hermes");
|
|
292
|
+
const hermes = which("hermes") || ensureRuntime();
|
|
191
293
|
if (!hermes) {
|
|
192
294
|
process.stderr.write(
|
|
193
|
-
"oracle chat needs
|
|
194
|
-
"
|
|
295
|
+
"oracle chat needs a local agent runtime\n" +
|
|
296
|
+
"run: oracle bootstrap\n",
|
|
195
297
|
);
|
|
196
298
|
return 1;
|
|
197
299
|
}
|
|
198
300
|
|
|
301
|
+
ensureOracleProfile(PROFILE);
|
|
199
302
|
ensureOracleSkin(PROFILE);
|
|
200
|
-
ensureQuickCommands(PROFILE);
|
|
201
303
|
ensureSkillCommands(PROFILE);
|
|
202
304
|
const skinSet = setDisplaySkin(PROFILE);
|
|
305
|
+
ensureQuickCommands(PROFILE);
|
|
203
306
|
if (!skinSet.ok && process.env.ORACLE_CLI_DEBUG) {
|
|
204
307
|
process.stderr.write(`oracle chat: skin note: ${skinSet.reason}\n`);
|
|
205
308
|
}
|
|
206
309
|
|
|
310
|
+
const explicitModel = args.includes("--model") || args.includes("-m") || args.includes("--provider");
|
|
311
|
+
if (!modelReady(PROFILE) && !explicitModel) {
|
|
312
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
313
|
+
process.stderr.write("oracle chat needs a configured model\nrun: oracle model\n");
|
|
314
|
+
return 1;
|
|
315
|
+
}
|
|
316
|
+
process.stdout.write("choose oracle's model before first chat\n\n");
|
|
317
|
+
const modelLaunch = oracleHarnessInvocation(hermes, ["-p", PROFILE, "model"]);
|
|
318
|
+
const selected = await spawnChild(
|
|
319
|
+
modelLaunch.command,
|
|
320
|
+
modelLaunch.args,
|
|
321
|
+
{ stdio: "inherit", env: process.env },
|
|
322
|
+
"oracle-model",
|
|
323
|
+
);
|
|
324
|
+
if (selected !== 0 || !modelReady(PROFILE)) return selected || 1;
|
|
325
|
+
}
|
|
326
|
+
|
|
207
327
|
// Parse a few passthrough flags.
|
|
208
328
|
const hermesArgs = ["-p", PROFILE, "chat"];
|
|
209
329
|
const pass = [];
|
|
@@ -244,20 +364,15 @@ export default {
|
|
|
244
364
|
const cleanEnv = activeChainEnv({
|
|
245
365
|
ORACLE_CHAT_SURFACE: "1",
|
|
246
366
|
ORACLE_PROFILE: PROFILE,
|
|
367
|
+
ORACLE_NODE_BIN: process.execPath,
|
|
368
|
+
ORACLE_CLI_ENTRY: path.join(PACKAGE_ROOT, "bin", "oracle.mjs"),
|
|
247
369
|
});
|
|
248
370
|
|
|
249
|
-
const
|
|
250
|
-
const runtime = process.env.ORACLE_PLAIN_HARNESS === "0"
|
|
251
|
-
? null
|
|
252
|
-
: resolveHermesPython(hermes);
|
|
253
|
-
const command = runtime && fs.existsSync(harness) ? runtime.command : hermes;
|
|
254
|
-
const launchArgs = runtime && fs.existsSync(harness)
|
|
255
|
-
? [...runtime.prefix, harness, ...hermesArgs, ...pass]
|
|
256
|
-
: [...hermesArgs, ...pass];
|
|
371
|
+
const launch = oracleHarnessInvocation(hermes, [...hermesArgs, ...pass]);
|
|
257
372
|
|
|
258
373
|
return spawnChild(
|
|
259
|
-
command,
|
|
260
|
-
|
|
374
|
+
launch.command,
|
|
375
|
+
launch.args,
|
|
261
376
|
{
|
|
262
377
|
stdio: "inherit",
|
|
263
378
|
env: cleanEnv,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { DOCTOR_SIGNING_OPTIONAL } from "../first-run.mjs";
|
|
4
|
+
import { resolveHermes } from "../runtime.mjs";
|
|
4
5
|
|
|
5
6
|
function nodeVersionOk() {
|
|
6
7
|
const [maj, min] = process.versions.node.split(".").map(Number);
|
|
@@ -17,13 +18,6 @@ function loadExecEnv(p) {
|
|
|
17
18
|
return out;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
function which(bin) {
|
|
21
|
-
for (const dir of (process.env.PATH || "").split(path.delimiter)) {
|
|
22
|
-
const c = path.join(dir, bin);
|
|
23
|
-
try { if (fs.existsSync(c)) return c; } catch {}
|
|
24
|
-
}
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
21
|
|
|
28
22
|
async function probeDataServer(url) {
|
|
29
23
|
try {
|
|
@@ -66,9 +60,10 @@ export default {
|
|
|
66
60
|
push("data_server", dataUp ? "ok" : "warn", dataUp ? `reachable ${dataUrl}` : `down ${dataUrl}`,
|
|
67
61
|
dataUp ? null : "oracle data serve");
|
|
68
62
|
|
|
69
|
-
const
|
|
70
|
-
push("
|
|
71
|
-
|
|
63
|
+
const runtime = resolveHermes();
|
|
64
|
+
push("agent_runtime", runtime.ok ? "ok" : "warn",
|
|
65
|
+
runtime.ok ? `present ${runtime.bin} (${runtime.source})` : "not installed",
|
|
66
|
+
runtime.ok ? null : "oracle bootstrap");
|
|
72
67
|
|
|
73
68
|
const profilesDir = path.join(ctx.paths.hermesRoot(), "profiles");
|
|
74
69
|
let laneCount = 0;
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
1
|
import { spawnChild } from "../spawn-child.mjs";
|
|
2
|
+
import { installManagedHermes, resolveHermes } from "../runtime.mjs";
|
|
3
|
+
import { oracleHarnessInvocation } from "./chat.mjs";
|
|
4
4
|
|
|
5
|
-
function
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
function runtimeBin() {
|
|
6
|
+
const found = resolveHermes();
|
|
7
|
+
if (found.ok) return found.bin;
|
|
8
|
+
if (process.env.ORACLE_NO_BOOTSTRAP === "1") return null;
|
|
9
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return null;
|
|
10
|
+
process.stdout.write("setting up oracle's local model runtime (one time)\n\n");
|
|
11
|
+
const installed = installManagedHermes();
|
|
12
|
+
if (!installed.ok) {
|
|
13
|
+
process.stderr.write(`oracle model: runtime setup failed: ${installed.reason}\n`);
|
|
14
|
+
return null;
|
|
10
15
|
}
|
|
11
|
-
return
|
|
16
|
+
return installed.bin;
|
|
12
17
|
}
|
|
13
18
|
|
|
14
19
|
export default {
|
|
@@ -16,20 +21,21 @@ export default {
|
|
|
16
21
|
summary: "choose the oracle profile's model/provider",
|
|
17
22
|
group: "read",
|
|
18
23
|
usage: [
|
|
19
|
-
"oracle model
|
|
24
|
+
"oracle model - interactive provider/model picker for the oracle profile",
|
|
20
25
|
"",
|
|
21
26
|
"inside chat: /model",
|
|
22
27
|
"one oracle persona stays loaded when the model changes.",
|
|
23
28
|
].join("\n"),
|
|
24
29
|
async run(ctx) {
|
|
25
|
-
const hermes =
|
|
30
|
+
const hermes = runtimeBin();
|
|
26
31
|
if (!hermes) {
|
|
27
|
-
process.stderr.write("oracle model needs
|
|
32
|
+
process.stderr.write("oracle model needs the local runtime\nrun: oracle bootstrap\n");
|
|
28
33
|
return 1;
|
|
29
34
|
}
|
|
35
|
+
const launch = oracleHarnessInvocation(hermes, ["-p", "oracle", "model", ...ctx.argv]);
|
|
30
36
|
return spawnChild(
|
|
31
|
-
|
|
32
|
-
|
|
37
|
+
launch.command,
|
|
38
|
+
launch.args,
|
|
33
39
|
{ stdio: "inherit", env: process.env },
|
|
34
40
|
"oracle-model",
|
|
35
41
|
);
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
2
|
import readline from "node:readline/promises";
|
|
5
3
|
import { stdin as input, stdout as output } from "node:process";
|
|
6
4
|
import {
|
|
@@ -15,16 +13,7 @@ import {
|
|
|
15
13
|
touchSetupState,
|
|
16
14
|
} from "../setup-state.mjs";
|
|
17
15
|
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
|
-
}
|
|
16
|
+
import { installManagedHermes, resolveHermes } from "../runtime.mjs";
|
|
28
17
|
|
|
29
18
|
function usage() {
|
|
30
19
|
return [
|
|
@@ -71,14 +60,17 @@ function stripProfileArgs(args) {
|
|
|
71
60
|
}
|
|
72
61
|
|
|
73
62
|
function runHermes(argv, { inherit = true, env = process.env } = {}) {
|
|
74
|
-
|
|
75
|
-
if (!
|
|
76
|
-
process.
|
|
77
|
-
|
|
78
|
-
);
|
|
63
|
+
let resolved = resolveHermes();
|
|
64
|
+
if (!resolved.ok && process.stdin.isTTY && process.stdout.isTTY && process.env.ORACLE_NO_BOOTSTRAP !== "1") {
|
|
65
|
+
process.stdout.write("setting up oracle's local messaging runtime (one time)\n\n");
|
|
66
|
+
const installed = installManagedHermes();
|
|
67
|
+
if (installed.ok) resolved = { ok: true, bin: installed.bin };
|
|
68
|
+
}
|
|
69
|
+
if (!resolved.ok) {
|
|
70
|
+
process.stderr.write("oracle setup needs the local runtime\nrun: oracle bootstrap\n");
|
|
79
71
|
return 1;
|
|
80
72
|
}
|
|
81
|
-
const r = spawnSync(
|
|
73
|
+
const r = spawnSync(resolved.bin, argv, {
|
|
82
74
|
stdio: inherit ? "inherit" : "pipe",
|
|
83
75
|
encoding: "utf8",
|
|
84
76
|
env,
|
|
@@ -214,7 +206,7 @@ async function configurePlatform(profile, platformKey, inlineArgs = []) {
|
|
|
214
206
|
|
|
215
207
|
export default {
|
|
216
208
|
name: "setup",
|
|
217
|
-
summary: "configure telegram/discord/slack
|
|
209
|
+
summary: "configure telegram/discord/slack messaging platforms",
|
|
218
210
|
group: "read",
|
|
219
211
|
usage: usage(),
|
|
220
212
|
async run(ctx) {
|
package/src/cli/first-run.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export const NEXT_STEPS_AFTER_INIT = [
|
|
2
2
|
"oracle data serve # loopback read/prepare plane on 127.0.0.1:8787",
|
|
3
3
|
"oracle doctor # verify",
|
|
4
|
-
"oracle chat #
|
|
4
|
+
"oracle chat # premium oracle chat surface",
|
|
5
5
|
"oracle chain list # pick hyperliquid/base/solana/...",
|
|
6
|
-
"oracle setup # telegram/discord/slack
|
|
6
|
+
"oracle setup # telegram/discord/slack messaging",
|
|
7
7
|
"oracle mcp install claude-code",
|
|
8
8
|
"# optional, explicit second step — signing stays a separate local package:",
|
|
9
9
|
"npm i -g @oracle-agent/operator",
|
package/src/cli/kernel.mjs
CHANGED
|
@@ -13,12 +13,13 @@ const HELP_HEADER = `oracle — self-custody multichain agent control plane`;
|
|
|
13
13
|
|
|
14
14
|
const STATIC_HELP = `READ / RESEARCH (no keys, this package)
|
|
15
15
|
oracle open the plain oracle chat harness (TTY)
|
|
16
|
+
oracle bootstrap install the local agent runtime chat needs
|
|
16
17
|
oracle init install agent lanes + read plane (dry-run by default)
|
|
17
18
|
oracle doctor check read plane; checks signer too when installed
|
|
18
|
-
oracle chat
|
|
19
|
+
oracle chat premium boxed oracle chat (multi-model)
|
|
19
20
|
oracle model choose the profile's default model/provider
|
|
20
21
|
oracle chain list/select working chains (hyperliquid, base, ...)
|
|
21
|
-
oracle setup telegram/discord/slack
|
|
22
|
+
oracle setup telegram/discord/slack messaging menu
|
|
22
23
|
oracle data serve|call|catalog|health
|
|
23
24
|
oracle data-mcp start the read-only MCP server
|
|
24
25
|
oracle scan chains|head|token|pools|risk|quote|sell
|
|
@@ -26,7 +27,7 @@ const STATIC_HELP = `READ / RESEARCH (no keys, this package)
|
|
|
26
27
|
oracle prepare build an unsigned swap (alias of route prepare)
|
|
27
28
|
oracle public serve secret-free public HTTP surface
|
|
28
29
|
oracle mcp install <t> wire Oracle into claude-code|claude-desktop|codex|chatgpt
|
|
29
|
-
oracle upgrade upgrade installed
|
|
30
|
+
oracle upgrade upgrade installed agent lanes
|
|
30
31
|
|
|
31
32
|
SIGNING (dispatches to @oracle-agent/operator on THIS machine; never in this package)
|
|
32
33
|
oracle sign init|doctor provision / check the local signer (opt-in)
|
|
@@ -90,7 +91,7 @@ export function renderHelp(version, commands) {
|
|
|
90
91
|
for (const cmd of commands.values()) {
|
|
91
92
|
if (["help", "version"].includes(cmd.name)) continue;
|
|
92
93
|
const known = new Set([
|
|
93
|
-
"init","doctor","chat","model","chain","setup","data","data-mcp","scan","route","prepare","public","mcp","upgrade",
|
|
94
|
+
"init","doctor","chat","model","chain","setup","data","data-mcp","scan","route","prepare","public","mcp","upgrade","bootstrap",
|
|
94
95
|
"sign","vault","signer","runner","credential",
|
|
95
96
|
]);
|
|
96
97
|
if (!known.has(cmd.name) && cmd.summary) {
|
|
@@ -174,7 +174,7 @@ export function renderSetupMenu({ profile = "oracle", statuses = {} } = {}) {
|
|
|
174
174
|
const lines = [
|
|
175
175
|
"oracle setup",
|
|
176
176
|
"",
|
|
177
|
-
"one oracle. many models.
|
|
177
|
+
"one oracle. many models. every connected channel.",
|
|
178
178
|
`profile: ${profile}`,
|
|
179
179
|
"",
|
|
180
180
|
"messaging platforms:",
|