import { readFileSync } from "node:fs"; import { rootPath } from "./config"; export type WindowsFsReadOnlyOperation = "pwd" | "ls" | "cat" | "head" | "tail" | "stat" | "wc" | "rg"; export const windowsFsReadOnlyOperations = new Set(["pwd", "ls", "cat", "head", "tail", "stat", "wc", "rg"]); interface WindowsRgPolicy { timeoutMs: number; maxFiles: number; maxMatches: number; maxBytesPerFile: number; } const configPath = "config/unidesk-cli.yaml"; const commonScriptSource = loadScript("common.ps1"); const operationScriptSources: Record = { pwd: loadScript("pwd.ps1"), ls: loadScript("ls.ps1"), cat: loadScript("cat.ps1"), head: loadScript("head-tail.ps1"), tail: loadScript("head-tail.ps1"), stat: loadScript("stat.ps1"), wc: loadScript("wc.ps1"), rg: loadScript("rg.ps1"), }; export function isWindowsFsReadOnlyOperation(operation: string): operation is WindowsFsReadOnlyOperation { return windowsFsReadOnlyOperations.has(operation); } export function windowsFsReadOnlyScript(basePath: string | null, operation: WindowsFsReadOnlyOperation, args: string[]): string { const rgPolicy = readWindowsRgPolicy(); const variables = [ `$basePath = ${powerShellSingleQuote(basePath ?? "")}`, `$operation = ${powerShellSingleQuote(operation)}`, `$argsJsonB64 = ${powerShellSingleQuote(Buffer.from(JSON.stringify(args), "utf8").toString("base64"))}`, `$rgPolicyJsonB64 = ${powerShellSingleQuote(Buffer.from(JSON.stringify(rgPolicy), "utf8").toString("base64"))}`, ].join("; "); return `${variables}; ${commonScriptSource} ${operationScriptSources[operation]}`; } function loadScript(name: string): string { return readFileSync(new URL(`./ssh-windows-fs/${name}`, import.meta.url), "utf8"); } function readWindowsRgPolicy(): WindowsRgPolicy { const document = asRecord(Bun.YAML.parse(readFileSync(rootPath(configPath), "utf8")), configPath); const trans = requiredRecord(document, "trans", configPath); const windowsFs = requiredRecord(trans, "windowsFs", `${configPath}#trans`); const rg = requiredRecord(windowsFs, "rg", `${configPath}#trans.windowsFs`); return { timeoutMs: positiveInt(rg, "timeoutMs", `${configPath}#trans.windowsFs.rg`), maxFiles: positiveInt(rg, "maxFiles", `${configPath}#trans.windowsFs.rg`), maxMatches: positiveInt(rg, "maxMatches", `${configPath}#trans.windowsFs.rg`), maxBytesPerFile: positiveInt(rg, "maxBytesPerFile", `${configPath}#trans.windowsFs.rg`), }; } function asRecord(value: unknown, path: string): Record { if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${path} must be an object`); return value as Record; } function requiredRecord(value: Record, key: string, path: string): Record { return asRecord(value[key], `${path}.${key}`); } function positiveInt(value: Record, key: string, path: string): number { const raw = value[key]; if (!Number.isSafeInteger(raw) || Number(raw) <= 0) throw new Error(`${path}.${key} must be a positive integer`); return Number(raw); } function powerShellSingleQuote(value: string): string { return `'${value.replace(/'/g, "''")}'`; }