import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { createPluginSdkFilesApi } from "../src/plugin-sdk-api-builder-platform-files.js"; import type { PluginHostCapabilities } from "../src/plugin-sdk-types.js"; import type { PluginRuntimeState } from "../src/plugin-sdk-state.js"; const desktopRoot = process.env.FAMILIAROS_DESKTOP_ROOT ?? resolve(dirname(fileURLToPath(import.meta.url)), ".."); const pluginSdkApiBuilderPlatformSource = readFileSync(resolve(desktopRoot, "src/plugin-sdk-api-builder-platform.ts"), "utf8"); const pluginSdkApiBuilderPlatformFilesSource = readFileSync(resolve(desktopRoot, "src/plugin-sdk-api-builder-platform-files.ts"), "utf8"); assert.match(pluginSdkApiBuilderPlatformSource, /from "\.\/plugin-sdk-api-builder-platform-files(?:\.js)?"/, "Plugin SDK platform seam must import the extracted files builder."); assert.match(pluginSdkApiBuilderPlatformFilesSource, /export function createPluginSdkFilesApi/, "Plugin SDK platform files seam must export the files namespace builder."); const requiredPermissions: string[] = []; const readCalls: Array<{ fileId: string; encoding: "text" | "bytes" }> = []; const saveCalls: Array<{ suggestedName: string; data: string | Uint8Array }> = []; const api = createPluginSdkFilesApi({ approvedPermissions: new Set(["files"]), capabilities: { files: { pick: async () => [{ fileId: "picked-1", name: "notes.txt", size: 5, type: "text/plain" }], read: async (fileId: string, encoding: "text" | "bytes") => { readCalls.push({ fileId, encoding }); return `${fileId}:${encoding}`; }, save: async (spec: { suggestedName: string; data: string | Uint8Array }) => { saveCalls.push(spec); }, }, } as unknown as PluginHostCapabilities, state: { pickedFiles: new Set(), } as unknown as PluginRuntimeState, requirePermission: (permission) => { requiredPermissions.push(permission); }, }); const picked = await api.pick({ accept: [".txt", ".md"], multiple: true }); assert.equal(requiredPermissions[0], "files", "File picking must require the files permission."); assert.deepEqual(picked, [{ fileId: "picked-1", name: "notes.txt", size: 5, type: "text/plain" }], "File picking must return the host-selected file descriptors."); assert.equal(await api.read("picked-1", "text"), "picked-1:text", "File reads must delegate to host file reads for approved picked handles."); assert.deepEqual(readCalls, [{ fileId: "picked-1", encoding: "text" }], "File reads must pass the normalized handle and encoding to host capabilities."); await assert.rejects(() => api.read("missing", "text"), /Plugin file handle is invalid/, "Unknown plugin file handles must be rejected."); await api.save({ suggestedName: "notes.txt", data: "hello" }); assert.deepEqual(saveCalls, [{ suggestedName: "notes.txt", data: "hello" }], "File saves must pass sanitized save payloads to host capabilities."); await assert.rejects(() => api.save({ suggestedName: "notes.txt", data: 5 }), /File save data must be a string or bytes/, "Invalid save payloads must be rejected."); console.error("Plugin SDK API builder platform files seam validation passed.");