// engine/plugins/sandbox.ts — Plugin isolation and context (SPEC-085 AC-08)
import { readFile, writeFile, mkdir, readdir } from 'node:fs/promises';
import { join } from 'node:path';
const DEFAULT_TIMEOUT_MS = 30_000;
/**
 * Creates a restricted storage API for a plugin.
 * Plugins can only access their own data directory.
 */
export function createPluginStorage(pluginId, dataDir) {
    const pluginDataDir = join(dataDir, 'plugins', pluginId);
    return {
        read: async (key) => {
            const filePath = join(pluginDataDir, `${key}.json`);
            try {
                const raw = await readFile(filePath, 'utf8');
                return JSON.parse(raw);
            }
            catch {
                return undefined;
            }
        },
        write: async (key, value) => {
            await mkdir(pluginDataDir, { recursive: true });
            const filePath = join(pluginDataDir, `${key}.json`);
            await writeFile(filePath, JSON.stringify(value, null, 2), 'utf8');
        },
        list: async () => {
            try {
                const files = await readdir(pluginDataDir);
                return files.filter((f) => f.endsWith('.json')).map((f) => f.replace(/\.json$/, ''));
            }
            catch {
                return [];
            }
        },
    };
}
/**
 * Creates a scoped logger for a plugin.
 */
export function createPluginLogger(pluginId) {
    const prefix = `[plugin:${pluginId}]`;
    return {
        info: (message) => {
            console.warn(`${prefix} [info] ${message}`);
        },
        warn: (message) => {
            console.warn(`${prefix} ${message}`);
        },
        error: (message) => {
            console.error(`${prefix} ${message}`);
        },
    };
}
/**
 * Creates a restricted PluginContext for a plugin.
 */
export function createPluginContext(pluginId, permissions, dataDir) {
    const hasStorageRead = permissions.includes('storage:read');
    const hasStorageWrite = permissions.includes('storage:write');
    const storage = createPluginStorage(pluginId, dataDir);
    // Wrap storage to enforce permissions
    const restrictedStorage = {
        read: async (key) => {
            if (!hasStorageRead && !hasStorageWrite) {
                throw new Error(`Plugin "${pluginId}" does not have storage:read permission`);
            }
            return storage.read(key);
        },
        write: async (key, value) => {
            if (!hasStorageWrite) {
                throw new Error(`Plugin "${pluginId}" does not have storage:write permission`);
            }
            return storage.write(key, value);
        },
        list: async () => {
            if (!hasStorageRead && !hasStorageWrite) {
                throw new Error(`Plugin "${pluginId}" does not have storage:read permission`);
            }
            return storage.list();
        },
    };
    return {
        pluginId,
        permissions,
        storage: restrictedStorage,
        logger: createPluginLogger(pluginId),
    };
}
/**
 * Wraps a plugin handler with timeout and error isolation.
 * Returns the result or throws with a descriptive error.
 */
export async function withPluginTimeout(pluginId, operation, fn, timeoutMs = DEFAULT_TIMEOUT_MS) {
    return new Promise((resolve, reject) => {
        const timer = setTimeout(() => {
            reject(new Error(`Plugin "${pluginId}" operation "${operation}" timed out after ${String(timeoutMs)}ms`));
        }, timeoutMs);
        fn()
            .then((result) => {
            clearTimeout(timer);
            resolve(result);
        })
            .catch((error) => {
            clearTimeout(timer);
            const message = error instanceof Error ? error.message : String(error);
            reject(new Error(`Plugin "${pluginId}" operation "${operation}" failed: ${message}`));
        });
    });
}
//# sourceMappingURL=sandbox.js.map