TESTER_VERBS gains usage, names, limits, plugins, instances, project.list, thread.list, name.set and project.create. A tester reads limits, plugins and instances and sends plugins.set, instances.set and routes.set, each logged with actor tester. The project file verbs refuse a tester tester-refused. The broker and CLI drop the [mos test] prefix rule (mos-0872 Q1). The CLI takes --tester on the seven reads and the W1-W7 write forms and refuses it (exit 4) elsewhere, bus start and stop included. project init --tester checks the project area, refuses the area root itself, binds the tester with one agents read, then commits the layout as tester (mos-0872 Q2). scripts/mosaic sends plugins, name, names, follow, threads and thread to packages/cli (sage-1081). The row 164 pins are untouched. Built by Dewey on9e74925c2(three commits to989699f7c, manifest 2e30f2a1...29d5f), approved by Filbert in round 3 (#1673 comment 29279), Sage's gate green on716ddcf1eplus the patch.
45 lines
2.1 KiB
JavaScript
45 lines
2.1 KiB
JavaScript
// `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": <from a read>, "plugins": { "<kind>": <name or [names] or null> } }. 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);
|
|
}
|
|
}
|