fix(opencode): install discoverable atomic plugin

This commit is contained in:
ZacharyZhang-NY committed 2026-07-20 17:46:37 -04:00
1 parent 3f43f4aad5
commit 69a75153f5
15 files changed
+1554 -348

No files matched your search

+40 -111
View File
@@ -1,134 +1,63 @@
import fsp from 'node:fs/promises';
import path from 'node:path';
import os from 'node:os';
import type { AgentPlugin, InstallConfig, PluginStatus } from './base.js';
import { AGENT_KEY } from './opencode-contract.js';
import {
commitOpenCodeInstallation,
commitOpenCodeUninstall,
preflightOpenCodeInstallation,
prepareOpenCodeInstallation,
prepareOpenCodeUninstall,
} from './opencode-installation.js';
import {
openCodeRuntimeFilesExist,
readOpenCodeSources,
requireAvailableOpenCodePlugin,
} from './opencode-io.js';
import { SUPPORTED_AGENTS } from './registry.js';
const AGENT_KEY = 'opencode';
const entry = SUPPORTED_AGENTS.get(AGENT_KEY)!;
function resolveConfigDir(): string {
return path.join(os.homedir(), entry.configDir);
}
function resolveConfigPath(): string {
return path.join(resolveConfigDir(), entry.configFile);
}
function generatePluginScript(hookScriptPath: string, guardScriptPath: string): string {
return `// Elydora Audit Plugin for OpenCode
// Generated by @elydora/sdk — DO NOT EDIT
import { spawn, spawnSync } from 'node:child_process';
const HOOK_SCRIPT_PATH = ${JSON.stringify(hookScriptPath)};
const GUARD_SCRIPT_PATH = ${JSON.stringify(guardScriptPath)};
export const ElydoraAuditPlugin = async (ctx) => {
return {
"tool.execute.before": async () => {
// Guard — blocks tool if agent is frozen
let result;
try {
result = spawnSync('node', [GUARD_SCRIPT_PATH], {
timeout: 5000,
stdio: ['pipe', 'ignore', 'pipe'],
});
} catch (error) {
throw new Error('Elydora guard failed: ' + error.message);
}
if (result.error) {
throw new Error('Elydora guard failed: ' + result.error.message);
}
if (result.status !== 0) {
const message = result.stderr?.toString().trim() || 'Guard exited with status ' + result.status;
const prefix = result.status === 2 ? '' : 'Elydora guard failed: ';
throw new Error(prefix + message);
}
},
"tool.execute.after": async (input, output) => {
const data = JSON.stringify({
tool_name: input?.tool || 'unknown',
tool_input: input?.args || output?.args || {},
session_id: input?.sessionID || ctx?.project?.name || 'unknown',
});
const child = spawn('node', [HOOK_SCRIPT_PATH], {
detached: true,
stdio: ['pipe', 'ignore', 'ignore'],
});
child.on('error', (error) => {
console.error('[elydora] audit hook failed:', error.message);
});
child.stdin.on('error', (error) => {
console.error('[elydora] audit input failed:', error.message);
});
child.stdin.end(data);
child.unref();
},
};
};
`;
function validateSources(sources: Awaited<ReturnType<typeof readOpenCodeSources>>): void {
requireAvailableOpenCodePlugin(sources.current);
}
export const opencodePlugin: AgentPlugin = {
async install(config: InstallConfig): Promise<void> {
const configDir = resolveConfigDir();
await fsp.mkdir(configDir, { recursive: true });
managesRuntime: true,
const configPath = resolveConfigPath();
const script = generatePluginScript(config.hookScriptPath, config.guardScriptPath);
await fsp.writeFile(configPath, script, 'utf-8');
async preflightInstall(config: InstallConfig): Promise<void> {
const sources = await readOpenCodeSources();
validateSources(sources);
await preflightOpenCodeInstallation(config, sources);
},
async uninstall(_agentId?: string): Promise<void> {
const configPath = resolveConfigPath();
try {
await fsp.unlink(configPath);
} catch {
// Already removed
}
async install(config: InstallConfig): Promise<void> {
const sources = await readOpenCodeSources();
validateSources(sources);
const prepared = await prepareOpenCodeInstallation(config, sources);
await commitOpenCodeInstallation(prepared);
console.log(` OpenCode global plugin: ${sources.paths.pluginPath}`);
console.log(' OpenCode activation: restart active OpenCode sessions.');
},
async uninstall(agentId?: string): Promise<void> {
const sources = await readOpenCodeSources();
const prepared = await prepareOpenCodeUninstall(sources, agentId);
await commitOpenCodeUninstall(prepared);
},
async status(): Promise<PluginStatus> {
const configPath = resolveConfigPath();
let hookConfigured = false;
let hookScriptPath = '';
let guardScriptPath = '';
try {
const raw = await fsp.readFile(configPath, 'utf-8');
const hookMatch = raw.match(/const HOOK_SCRIPT_PATH = ("(?:\\.|[^"\\])*");/);
const guardMatch = raw.match(/const GUARD_SCRIPT_PATH = ("(?:\\.|[^"\\])*");/);
if (hookMatch && guardMatch) {
hookScriptPath = JSON.parse(hookMatch[1]);
guardScriptPath = JSON.parse(guardMatch[1]);
hookConfigured = true;
}
} catch {
// File doesn't exist
}
let hookScriptExists = false;
if (hookScriptPath && guardScriptPath) {
try {
await fsp.access(hookScriptPath);
await fsp.access(guardScriptPath);
hookScriptExists = true;
} catch {
// File doesn't exist
}
}
const sources = await readOpenCodeSources();
validateSources(sources);
const contract = sources.legacy.contract ? undefined : sources.current.contract;
const hookConfigured = contract !== undefined;
const hookScriptExists = contract !== undefined
&& await openCodeRuntimeFilesExist(contract);
return {
installed: hookConfigured && hookScriptExists,
agentName: AGENT_KEY,
displayName: entry.name,
hookConfigured,
hookScriptExists,
configPath,
configPath: sources.paths.pluginPath,
};
},
};