feat(FN-3858): untrack plugin dist artifacts to unblock autostash
Removes committed `dist/` build artifacts from `fusion-plugin-hermes-runtime` and `fusion-plugin-openclaw-runtime` (56 files, ~2200 lines deleted), unblocking autostash during rebase by ensuring compiled output is no longer tracked in git. Fusion-Task-Id: FN-3858
This commit is contained in:
@@ -1,2 +0,0 @@
|
||||
export {};
|
||||
//# sourceMappingURL=cli-spawn.test.d.ts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"cli-spawn.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/cli-spawn.test.ts"],"names":[],"mappings":""}
|
||||
@@ -1,353 +0,0 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { EventEmitter } from "node:events";
|
||||
// ── Mock node:child_process before imports that use it ─────────────────────
|
||||
const { mockSpawn } = vi.hoisted(() => ({ mockSpawn: vi.fn() }));
|
||||
vi.mock("node:child_process", () => ({ spawn: mockSpawn }));
|
||||
import { buildHermesArgs, invokeHermesCli, listHermesProfiles, parseHermesOutput, resolveCliSettings, } from "../cli-spawn.js";
|
||||
// ── Helpers ────────────────────────────────────────────────────────────────
|
||||
function defaultSettings(overrides = {}) {
|
||||
return {
|
||||
binaryPath: "hermes",
|
||||
maxTurns: 12,
|
||||
yolo: false,
|
||||
cliTimeoutMs: 5_000,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Create a fake ChildProcess-like EventEmitter with controllable
|
||||
* stdout/stderr streams and a kill spy.
|
||||
*/
|
||||
function makeFakeChild() {
|
||||
const main = new EventEmitter();
|
||||
const stdoutEmitter = new EventEmitter();
|
||||
const stderrEmitter = new EventEmitter();
|
||||
main.stdout = stdoutEmitter;
|
||||
main.stderr = stderrEmitter;
|
||||
const kill = vi.fn().mockReturnValue(true);
|
||||
main.kill = kill;
|
||||
return {
|
||||
child: main,
|
||||
emitStdout: (data) => stdoutEmitter.emit("data", Buffer.from(data)),
|
||||
emitStderr: (data) => stderrEmitter.emit("data", Buffer.from(data)),
|
||||
emitError: (err) => main.emit("error", err),
|
||||
emitClose: (code) => main.emit("close", code),
|
||||
kill,
|
||||
};
|
||||
}
|
||||
/** Build a stdout string that hermes would produce for a given body + session id. */
|
||||
function fakeHermesOutput(body, sessionId = "20260427_120000_abcd12") {
|
||||
return `${body}\nsession_id: ${sessionId}\n`;
|
||||
}
|
||||
// ── resolveCliSettings ──────────────────────────────────────────────────────
|
||||
describe("resolveCliSettings", () => {
|
||||
const origEnv = { ...process.env };
|
||||
afterEach(() => {
|
||||
process.env = { ...origEnv };
|
||||
});
|
||||
it("returns defaults when settings and env are empty", () => {
|
||||
delete process.env.HERMES_BIN;
|
||||
delete process.env.HERMES_MODEL_ID;
|
||||
delete process.env.HERMES_PROVIDER;
|
||||
delete process.env.HERMES_MAX_TURNS;
|
||||
delete process.env.HERMES_YOLO;
|
||||
delete process.env.HERMES_CLI_TIMEOUT_MS;
|
||||
expect(resolveCliSettings()).toEqual({
|
||||
binaryPath: "hermes",
|
||||
model: undefined,
|
||||
provider: undefined,
|
||||
maxTurns: 12,
|
||||
yolo: false,
|
||||
cliTimeoutMs: 300_000,
|
||||
});
|
||||
});
|
||||
it("prefers settings over env vars", () => {
|
||||
process.env.HERMES_BIN = "/usr/bin/hermes";
|
||||
process.env.HERMES_MODEL_ID = "env-model";
|
||||
process.env.HERMES_PROVIDER = "openrouter";
|
||||
expect(resolveCliSettings({
|
||||
binaryPath: "/custom/hermes",
|
||||
model: "gpt-4o",
|
||||
provider: "openai-codex",
|
||||
maxTurns: 5,
|
||||
yolo: true,
|
||||
cliTimeoutMs: 10_000,
|
||||
})).toEqual({
|
||||
binaryPath: "/custom/hermes",
|
||||
model: "gpt-4o",
|
||||
provider: "openai-codex",
|
||||
maxTurns: 5,
|
||||
yolo: true,
|
||||
cliTimeoutMs: 10_000,
|
||||
});
|
||||
});
|
||||
it("falls back to env vars when settings omit values", () => {
|
||||
process.env.HERMES_BIN = "/env/hermes";
|
||||
process.env.HERMES_MODEL_ID = "env-model";
|
||||
process.env.HERMES_PROVIDER = "gemini";
|
||||
process.env.HERMES_MAX_TURNS = "7";
|
||||
process.env.HERMES_YOLO = "true";
|
||||
process.env.HERMES_CLI_TIMEOUT_MS = "60000";
|
||||
expect(resolveCliSettings({})).toEqual({
|
||||
binaryPath: "/env/hermes",
|
||||
model: "env-model",
|
||||
provider: "gemini",
|
||||
maxTurns: 7,
|
||||
yolo: true,
|
||||
cliTimeoutMs: 60_000,
|
||||
});
|
||||
});
|
||||
});
|
||||
// ── buildHermesArgs ────────────────────────────────────────────────────────
|
||||
describe("buildHermesArgs", () => {
|
||||
it("builds minimal args without resume or optional flags", () => {
|
||||
const args = buildHermesArgs("hello world", defaultSettings());
|
||||
expect(args).toEqual(["chat", "-q", "hello world", "-Q", "--source", "tool", "--max-turns", "12"]);
|
||||
});
|
||||
it("adds --resume when sessionId provided", () => {
|
||||
const args = buildHermesArgs("hello", defaultSettings(), "20260427_120000_abcd12");
|
||||
expect(args).toContain("--resume");
|
||||
expect(args[args.indexOf("--resume") + 1]).toBe("20260427_120000_abcd12");
|
||||
});
|
||||
it("adds -m and --provider when configured", () => {
|
||||
const args = buildHermesArgs("hello", defaultSettings({ model: "claude-sonnet-4-5", provider: "anthropic" }));
|
||||
expect(args).toContain("-m");
|
||||
expect(args[args.indexOf("-m") + 1]).toBe("claude-sonnet-4-5");
|
||||
expect(args).toContain("--provider");
|
||||
expect(args[args.indexOf("--provider") + 1]).toBe("anthropic");
|
||||
});
|
||||
it("adds --yolo when yolo is true", () => {
|
||||
const args = buildHermesArgs("hello", defaultSettings({ yolo: true }));
|
||||
expect(args).toContain("--yolo");
|
||||
});
|
||||
it("does not add --yolo when yolo is false", () => {
|
||||
const args = buildHermesArgs("hello", defaultSettings({ yolo: false }));
|
||||
expect(args).not.toContain("--yolo");
|
||||
});
|
||||
it("uses configured maxTurns", () => {
|
||||
const args = buildHermesArgs("hello", defaultSettings({ maxTurns: 5 }));
|
||||
expect(args[args.indexOf("--max-turns") + 1]).toBe("5");
|
||||
});
|
||||
it("first call has no --resume arg", () => {
|
||||
const args = buildHermesArgs("hi", defaultSettings());
|
||||
expect(args).not.toContain("--resume");
|
||||
});
|
||||
});
|
||||
// ── parseHermesOutput ──────────────────────────────────────────────────────
|
||||
describe("parseHermesOutput", () => {
|
||||
it("extracts session_id and body from clean output", () => {
|
||||
const stdout = "This is the response\nsession_id: 20260427_120000_abcd12\n";
|
||||
const result = parseHermesOutput(stdout, "");
|
||||
expect(result.sessionId).toBe("20260427_120000_abcd12");
|
||||
expect(result.body).toBe("This is the response");
|
||||
});
|
||||
it("strips ANSI escape codes from body", () => {
|
||||
const stdout = "\x1b[32mGreen text\x1b[0m\nsession_id: 20260427_120000_abcd12\n";
|
||||
const result = parseHermesOutput(stdout, "");
|
||||
expect(result.body).toBe("Green text");
|
||||
expect(result.body).not.toContain("\x1b");
|
||||
});
|
||||
it("strips preamble chrome lines", () => {
|
||||
const stdout = [
|
||||
"╭─ Hermes ─╮",
|
||||
"↻ Resumed session foo",
|
||||
" ┊ preparing memory…",
|
||||
"Query: what is 2+2?",
|
||||
"4",
|
||||
"╰─────────╯",
|
||||
"session_id: 20260427_120000_abcd12",
|
||||
].join("\n") + "\n";
|
||||
const result = parseHermesOutput(stdout, "");
|
||||
expect(result.body).toBe("4");
|
||||
expect(result.body).not.toContain("Hermes");
|
||||
expect(result.body).not.toContain("Resumed");
|
||||
expect(result.body).not.toContain("preparing");
|
||||
expect(result.body).not.toContain("Query:");
|
||||
expect(result.body).not.toContain("╭");
|
||||
expect(result.body).not.toContain("╰");
|
||||
});
|
||||
it("normalizes CRLF to LF", () => {
|
||||
const stdout = "line1\r\nline2\r\nsession_id: 20260427_120000_abcd12\r\n";
|
||||
const result = parseHermesOutput(stdout, "");
|
||||
expect(result.body).toBe("line1\nline2");
|
||||
});
|
||||
it("throws when session_id line is missing", () => {
|
||||
expect(() => parseHermesOutput("some output without id", "stderr text")).toThrow(/missing session_id/);
|
||||
});
|
||||
});
|
||||
// ── invokeHermesCli ────────────────────────────────────────────────────────
|
||||
describe("invokeHermesCli", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
it("first call passes correct args and no --resume", async () => {
|
||||
const { child, emitStdout, emitClose } = makeFakeChild();
|
||||
mockSpawn.mockReturnValue(child);
|
||||
const PROMPT = "what is typescript?";
|
||||
const promise = invokeHermesCli(PROMPT, defaultSettings());
|
||||
emitStdout(fakeHermesOutput("TypeScript is a language."));
|
||||
emitClose(0);
|
||||
const result = await promise;
|
||||
expect(mockSpawn).toHaveBeenCalledOnce();
|
||||
const [bin, args] = mockSpawn.mock.calls[0];
|
||||
expect(bin).toBe("hermes");
|
||||
expect(args).toEqual(["chat", "-q", PROMPT, "-Q", "--source", "tool", "--max-turns", "12"]);
|
||||
expect(result.body).toBe("TypeScript is a language.");
|
||||
expect(result.sessionId).toBe("20260427_120000_abcd12");
|
||||
});
|
||||
it("subsequent call with sessionId passes --resume", async () => {
|
||||
const { child, emitStdout, emitClose } = makeFakeChild();
|
||||
mockSpawn.mockReturnValue(child);
|
||||
const promise = invokeHermesCli("hello again", defaultSettings(), "20260427_120000_abcd12");
|
||||
emitStdout(fakeHermesOutput("Hi there!"));
|
||||
emitClose(0);
|
||||
await promise;
|
||||
const args = mockSpawn.mock.calls[0][1];
|
||||
expect(args).toContain("--resume");
|
||||
expect(args[args.indexOf("--resume") + 1]).toBe("20260427_120000_abcd12");
|
||||
});
|
||||
it("captures session id from stdout", async () => {
|
||||
const { child, emitStdout, emitClose } = makeFakeChild();
|
||||
mockSpawn.mockReturnValue(child);
|
||||
const promise = invokeHermesCli("hi", defaultSettings());
|
||||
emitStdout("Hello!\nsession_id: 20260427_120000_abcd12\n");
|
||||
emitClose(0);
|
||||
const result = await promise;
|
||||
expect(result.sessionId).toBe("20260427_120000_abcd12");
|
||||
});
|
||||
it("rejects on non-zero exit code and surfaces stderr", async () => {
|
||||
const { child, emitStdout, emitStderr, emitClose } = makeFakeChild();
|
||||
mockSpawn.mockReturnValue(child);
|
||||
const promise = invokeHermesCli("hi", defaultSettings());
|
||||
emitStdout("partial output");
|
||||
emitStderr("fatal error from hermes");
|
||||
emitClose(1);
|
||||
await expect(promise).rejects.toThrow(/exited with code 1/);
|
||||
});
|
||||
it("rejects when session_id is missing from stdout on exit 0", async () => {
|
||||
const { child, emitStdout, emitClose } = makeFakeChild();
|
||||
mockSpawn.mockReturnValue(child);
|
||||
const promise = invokeHermesCli("hi", defaultSettings());
|
||||
emitStdout("Some output without session id line");
|
||||
emitClose(0);
|
||||
await expect(promise).rejects.toThrow(/missing session_id/);
|
||||
});
|
||||
it("includes -m, --provider, --max-turns, --yolo when settings configured", async () => {
|
||||
const { child, emitStdout, emitClose } = makeFakeChild();
|
||||
mockSpawn.mockReturnValue(child);
|
||||
const settings = defaultSettings({
|
||||
model: "claude-sonnet-4-5",
|
||||
provider: "anthropic",
|
||||
maxTurns: 20,
|
||||
yolo: true,
|
||||
});
|
||||
const promise = invokeHermesCli("test", settings);
|
||||
emitStdout(fakeHermesOutput("ok"));
|
||||
emitClose(0);
|
||||
await promise;
|
||||
const args = mockSpawn.mock.calls[0][1];
|
||||
expect(args).toContain("-m");
|
||||
expect(args[args.indexOf("-m") + 1]).toBe("claude-sonnet-4-5");
|
||||
expect(args).toContain("--provider");
|
||||
expect(args[args.indexOf("--provider") + 1]).toBe("anthropic");
|
||||
expect(args[args.indexOf("--max-turns") + 1]).toBe("20");
|
||||
expect(args).toContain("--yolo");
|
||||
});
|
||||
it("rejects on ENOENT with not-found message", async () => {
|
||||
const { child, emitError } = makeFakeChild();
|
||||
mockSpawn.mockReturnValue(child);
|
||||
const promise = invokeHermesCli("hi", defaultSettings());
|
||||
const err = Object.assign(new Error("not found"), { code: "ENOENT" });
|
||||
emitError(err);
|
||||
await expect(promise).rejects.toThrow(/binary not found/);
|
||||
});
|
||||
it("AbortSignal triggers child.kill and rejects", async () => {
|
||||
const { child, kill, emitStdout } = makeFakeChild();
|
||||
mockSpawn.mockReturnValue(child);
|
||||
const ac = new AbortController();
|
||||
const promise = invokeHermesCli("hi", defaultSettings(), undefined, ac.signal);
|
||||
// Abort before any output arrives.
|
||||
ac.abort();
|
||||
emitStdout("irrelevant");
|
||||
await expect(promise).rejects.toThrow(/aborted/);
|
||||
expect(kill).toHaveBeenCalledWith("SIGTERM");
|
||||
});
|
||||
});
|
||||
// ── listHermesProfiles ─────────────────────────────────────────────────────
|
||||
describe("listHermesProfiles", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
const SAMPLE_OUTPUT = [
|
||||
" Profile Model Gateway Alias",
|
||||
" ─────────────── ───────────────────────── ─────────── ────────────",
|
||||
" ◆default MiniMax-M2.7 stopped —",
|
||||
].join("\n") + "\n";
|
||||
it("happy path: parses single default profile", async () => {
|
||||
const { child, emitStdout, emitClose } = makeFakeChild();
|
||||
mockSpawn.mockReturnValue(child);
|
||||
const promise = listHermesProfiles();
|
||||
emitStdout(SAMPLE_OUTPUT);
|
||||
emitClose(0);
|
||||
const profiles = await promise;
|
||||
expect(profiles).toHaveLength(1);
|
||||
expect(profiles[0]).toMatchObject({
|
||||
name: "default",
|
||||
model: "MiniMax-M2.7",
|
||||
gateway: "stopped",
|
||||
alias: undefined,
|
||||
isDefault: true,
|
||||
});
|
||||
});
|
||||
it("happy path: multi-profile ordered correctly", async () => {
|
||||
const { child, emitStdout, emitClose } = makeFakeChild();
|
||||
mockSpawn.mockReturnValue(child);
|
||||
const multiOutput = [
|
||||
" Profile Model Gateway Alias",
|
||||
" ─────────────── ───────────────────────── ─────────── ────────────",
|
||||
" ◆default MiniMax-M2.7 stopped —",
|
||||
" work claude-sonnet-4-5 running work-hermes",
|
||||
].join("\n") + "\n";
|
||||
const promise = listHermesProfiles();
|
||||
emitStdout(multiOutput);
|
||||
emitClose(0);
|
||||
const profiles = await promise;
|
||||
expect(profiles).toHaveLength(2);
|
||||
expect(profiles[0].name).toBe("default");
|
||||
expect(profiles[0].isDefault).toBe(true);
|
||||
expect(profiles[1].name).toBe("work");
|
||||
expect(profiles[1].isDefault).toBe(false);
|
||||
expect(profiles[1].model).toBe("claude-sonnet-4-5");
|
||||
expect(profiles[1].alias).toBe("work-hermes");
|
||||
});
|
||||
it("ENOENT rejects with binary-not-found error", async () => {
|
||||
const { child, emitError } = makeFakeChild();
|
||||
mockSpawn.mockReturnValue(child);
|
||||
const promise = listHermesProfiles({ binaryPath: "/no/such/hermes" });
|
||||
const err = Object.assign(new Error("not found"), { code: "ENOENT" });
|
||||
emitError(err);
|
||||
await expect(promise).rejects.toThrow(/hermes profile list failed.*binary not found/);
|
||||
});
|
||||
it("non-zero exit rejects with exit code error", async () => {
|
||||
const { child, emitStdout, emitStderr, emitClose } = makeFakeChild();
|
||||
mockSpawn.mockReturnValue(child);
|
||||
const promise = listHermesProfiles();
|
||||
emitStdout("");
|
||||
emitStderr("unknown subcommand");
|
||||
emitClose(2);
|
||||
await expect(promise).rejects.toThrow(/hermes profile list failed.*exited with code 2/);
|
||||
});
|
||||
it("spawns with 'profile list' args", async () => {
|
||||
const { child, emitStdout, emitClose } = makeFakeChild();
|
||||
mockSpawn.mockReturnValue(child);
|
||||
const promise = listHermesProfiles({ binaryPath: "/custom/hermes" });
|
||||
emitStdout(SAMPLE_OUTPUT);
|
||||
emitClose(0);
|
||||
await promise;
|
||||
expect(mockSpawn).toHaveBeenCalledOnce();
|
||||
const [bin, args] = mockSpawn.mock.calls[0];
|
||||
expect(bin).toBe("/custom/hermes");
|
||||
expect(args).toEqual(["profile", "list"]);
|
||||
});
|
||||
});
|
||||
//# sourceMappingURL=cli-spawn.test.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
|
||||
export {};
|
||||
//# sourceMappingURL=fusion-skill-install.test.d.ts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"fusion-skill-install.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/fusion-skill-install.test.ts"],"names":[],"mappings":""}
|
||||
@@ -1,93 +0,0 @@
|
||||
import { mkdtempSync, mkdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { getFusionSkillSourceCandidates, installFusionSkillIntoHermesHome, resolveBundledFusionSkillSourceFromCandidates, } from "../fusion-skill-install.js";
|
||||
const tempDirs = [];
|
||||
function tempDir(prefix) {
|
||||
const dir = mkdtempSync(path.join(os.tmpdir(), prefix));
|
||||
tempDirs.push(dir);
|
||||
return dir;
|
||||
}
|
||||
function makeSkillSource(dir) {
|
||||
mkdirSync(dir, { recursive: true });
|
||||
writeFileSync(path.join(dir, "SKILL.md"), "# Fusion Skill\n");
|
||||
}
|
||||
afterEach(() => {
|
||||
delete process.env.HERMES_HOME;
|
||||
for (const dir of tempDirs.splice(0))
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
});
|
||||
describe("fusion skill installer", () => {
|
||||
it("resolves workspace and packaged candidate layouts", () => {
|
||||
const workspaceCandidates = getFusionSkillSourceCandidates("file:///repo/plugins/fusion-plugin-hermes-runtime/src/index.ts");
|
||||
expect(workspaceCandidates[0].replace(/\\/g, "/")).toMatch(/\/packages\/cli\/skill\/fusion$/);
|
||||
const packagedCandidates = getFusionSkillSourceCandidates("file:///repo/packages/cli/dist/plugins/fusion-plugin-hermes-runtime/bundled.js");
|
||||
expect(packagedCandidates.some((candidate) => candidate.replace(/\\/g, "/").endsWith("/packages/cli/dist/skill/fusion"))).toBe(true);
|
||||
});
|
||||
it("installs skill for configured Hermes home", () => {
|
||||
const home = tempDir("hermes-home-");
|
||||
const source = path.join(tempDir("fusion-source-"), "fusion");
|
||||
makeSkillSource(source);
|
||||
process.env.HERMES_HOME = home;
|
||||
const result = installFusionSkillIntoHermesHome({ sourceDir: source });
|
||||
expect(result.outcome).toBe("installed");
|
||||
expect(result.targetDir).toBe(path.join(home, "skills", "fusion"));
|
||||
});
|
||||
it("no-ops when already installed to same source", () => {
|
||||
const home = tempDir("hermes-home-");
|
||||
const source = path.join(tempDir("fusion-source-"), "fusion");
|
||||
makeSkillSource(source);
|
||||
process.env.HERMES_HOME = home;
|
||||
expect(installFusionSkillIntoHermesHome({ sourceDir: source }).outcome).toBe("installed");
|
||||
expect(installFusionSkillIntoHermesHome({ sourceDir: source }).outcome).toBe("already-installed");
|
||||
});
|
||||
it("replaces prior fusion directory install safely", () => {
|
||||
const home = tempDir("hermes-home-");
|
||||
const source = path.join(tempDir("fusion-source-"), "fusion");
|
||||
makeSkillSource(source);
|
||||
process.env.HERMES_HOME = home;
|
||||
const target = path.join(home, "skills", "fusion");
|
||||
makeSkillSource(target);
|
||||
writeFileSync(path.join(target, "old.txt"), "stale");
|
||||
const result = installFusionSkillIntoHermesHome({ sourceDir: source });
|
||||
expect(result.outcome).toBe("replaced");
|
||||
});
|
||||
it("skips replacement for unrelated existing directory", () => {
|
||||
const home = tempDir("hermes-home-");
|
||||
const source = path.join(tempDir("fusion-source-"), "fusion");
|
||||
makeSkillSource(source);
|
||||
process.env.HERMES_HOME = home;
|
||||
const target = path.join(home, "skills", "fusion");
|
||||
mkdirSync(target, { recursive: true });
|
||||
writeFileSync(path.join(target, "README.md"), "my custom skill");
|
||||
const result = installFusionSkillIntoHermesHome({ sourceDir: source });
|
||||
expect(result.outcome).toBe("skipped");
|
||||
});
|
||||
it("warns when source is missing", () => {
|
||||
const home = tempDir("hermes-home-");
|
||||
process.env.HERMES_HOME = home;
|
||||
const result = installFusionSkillIntoHermesHome({ sourceDir: null });
|
||||
expect(result.outcome).toBe("warning");
|
||||
});
|
||||
it("can resolve first existing candidate", () => {
|
||||
const source = path.join(tempDir("fusion-source-"), "fusion");
|
||||
makeSkillSource(source);
|
||||
const resolved = resolveBundledFusionSkillSourceFromCandidates(["/missing", source]);
|
||||
expect(resolved).toBe(source);
|
||||
});
|
||||
it("replaces stale fusion symlink", () => {
|
||||
const home = tempDir("hermes-home-");
|
||||
const source = path.join(tempDir("fusion-source-"), "fusion");
|
||||
const oldSource = path.join(tempDir("fusion-old-source-"), "fusion");
|
||||
makeSkillSource(source);
|
||||
makeSkillSource(oldSource);
|
||||
process.env.HERMES_HOME = home;
|
||||
const target = path.join(home, "skills", "fusion");
|
||||
mkdirSync(path.dirname(target), { recursive: true });
|
||||
symlinkSync(oldSource, target, "dir");
|
||||
const result = installFusionSkillIntoHermesHome({ sourceDir: source });
|
||||
expect(result.outcome).toBe("replaced");
|
||||
});
|
||||
});
|
||||
//# sourceMappingURL=fusion-skill-install.test.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
|
||||
export {};
|
||||
//# sourceMappingURL=index.test.d.ts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/index.test.ts"],"names":[],"mappings":""}
|
||||
@@ -1,114 +0,0 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
const { mockResolveCli, mockInstallFusionSkill } = vi.hoisted(() => ({
|
||||
mockResolveCli: vi.fn().mockReturnValue({
|
||||
binaryPath: "hermes",
|
||||
model: undefined,
|
||||
provider: undefined,
|
||||
maxTurns: 12,
|
||||
yolo: false,
|
||||
cliTimeoutMs: 300_000,
|
||||
}),
|
||||
mockInstallFusionSkill: vi.fn().mockReturnValue({
|
||||
outcome: "installed",
|
||||
sourceDir: "/tmp/source",
|
||||
targetDir: "/tmp/target",
|
||||
}),
|
||||
}));
|
||||
vi.mock("../cli-spawn.js", async () => {
|
||||
const actual = await vi.importActual("../cli-spawn.js");
|
||||
return {
|
||||
...actual,
|
||||
resolveCliSettings: mockResolveCli,
|
||||
};
|
||||
});
|
||||
vi.mock("../fusion-skill-install.js", async () => {
|
||||
const actual = await vi.importActual("../fusion-skill-install.js");
|
||||
return {
|
||||
...actual,
|
||||
installFusionSkillIntoHermesHome: mockInstallFusionSkill,
|
||||
};
|
||||
});
|
||||
import plugin, { HERMES_RUNTIME_ID, hermesRuntimeFactory, hermesRuntimeMetadata } from "../index.js";
|
||||
import { HermesRuntimeAdapter } from "../runtime-adapter.js";
|
||||
function createMockContext(settings = {}) {
|
||||
return {
|
||||
pluginId: "fusion-plugin-hermes-runtime",
|
||||
settings,
|
||||
logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() },
|
||||
emitEvent: vi.fn(),
|
||||
taskStore: { getTask: vi.fn() },
|
||||
};
|
||||
}
|
||||
describe("hermes-runtime plugin", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockResolveCli.mockReturnValue({
|
||||
binaryPath: "hermes",
|
||||
model: undefined,
|
||||
provider: undefined,
|
||||
maxTurns: 12,
|
||||
yolo: false,
|
||||
cliTimeoutMs: 300_000,
|
||||
profile: undefined,
|
||||
});
|
||||
mockInstallFusionSkill.mockReturnValue({
|
||||
outcome: "installed",
|
||||
sourceDir: "/tmp/source",
|
||||
targetDir: "/tmp/target",
|
||||
});
|
||||
});
|
||||
it("has expected manifest identity", () => {
|
||||
expect(plugin.manifest.id).toBe("fusion-plugin-hermes-runtime");
|
||||
expect(plugin.manifest.name).toBe("Hermes Runtime Plugin");
|
||||
expect(plugin.state).toBe("installed");
|
||||
});
|
||||
it("registers runtime metadata and exports matching constants", () => {
|
||||
expect(HERMES_RUNTIME_ID).toBe("hermes");
|
||||
expect(plugin.runtime?.metadata.runtimeId).toBe("hermes");
|
||||
expect(plugin.runtime?.metadata.name).toBe("Hermes Runtime");
|
||||
expect(plugin.runtime?.metadata.description).toContain("hermes");
|
||||
expect(plugin.manifest.runtime).toEqual(hermesRuntimeMetadata);
|
||||
});
|
||||
it("onLoad logs selected binary path & model and emits loaded event", async () => {
|
||||
mockResolveCli.mockReturnValue({
|
||||
binaryPath: "/opt/homebrew/bin/hermes",
|
||||
model: "claude-sonnet-4-5",
|
||||
provider: "anthropic",
|
||||
maxTurns: 12,
|
||||
yolo: false,
|
||||
cliTimeoutMs: 300_000,
|
||||
profile: undefined,
|
||||
});
|
||||
const ctx = createMockContext({ binaryPath: "/opt/homebrew/bin/hermes" });
|
||||
await plugin.hooks.onLoad(ctx);
|
||||
expect(mockResolveCli).toHaveBeenCalledWith(ctx.settings);
|
||||
expect(mockInstallFusionSkill).toHaveBeenCalledWith({ profile: undefined });
|
||||
expect(ctx.logger.info).toHaveBeenCalledWith(expect.stringContaining("fusionSkill=installed"));
|
||||
expect(ctx.emitEvent).toHaveBeenCalledWith("hermes-runtime:loaded", {
|
||||
runtimeId: "hermes",
|
||||
version: plugin.manifest.version,
|
||||
});
|
||||
});
|
||||
it("onLoad warns but continues when skill install warns", async () => {
|
||||
mockInstallFusionSkill.mockReturnValue({
|
||||
outcome: "warning",
|
||||
sourceDir: null,
|
||||
targetDir: "/tmp/target",
|
||||
reason: "missing skill source",
|
||||
});
|
||||
const ctx = createMockContext();
|
||||
await plugin.hooks.onLoad(ctx);
|
||||
expect(ctx.logger.warn).toHaveBeenCalledWith(expect.stringContaining("auto-install warning"));
|
||||
expect(ctx.emitEvent).toHaveBeenCalledWith("hermes-runtime:loaded", {
|
||||
runtimeId: "hermes",
|
||||
version: plugin.manifest.version,
|
||||
});
|
||||
});
|
||||
it("factory returns a HermesRuntimeAdapter", async () => {
|
||||
const ctx = createMockContext({ binaryPath: "hermes" });
|
||||
const runtime = (await hermesRuntimeFactory(ctx));
|
||||
expect(runtime).toBeInstanceOf(HermesRuntimeAdapter);
|
||||
expect(runtime.id).toBe("hermes");
|
||||
});
|
||||
});
|
||||
//# sourceMappingURL=index.test.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../src/__tests__/index.test.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,2 +0,0 @@
|
||||
export {};
|
||||
//# sourceMappingURL=probe.test.d.ts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"probe.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/probe.test.ts"],"names":[],"mappings":""}
|
||||
@@ -1,107 +0,0 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { EventEmitter } from "node:events";
|
||||
// ── Mock node:child_process ────────────────────────────────────────────────
|
||||
const { mockSpawn } = vi.hoisted(() => ({ mockSpawn: vi.fn() }));
|
||||
vi.mock("node:child_process", () => ({ spawn: mockSpawn }));
|
||||
import { probeHermesBinary } from "../probe.js";
|
||||
// ── Helpers ────────────────────────────────────────────────────────────────
|
||||
/** Yields to the microtask and I/O queue so awaited code can continue. */
|
||||
function flushAsync() {
|
||||
return new Promise((resolve) => setImmediate(resolve));
|
||||
}
|
||||
function makeFakeChild() {
|
||||
const main = new EventEmitter();
|
||||
const stdoutEmitter = new EventEmitter();
|
||||
const stderrEmitter = new EventEmitter();
|
||||
main.stdout = stdoutEmitter;
|
||||
main.stderr = stderrEmitter;
|
||||
main.kill = vi.fn();
|
||||
return {
|
||||
child: main,
|
||||
emitStdout: (data) => stdoutEmitter.emit("data", Buffer.from(data)),
|
||||
emitStderr: (data) => stderrEmitter.emit("data", Buffer.from(data)),
|
||||
emitError: (err) => main.emit("error", err),
|
||||
emitClose: (code) => main.emit("close", code),
|
||||
};
|
||||
}
|
||||
// ── Tests ──────────────────────────────────────────────────────────────────
|
||||
describe("probeHermesBinary", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
it("returns available: false with not-found reason on ENOENT", async () => {
|
||||
// probeHermesBinary first awaits tryResolveBinaryPath (which/where spawn),
|
||||
// then spawns the --version process. We must drive them sequentially.
|
||||
const whichChild = makeFakeChild();
|
||||
const versionChild = makeFakeChild();
|
||||
mockSpawn
|
||||
.mockReturnValueOnce(whichChild.child) // which hermes
|
||||
.mockReturnValueOnce(versionChild.child); // hermes --version
|
||||
const promise = probeHermesBinary({ timeoutMs: 500 });
|
||||
// Settle the `which` call — which causes tryResolveBinaryPath to resolve.
|
||||
whichChild.emitClose(1);
|
||||
// Yield so the awaited tryResolveBinaryPath continuation runs and spawns
|
||||
// the version child before we emit its error.
|
||||
await flushAsync();
|
||||
const enoent = Object.assign(new Error("ENOENT"), { code: "ENOENT" });
|
||||
versionChild.emitError(enoent);
|
||||
const result = await promise;
|
||||
expect(result.available).toBe(false);
|
||||
expect(result.reason).toMatch(/not found on PATH/);
|
||||
expect(result.probeDurationMs).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
it("returns available: true with parsed version on success", async () => {
|
||||
const whichChild = makeFakeChild();
|
||||
const versionChild = makeFakeChild();
|
||||
mockSpawn
|
||||
.mockReturnValueOnce(whichChild.child)
|
||||
.mockReturnValueOnce(versionChild.child);
|
||||
const promise = probeHermesBinary({ binaryPath: "hermes", timeoutMs: 500 });
|
||||
whichChild.emitStdout("/usr/local/bin/hermes\n");
|
||||
whichChild.emitClose(0);
|
||||
await flushAsync();
|
||||
versionChild.emitStdout("Hermes Agent v1.2.3\n");
|
||||
versionChild.emitClose(0);
|
||||
const result = await promise;
|
||||
expect(result.available).toBe(true);
|
||||
expect(result.version).toBe("Hermes Agent v1.2.3");
|
||||
expect(result.binaryPath).toBe("/usr/local/bin/hermes");
|
||||
expect(result.reason).toBeUndefined();
|
||||
expect(result.probeDurationMs).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
it("returns available: false when version exits non-zero", async () => {
|
||||
const whichChild = makeFakeChild();
|
||||
const versionChild = makeFakeChild();
|
||||
mockSpawn
|
||||
.mockReturnValueOnce(whichChild.child)
|
||||
.mockReturnValueOnce(versionChild.child);
|
||||
const promise = probeHermesBinary({ timeoutMs: 500 });
|
||||
whichChild.emitClose(1);
|
||||
await flushAsync();
|
||||
versionChild.emitStderr("error: something went wrong");
|
||||
versionChild.emitClose(2);
|
||||
const result = await promise;
|
||||
expect(result.available).toBe(false);
|
||||
expect(result.reason).toContain("error: something went wrong");
|
||||
});
|
||||
it("uses custom binaryPath when provided", async () => {
|
||||
const whichChild = makeFakeChild();
|
||||
const versionChild = makeFakeChild();
|
||||
mockSpawn
|
||||
.mockReturnValueOnce(whichChild.child)
|
||||
.mockReturnValueOnce(versionChild.child);
|
||||
const promise = probeHermesBinary({ binaryPath: "/opt/bin/hermes", timeoutMs: 500 });
|
||||
whichChild.emitClose(1);
|
||||
await flushAsync();
|
||||
versionChild.emitStdout("Hermes Agent v2.0.0\n");
|
||||
versionChild.emitClose(0);
|
||||
const result = await promise;
|
||||
expect(result.available).toBe(true);
|
||||
expect(result.version).toBe("Hermes Agent v2.0.0");
|
||||
// Verify the version spawn used our custom path.
|
||||
const versionSpawnArgs = mockSpawn.mock.calls[1];
|
||||
expect(versionSpawnArgs[0]).toBe("/opt/bin/hermes");
|
||||
expect(versionSpawnArgs[1]).toEqual(["--version"]);
|
||||
});
|
||||
});
|
||||
//# sourceMappingURL=probe.test.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"probe.test.js","sourceRoot":"","sources":["../../src/__tests__/probe.test.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,2 +0,0 @@
|
||||
export {};
|
||||
//# sourceMappingURL=runtime-adapter.test.d.ts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"runtime-adapter.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/runtime-adapter.test.ts"],"names":[],"mappings":""}
|
||||
@@ -1,138 +0,0 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { HermesRuntimeAdapter } from "../runtime-adapter.js";
|
||||
const { mockInvoke } = vi.hoisted(() => ({
|
||||
mockInvoke: vi.fn(),
|
||||
}));
|
||||
vi.mock("../cli-spawn.js", async () => {
|
||||
const actual = await vi.importActual("../cli-spawn.js");
|
||||
return {
|
||||
...actual,
|
||||
invokeHermesCli: mockInvoke,
|
||||
};
|
||||
});
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockInvoke.mockResolvedValue({
|
||||
body: "hello from hermes",
|
||||
sessionId: "20260427_120000_abc123",
|
||||
});
|
||||
});
|
||||
describe("HermesRuntimeAdapter — identity", () => {
|
||||
it("has stable id/name", () => {
|
||||
const adapter = new HermesRuntimeAdapter({});
|
||||
expect(adapter.id).toBe("hermes");
|
||||
expect(adapter.name).toBe("Hermes Runtime");
|
||||
});
|
||||
});
|
||||
describe("HermesRuntimeAdapter — createSession", () => {
|
||||
it("returns a session with empty sessionId and undefined sessionFile", async () => {
|
||||
const adapter = new HermesRuntimeAdapter({});
|
||||
const onText = vi.fn();
|
||||
const result = await adapter.createSession({
|
||||
cwd: "/repo",
|
||||
systemPrompt: "be helpful",
|
||||
onText,
|
||||
});
|
||||
expect(result.sessionFile).toBeUndefined();
|
||||
expect(result.session.sessionId).toBe("");
|
||||
expect(result.session.systemPrompt).toBe("be helpful");
|
||||
expect(result.session.callbacks.onText).toBe(onText);
|
||||
});
|
||||
});
|
||||
describe("HermesRuntimeAdapter — promptWithFallback", () => {
|
||||
it("invokes hermes CLI with no resume on first call", async () => {
|
||||
const adapter = new HermesRuntimeAdapter({ model: "claude-sonnet-4-5" });
|
||||
const onText = vi.fn();
|
||||
const { session } = await adapter.createSession({
|
||||
cwd: "/repo",
|
||||
systemPrompt: "sys",
|
||||
onText,
|
||||
});
|
||||
await adapter.promptWithFallback(session, "first prompt");
|
||||
expect(mockInvoke).toHaveBeenCalledTimes(1);
|
||||
const [prompt, settings, resumeId] = mockInvoke.mock.calls[0];
|
||||
expect(prompt).toContain("User request:\nfirst prompt");
|
||||
expect(prompt).toContain("Fusion runtime context:");
|
||||
expect(settings.model).toBe("claude-sonnet-4-5");
|
||||
expect(resumeId).toBeUndefined();
|
||||
expect(onText).toHaveBeenCalledWith("hello from hermes");
|
||||
// Session id is captured for next call
|
||||
expect(session.sessionId).toBe("20260427_120000_abc123");
|
||||
});
|
||||
it("passes captured session id as --resume on subsequent calls", async () => {
|
||||
const adapter = new HermesRuntimeAdapter({});
|
||||
const { session } = await adapter.createSession({
|
||||
cwd: "/repo",
|
||||
systemPrompt: "sys",
|
||||
});
|
||||
await adapter.promptWithFallback(session, "p1");
|
||||
await adapter.promptWithFallback(session, "p2");
|
||||
const [prompt2, , resume2] = mockInvoke.mock.calls[1];
|
||||
expect(prompt2).toBe("p2");
|
||||
expect(resume2).toBe("20260427_120000_abc123");
|
||||
});
|
||||
it("propagates CLI errors", async () => {
|
||||
mockInvoke.mockRejectedValueOnce(new Error("hermes: missing session_id"));
|
||||
const adapter = new HermesRuntimeAdapter({});
|
||||
const { session } = await adapter.createSession({
|
||||
cwd: "/repo",
|
||||
systemPrompt: "sys",
|
||||
});
|
||||
await expect(adapter.promptWithFallback(session, "p")).rejects.toThrow(/missing session_id/);
|
||||
});
|
||||
it("does NOT call onText when body is empty", async () => {
|
||||
mockInvoke.mockResolvedValueOnce({
|
||||
body: "",
|
||||
sessionId: "20260427_120000_def456",
|
||||
});
|
||||
const adapter = new HermesRuntimeAdapter({});
|
||||
const onText = vi.fn();
|
||||
const { session } = await adapter.createSession({
|
||||
cwd: "/repo",
|
||||
systemPrompt: "sys",
|
||||
onText,
|
||||
});
|
||||
await adapter.promptWithFallback(session, "p");
|
||||
expect(onText).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
describe("HermesRuntimeAdapter — describeModel", () => {
|
||||
it("returns hermes/<provider>/<model> when both set", async () => {
|
||||
const adapter = new HermesRuntimeAdapter({
|
||||
provider: "anthropic",
|
||||
model: "claude-sonnet-4-5",
|
||||
});
|
||||
const { session } = await adapter.createSession({
|
||||
cwd: "/repo",
|
||||
systemPrompt: "sys",
|
||||
});
|
||||
expect(adapter.describeModel(session)).toBe("hermes/anthropic/claude-sonnet-4-5");
|
||||
});
|
||||
it("returns hermes/<model> when only model set", async () => {
|
||||
const adapter = new HermesRuntimeAdapter({ model: "MiniMax-M2.7" });
|
||||
const { session } = await adapter.createSession({
|
||||
cwd: "/repo",
|
||||
systemPrompt: "sys",
|
||||
});
|
||||
expect(adapter.describeModel(session)).toBe("hermes/MiniMax-M2.7");
|
||||
});
|
||||
it("returns plain 'hermes' when neither set", async () => {
|
||||
const adapter = new HermesRuntimeAdapter({});
|
||||
const { session } = await adapter.createSession({
|
||||
cwd: "/repo",
|
||||
systemPrompt: "sys",
|
||||
});
|
||||
expect(adapter.describeModel(session)).toBe("hermes");
|
||||
});
|
||||
});
|
||||
describe("HermesRuntimeAdapter — dispose", () => {
|
||||
it("is a no-op", async () => {
|
||||
const adapter = new HermesRuntimeAdapter({});
|
||||
const { session } = await adapter.createSession({
|
||||
cwd: "/repo",
|
||||
systemPrompt: "sys",
|
||||
});
|
||||
await expect(adapter.dispose(session)).resolves.toBeUndefined();
|
||||
});
|
||||
});
|
||||
//# sourceMappingURL=runtime-adapter.test.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,96 +0,0 @@
|
||||
/**
|
||||
* Hermes CLI spawn module.
|
||||
*
|
||||
* Drives the local `hermes` binary as a subprocess instead of using the
|
||||
* @mariozechner/pi-ai SDK. Session continuity is maintained by capturing
|
||||
* the `session_id:` line from stdout and passing `--resume <id>` on
|
||||
* subsequent invocations.
|
||||
*
|
||||
* There is NO per-token streaming on this surface — `hermes chat -q`
|
||||
* buffers output in prompt_toolkit. The full response is delivered in
|
||||
* one chunk once the process exits.
|
||||
*/
|
||||
/**
|
||||
* Summary of a single Hermes profile as returned by `hermes profile list`.
|
||||
*/
|
||||
export interface HermesProfileSummary {
|
||||
/** Profile name, e.g. "default". */
|
||||
name: string;
|
||||
/** Model configured for this profile, if any. */
|
||||
model?: string;
|
||||
/** Gateway status string, e.g. "stopped". */
|
||||
gateway?: string;
|
||||
/** Alias wrapper script name, if set. */
|
||||
alias?: string;
|
||||
/** True when this profile has the `◆` sticky-default marker. */
|
||||
isDefault: boolean;
|
||||
}
|
||||
/**
|
||||
* List all Hermes profiles by running `hermes profile list`.
|
||||
*
|
||||
* @param opts.binaryPath - Path to the hermes binary (default: "hermes").
|
||||
* @param opts.timeoutMs - Maximum wait time in ms (default: 5000).
|
||||
* @returns Array of profile summaries, ordered as hermes returns them.
|
||||
* @throws When hermes is not found (ENOENT) or exits non-zero.
|
||||
*/
|
||||
export declare function listHermesProfiles(opts?: {
|
||||
binaryPath?: string;
|
||||
timeoutMs?: number;
|
||||
}): Promise<HermesProfileSummary[]>;
|
||||
/**
|
||||
* Settings resolved from plugin ctx.settings + env-var fallbacks.
|
||||
*/
|
||||
export interface HermesCliSettings {
|
||||
/** Path to the hermes binary. Default: "hermes" (rely on PATH). */
|
||||
binaryPath: string;
|
||||
/** Model identifier, e.g. "claude-sonnet-4-5". */
|
||||
model?: string;
|
||||
/** Provider identifier, e.g. "anthropic". */
|
||||
provider?: string;
|
||||
/** Maximum agent turns per invocation. Default: 12. */
|
||||
maxTurns: number;
|
||||
/** Pass --yolo to hermes (skip confirmations). Default: false. */
|
||||
yolo: boolean;
|
||||
/** Hard kill timeout in milliseconds. Default: 300000 (5 min). */
|
||||
cliTimeoutMs: number;
|
||||
/**
|
||||
* Hermes profile name to activate when spawning the CLI.
|
||||
* Implemented by setting HERMES_HOME to the profile directory in the
|
||||
* subprocess environment — hermes has no `--profile` CLI flag on `chat`.
|
||||
* Empty string / undefined = use the current sticky-default profile.
|
||||
*/
|
||||
profile?: string;
|
||||
}
|
||||
/** Result of a single hermes CLI invocation. */
|
||||
export interface HermesCliResult {
|
||||
/** Parsed assistant response text. */
|
||||
body: string;
|
||||
/** The session id captured from stdout (used for --resume on next call). */
|
||||
sessionId: string;
|
||||
}
|
||||
/**
|
||||
* Resolve HermesCliSettings from a plugin settings record and environment
|
||||
* variable fallbacks.
|
||||
*/
|
||||
export declare function resolveCliSettings(settings?: Record<string, unknown>): HermesCliSettings;
|
||||
/**
|
||||
* Parse the raw stdout from `hermes chat -q ... -Q`.
|
||||
*
|
||||
* Returns `{ body, sessionId }` on success or throws with a descriptive error.
|
||||
*/
|
||||
export declare function parseHermesOutput(rawStdout: string, rawStderr: string): HermesCliResult;
|
||||
/**
|
||||
* Build the argv array for a `hermes chat` invocation.
|
||||
*/
|
||||
export declare function buildHermesArgs(prompt: string, settings: HermesCliSettings, resumeSessionId?: string): string[];
|
||||
/**
|
||||
* Invoke the hermes CLI for a single prompt/response turn.
|
||||
*
|
||||
* @param prompt - The user prompt to send.
|
||||
* @param settings - Resolved CLI settings.
|
||||
* @param resumeSessionId - Hermes session id from a prior call, if continuing.
|
||||
* @param signal - Optional AbortSignal; will SIGTERM the subprocess on abort.
|
||||
* @returns Parsed response body and the new/existing session id.
|
||||
*/
|
||||
export declare function invokeHermesCli(prompt: string, settings: HermesCliSettings, resumeSessionId?: string, signal?: AbortSignal): Promise<HermesCliResult>;
|
||||
//# sourceMappingURL=cli-spawn.d.ts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"cli-spawn.d.ts","sourceRoot":"","sources":["../src/cli-spawn.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAqDH;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,SAAS,EAAE,OAAO,CAAC;CACpB;AAyED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,CAAC,EAAE;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAqDlC;AAID;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,IAAI,EAAE,OAAO,CAAC;IACd,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,gDAAgD;AAChD,MAAM,WAAW,eAAe;IAC9B,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,iBAAiB,CA+BxF;AAkBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,eAAe,CAgBvF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,iBAAiB,EAC3B,eAAe,CAAC,EAAE,MAAM,GACvB,MAAM,EAAE,CAsBV;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,iBAAiB,EAC3B,eAAe,CAAC,EAAE,MAAM,EACxB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,eAAe,CAAC,CA8F1B"}
|
||||
@@ -1,362 +0,0 @@
|
||||
/**
|
||||
* Hermes CLI spawn module.
|
||||
*
|
||||
* Drives the local `hermes` binary as a subprocess instead of using the
|
||||
* @mariozechner/pi-ai SDK. Session continuity is maintained by capturing
|
||||
* the `session_id:` line from stdout and passing `--resume <id>` on
|
||||
* subsequent invocations.
|
||||
*
|
||||
* There is NO per-token streaming on this surface — `hermes chat -q`
|
||||
* buffers output in prompt_toolkit. The full response is delivered in
|
||||
* one chunk once the process exits.
|
||||
*/
|
||||
import { spawn, spawnSync } from "node:child_process";
|
||||
import os from "node:os";
|
||||
import path, { sep as PATH_SEP } from "node:path";
|
||||
/**
|
||||
* On Windows, `spawn("hermes", ...)` won't find `hermes.cmd`/`.bat` shims —
|
||||
* Node doesn't honor PATHEXT. We resolve via `where` (which does) and spawn
|
||||
* the absolute path instead. No-op on POSIX. Cached per process.
|
||||
*/
|
||||
const resolvedBinaryCache = new Map();
|
||||
function resolveBinaryForSpawn(binary) {
|
||||
if (process.platform !== "win32")
|
||||
return binary;
|
||||
if (binary.includes(PATH_SEP) || binary.includes("/") || /\.[a-z]{2,4}$/i.test(binary)) {
|
||||
return binary;
|
||||
}
|
||||
const cached = resolvedBinaryCache.get(binary);
|
||||
if (cached)
|
||||
return cached;
|
||||
try {
|
||||
const result = spawnSync("where", [binary], { encoding: "utf-8" });
|
||||
if (result.status === 0) {
|
||||
const first = (result.stdout ?? "").trim().split(/\r?\n/)[0];
|
||||
if (first?.length) {
|
||||
resolvedBinaryCache.set(binary, first);
|
||||
return first;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
// fall through
|
||||
}
|
||||
return binary;
|
||||
}
|
||||
/** ANSI escape code stripping regex. */
|
||||
// eslint-disable-next-line no-control-regex -- ANSI escapes are control chars by definition
|
||||
const ANSI_RE = /\x1b\[[0-9;]*[A-Za-z]/g;
|
||||
/** Pattern to locate the session id appended by hermes near end of stdout. */
|
||||
const SESSION_ID_RE = /^session_id:\s+([0-9]{8}_[0-9]{6}_[0-9a-f]{6})\s*$/m;
|
||||
/** Lines that are part of the hermes TUI chrome and should be stripped from the body. */
|
||||
const CHROME_LINE_RES = [
|
||||
/^\s*┊\s/, // memory/status sidebar lines
|
||||
/^↻ Resumed session /, // resume banner
|
||||
/^╭─.*╮\s*$/, // top box border
|
||||
/^╰─.*╯\s*$/, // bottom box border
|
||||
/^Query:\s*/, // query echo line
|
||||
];
|
||||
/** Regex matching lines made entirely of `─` box-drawing chars (the rule row). */
|
||||
const PROFILE_RULE_RE = /^[\s─]+$/;
|
||||
/** Em-dash placeholder used by `hermes profile list` for empty values. */
|
||||
const EM_DASH = "—";
|
||||
/**
|
||||
* Parse the stdout of `hermes profile list` into an array of profile summaries.
|
||||
*
|
||||
* The output looks like:
|
||||
*
|
||||
* ```
|
||||
* Profile Model Gateway Alias
|
||||
* ─────────────── ───────────────────────── ─────────── ────────────
|
||||
* ◆default MiniMax-M2.7 stopped —
|
||||
* ```
|
||||
*
|
||||
* Columns are whitespace-aligned (variable-width). The function splits on runs
|
||||
* of two-or-more spaces to handle variable column widths robustly.
|
||||
*/
|
||||
function parseProfileListOutput(raw) {
|
||||
const lines = raw.replace(ANSI_RE, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
||||
const profiles = [];
|
||||
for (const line of lines) {
|
||||
// Skip blank lines.
|
||||
if (line.trim() === "")
|
||||
continue;
|
||||
// Skip the header line (starts with " Profile").
|
||||
if (/^\s*Profile\b/.test(line))
|
||||
continue;
|
||||
// Skip rule lines (all box-drawing dashes / spaces).
|
||||
if (PROFILE_RULE_RE.test(line))
|
||||
continue;
|
||||
// Split on 2+ spaces to separate columns. Trim leading space first.
|
||||
const stripped = line.replace(/^\s+/, "");
|
||||
const columns = stripped.split(/\s{2,}/);
|
||||
const rawName = columns[0] ?? "";
|
||||
const isDefault = rawName.startsWith("◆");
|
||||
const name = rawName.replace(/^◆/, "").trim();
|
||||
if (!name)
|
||||
continue;
|
||||
const toUndef = (v) => v === undefined || v.trim() === "" || v.trim() === EM_DASH ? undefined : v.trim();
|
||||
profiles.push({
|
||||
name,
|
||||
model: toUndef(columns[1]),
|
||||
gateway: toUndef(columns[2]),
|
||||
alias: toUndef(columns[3]),
|
||||
isDefault,
|
||||
});
|
||||
}
|
||||
return profiles;
|
||||
}
|
||||
/**
|
||||
* Resolve the HERMES_HOME directory for a named profile.
|
||||
*
|
||||
* Mirrors hermes's own logic:
|
||||
* - "default" → ~/.hermes
|
||||
* - other name → ~/.hermes/profiles/<name>
|
||||
*
|
||||
* This is used to set HERMES_HOME when spawning hermes with a specific profile.
|
||||
*/
|
||||
function hermesProfileHome(profileName) {
|
||||
const base = process.env.HERMES_HOME ?? path.join(os.homedir(), ".hermes");
|
||||
if (profileName === "default" || profileName === "")
|
||||
return base;
|
||||
return path.join(base, "profiles", profileName);
|
||||
}
|
||||
/**
|
||||
* List all Hermes profiles by running `hermes profile list`.
|
||||
*
|
||||
* @param opts.binaryPath - Path to the hermes binary (default: "hermes").
|
||||
* @param opts.timeoutMs - Maximum wait time in ms (default: 5000).
|
||||
* @returns Array of profile summaries, ordered as hermes returns them.
|
||||
* @throws When hermes is not found (ENOENT) or exits non-zero.
|
||||
*/
|
||||
export async function listHermesProfiles(opts) {
|
||||
const binary = resolveBinaryForSpawn(opts?.binaryPath ?? "hermes");
|
||||
const timeoutMs = opts?.timeoutMs ?? 5_000;
|
||||
return new Promise((resolve, reject) => {
|
||||
let settled = false;
|
||||
const child = spawn(binary, ["profile", "list"], {
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
env: { ...process.env },
|
||||
});
|
||||
const timer = setTimeout(() => {
|
||||
if (settled)
|
||||
return;
|
||||
settled = true;
|
||||
try {
|
||||
child.kill("SIGKILL");
|
||||
}
|
||||
catch { /* already gone */ }
|
||||
reject(new Error(`hermes profile list failed: timed out after ${timeoutMs}ms`));
|
||||
}, timeoutMs);
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
child.stdout?.on("data", (chunk) => { stdout += chunk.toString("utf-8"); });
|
||||
child.stderr?.on("data", (chunk) => { stderr += chunk.toString("utf-8"); });
|
||||
child.on("error", (err) => {
|
||||
if (settled)
|
||||
return;
|
||||
settled = true;
|
||||
clearTimeout(timer);
|
||||
const isNotFound = err.code === "ENOENT";
|
||||
reject(new Error(isNotFound
|
||||
? `hermes profile list failed: binary not found at "${opts?.binaryPath ?? "hermes"}"`
|
||||
: `hermes profile list failed: ${err.message}`));
|
||||
});
|
||||
child.on("close", (code) => {
|
||||
if (settled)
|
||||
return;
|
||||
settled = true;
|
||||
clearTimeout(timer);
|
||||
if (code !== 0) {
|
||||
const combined = [stdout, stderr].filter(Boolean).join("\n");
|
||||
reject(new Error(`hermes profile list failed: process exited with code ${String(code)}.\n${combined}`));
|
||||
return;
|
||||
}
|
||||
resolve(parseProfileListOutput(stdout));
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Resolve HermesCliSettings from a plugin settings record and environment
|
||||
* variable fallbacks.
|
||||
*/
|
||||
export function resolveCliSettings(settings) {
|
||||
const str = (v) => typeof v === "string" && v.trim().length > 0 ? v.trim() : undefined;
|
||||
const num = (v, envKey, fallback) => {
|
||||
// Accept numeric values directly.
|
||||
if (typeof v === "number" && Number.isFinite(v) && v > 0)
|
||||
return v;
|
||||
const raw = str(v) ?? str(process.env[envKey]);
|
||||
if (raw !== undefined) {
|
||||
const parsed = Number(raw);
|
||||
if (Number.isFinite(parsed) && parsed > 0)
|
||||
return parsed;
|
||||
}
|
||||
return fallback;
|
||||
};
|
||||
const bool = (v, envKey, fallback) => {
|
||||
if (typeof v === "boolean")
|
||||
return v;
|
||||
const raw = str(v) ?? str(process.env[envKey]);
|
||||
if (raw !== undefined)
|
||||
return raw === "1" || raw.toLowerCase() === "true";
|
||||
return fallback;
|
||||
};
|
||||
return {
|
||||
binaryPath: str(settings?.binaryPath) ?? str(process.env.HERMES_BIN) ?? "hermes",
|
||||
model: str(settings?.model) ?? str(process.env.HERMES_MODEL_ID),
|
||||
provider: str(settings?.provider) ?? str(process.env.HERMES_PROVIDER),
|
||||
maxTurns: num(settings?.maxTurns, "HERMES_MAX_TURNS", 12),
|
||||
yolo: bool(settings?.yolo, "HERMES_YOLO", false),
|
||||
cliTimeoutMs: num(settings?.cliTimeoutMs, "HERMES_CLI_TIMEOUT_MS", 300_000),
|
||||
profile: str(settings?.profile) ?? str(process.env.HERMES_PROFILE),
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Strip ANSI escape codes and normalize CRLF to LF.
|
||||
*/
|
||||
function cleanText(raw) {
|
||||
return raw.replace(ANSI_RE, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
||||
}
|
||||
/**
|
||||
* Remove hermes TUI chrome lines from the captured body.
|
||||
*/
|
||||
function stripChrome(text) {
|
||||
const lines = text.split("\n");
|
||||
const filtered = lines.filter((line) => !CHROME_LINE_RES.some((re) => re.test(line)));
|
||||
return filtered.join("\n").trim();
|
||||
}
|
||||
/**
|
||||
* Parse the raw stdout from `hermes chat -q ... -Q`.
|
||||
*
|
||||
* Returns `{ body, sessionId }` on success or throws with a descriptive error.
|
||||
*/
|
||||
export function parseHermesOutput(rawStdout, rawStderr) {
|
||||
const cleaned = cleanText(rawStdout);
|
||||
const match = SESSION_ID_RE.exec(cleaned);
|
||||
if (!match) {
|
||||
const combined = [rawStdout, rawStderr].filter(Boolean).join("\n");
|
||||
throw new Error(`hermes: missing session_id in output.\n${combined}`);
|
||||
}
|
||||
const sessionId = match[1];
|
||||
// Body is everything before the session_id line.
|
||||
const sessionIdLineStart = cleaned.lastIndexOf("\nsession_id:");
|
||||
const bodyRaw = sessionIdLineStart >= 0 ? cleaned.slice(0, sessionIdLineStart) : cleaned;
|
||||
const body = stripChrome(bodyRaw);
|
||||
return { body, sessionId };
|
||||
}
|
||||
/**
|
||||
* Build the argv array for a `hermes chat` invocation.
|
||||
*/
|
||||
export function buildHermesArgs(prompt, settings, resumeSessionId) {
|
||||
const args = ["chat", "-q", prompt, "-Q", "--source", "tool"];
|
||||
if (resumeSessionId) {
|
||||
args.push("--resume", resumeSessionId);
|
||||
}
|
||||
if (settings.model) {
|
||||
args.push("-m", settings.model);
|
||||
}
|
||||
if (settings.provider) {
|
||||
args.push("--provider", settings.provider);
|
||||
}
|
||||
args.push("--max-turns", String(settings.maxTurns));
|
||||
if (settings.yolo) {
|
||||
args.push("--yolo");
|
||||
}
|
||||
return args;
|
||||
}
|
||||
/**
|
||||
* Invoke the hermes CLI for a single prompt/response turn.
|
||||
*
|
||||
* @param prompt - The user prompt to send.
|
||||
* @param settings - Resolved CLI settings.
|
||||
* @param resumeSessionId - Hermes session id from a prior call, if continuing.
|
||||
* @param signal - Optional AbortSignal; will SIGTERM the subprocess on abort.
|
||||
* @returns Parsed response body and the new/existing session id.
|
||||
*/
|
||||
export async function invokeHermesCli(prompt, settings, resumeSessionId, signal) {
|
||||
const args = buildHermesArgs(prompt, settings, resumeSessionId);
|
||||
const binary = resolveBinaryForSpawn(settings.binaryPath);
|
||||
return new Promise((resolve, reject) => {
|
||||
let settled = false;
|
||||
const spawnEnv = { ...process.env, PYTHONUNBUFFERED: "1" };
|
||||
if (settings.profile) {
|
||||
spawnEnv.HERMES_HOME = hermesProfileHome(settings.profile);
|
||||
}
|
||||
const child = spawn(binary, args, {
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
env: spawnEnv,
|
||||
});
|
||||
const hardKillTimer = setTimeout(() => {
|
||||
if (settled)
|
||||
return;
|
||||
settled = true;
|
||||
try {
|
||||
child.kill("SIGKILL");
|
||||
}
|
||||
catch {
|
||||
// already gone
|
||||
}
|
||||
reject(new Error(`hermes: process timed out after ${settings.cliTimeoutMs}ms`));
|
||||
}, settings.cliTimeoutMs);
|
||||
// Forward AbortSignal.
|
||||
const onAbort = () => {
|
||||
if (settled)
|
||||
return;
|
||||
settled = true;
|
||||
clearTimeout(hardKillTimer);
|
||||
try {
|
||||
child.kill("SIGTERM");
|
||||
}
|
||||
catch {
|
||||
// already gone
|
||||
}
|
||||
reject(new Error("hermes: invocation aborted"));
|
||||
};
|
||||
if (signal) {
|
||||
if (signal.aborted) {
|
||||
onAbort();
|
||||
return;
|
||||
}
|
||||
signal.addEventListener("abort", onAbort, { once: true });
|
||||
}
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
child.stdout?.on("data", (chunk) => {
|
||||
stdout += chunk.toString("utf-8");
|
||||
});
|
||||
child.stderr?.on("data", (chunk) => {
|
||||
stderr += chunk.toString("utf-8");
|
||||
});
|
||||
child.on("error", (err) => {
|
||||
if (settled)
|
||||
return;
|
||||
settled = true;
|
||||
clearTimeout(hardKillTimer);
|
||||
signal?.removeEventListener("abort", onAbort);
|
||||
const isNotFound = err.code === "ENOENT";
|
||||
reject(new Error(isNotFound
|
||||
? `hermes: binary not found at "${settings.binaryPath}". Install hermes or set binaryPath/HERMES_BIN.`
|
||||
: `hermes: spawn error — ${err.message}`));
|
||||
});
|
||||
child.on("close", (code) => {
|
||||
if (settled)
|
||||
return;
|
||||
settled = true;
|
||||
clearTimeout(hardKillTimer);
|
||||
signal?.removeEventListener("abort", onAbort);
|
||||
if (code !== 0) {
|
||||
const combined = [stdout, stderr].filter(Boolean).join("\n");
|
||||
reject(new Error(`hermes: process exited with code ${String(code)}.\n${combined}`));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
resolve(parseHermesOutput(stdout, stderr));
|
||||
}
|
||||
catch (parseErr) {
|
||||
reject(parseErr);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
//# sourceMappingURL=cli-spawn.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,17 +0,0 @@
|
||||
export declare const FUSION_SKILL_NAME = "fusion";
|
||||
export type HermesFusionSkillInstallOutcome = "installed" | "already-installed" | "replaced" | "skipped" | "warning";
|
||||
export interface HermesFusionSkillInstallResult {
|
||||
outcome: HermesFusionSkillInstallOutcome;
|
||||
sourceDir: string | null;
|
||||
targetDir: string;
|
||||
reason?: string;
|
||||
}
|
||||
export declare function resolveHermesHome(profile?: string): string;
|
||||
export declare function getFusionSkillSourceCandidates(moduleUrl?: string): string[];
|
||||
export declare function resolveBundledFusionSkillSource(): string | null;
|
||||
export declare function resolveBundledFusionSkillSourceFromCandidates(candidates: string[]): string | null;
|
||||
export declare function installFusionSkillIntoHermesHome(options?: {
|
||||
profile?: string;
|
||||
sourceDir?: string | null;
|
||||
}): HermesFusionSkillInstallResult;
|
||||
//# sourceMappingURL=fusion-skill-install.d.ts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"fusion-skill-install.d.ts","sourceRoot":"","sources":["../src/fusion-skill-install.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAE1C,MAAM,MAAM,+BAA+B,GACvC,WAAW,GACX,mBAAmB,GACnB,UAAU,GACV,SAAS,GACT,SAAS,CAAC;AAEd,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,+BAA+B,CAAC;IACzC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAI1D;AAED,wBAAgB,8BAA8B,CAAC,SAAS,SAAkB,GAAG,MAAM,EAAE,CAUpF;AAED,wBAAgB,+BAA+B,IAAI,MAAM,GAAG,IAAI,CAQ/D;AAED,wBAAgB,6CAA6C,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAMjG;AAED,wBAAgB,gCAAgC,CAAC,OAAO,GAAE;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,GAAG,8BAA8B,CA+EtC"}
|
||||
@@ -1,152 +0,0 @@
|
||||
import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync, rmSync, symlinkSync, unlinkSync, } from "node:fs";
|
||||
import { homedir } from "node:os";
|
||||
import { basename, dirname, join, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
export const FUSION_SKILL_NAME = "fusion";
|
||||
export function resolveHermesHome(profile) {
|
||||
const base = process.env.HERMES_HOME ?? join(homedir(), ".hermes");
|
||||
if (!profile || profile === "default")
|
||||
return base;
|
||||
return join(base, "profiles", profile);
|
||||
}
|
||||
export function getFusionSkillSourceCandidates(moduleUrl = import.meta.url) {
|
||||
const here = fileURLToPath(moduleUrl);
|
||||
const moduleDir = dirname(here);
|
||||
return [
|
||||
resolve(moduleDir, "..", "..", "..", "..", "packages", "cli", "skill", FUSION_SKILL_NAME),
|
||||
resolve(moduleDir, "..", "..", "..", "skill", FUSION_SKILL_NAME),
|
||||
resolve(moduleDir, "..", "..", "skill", FUSION_SKILL_NAME),
|
||||
resolve(moduleDir, "..", "..", "..", "..", "skill", FUSION_SKILL_NAME),
|
||||
];
|
||||
}
|
||||
export function resolveBundledFusionSkillSource() {
|
||||
const candidates = getFusionSkillSourceCandidates();
|
||||
for (const candidate of candidates) {
|
||||
if (existsSync(join(candidate, "SKILL.md")))
|
||||
return candidate;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
export function resolveBundledFusionSkillSourceFromCandidates(candidates) {
|
||||
for (const candidate of candidates) {
|
||||
if (existsSync(join(candidate, "SKILL.md")))
|
||||
return candidate;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
export function installFusionSkillIntoHermesHome(options = {}) {
|
||||
const sourceDir = options.sourceDir ?? resolveBundledFusionSkillSource();
|
||||
const targetDir = join(resolveHermesHome(options.profile), "skills", FUSION_SKILL_NAME);
|
||||
if (!sourceDir) {
|
||||
return {
|
||||
outcome: "warning",
|
||||
sourceDir,
|
||||
targetDir,
|
||||
reason: "bundled Fusion skill source directory not found",
|
||||
};
|
||||
}
|
||||
try {
|
||||
mkdirSync(dirname(targetDir), { recursive: true });
|
||||
let replaced = false;
|
||||
if (existsSync(targetDir) || isBrokenSymlink(targetDir)) {
|
||||
const stat = lstatSync(targetDir);
|
||||
if (stat.isSymbolicLink()) {
|
||||
const currentTarget = safeReadlink(targetDir);
|
||||
if (currentTarget && resolve(dirname(targetDir), currentTarget) === resolve(sourceDir)) {
|
||||
return { outcome: "already-installed", sourceDir, targetDir };
|
||||
}
|
||||
if (!looksLikeFusionSkillTarget(resolve(dirname(targetDir), currentTarget ?? ""))) {
|
||||
return {
|
||||
outcome: "skipped",
|
||||
sourceDir,
|
||||
targetDir,
|
||||
reason: "existing symlink does not look like a Fusion skill install",
|
||||
};
|
||||
}
|
||||
unlinkSync(targetDir);
|
||||
replaced = true;
|
||||
}
|
||||
else {
|
||||
if (!looksLikePriorFusionInstall(targetDir)) {
|
||||
return {
|
||||
outcome: "skipped",
|
||||
sourceDir,
|
||||
targetDir,
|
||||
reason: "existing directory does not look like a Fusion skill install",
|
||||
};
|
||||
}
|
||||
rmSync(targetDir, { recursive: true, force: true });
|
||||
replaced = true;
|
||||
}
|
||||
}
|
||||
try {
|
||||
symlinkSync(sourceDir, targetDir, "dir");
|
||||
}
|
||||
catch (error) {
|
||||
const symlinkReason = error instanceof Error ? error.message : String(error);
|
||||
try {
|
||||
cpSync(sourceDir, targetDir, { recursive: true });
|
||||
return {
|
||||
outcome: replaced ? "replaced" : "installed",
|
||||
sourceDir,
|
||||
targetDir,
|
||||
reason: `symlink failed (${symlinkReason}); copied files instead`,
|
||||
};
|
||||
}
|
||||
catch (copyError) {
|
||||
return {
|
||||
outcome: "warning",
|
||||
sourceDir,
|
||||
targetDir,
|
||||
reason: copyError instanceof Error ? copyError.message : String(copyError),
|
||||
};
|
||||
}
|
||||
}
|
||||
return { outcome: replaced ? "replaced" : "installed", sourceDir, targetDir };
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
outcome: "warning",
|
||||
sourceDir,
|
||||
targetDir,
|
||||
reason: error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
}
|
||||
}
|
||||
function safeReadlink(path) {
|
||||
try {
|
||||
return readlinkSync(path);
|
||||
}
|
||||
catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
function isBrokenSymlink(path) {
|
||||
try {
|
||||
const stat = lstatSync(path);
|
||||
return stat.isSymbolicLink() && !existsSync(path);
|
||||
}
|
||||
catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function looksLikePriorFusionInstall(path) {
|
||||
const skillMd = join(path, "SKILL.md");
|
||||
if (!existsSync(skillMd))
|
||||
return false;
|
||||
try {
|
||||
const body = readFileSync(skillMd, "utf-8");
|
||||
return /\bfusion\b/i.test(body) && /\bskill\b/i.test(body);
|
||||
}
|
||||
catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function looksLikeFusionSkillTarget(path) {
|
||||
if (!path)
|
||||
return false;
|
||||
if (basename(path).toLowerCase() === FUSION_SKILL_NAME)
|
||||
return true;
|
||||
return existsSync(join(path, "SKILL.md"));
|
||||
}
|
||||
//# sourceMappingURL=fusion-skill-install.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"fusion-skill-install.js","sourceRoot":"","sources":["../src/fusion-skill-install.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,22 +0,0 @@
|
||||
/**
|
||||
* Hermes Runtime Plugin
|
||||
*
|
||||
* Provides an executable Hermes runtime adapter that drives the local `hermes`
|
||||
* CLI as a subprocess. Discovered by Fusion's plugin runtime registry; the
|
||||
* settings configured in the dashboard's "Runtimes → Hermes" page flow through
|
||||
* `ctx.settings` into the CLI invocation.
|
||||
*/
|
||||
import type { FusionPlugin, PluginRuntimeFactory, PluginRuntimeManifestMetadata } from "@fusion/plugin-sdk";
|
||||
declare const HERMES_RUNTIME_ID = "hermes";
|
||||
declare const hermesRuntimeMetadata: PluginRuntimeManifestMetadata;
|
||||
declare const hermesRuntimeFactory: PluginRuntimeFactory;
|
||||
declare const plugin: FusionPlugin;
|
||||
export default plugin;
|
||||
export { hermesRuntimeMetadata, hermesRuntimeFactory, HERMES_RUNTIME_ID };
|
||||
export { HermesRuntimeAdapter } from "./runtime-adapter.js";
|
||||
export { resolveCliSettings, invokeHermesCli, buildHermesArgs, parseHermesOutput, listHermesProfiles, } from "./cli-spawn.js";
|
||||
export { installFusionSkillIntoHermesHome, resolveBundledFusionSkillSource, resolveHermesHome, } from "./fusion-skill-install.js";
|
||||
export type { HermesCliSettings, HermesCliResult, HermesProfileSummary } from "./cli-spawn.js";
|
||||
export { probeHermesBinary } from "./probe.js";
|
||||
export type { HermesBinaryStatus } from "./probe.js";
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,6BAA6B,EAC9B,MAAM,oBAAoB,CAAC;AAI5B,QAAA,MAAM,iBAAiB,WAAW,CAAC;AAGnC,QAAA,MAAM,qBAAqB,EAAE,6BAK5B,CAAC;AAIF,QAAA,MAAM,oBAAoB,EAAE,oBAE3B,CAAC;AAIF,QAAA,MAAM,MAAM,EAAE,YA2CZ,CAAC;AAEH,eAAe,MAAM,CAAC;AAItB,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,CAAC;AAC1E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,gCAAgC,EAChC,+BAA+B,EAC/B,iBAAiB,GAClB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAG/F,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC"}
|
||||
@@ -1,71 +0,0 @@
|
||||
/**
|
||||
* Hermes Runtime Plugin
|
||||
*
|
||||
* Provides an executable Hermes runtime adapter that drives the local `hermes`
|
||||
* CLI as a subprocess. Discovered by Fusion's plugin runtime registry; the
|
||||
* settings configured in the dashboard's "Runtimes → Hermes" page flow through
|
||||
* `ctx.settings` into the CLI invocation.
|
||||
*/
|
||||
import { definePlugin } from "@fusion/plugin-sdk";
|
||||
import { resolveCliSettings } from "./cli-spawn.js";
|
||||
import { installFusionSkillIntoHermesHome } from "./fusion-skill-install.js";
|
||||
import { HermesRuntimeAdapter } from "./runtime-adapter.js";
|
||||
// ── Hermes Runtime Metadata ───────────────────────────────────────────────────
|
||||
const HERMES_RUNTIME_ID = "hermes";
|
||||
const HERMES_RUNTIME_VERSION = "0.2.0";
|
||||
const hermesRuntimeMetadata = {
|
||||
runtimeId: HERMES_RUNTIME_ID,
|
||||
name: "Hermes Runtime",
|
||||
description: "Drives the local `hermes` CLI (NousResearch/hermes-agent)",
|
||||
version: HERMES_RUNTIME_VERSION,
|
||||
};
|
||||
// ── Hermes Runtime Factory ────────────────────────────────────────────────────
|
||||
const hermesRuntimeFactory = async (ctx) => {
|
||||
return new HermesRuntimeAdapter(ctx.settings);
|
||||
};
|
||||
// ── Plugin Definition ─────────────────────────────────────────────────────────
|
||||
const plugin = definePlugin({
|
||||
manifest: {
|
||||
id: "fusion-plugin-hermes-runtime",
|
||||
name: "Hermes Runtime Plugin",
|
||||
version: HERMES_RUNTIME_VERSION,
|
||||
description: "Drives the local `hermes` CLI for Fusion agents — captures session ids and resumes via --resume.",
|
||||
author: "Fusion Team",
|
||||
homepage: "https://github.com/NousResearch/hermes-agent",
|
||||
runtime: hermesRuntimeMetadata,
|
||||
},
|
||||
state: "installed",
|
||||
hooks: {
|
||||
onLoad: (ctx) => {
|
||||
const settings = resolveCliSettings(ctx.settings);
|
||||
const skillInstall = installFusionSkillIntoHermesHome({ profile: settings.profile });
|
||||
if (skillInstall.outcome === "warning") {
|
||||
ctx.logger.warn(`Hermes Runtime Plugin: Fusion skill auto-install warning: ${skillInstall.reason ?? "unknown"}`);
|
||||
}
|
||||
else if (skillInstall.outcome === "skipped") {
|
||||
ctx.logger.warn(`Hermes Runtime Plugin: Fusion skill auto-install skipped: ${skillInstall.reason ?? "unknown"}`);
|
||||
}
|
||||
ctx.logger.info(`Hermes Runtime Plugin loaded — binary=${settings.binaryPath} model=${settings.model ?? "(default)"} fusionSkill=${skillInstall.outcome}`);
|
||||
ctx.emitEvent("hermes-runtime:loaded", {
|
||||
runtimeId: HERMES_RUNTIME_ID,
|
||||
version: HERMES_RUNTIME_VERSION,
|
||||
});
|
||||
},
|
||||
onUnload: () => {
|
||||
// No persistent state to clean up — each prompt spawns a fresh subprocess.
|
||||
},
|
||||
},
|
||||
runtime: {
|
||||
metadata: hermesRuntimeMetadata,
|
||||
factory: hermesRuntimeFactory,
|
||||
},
|
||||
});
|
||||
export default plugin;
|
||||
// ── Public exports ────────────────────────────────────────────────────────────
|
||||
export { hermesRuntimeMetadata, hermesRuntimeFactory, HERMES_RUNTIME_ID };
|
||||
export { HermesRuntimeAdapter } from "./runtime-adapter.js";
|
||||
export { resolveCliSettings, invokeHermesCli, buildHermesArgs, parseHermesOutput, listHermesProfiles, } from "./cli-spawn.js";
|
||||
export { installFusionSkillIntoHermesHome, resolveBundledFusionSkillSource, resolveHermesHome, } from "./fusion-skill-install.js";
|
||||
// Probe re-export for the dashboard's runtime-provider-probes façade.
|
||||
export { probeHermesBinary } from "./probe.js";
|
||||
//# sourceMappingURL=index.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,gCAAgC,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAO5D,iFAAiF;AAEjF,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AACnC,MAAM,sBAAsB,GAAG,OAAO,CAAC;AAEvC,MAAM,qBAAqB,GAAkC;IAC3D,SAAS,EAAE,iBAAiB;IAC5B,IAAI,EAAE,gBAAgB;IACtB,WAAW,EAAE,2DAA2D;IACxE,OAAO,EAAE,sBAAsB;CAChC,CAAC;AAEF,iFAAiF;AAEjF,MAAM,oBAAoB,GAAyB,KAAK,EAAE,GAAG,EAAE,EAAE;IAC/D,OAAO,IAAI,oBAAoB,CAAC,GAAG,CAAC,QAA+C,CAAC,CAAC;AACvF,CAAC,CAAC;AAEF,iFAAiF;AAEjF,MAAM,MAAM,GAAiB,YAAY,CAAC;IACxC,QAAQ,EAAE;QACR,EAAE,EAAE,8BAA8B;QAClC,IAAI,EAAE,uBAAuB;QAC7B,OAAO,EAAE,sBAAsB;QAC/B,WAAW,EACT,kGAAkG;QACpG,MAAM,EAAE,aAAa;QACrB,QAAQ,EAAE,8CAA8C;QACxD,OAAO,EAAE,qBAAqB;KAC/B;IACD,KAAK,EAAE,WAAW;IAClB,KAAK,EAAE;QACL,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE;YACd,MAAM,QAAQ,GAAG,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAClD,MAAM,YAAY,GAAG,gCAAgC,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;YAErF,IAAI,YAAY,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;gBACvC,GAAG,CAAC,MAAM,CAAC,IAAI,CACb,6DAA6D,YAAY,CAAC,MAAM,IAAI,SAAS,EAAE,CAChG,CAAC;YACJ,CAAC;iBAAM,IAAI,YAAY,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC9C,GAAG,CAAC,MAAM,CAAC,IAAI,CACb,6DAA6D,YAAY,CAAC,MAAM,IAAI,SAAS,EAAE,CAChG,CAAC;YACJ,CAAC;YAED,GAAG,CAAC,MAAM,CAAC,IAAI,CACb,yCAAyC,QAAQ,CAAC,UAAU,UAAU,QAAQ,CAAC,KAAK,IAAI,WAAW,gBAAgB,YAAY,CAAC,OAAO,EAAE,CAC1I,CAAC;YACF,GAAG,CAAC,SAAS,CAAC,uBAAuB,EAAE;gBACrC,SAAS,EAAE,iBAAiB;gBAC5B,OAAO,EAAE,sBAAsB;aAChC,CAAC,CAAC;QACL,CAAC;QACD,QAAQ,EAAE,GAAG,EAAE;YACb,2EAA2E;QAC7E,CAAC;KACF;IACD,OAAO,EAAE;QACP,QAAQ,EAAE,qBAAqB;QAC/B,OAAO,EAAE,oBAAoB;KAC9B;CACF,CAAC,CAAC;AAEH,eAAe,MAAM,CAAC;AAEtB,iFAAiF;AAEjF,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,CAAC;AAC1E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,gCAAgC,EAChC,+BAA+B,EAC/B,iBAAiB,GAClB,MAAM,2BAA2B,CAAC;AAGnC,sEAAsE;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC"}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { type Message } from "@mariozechner/pi-ai";
|
||||
import type { HermesCallbacks, HermesStreamSession, ResolvedModelConfig } from "./types.js";
|
||||
export declare function resolveModelConfig(settings?: Record<string, unknown>): ResolvedModelConfig;
|
||||
export declare function createStreamSession(options: {
|
||||
provider: string;
|
||||
modelId: string;
|
||||
apiKey?: string;
|
||||
thinkingLevel?: string;
|
||||
systemPrompt: string;
|
||||
callbacks?: HermesCallbacks;
|
||||
}): HermesStreamSession;
|
||||
export declare function streamPrompt(session: HermesStreamSession, _userMessage: Message): Promise<void>;
|
||||
export declare function describeStreamModel(session: HermesStreamSession): string;
|
||||
//# sourceMappingURL=pi-module.d.ts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"pi-module.d.ts","sourceRoot":"","sources":["../src/pi-module.ts"],"names":[],"mappings":"AACA,OAAO,EAML,KAAK,OAAO,EAIb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AASpB,wBAAgB,kBAAkB,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,mBAAmB,CAU1F;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,eAAe,CAAC;CAC7B,GAAG,mBAAmB,CAetB;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAoCrG;AAkCD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAExE"}
|
||||
@@ -1,85 +0,0 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { getModel, streamSimple, } from "@mariozechner/pi-ai";
|
||||
const DEFAULT_PROVIDER = "anthropic";
|
||||
const DEFAULT_MODEL_ID = "claude-sonnet-4-5";
|
||||
function resolveStringSetting(value) {
|
||||
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
||||
}
|
||||
export function resolveModelConfig(settings) {
|
||||
const provider = resolveStringSetting(settings?.provider) ?? resolveStringSetting(process.env.HERMES_PROVIDER) ?? DEFAULT_PROVIDER;
|
||||
const modelId = resolveStringSetting(settings?.modelId) ?? resolveStringSetting(process.env.HERMES_MODEL_ID) ?? DEFAULT_MODEL_ID;
|
||||
const apiKey = resolveStringSetting(settings?.apiKey) ?? resolveStringSetting(process.env.HERMES_API_KEY);
|
||||
const thinkingLevel = resolveStringSetting(settings?.thinkingLevel) ?? resolveStringSetting(process.env.HERMES_THINKING_LEVEL) ?? undefined;
|
||||
return { provider, modelId, apiKey, thinkingLevel };
|
||||
}
|
||||
export function createStreamSession(options) {
|
||||
const model = getModel(options.provider, options.modelId);
|
||||
return {
|
||||
model,
|
||||
systemPrompt: options.systemPrompt,
|
||||
messages: [],
|
||||
apiKey: options.apiKey,
|
||||
thinkingLevel: options.thinkingLevel,
|
||||
sessionId: randomUUID(),
|
||||
lastModelDescription: `${model.provider}/${model.id}`,
|
||||
callbacks: options.callbacks ?? {},
|
||||
usage: undefined,
|
||||
dispose: () => undefined,
|
||||
};
|
||||
}
|
||||
export async function streamPrompt(session, _userMessage) {
|
||||
const context = {
|
||||
systemPrompt: session.systemPrompt,
|
||||
messages: [...session.messages],
|
||||
};
|
||||
const options = {
|
||||
sessionId: session.sessionId,
|
||||
};
|
||||
if (session.apiKey) {
|
||||
options.apiKey = session.apiKey;
|
||||
}
|
||||
if (session.thinkingLevel) {
|
||||
options.reasoning = session.thinkingLevel;
|
||||
}
|
||||
const stream = streamSimple(session.model, context, options);
|
||||
let fullText = "";
|
||||
for await (const event of stream) {
|
||||
handleStreamEvent(event, session, (delta) => {
|
||||
fullText += delta;
|
||||
});
|
||||
}
|
||||
const finalMessage = await stream.result();
|
||||
const responseText = finalMessage.content
|
||||
.filter((content) => content.type === "text")
|
||||
.map((content) => content.text)
|
||||
.join("") || fullText;
|
||||
session.messages.push({ role: "assistant", content: responseText });
|
||||
session.lastModelDescription = `${session.model.provider}/${session.model.id}`;
|
||||
}
|
||||
function handleStreamEvent(event, session, onTextDelta) {
|
||||
if (event.type === "text_delta") {
|
||||
session.callbacks.onText?.(event.delta);
|
||||
onTextDelta(event.delta);
|
||||
return;
|
||||
}
|
||||
if (event.type === "thinking_delta") {
|
||||
session.callbacks.onThinking?.(event.delta);
|
||||
return;
|
||||
}
|
||||
if (event.type === "toolcall_end") {
|
||||
session.callbacks.onToolStart?.(event.toolCall.name, event.toolCall.arguments);
|
||||
session.callbacks.onToolEnd?.(event.toolCall.name, false, event.toolCall.arguments);
|
||||
return;
|
||||
}
|
||||
if (event.type === "error") {
|
||||
const errorMessage = event.error.errorMessage ?? "Hermes stream failed";
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
if (event.type === "done") {
|
||||
session.usage = event.message.usage;
|
||||
}
|
||||
}
|
||||
export function describeStreamModel(session) {
|
||||
return session.lastModelDescription;
|
||||
}
|
||||
//# sourceMappingURL=pi-module.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"pi-module.js","sourceRoot":"","sources":["../src/pi-module.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,35 +0,0 @@
|
||||
/**
|
||||
* Hermes binary probe helper.
|
||||
*
|
||||
* Mirrors the probeClaudeCli pattern from packages/dashboard/src/claude-cli-probe.ts.
|
||||
* Never throws — all failures are captured as `available: false` with a reason.
|
||||
*/
|
||||
/**
|
||||
* Result of probing for the hermes binary.
|
||||
*/
|
||||
export interface HermesBinaryStatus {
|
||||
/** True if the binary was found and ran to completion successfully. */
|
||||
available: boolean;
|
||||
/** Absolute path resolved via `which`/`where`, if found. */
|
||||
binaryPath?: string;
|
||||
/** Version string from `hermes --version` stdout, if available. */
|
||||
version?: string;
|
||||
/** Human-readable failure reason when `available === false`. */
|
||||
reason?: string;
|
||||
/** Wall-clock duration of the probe in milliseconds. */
|
||||
probeDurationMs: number;
|
||||
}
|
||||
/**
|
||||
* Probe for the hermes binary.
|
||||
*
|
||||
* Runs `<binaryPath> --version` with a short timeout. Use this from
|
||||
* the dashboard status endpoint to check binary presence without crashing.
|
||||
*
|
||||
* @param opts.binaryPath - Override the binary path (default: "hermes").
|
||||
* @param opts.timeoutMs - Override probe timeout in ms (default: 2000).
|
||||
*/
|
||||
export declare function probeHermesBinary(opts?: {
|
||||
binaryPath?: string;
|
||||
timeoutMs?: number;
|
||||
}): Promise<HermesBinaryStatus>;
|
||||
//# sourceMappingURL=probe.d.ts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../src/probe.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,uEAAuE;IACvE,SAAS,EAAE,OAAO,CAAC;IACnB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,CAAC,EAAE;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAiF9B"}
|
||||
118
plugins/fusion-plugin-hermes-runtime/dist/probe.js
vendored
118
plugins/fusion-plugin-hermes-runtime/dist/probe.js
vendored
@@ -1,118 +0,0 @@
|
||||
/**
|
||||
* Hermes binary probe helper.
|
||||
*
|
||||
* Mirrors the probeClaudeCli pattern from packages/dashboard/src/claude-cli-probe.ts.
|
||||
* Never throws — all failures are captured as `available: false` with a reason.
|
||||
*/
|
||||
import { spawn } from "node:child_process";
|
||||
/** Default probe timeout in milliseconds. */
|
||||
const DEFAULT_PROBE_TIMEOUT_MS = 2000;
|
||||
/**
|
||||
* Probe for the hermes binary.
|
||||
*
|
||||
* Runs `<binaryPath> --version` with a short timeout. Use this from
|
||||
* the dashboard status endpoint to check binary presence without crashing.
|
||||
*
|
||||
* @param opts.binaryPath - Override the binary path (default: "hermes").
|
||||
* @param opts.timeoutMs - Override probe timeout in ms (default: 2000).
|
||||
*/
|
||||
export async function probeHermesBinary(opts) {
|
||||
const startedAt = Date.now();
|
||||
const binary = typeof opts?.binaryPath === "string" && opts.binaryPath.trim().length > 0
|
||||
? opts.binaryPath.trim()
|
||||
: "hermes";
|
||||
const timeoutMs = opts?.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;
|
||||
const resolvedPath = await tryResolveBinaryPath(binary);
|
||||
return new Promise((resolvePromise) => {
|
||||
const finish = (result) => {
|
||||
resolvePromise({ ...result, probeDurationMs: Date.now() - startedAt });
|
||||
};
|
||||
let settled = false;
|
||||
const child = spawn(resolvedPath ?? binary, ["--version"], {
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
const timer = setTimeout(() => {
|
||||
if (settled)
|
||||
return;
|
||||
settled = true;
|
||||
try {
|
||||
child.kill("SIGKILL");
|
||||
}
|
||||
catch {
|
||||
// Process already gone.
|
||||
}
|
||||
finish({
|
||||
available: false,
|
||||
binaryPath: resolvedPath,
|
||||
reason: `Probe timed out after ${timeoutMs}ms`,
|
||||
});
|
||||
}, timeoutMs);
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
child.stdout?.on("data", (chunk) => {
|
||||
stdout += chunk.toString("utf-8");
|
||||
});
|
||||
child.stderr?.on("data", (chunk) => {
|
||||
stderr += chunk.toString("utf-8");
|
||||
});
|
||||
child.on("error", (err) => {
|
||||
if (settled)
|
||||
return;
|
||||
settled = true;
|
||||
clearTimeout(timer);
|
||||
const isNotFound = err.code === "ENOENT";
|
||||
finish({
|
||||
available: false,
|
||||
binaryPath: resolvedPath,
|
||||
reason: isNotFound
|
||||
? `\`${binary}\` not found on PATH`
|
||||
: err.message,
|
||||
});
|
||||
});
|
||||
child.on("close", (code) => {
|
||||
if (settled)
|
||||
return;
|
||||
settled = true;
|
||||
clearTimeout(timer);
|
||||
if (code === 0) {
|
||||
finish({
|
||||
available: true,
|
||||
version: stdout.trim() || undefined,
|
||||
binaryPath: resolvedPath,
|
||||
});
|
||||
}
|
||||
else {
|
||||
finish({
|
||||
available: false,
|
||||
binaryPath: resolvedPath,
|
||||
reason: stderr.trim() || `hermes --version exited with code ${String(code)}`,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Best-effort path resolution via `which` (POSIX) or `where` (Windows).
|
||||
* Returns undefined on failure — the spawn above is the actual authority.
|
||||
*/
|
||||
async function tryResolveBinaryPath(binary) {
|
||||
return new Promise((resolvePromise) => {
|
||||
const which = process.platform === "win32" ? "where" : "which";
|
||||
const child = spawn(which, [binary], { stdio: ["ignore", "pipe", "ignore"] });
|
||||
let out = "";
|
||||
child.stdout?.on("data", (chunk) => {
|
||||
out += chunk.toString("utf-8");
|
||||
});
|
||||
child.on("error", () => resolvePromise(undefined));
|
||||
child.on("close", (code) => {
|
||||
if (code === 0) {
|
||||
const first = out.trim().split(/\r?\n/)[0];
|
||||
resolvePromise(first?.length ? first : undefined);
|
||||
}
|
||||
else {
|
||||
resolvePromise(undefined);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
//# sourceMappingURL=probe.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"probe.js","sourceRoot":"","sources":["../src/probe.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Hermes Runtime Adapter — drives the local `hermes` CLI as a subprocess.
|
||||
*
|
||||
* Each call to `promptWithFallback` invokes `hermes chat -q ... -Q --source tool`
|
||||
* and captures the resulting `session_id:` line. Subsequent calls on the same
|
||||
* session pass `--resume <id>` to continue the conversation.
|
||||
*/
|
||||
import type { HermesCliSettings } from "./cli-spawn.js";
|
||||
import type { AgentRuntime, AgentRuntimeOptions, AgentSession, AgentSessionResult } from "./types.js";
|
||||
export declare class HermesRuntimeAdapter implements AgentRuntime {
|
||||
readonly id = "hermes";
|
||||
readonly name = "Hermes Runtime";
|
||||
private readonly settings;
|
||||
constructor(settings?: Record<string, unknown> | HermesCliSettings);
|
||||
createSession(options: AgentRuntimeOptions): Promise<AgentSessionResult>;
|
||||
promptWithFallback(session: AgentSession, prompt: string, _options?: unknown): Promise<void>;
|
||||
describeModel(session: AgentSession): string;
|
||||
dispose(_session: AgentSession): Promise<void>;
|
||||
private describeFromSettings;
|
||||
}
|
||||
//# sourceMappingURL=runtime-adapter.d.ts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"runtime-adapter.d.ts","sourceRoot":"","sources":["../src/runtime-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EACV,YAAY,EACZ,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,EAEnB,MAAM,YAAY,CAAC;AAwBpB,qBAAa,oBAAqB,YAAW,YAAY;IACvD,QAAQ,CAAC,EAAE,YAAY;IACvB,QAAQ,CAAC,IAAI,oBAAoB;IAEjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;gBAEjC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,iBAAiB;IAM5D,aAAa,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAuBxE,kBAAkB,CACtB,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,OAAO,GACjB,OAAO,CAAC,IAAI,CAAC;IAehB,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM;IAItC,OAAO,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpD,OAAO,CAAC,oBAAoB;CAQ7B"}
|
||||
@@ -1,84 +0,0 @@
|
||||
/**
|
||||
* Hermes Runtime Adapter — drives the local `hermes` CLI as a subprocess.
|
||||
*
|
||||
* Each call to `promptWithFallback` invokes `hermes chat -q ... -Q --source tool`
|
||||
* and captures the resulting `session_id:` line. Subsequent calls on the same
|
||||
* session pass `--resume <id>` to continue the conversation.
|
||||
*/
|
||||
import { invokeHermesCli, resolveCliSettings } from "./cli-spawn.js";
|
||||
function buildRuntimeContextSection(options) {
|
||||
const skillNames = Array.isArray(options.skills) ? options.skills.filter((value) => typeof value === "string" && value.trim().length > 0) : [];
|
||||
const skillSelection = options.skillSelection;
|
||||
const selectionSkillNames = Array.isArray(skillSelection?.requestedSkillNames)
|
||||
? skillSelection.requestedSkillNames.filter((value) => typeof value === "string" && value.trim().length > 0)
|
||||
: [];
|
||||
const mergedSkills = skillNames.length > 0 ? skillNames : selectionSkillNames;
|
||||
const lines = [
|
||||
"Fusion runtime context:",
|
||||
`- Tool mode: ${options.tools ?? "coding"}`,
|
||||
];
|
||||
if (mergedSkills.length > 0) {
|
||||
lines.push(`- Requested skills: ${mergedSkills.join(", ")}`);
|
||||
}
|
||||
lines.push("- If fn_* tools are available in your runtime, use them directly for coordination/memory/task actions.");
|
||||
return lines.join("\n");
|
||||
}
|
||||
export class HermesRuntimeAdapter {
|
||||
id = "hermes";
|
||||
name = "Hermes Runtime";
|
||||
settings;
|
||||
constructor(settings) {
|
||||
this.settings = resolveCliSettings(settings);
|
||||
}
|
||||
async createSession(options) {
|
||||
const session = {
|
||||
model: undefined,
|
||||
systemPrompt: options.systemPrompt,
|
||||
messages: [],
|
||||
apiKey: undefined,
|
||||
thinkingLevel: undefined,
|
||||
sessionId: "",
|
||||
lastModelDescription: this.describeFromSettings(),
|
||||
callbacks: {
|
||||
onText: options.onText,
|
||||
onThinking: options.onThinking,
|
||||
onToolStart: options.onToolStart,
|
||||
onToolEnd: options.onToolEnd,
|
||||
},
|
||||
runtimeContext: options.runtimeContext,
|
||||
fusedSystemPrompt: [options.systemPrompt.trim(), buildRuntimeContextSection(options).trim()].filter((part) => part.length > 0).join("\n\n"),
|
||||
dispose: () => undefined,
|
||||
};
|
||||
return { session, sessionFile: undefined };
|
||||
}
|
||||
async promptWithFallback(session, prompt, _options) {
|
||||
const resumeId = session.sessionId || undefined;
|
||||
const promptWithContext = resumeId
|
||||
? prompt
|
||||
: `${session.fusedSystemPrompt}\n\nUser request:\n${prompt}`;
|
||||
const result = await invokeHermesCli(promptWithContext, this.settings, resumeId);
|
||||
session.sessionId = result.sessionId;
|
||||
session.lastModelDescription = this.describeFromSettings();
|
||||
if (result.body) {
|
||||
session.callbacks.onText?.(result.body);
|
||||
}
|
||||
}
|
||||
describeModel(session) {
|
||||
return session.lastModelDescription || this.describeFromSettings();
|
||||
}
|
||||
async dispose(_session) {
|
||||
// No persistent resources to release — the hermes CLI process exits per turn.
|
||||
}
|
||||
describeFromSettings() {
|
||||
const provider = this.settings.provider;
|
||||
const model = this.settings.model;
|
||||
if (provider && model)
|
||||
return `hermes/${provider}/${model}`;
|
||||
if (model)
|
||||
return `hermes/${model}`;
|
||||
if (provider)
|
||||
return `hermes/${provider}`;
|
||||
return "hermes";
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=runtime-adapter.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"runtime-adapter.js","sourceRoot":"","sources":["../src/runtime-adapter.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,84 +0,0 @@
|
||||
/**
|
||||
* Hermes Runtime Plugin - Type Definitions
|
||||
*
|
||||
* The runtime contract is defined locally to avoid compile-time coupling to
|
||||
* internal engine exports.
|
||||
*/
|
||||
export interface HermesCallbacks {
|
||||
onText?: (text: string) => void;
|
||||
onThinking?: (text: string) => void;
|
||||
onToolStart?: (toolName: string, args?: unknown) => void;
|
||||
onToolEnd?: (toolName: string, isError: boolean, result?: unknown) => void;
|
||||
}
|
||||
export interface HermesRuntimeContext {
|
||||
sessionPurpose?: string;
|
||||
toolMode?: "coding" | "readonly";
|
||||
customToolNames?: string[];
|
||||
requestedSkillNames?: string[];
|
||||
}
|
||||
export interface HermesStreamSession {
|
||||
model: unknown;
|
||||
systemPrompt: string;
|
||||
messages: unknown[];
|
||||
apiKey: string | undefined;
|
||||
thinkingLevel: string | undefined;
|
||||
sessionId: string;
|
||||
lastModelDescription: string;
|
||||
callbacks: HermesCallbacks;
|
||||
usage?: unknown;
|
||||
runtimeContext?: HermesRuntimeContext;
|
||||
fusedSystemPrompt: string;
|
||||
dispose(): void;
|
||||
}
|
||||
export type AgentSession = HermesStreamSession;
|
||||
/**
|
||||
* Options for creating an agent session.
|
||||
* Mirrors the engine's runtime options shape. Hermes accepts these options
|
||||
* for compatibility and silently ignores Pi-specific fields.
|
||||
*/
|
||||
export interface AgentRuntimeOptions {
|
||||
cwd: string;
|
||||
systemPrompt: string;
|
||||
tools?: "coding" | "readonly";
|
||||
customTools?: unknown;
|
||||
onText?: (text: string) => void;
|
||||
onThinking?: (text: string) => void;
|
||||
onToolStart?: (toolName: string, args?: unknown) => void;
|
||||
onToolEnd?: (toolName: string, isError: boolean, result?: unknown) => void;
|
||||
defaultProvider?: string;
|
||||
defaultModelId?: string;
|
||||
fallbackProvider?: string;
|
||||
fallbackModelId?: string;
|
||||
defaultThinkingLevel?: string;
|
||||
sessionManager?: unknown;
|
||||
skillSelection?: unknown;
|
||||
skills?: string[];
|
||||
runtimeContext?: HermesRuntimeContext;
|
||||
}
|
||||
/** Result of creating a session. */
|
||||
export interface AgentSessionResult {
|
||||
session: AgentSession;
|
||||
sessionFile?: string;
|
||||
}
|
||||
/** Agent runtime adapter interface. */
|
||||
export interface AgentRuntime {
|
||||
id: string;
|
||||
name: string;
|
||||
createSession(options: AgentRuntimeOptions): Promise<AgentSessionResult>;
|
||||
promptWithFallback(session: AgentSession, prompt: string, options?: unknown): Promise<void>;
|
||||
describeModel(session: AgentSession): string;
|
||||
dispose?(session: AgentSession): Promise<void>;
|
||||
}
|
||||
export interface HermesModelConfig {
|
||||
provider: string;
|
||||
modelId: string;
|
||||
apiKey?: string;
|
||||
thinkingLevel?: string;
|
||||
}
|
||||
export interface ResolvedModelConfig {
|
||||
provider: string;
|
||||
modelId: string;
|
||||
apiKey: string | undefined;
|
||||
thinkingLevel: string | undefined;
|
||||
}
|
||||
//# sourceMappingURL=types.d.ts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* Hermes Runtime Plugin - Type Definitions
|
||||
*
|
||||
* The runtime contract is defined locally to avoid compile-time coupling to
|
||||
* internal engine exports.
|
||||
*/
|
||||
export {};
|
||||
//# sourceMappingURL=types.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
||||
Reference in New Issue
Block a user