Adds plugin kind `channel` (interfaceVersion 1, dispatch(request, {call}))
with cli and webui modules. Project and thread surfaces and thread
talk/follow in the CLI and the Console chat go through the selected
channel. An empty or absent channel selection is off: the CLI exits 3 and
the Console answers 503, each naming `mosaic plugins` and `mosaic plugins
set` with the business selector. Management plugins and plugins.set stay
on the core transport. The loader gains a synchronous snapshot(kind)
getter; loaded(), modules() and last-good handling are unchanged. No lock
is held across dispatch. serve.mjs, public/ and the bus are untouched.
Owner Rocko. Reviewer Dewey: r1 changes (comment 28980, B1: the recovery
sentence was never asserted), r2 approve (comment 28984) on candidate
manifest ae2fcfda...1fc8, the B1 assert killing mutant D1. Design accepted
by Mos (mos-0768, mos-0773). Fixture rewrite at project.test.mjs:47 acked
by Mos (mos-0789) on Dewey's line-by-line check: lines 48-51 identical,
one added assert. Sage gate on e5b10d275: 25 steps green, 2081 package tests, 156 queue node tests, 422 script selftests, 0 failed.
Live stays as pinned. The re-pin that takes this live puts `mosaic
plugins set` in Jason's window right after activate (mos-0768).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
43 lines
2.3 KiB
JavaScript
43 lines
2.3 KiB
JavaScript
// Row 164: switching CLI chat off never gates the core management verbs.
|
|
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { run } from "../src/cli.mjs";
|
|
import { io, tmp } from "./helpers.mjs";
|
|
|
|
function setup(t, selected) {
|
|
const calls = [];
|
|
const deps = { system: { dataRoot: tmp(t, "channel-cli-") }, transport: () => async (verb, args = {}) => {
|
|
calls.push([verb, args]);
|
|
if (verb === "plugins") return { business: "demo", revision: "a".repeat(64), plugins: selected === undefined ? {} : { channel: selected } };
|
|
if (["project.list", "thread.list"].includes(verb)) return [];
|
|
assert.fail("unexpected operation " + verb);
|
|
} };
|
|
return { calls, async cli(argv) { const x = io(); return { code: await run([...argv, "--business", "demo"], x, deps), out: x.out.text, err: x.err.text }; } };
|
|
}
|
|
|
|
test("CLI off, absent and other-only channel selections refuse every project/thread flow before any effect", async t => {
|
|
for (const selected of [[], undefined, ["webui"]]) {
|
|
const s = setup(t, selected);
|
|
for (const argv of [["project", "list"], ["project", "create", "work"], ["threads"], ["thread", "read", "stack/chat"], ["thread", "open", "stack/chat"], ["thread", "create", "stack", "chat"], ["talk", "--thread", "stack/chat", "hello", "--wait", "0"], ["follow", "--thread", "stack/chat"]]) {
|
|
s.calls.length = 0;
|
|
const result = await s.cli(argv);
|
|
assert.equal(result.code, 3, argv.join(" "));
|
|
assert.match(result.err, /mosaic plugins.*mosaic plugins set/s);
|
|
assert.match(result.err, /plugins\.channel/);
|
|
assert.ok(result.err.includes('Pass --business "demo" to both commands.'));
|
|
assert.match(result.err, /cli.*webui/);
|
|
assert.equal(result.out, "");
|
|
assert.deepEqual(s.calls, [["plugins", {}]]);
|
|
}
|
|
}
|
|
});
|
|
|
|
test("CLI enabled project/thread lists dispatch through selection without changing output", async t => {
|
|
const s = setup(t, ["cli"]);
|
|
assert.deepEqual(await s.cli(["project", "list"]), { code: 0, out: "no projects\n", err: "" });
|
|
assert.deepEqual(s.calls, [["plugins", {}], ["project.list", {}]]);
|
|
s.calls.length = 0;
|
|
assert.deepEqual(await s.cli(["threads"]), { code: 0, out: "no threads\n", err: "" });
|
|
assert.deepEqual(s.calls, [["plugins", {}], ["thread.list", {}]]);
|
|
});
|