fix(opencode): install discoverable atomic plugin
This commit is contained in:
1 parent
3f43f4aad5
commit
69a75153f5
15 files changed
+1554
-348
No files matched your search
@@ -0,0 +1,98 @@
|
||||
import fsp from 'node:fs/promises';
|
||||
import {
|
||||
AGENT_KEY,
|
||||
OPENCODE_AUDIT_OPTIONS,
|
||||
parseLegacyOpenCodePlugin,
|
||||
parseOpenCodePlugin,
|
||||
resolveOpenCodePaths,
|
||||
type OpenCodeLegacyContract,
|
||||
type OpenCodePaths,
|
||||
type OpenCodeRuntimeContract,
|
||||
} from './opencode-contract.js';
|
||||
import { inspectPhysicalDirectory, readPhysicalFile, type FileSnapshot } from './managed-files.js';
|
||||
import { managedRuntimeFilesExist } from './managed-runtime-status.js';
|
||||
|
||||
const MAX_PLUGIN_BYTES = 512 * 1024;
|
||||
|
||||
export interface OpenCodePluginFile<TContract> {
|
||||
readonly exists: boolean;
|
||||
readonly filePath: string;
|
||||
readonly source?: string;
|
||||
readonly snapshot?: FileSnapshot;
|
||||
readonly contract?: TContract;
|
||||
}
|
||||
|
||||
export interface OpenCodeSources {
|
||||
readonly paths: OpenCodePaths;
|
||||
readonly current: OpenCodePluginFile<OpenCodeRuntimeContract>;
|
||||
readonly legacy: OpenCodePluginFile<OpenCodeLegacyContract>;
|
||||
}
|
||||
|
||||
async function inspectOpenCodeDirectories(paths: OpenCodePaths): Promise<void> {
|
||||
await inspectPhysicalDirectory(paths.homeDirectory, 'OpenCode home directory');
|
||||
await inspectPhysicalDirectory(paths.configRoot, 'OpenCode XDG config directory');
|
||||
await inspectPhysicalDirectory(paths.configDirectory, 'OpenCode config directory');
|
||||
await inspectPhysicalDirectory(paths.pluginsDirectory, 'OpenCode plugins directory');
|
||||
}
|
||||
|
||||
async function readPluginFile<TContract>(
|
||||
filePath: string,
|
||||
label: string,
|
||||
parse: (filePath: string, source: string) => TContract | undefined,
|
||||
): Promise<OpenCodePluginFile<TContract>> {
|
||||
const snapshot = await readPhysicalFile(filePath, label, MAX_PLUGIN_BYTES);
|
||||
if (!snapshot) return { exists: false, filePath };
|
||||
return {
|
||||
exists: true,
|
||||
filePath,
|
||||
source: snapshot.contents,
|
||||
snapshot,
|
||||
contract: parse(filePath, snapshot.contents),
|
||||
};
|
||||
}
|
||||
|
||||
export async function readOpenCodeSources(): Promise<OpenCodeSources> {
|
||||
const paths = resolveOpenCodePaths();
|
||||
await inspectOpenCodeDirectories(paths);
|
||||
const [current, legacy] = await Promise.all([
|
||||
readPluginFile(paths.pluginPath, 'OpenCode plugin', parseOpenCodePlugin),
|
||||
readPluginFile(
|
||||
paths.legacyPluginPath,
|
||||
'legacy OpenCode plugin',
|
||||
(_filePath, source) => parseLegacyOpenCodePlugin(source),
|
||||
),
|
||||
]);
|
||||
return { paths, current, legacy };
|
||||
}
|
||||
|
||||
export function requireAvailableOpenCodePlugin(
|
||||
file: OpenCodePluginFile<OpenCodeRuntimeContract>,
|
||||
): void {
|
||||
if (file.exists && !file.contract) {
|
||||
throw new Error(`OpenCode plugin path is owned by another integration: ${file.filePath}`);
|
||||
}
|
||||
}
|
||||
|
||||
function hasCode(error: unknown, code: string): boolean {
|
||||
return typeof error === 'object' && error !== null && 'code' in error && error.code === code;
|
||||
}
|
||||
|
||||
async function executableExists(filePath: string): Promise<boolean> {
|
||||
try {
|
||||
return (await fsp.stat(filePath)).isFile();
|
||||
} catch (error) {
|
||||
if (hasCode(error, 'ENOENT') || hasCode(error, 'ENOTDIR')) return false;
|
||||
throw new Error(`Inspect OpenCode Node.js runtime at ${filePath}: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`, { cause: error instanceof Error ? error : new Error(String(error)) });
|
||||
}
|
||||
}
|
||||
|
||||
export async function openCodeRuntimeFilesExist(
|
||||
contract: OpenCodeRuntimeContract,
|
||||
): Promise<boolean> {
|
||||
return await executableExists(contract.executablePath)
|
||||
&& await managedRuntimeFilesExist(contract, AGENT_KEY, {
|
||||
auditOptions: OPENCODE_AUDIT_OPTIONS,
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user