// `mosaic plugins` (vikunja:19/305, REQ-PLUG-1): read and switch the organization file's plug-in // modules through the broker's plugins and plugins.set verbs, the human's and, with --tester, the // tester's (row 175, #1673, Q173). The broker refuses an agent and a reader, a stale revision, a bad // shape and a module that doesn't exist, and the change is logged with its actor (the human or // `tester`) in the organization's changes.jsonl. import { readFileSync } from "node:fs"; import { CliError } from "./errors.mjs"; export const PLUGINS_USAGE = "mosaic plugins [--business ID] [--json] [--tester] | set --file PATCH [--business ID] [--tester]"; function parse(args) { const out = { positional: [], flags: new Set(), values: {} }; for (let i = 0; i < args.length; i++) { const arg = args[i]; if (arg === "--json" || arg === "--tester") out.flags.add(arg); else if (["--file", "--business"].includes(arg)) { if (!args[i + 1] || Object.hasOwn(out.values, arg)) throw new CliError(PLUGINS_USAGE, 4); out.values[arg] = args[++i]; } else if (arg.startsWith("--")) throw new CliError(PLUGINS_USAGE, 4); else out.positional.push(arg); } return out; } // PATCH is { "revision": , "plugins": { "": } }. It names // the kinds it changes: a kind with a value is switched to it, a kind with null is removed, and a // kind it doesn't name keeps its setting (vikunja:19/309). An empty patch is refused. export async function pluginsCommand(args, io, callFor) { const parsed = parse(args); const verb = parsed.positional[0] ?? "view"; if (parsed.positional.length > 1 || !["view", "set"].includes(verb) || (verb === "set") !== Boolean(parsed.values["--file"])) { throw new CliError(PLUGINS_USAGE, 4); } try { const call = callFor(parsed); const result = verb === "set" ? await call("plugins.set", JSON.parse(readFileSync(parsed.values["--file"], "utf8"))) : await call("plugins", {}); io.stdout.write(JSON.stringify(result, null, 2) + "\n"); } catch (error) { if (error instanceof CliError) throw error; throw new CliError(error.message, 2); } }