chore(node-sdk): audit providers kirocli kiroide opencode

Kiro and OpenCode files use common.ts, shell-command.ts, and managedScriptReference; exactKeys shared from kiroide-contract.

Claude-Session: https://claude.ai/code/session_01Agoc9bRVE9KL7mmWtey9Vq
This commit is contained in:
雷电芽衣 committed 2026-09-02 22:10:57 -04:00
1 parent 3b5cbd6e17
commit 25bfa39fcf
9 files changed
+57 -228

No files matched your search

+16 -28
View File
@@ -1,14 +1,14 @@
import os from 'node:os';
import path from 'node:path';
import { managedScriptReference, sameAgentId, samePath } from './common.js';
import {
buildKiroIdeCommand,
kiroIdeRuntimeReference,
sameKiroIdeAgentId,
sameKiroIdePath,
type KiroIdeRuntimeReference,
} from './kiroide-command.js';
import {
buildKiroIdeHook,
exactKeys,
kiroIdeRuntimeContracts,
renderKiroIdeDocument,
type KiroIdeDocument,
@@ -16,6 +16,7 @@ import {
type RenderedKiroIdeDocument,
type KiroIdeRuntimeContract,
} from './kiroide-contract.js';
import { isNodeExecutable, parseLegacyWindowsCommand } from './shell-command.js';
import { isObject, parseStrictJsonObject, type JsonObject } from './strict-json.js';
export const AGENT_KEY = 'kirocli';
@@ -162,27 +163,14 @@ function eventHooks(document: KiroCliV2Document, event: string): readonly KiroCl
return (document.hooks[event] as readonly KiroCliV2Hook[] | undefined) ?? [];
}
function exactKeys(value: JsonObject, keys: readonly string[]): boolean {
return Object.keys(value).sort().join('|') === [...keys].sort().join('|');
}
function legacyWindowsReference(
command: unknown,
scriptName: string,
): KiroIdeRuntimeReference | undefined {
if (typeof command !== 'string') return undefined;
const match = /^"([^"\r\n]+)" "([^"\r\n]+)"$/.exec(command);
if (!match
|| !path.isAbsolute(match[1])
|| !['node', 'node.exe'].includes(path.basename(match[1]).toLowerCase())
|| !path.isAbsolute(match[2])
|| path.basename(match[2]) !== scriptName) return undefined;
const agentDirectory = path.dirname(match[2]);
if (!sameKiroIdePath(path.dirname(agentDirectory), path.join(os.homedir(), '.elydora'))) {
return undefined;
}
const agentId = path.basename(agentDirectory);
return agentId ? { agentId, scriptPath: match[2] } : undefined;
const parsed = parseLegacyWindowsCommand(command);
if (!parsed || !path.isAbsolute(parsed[0]) || !isNodeExecutable(parsed[0])) return undefined;
return managedScriptReference(parsed[1], scriptName);
}
function ownedReference(command: unknown, scriptName: string): KiroIdeRuntimeReference | undefined {
@@ -219,7 +207,7 @@ function hasOwnedV2Hooks(document: KiroCliV2Document, agentId?: string): boolean
return ['preToolUse', 'postToolUse'].some((event) => eventHooks(document, event).some((hook) => {
const reference = v2OwnedReference(hook, event);
return reference !== undefined
&& (agentId === undefined || sameKiroIdeAgentId(reference.agentId, agentId));
&& (agentId === undefined || sameAgentId(reference.agentId, agentId));
}));
}
@@ -237,7 +225,7 @@ export function withoutManagedKiroCliV2Hooks(
for (const event of ['preToolUse', 'postToolUse']) {
const remaining = eventHooks(document, event).filter((hook) => {
const reference = v2OwnedReference(hook, event);
return !reference || (agentId !== undefined && !sameKiroIdeAgentId(reference.agentId, agentId));
return !reference || (agentId !== undefined && !sameAgentId(reference.agentId, agentId));
});
if (remaining.length > 0) next[event] = remaining;
else delete next[event];
@@ -288,7 +276,7 @@ function entirelyManagedV2Document(document: KiroCliV2Document, agentId?: string
const hooks = eventHooks(document, event);
if (hooks.length !== 1) return false;
const reference = v2OwnedReference(hooks[0], event);
if (!reference || (agentId !== undefined && !sameKiroIdeAgentId(reference.agentId, agentId))) {
if (!reference || (agentId !== undefined && !sameAgentId(reference.agentId, agentId))) {
return false;
}
}
@@ -324,7 +312,7 @@ export function kiroCliV2RuntimeContracts(
.map((hook) => v2ManagedReference(hook, 'postToolUse'))
.filter((value): value is KiroIdeRuntimeReference => value !== undefined);
if (guards.length !== 1 || audits.length !== 1
|| !sameKiroIdeAgentId(guards[0].agentId, audits[0].agentId)) return [];
|| !sameAgentId(guards[0].agentId, audits[0].agentId)) return [];
return [{ agentId: guards[0].agentId, guardPath: guards[0].scriptPath, auditPath: audits[0].scriptPath }];
}
@@ -355,7 +343,7 @@ export function withoutManagedKiroCliV3Hooks(
): KiroIdeHook[] {
return hooks.filter((hook) => {
const reference = v3OwnedReference(hook);
return !reference || (agentId !== undefined && !sameKiroIdeAgentId(reference.agentId, agentId));
return !reference || (agentId !== undefined && !sameAgentId(reference.agentId, agentId));
});
}
@@ -363,7 +351,7 @@ function hasOwnedV3Hooks(hooks: readonly KiroIdeHook[], agentId?: string): boole
return hooks.some((hook) => {
const reference = v3OwnedReference(hook);
return reference !== undefined
&& (agentId === undefined || sameKiroIdeAgentId(reference.agentId, agentId));
&& (agentId === undefined || sameAgentId(reference.agentId, agentId));
});
}
@@ -374,7 +362,7 @@ function entirelyManagedV3Document(document: KiroIdeDocument, agentId?: string):
&& document.hooks.every((hook) => {
const reference = v3OwnedReference(hook);
return reference !== undefined
&& (agentId === undefined || sameKiroIdeAgentId(reference.agentId, agentId));
&& (agentId === undefined || sameAgentId(reference.agentId, agentId));
});
}
@@ -409,8 +397,8 @@ export function commonKiroCliRuntimeContract(
const v2 = kiroCliV2RuntimeContracts(v2Document);
const v3 = kiroIdeRuntimeContracts(v3Hooks);
if (v2.length !== 1 || v3.length !== 1
|| !sameKiroIdeAgentId(v2[0].agentId, v3[0].agentId)
|| !sameKiroIdePath(v2[0].guardPath, v3[0].guardPath)
|| !sameKiroIdePath(v2[0].auditPath, v3[0].auditPath)) return undefined;
|| !sameAgentId(v2[0].agentId, v3[0].agentId)
|| !samePath(v2[0].guardPath, v3[0].guardPath)
|| !samePath(v2[0].auditPath, v3[0].auditPath)) return undefined;
return v2[0];
}
+3 -7
View File
@@ -27,10 +27,6 @@ const DISPLAY_NAME = 'Kiro CLI';
const V2_DIRECTORY_LABEL = 'Kiro CLI agents directory';
const V3_DIRECTORY_LABEL = 'Kiro CLI global hooks directory';
export type KiroCliRuntimePaths = ManagedRuntimePaths;
export type PreparedKiroCliInstallation = PreparedManagedInstallation;
export type { RenameFile };
function hookLocations(sources: KiroCliSources) {
return [
{ directoryLabel: V2_DIRECTORY_LABEL, filePath: sources.paths.v2Path },
@@ -41,7 +37,7 @@ function hookLocations(sources: KiroCliSources) {
export async function preflightKiroCliInstallation(
config: InstallConfig,
sources: KiroCliSources,
): Promise<KiroCliRuntimePaths> {
): Promise<ManagedRuntimePaths> {
return preflightManagedInstallation({
agentKey: AGENT_KEY,
hookLocations: hookLocations(sources),
@@ -54,7 +50,7 @@ export async function prepareKiroCliInstallation(
sources: KiroCliSources,
v2: RenderedKiroCliV2Document,
v3: RenderedKiroIdeDocument,
): Promise<PreparedKiroCliInstallation> {
): Promise<PreparedManagedInstallation> {
if (v2.next === undefined || v3.next === undefined) {
throw new Error('Kiro CLI installation requires both v2 and v3 hook documents');
}
@@ -83,7 +79,7 @@ export async function prepareKiroCliInstallation(
}
export async function commitKiroCliInstallation(
prepared: PreparedKiroCliInstallation,
prepared: PreparedManagedInstallation,
renameFile?: RenameFile,
): Promise<void> {
await commitManagedInstallation(prepared, renameFile);
+3 -4
View File
@@ -12,11 +12,10 @@ import {
type KiroIdeDocument,
type KiroIdeRuntimeContract,
} from './kiroide-contract.js';
import { MAX_CONFIG_BYTES } from './common.js';
import { inspectPhysicalDirectory, readPhysicalFile } from './managed-files.js';
import { managedRuntimeFilesExist } from './managed-runtime-status.js';
const MAX_HOOK_BYTES = 512 * 1024;
export interface KiroCliSources {
readonly paths: KiroCliPaths;
readonly v2: KiroCliV2Document;
@@ -36,7 +35,7 @@ async function readV2Document(paths: KiroCliPaths): Promise<KiroCliV2Document> {
const snapshot = await readPhysicalFile(
paths.v2Path,
'Kiro CLI v2 agent config',
MAX_HOOK_BYTES,
MAX_CONFIG_BYTES,
);
return snapshot
? parseKiroCliV2Document(paths.v2Path, snapshot.contents)
@@ -47,7 +46,7 @@ async function readV3Document(paths: KiroCliPaths): Promise<KiroIdeDocument> {
const snapshot = await readPhysicalFile(
paths.v3Path,
'Kiro CLI v3 global hooks',
MAX_HOOK_BYTES,
MAX_CONFIG_BYTES,
);
return snapshot
? parseKiroIdeDocument(paths.v3Path, snapshot.contents, 'Kiro CLI v3 global hooks')
+13 -136
View File
@@ -1,132 +1,20 @@
import os from 'node:os';
import path from 'node:path';
import { managedScriptReference, type ManagedScriptReference } from './common.js';
import {
encodedWindowsCommand,
isNodeExecutable,
parseEncodedWindowsCommand,
parsePosixCommand,
posixSource,
} from './shell-command.js';
interface ParsedArgument {
readonly value: string;
readonly next: number;
}
export interface KiroIdeRuntimeReference {
readonly agentId: string;
readonly scriptPath: string;
}
export function sameKiroIdePath(left: string, right: string): boolean {
const normalizedLeft = path.resolve(left);
const normalizedRight = path.resolve(right);
return process.platform === 'win32'
? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase()
: normalizedLeft === normalizedRight;
}
export function sameKiroIdeAgentId(left: string, right: string): boolean {
return process.platform === 'win32' ? left.toLowerCase() === right.toLowerCase() : left === right;
}
function quotePosix(value: string): string {
return `'${value.replaceAll("'", `'"'"'`)}'`;
}
function quotePowerShell(value: string): string {
return `'${value.replaceAll("'", "''")}'`;
}
function windowsPowerShellPath(): string {
const configuredRoot = process.env.SystemRoot;
const systemRoot = configuredRoot
&& path.win32.isAbsolute(configuredRoot)
&& !/["%\r\n]/.test(configuredRoot)
? configuredRoot
: 'C:\\Windows';
return path.win32.join(
systemRoot,
'System32',
'WindowsPowerShell',
'v1.0',
'powershell.exe',
);
}
function windowsCommand(scriptPath: string): string {
const source = `& ${quotePowerShell(process.execPath)} ${quotePowerShell(scriptPath)}; exit $LASTEXITCODE`;
const encoded = Buffer.from(source, 'utf16le').toString('base64');
return `"${windowsPowerShellPath()}" -NoLogo -NoProfile -NonInteractive -EncodedCommand ${encoded}`;
}
export type KiroIdeRuntimeReference = ManagedScriptReference;
export function buildKiroIdeCommand(scriptPath: string): string {
if (!path.isAbsolute(process.execPath) || !path.isAbsolute(scriptPath)) {
throw new Error('Kiro IDE hook commands require absolute executable and script paths');
}
return process.platform === 'win32'
? windowsCommand(scriptPath)
: `${quotePosix(process.execPath)} ${quotePosix(scriptPath)}`;
}
function readPosixArgument(command: string, start: number): ParsedArgument | undefined {
if (command[start] !== "'") return undefined;
const apostrophe = `'"'"'`;
let value = '';
for (let index = start + 1; index < command.length;) {
if (command.startsWith(apostrophe, index)) {
value += "'";
index += apostrophe.length;
continue;
}
if (command[index] === "'") return { value, next: index + 1 };
value += command[index];
index += 1;
}
return undefined;
}
function parsePosixCommand(command: string): readonly [string, string] | undefined {
const executable = readPosixArgument(command, 0);
if (!executable || command[executable.next] !== ' ') return undefined;
const script = readPosixArgument(command, executable.next + 1);
return script && script.next === command.length ? [executable.value, script.value] : undefined;
}
function readPowerShellArgument(command: string, start: number): ParsedArgument | undefined {
if (command[start] !== "'") return undefined;
let value = '';
for (let index = start + 1; index < command.length; index += 1) {
if (command[index] !== "'") {
value += command[index];
continue;
}
if (command[index + 1] === "'") {
value += "'";
index += 1;
continue;
}
return { value, next: index + 1 };
}
return undefined;
}
function parsePowerShellSource(source: string): readonly [string, string] | undefined {
if (!source.startsWith('& ')) return undefined;
const executable = readPowerShellArgument(source, 2);
if (!executable || source[executable.next] !== ' ') return undefined;
const script = readPowerShellArgument(source, executable.next + 1);
return script && source.slice(script.next) === '; exit $LASTEXITCODE'
? [executable.value, script.value]
: undefined;
}
function parseWindowsCommand(command: string): readonly [string, string] | undefined {
const match = /^"([^"\r\n]+)" -NoLogo -NoProfile -NonInteractive -EncodedCommand ([A-Za-z0-9+/]+={0,2})$/.exec(command);
if (!match
|| !path.win32.isAbsolute(match[1])
|| path.win32.basename(match[1]).toLowerCase() !== 'powershell.exe') return undefined;
const buffer = Buffer.from(match[2], 'base64');
if (buffer.length % 2 !== 0 || buffer.toString('base64') !== match[2]) return undefined;
return parsePowerShellSource(buffer.toString('utf16le'));
}
function isNodeExecutable(filePath: string): boolean {
const basename = path.basename(filePath).toLowerCase();
return basename === 'node' || basename === 'node.exe';
return process.platform === 'win32' ? encodedWindowsCommand(scriptPath) : posixSource(scriptPath);
}
export function kiroIdeRuntimeReference(
@@ -134,19 +22,8 @@ export function kiroIdeRuntimeReference(
scriptName: string,
): KiroIdeRuntimeReference | undefined {
const parsed = process.platform === 'win32'
? parseWindowsCommand(command)
? parseEncodedWindowsCommand(command)
: parsePosixCommand(command);
if (!parsed
|| !path.isAbsolute(parsed[0])
|| !isNodeExecutable(parsed[0])
|| !path.isAbsolute(parsed[1])
|| path.basename(parsed[1]) !== scriptName) return undefined;
const agentDirectory = path.dirname(parsed[1]);
if (!sameKiroIdePath(path.dirname(agentDirectory), path.join(os.homedir(), '.elydora'))) {
return undefined;
}
const agentId = path.basename(agentDirectory);
return agentId && agentId !== '.' && agentId !== '..'
? { agentId, scriptPath: parsed[1] }
: undefined;
if (!parsed || !path.isAbsolute(parsed[0]) || !isNodeExecutable(parsed[0])) return undefined;
return managedScriptReference(parsed[1], scriptName);
}
+5 -11
View File
@@ -1,10 +1,9 @@
import os from 'node:os';
import path from 'node:path';
import { managedScriptReference, sameAgentId } from './common.js';
import {
buildKiroIdeCommand,
kiroIdeRuntimeReference,
sameKiroIdeAgentId,
sameKiroIdePath,
type KiroIdeRuntimeReference,
} from './kiroide-command.js';
import { isObject, parseStrictJsonObject, type JsonObject } from './strict-json.js';
@@ -154,7 +153,7 @@ export function createKiroIdeDocument(filePath: string): KiroIdeDocument {
return { exists: false, filePath, root: {}, hooks: [] };
}
function exactKeys(value: JsonObject, keys: readonly string[]): boolean {
export function exactKeys(value: JsonObject, keys: readonly string[]): boolean {
return Object.keys(value).sort().join('|') === [...keys].sort().join('|');
}
@@ -228,7 +227,7 @@ export function withoutManagedKiroIdeHooks(
): KiroIdeHook[] {
return hooks.filter((hook) => {
const reference = ownedReference(hook);
return !reference || (agentId !== undefined && !sameKiroIdeAgentId(reference.agentId, agentId));
return !reference || (agentId !== undefined && !sameAgentId(reference.agentId, agentId));
});
}
@@ -285,12 +284,7 @@ export function kiroIdeRuntimeContracts(
function legacyReference(command: unknown, scriptName: string) {
if (typeof command !== 'string') return undefined;
const match = /^node "([^"\r\n]+)"$/.exec(command);
if (!match || !path.isAbsolute(match[1]) || path.basename(match[1]) !== scriptName) return undefined;
const agentDirectory = path.dirname(match[1]);
if (!sameKiroIdePath(path.dirname(agentDirectory), path.join(os.homedir(), '.elydora'))) {
return undefined;
}
return { agentId: path.basename(agentDirectory), scriptPath: match[1] };
return match ? managedScriptReference(match[1], scriptName) : undefined;
}
export function legacyKiroIdeRuntimeContract(raw: string, filePath: string): KiroIdeRuntimeContract | undefined {
@@ -311,6 +305,6 @@ export function legacyKiroIdeRuntimeContract(raw: string, filePath: string): Kir
|| auditHook.timeout_ms !== 5000) return undefined;
const guard = legacyReference(guardHook.command, GUARD_SCRIPT);
const audit = legacyReference(auditHook.command, AUDIT_SCRIPT);
if (!guard || !audit || !sameKiroIdeAgentId(guard.agentId, audit.agentId)) return undefined;
if (!guard || !audit || !sameAgentId(guard.agentId, audit.agentId)) return undefined;
return { agentId: guard.agentId, guardPath: guard.scriptPath, auditPath: audit.scriptPath };
}
+5 -9
View File
@@ -1,6 +1,6 @@
import path from 'node:path';
import type { InstallConfig } from './base.js';
import { sameKiroIdeAgentId } from './kiroide-command.js';
import { sameAgentId } from './common.js';
import {
AGENT_KEY,
AUDIT_SCRIPT,
@@ -31,17 +31,13 @@ import {
const DISPLAY_NAME = 'Kiro IDE';
const DIRECTORY_LABEL = 'Kiro IDE hooks directory';
export type KiroIdeRuntimePaths = ManagedRuntimePaths;
export type PreparedKiroIdeInstallation = PreparedManagedInstallation;
export type { RenameFile };
function hookLocation(paths: KiroIdePaths) {
return [{ directoryLabel: DIRECTORY_LABEL, filePath: paths.configPath }];
}
function removesLegacy(legacy: LegacyKiroIdeDocument, agentId?: string): boolean {
return legacy.contract !== undefined
&& (agentId === undefined || sameKiroIdeAgentId(legacy.contract.agentId, agentId));
&& (agentId === undefined || sameAgentId(legacy.contract.agentId, agentId));
}
async function prepareLegacyRemoval(
@@ -64,7 +60,7 @@ async function prepareLegacyRemoval(
export async function preflightKiroIdeInstallation(
config: InstallConfig,
paths: KiroIdePaths,
): Promise<KiroIdeRuntimePaths> {
): Promise<ManagedRuntimePaths> {
return preflightManagedInstallation({
agentKey: AGENT_KEY,
hookLocations: hookLocation(paths),
@@ -76,7 +72,7 @@ export async function prepareKiroIdeInstallation(
config: InstallConfig,
sources: KiroIdeSources,
rendered: RenderedKiroIdeDocument,
): Promise<PreparedKiroIdeInstallation> {
): Promise<PreparedManagedInstallation> {
if (rendered.next === undefined) throw new Error('Kiro IDE installation requires a hook document');
const prepared = await prepareManagedInstallation({
agentKey: AGENT_KEY,
@@ -107,7 +103,7 @@ export async function prepareKiroIdeInstallation(
}
export async function commitKiroIdeInstallation(
prepared: PreparedKiroIdeInstallation,
prepared: PreparedManagedInstallation,
renameFile?: RenameFile,
): Promise<void> {
await commitManagedInstallation(prepared, renameFile);
+2 -14
View File
@@ -1,7 +1,8 @@
import os from 'node:os';
import path from 'node:path';
import type { HookScriptOptions } from './hook-template.js';
import { samePath } from './common.js';
import { errorMessage, samePath } from './common.js';
import { isNodeExecutable } from './shell-command.js';
import { parseStrictJsonObject } from './strict-json.js';
export const AGENT_KEY = 'opencode';
@@ -51,10 +52,6 @@ interface OpenCodePluginMetadata {
readonly auditPath: string;
}
export function sameOpenCodeAgentId(left: string, right: string): boolean {
return process.platform === 'win32' ? left.toLowerCase() === right.toLowerCase() : left === right;
}
export function resolveOpenCodePaths(
homeDirectory = os.homedir(),
xdgConfigHome = process.env.XDG_CONFIG_HOME,
@@ -83,11 +80,6 @@ function validAgentSegment(agentId: string): boolean {
&& path.basename(agentId) === agentId;
}
function isNodeExecutable(filePath: string): boolean {
const basename = path.basename(filePath).toLowerCase();
return basename === 'node' || basename === 'node.exe';
}
function validateRuntimePaths(metadata: OpenCodePluginMetadata): void {
if (!validAgentSegment(metadata.agentId)) {
throw new Error('OpenCode plugin metadata contains an invalid agentId');
@@ -287,10 +279,6 @@ export const ElydoraAuditPlugin = async () => ({
`);
}
function errorMessage(error: unknown): string {
return error instanceof Error ? error.message : String(error);
}
export function parseOpenCodePlugin(
filePath: string,
source: string,
+5 -9
View File
@@ -7,8 +7,8 @@ import {
OPENCODE_AUDIT_OPTIONS,
buildOpenCodeMetadata,
buildOpenCodePlugin,
sameOpenCodeAgentId,
} from './opencode-contract.js';
import { sameAgentId } from './common.js';
import type { OpenCodePluginFile, OpenCodeSources } from './opencode-io.js';
import {
commitManagedInstallation,
@@ -29,14 +29,10 @@ import {
const DISPLAY_NAME = 'OpenCode';
const DIRECTORY_LABEL = 'OpenCode plugins directory';
export type OpenCodeRuntimePaths = ManagedRuntimePaths;
export type PreparedOpenCodeInstallation = PreparedManagedInstallation;
export type { RenameFile };
export async function preflightOpenCodeInstallation(
config: InstallConfig,
sources: OpenCodeSources,
): Promise<OpenCodeRuntimePaths> {
): Promise<ManagedRuntimePaths> {
return preflightManagedInstallation({
agentKey: AGENT_KEY,
hookLocations: [{
@@ -50,7 +46,7 @@ export async function preflightOpenCodeInstallation(
export async function prepareOpenCodeInstallation(
config: InstallConfig,
sources: OpenCodeSources,
): Promise<PreparedOpenCodeInstallation> {
): Promise<PreparedManagedInstallation> {
const paths = await preflightOpenCodeInstallation(config, sources);
const metadata = buildOpenCodeMetadata(
config.agentId,
@@ -85,7 +81,7 @@ export async function prepareOpenCodeInstallation(
}
export async function commitOpenCodeInstallation(
prepared: PreparedOpenCodeInstallation,
prepared: PreparedManagedInstallation,
renameFile?: RenameFile,
): Promise<void> {
await commitManagedInstallation(prepared, renameFile);
@@ -96,7 +92,7 @@ function ownedByAgent<TContract extends { readonly agentId: string }>(
agentId?: string,
): boolean {
return file.contract !== undefined
&& (agentId === undefined || sameOpenCodeAgentId(file.contract.agentId, agentId));
&& (agentId === undefined || sameAgentId(file.contract.agentId, agentId));
}
export async function prepareOpenCodeUninstall(
+5 -10
View File
@@ -9,11 +9,10 @@ import {
type OpenCodePaths,
type OpenCodeRuntimeContract,
} from './opencode-contract.js';
import { errorMessage, hasCode, MAX_CONFIG_BYTES } from './common.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;
@@ -40,7 +39,7 @@ async function readPluginFile<TContract>(
label: string,
parse: (filePath: string, source: string) => TContract | undefined,
): Promise<OpenCodePluginFile<TContract>> {
const snapshot = await readPhysicalFile(filePath, label, MAX_PLUGIN_BYTES);
const snapshot = await readPhysicalFile(filePath, label, MAX_CONFIG_BYTES);
if (!snapshot) return { exists: false, filePath };
return {
exists: true,
@@ -73,18 +72,14 @@ export function requireAvailableOpenCodePlugin(
}
}
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)) });
throw new Error(`Inspect OpenCode Node.js runtime at ${filePath}: ${errorMessage(error)}`, {
cause: error instanceof Error ? error : new Error(String(error)),
});
}
}