Files
fusion/plugins/fusion-plugin-omp-runtime/src/cli-spawn.ts
gsxdsm b563b12662 feat: add Oh My Pi (omp) ACP runtime plugin (#2083)
## Summary

- Add `fusion-plugin-omp-runtime` so Fusion agents can run through
operator-installed **Oh My Pi (`omp`)** over the [Agent Client
Protocol](https://omp.sh/docs/acp) (`omp acp`).
- Wire staged/bundled install, Settings → Authentication card (enable +
binary path), model discovery (`omp models` → `omp-cli/*`), and MCP
eligibility for runtime id `omp`.
- Forward Fusion `systemPrompt` via ACP `session/new`
`_meta.systemPromptOverride`.

## How operators use it

1. Install/auth `omp` (credentials under `~/.omp`).
2. Enable **Oh My Pi — via omp ACP** in Settings → Authentication
(optional binary path).
3. Set agent **Runtime Source → OMP Runtime** (`runtimeHint: "omp"`), or
pick an `omp-cli/*` model when enabled.

## Known v1 gaps

- No Grok-style Fusion `fn_*` loopback tool bridge yet (operator MCP is
forwarded; in-process custom tools are not).
- Model is fixed at spawn (`omp --model … acp`); no mid-session Fusion
model switch.

## Test plan

- [x] `pnpm --filter @fusion-plugin-examples/omp-runtime test` (unit +
live ACP when `omp` is on PATH)
- [x] Auth routes: `POST /api/auth/omp-cli`, `GET
/api/providers/omp-cli/status`
- [x] Engine `runtimeSupportsMcp("omp")`
- [ ] Manual: enable card in dashboard, select OMP runtime on an agent,
run a short chat turn

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added Oh My Pi (OMP) CLI support as an ACP-backed runtime and model
provider, including model discovery and probing.
* Added dashboard auth/status controls to enable OMP, check readiness,
and configure the local binary path (with validation).
* Exposed OMP custom `fn_*` tools via an MCP loopback bridge, plus
optional filesystem capabilities and stricter tool permission gating.
* **Documentation**
* Added/expanded OMP runtime contract and integration docs (including
the ACP session/handshake flow).
* **Tests**
* Added Vitest coverage for settings wiring, provider status, model
discovery, runtime sessions, permissions, MCP bridging, and live
connectivity.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-14 08:18:52 -07:00

62 lines
1.8 KiB
TypeScript

import { spawn } from "node:child_process";
function formatSpawnError(error: Error & { code?: unknown }): string {
const code = typeof error.code === "string" ? `${error.code}: ` : "";
return `spawn error: ${code}${error.message}`.trim();
}
export async function runOmpCommand(
binary: string,
args: string[],
timeoutMs: number,
): Promise<{ code: number | null; stdout: string; stderr: string }> {
return new Promise((resolve) => {
let stdout = "";
let stderr = "";
let settled = false;
let timer: NodeJS.Timeout | undefined;
const finish = (result: { code: number | null; stdout: string; stderr: string }) => {
if (settled) return;
settled = true;
if (timer) clearTimeout(timer);
resolve(result);
};
/*
FNXC:OmpAcp 2026-07-11-23:35:
Windows installers/npm-style shims can expose `omp.cmd` or `omp.bat` on PATH;
Node cannot direct-spawn those batch wrappers without the command shell.
Keep Unix/macOS on direct spawn.
*/
const child = spawn(binary, args, {
stdio: ["ignore", "pipe", "pipe"],
shell: process.platform === "win32",
});
timer = setTimeout(() => {
try {
child.kill("SIGKILL");
} catch {
// best effort
}
finish({ code: 124, stdout, stderr });
}, timeoutMs);
child.stdout?.on("data", (c: Buffer) => {
stdout += c.toString("utf-8");
});
child.stderr?.on("data", (c: Buffer) => {
stderr += c.toString("utf-8");
});
child.once("error", (error: Error & { code?: unknown }) => {
const diagnostic = formatSpawnError(error);
stderr = stderr ? `${stderr}\n${diagnostic}` : diagnostic;
finish({ code: 127, stdout, stderr });
});
child.once("close", (code) => {
finish({ code, stdout, stderr });
});
});
}