Initial commit: Elydora Node SDK
TypeScript SDK for Elydora agent operations, cryptographic signing, and audit trail integration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
65910a3ad6
23 files changed
+2663
No files matched your search
@@ -0,0 +1,122 @@
|
||||
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 { SUPPORTED_AGENTS } from './registry.js';
|
||||
|
||||
const AGENT_KEY = 'opencode';
|
||||
const entry = SUPPORTED_AGENTS.get(AGENT_KEY)!;
|
||||
|
||||
function resolveConfigDir(): string {
|
||||
// .opencode/plugins is project-relative, but for global install use home dir
|
||||
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
|
||||
|
||||
const { spawn, spawnSync } = require('node:child_process');
|
||||
|
||||
module.exports = {
|
||||
name: 'elydora-audit',
|
||||
version: '1.0.0',
|
||||
description: 'Sends tool-use events to the Elydora tamper-evident audit platform',
|
||||
|
||||
hooks: {
|
||||
preToolUse(context) {
|
||||
// Synchronous guard — blocks tool if agent is frozen
|
||||
try {
|
||||
const result = spawnSync('node', ['__ELYDORA_GUARD_SCRIPT_PATH__'], {
|
||||
timeout: 5000,
|
||||
stdio: ['pipe', 'ignore', 'pipe'],
|
||||
});
|
||||
if (result.status !== 0) {
|
||||
const msg = result.stderr ? result.stderr.toString().trim() : 'Agent is frozen by Elydora.';
|
||||
return { blocked: true, reason: msg };
|
||||
}
|
||||
} catch {
|
||||
// Fail-open — allow if guard can't run
|
||||
}
|
||||
},
|
||||
|
||||
postToolUse(context) {
|
||||
try {
|
||||
const input = JSON.stringify({
|
||||
tool_name: context.toolName || context.tool_name || 'unknown',
|
||||
tool_input: context.toolInput || context.tool_input || {},
|
||||
session_id: context.sessionId || context.session_id || 'unknown',
|
||||
});
|
||||
|
||||
const child = spawn('node', ['__ELYDORA_HOOK_SCRIPT_PATH__'], {
|
||||
detached: true,
|
||||
stdio: ['pipe', 'ignore', 'ignore'],
|
||||
});
|
||||
child.stdin.write(input);
|
||||
child.stdin.end();
|
||||
child.unref();
|
||||
} catch {
|
||||
// Fire-and-forget — never block the developer
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
`;
|
||||
}
|
||||
|
||||
export const opencodePlugin: AgentPlugin = {
|
||||
async install(config: InstallConfig): Promise<void> {
|
||||
const configDir = resolveConfigDir();
|
||||
await fsp.mkdir(configDir, { recursive: true });
|
||||
|
||||
const configPath = resolveConfigPath();
|
||||
const script = generatePluginScript(config.hookScriptPath, config.guardScriptPath)
|
||||
.replace('__ELYDORA_HOOK_SCRIPT_PATH__', config.hookScriptPath.replace(/\\/g, '\\\\'))
|
||||
.replace('__ELYDORA_GUARD_SCRIPT_PATH__', config.guardScriptPath.replace(/\\/g, '\\\\'));
|
||||
|
||||
await fsp.writeFile(configPath, script, 'utf-8');
|
||||
},
|
||||
|
||||
async uninstall(): Promise<void> {
|
||||
const configPath = resolveConfigPath();
|
||||
try {
|
||||
await fsp.unlink(configPath);
|
||||
} catch {
|
||||
// Already removed
|
||||
}
|
||||
},
|
||||
|
||||
async status(): Promise<PluginStatus> {
|
||||
const configPath = resolveConfigPath();
|
||||
const hookScriptPath = path.join(os.homedir(), '.elydora', 'hooks', `${AGENT_KEY}-hook.js`);
|
||||
|
||||
let hookConfigured = false;
|
||||
try {
|
||||
await fsp.access(configPath);
|
||||
hookConfigured = true;
|
||||
} catch {
|
||||
// File doesn't exist
|
||||
}
|
||||
|
||||
let hookScriptExists = false;
|
||||
try {
|
||||
await fsp.access(hookScriptPath);
|
||||
hookScriptExists = true;
|
||||
} catch {
|
||||
// File doesn't exist
|
||||
}
|
||||
|
||||
return {
|
||||
installed: hookConfigured && hookScriptExists,
|
||||
agentName: AGENT_KEY,
|
||||
displayName: entry.name,
|
||||
hookConfigured,
|
||||
hookScriptExists,
|
||||
configPath,
|
||||
};
|
||||
},
|
||||
};
|
||||
Reference in new issue
Block a user