build.patch againstd13b9300(brief blobf8d14d70, rev 630), candidate manifest, identity and mutant harnesses with results, gate summary. Co-Authored-By: Claude Opus 5.5 <[email protected]>
367 lines
21 KiB
Diff
367 lines
21 KiB
Diff
diff --git a/docs/TOOLS.md b/docs/TOOLS.md
|
|
index 631e1814..89c85715 100644
|
|
--- a/docs/TOOLS.md
|
|
+++ b/docs/TOOLS.md
|
|
@@ -246,17 +246,22 @@ scripts/webui-service.sh install [--dir DIR] [--no-reload] [FLAGS]
|
|
scripts/webui-service.sh uninstall [--dir DIR] [--no-reload]
|
|
scripts/webui-service.sh status [--dir DIR]
|
|
# FLAGS: [--port N] [--business ID] [--board URL]
|
|
-# [--public-host NAME --auth-header HEADER --allow-user ID]
|
|
+# [--public-host NAME --auth-header HEADER --allow-user ID [--send]]
|
|
```
|
|
|
|
The systemd user unit `mosaic-webui.service` runs the Console,
|
|
`node packages/webui/src/cli.mjs serve`, with serve's flags as given:
|
|
`--port` defaults to 7332 (7330 is the operator's own Console), and the
|
|
-preview set goes all three or none. The template is
|
|
+preview set goes all three or none. `--send` (row 83) goes only with the
|
|
+preview set and is refused otherwise (exit 1, nothing written); ExecStart
|
|
+then ends with `--send`, and the Console needs the system config at start
|
|
+(`--send needs the bus configured`, exit 2). Without `--send` the unit is
|
|
+read-only. The template is
|
|
`packages/webui/systemd/mosaic-webui.service.in`. `install` writes a temp
|
|
file, renames it and runs `systemctl --user daemon-reload`; `uninstall`
|
|
-refuses while the unit is active; `status` prints the unit state and
|
|
-`GET /healthz` on the installed port. Each value is held to a plain
|
|
+refuses while the unit is active; `status` prints the unit state, whether
|
|
+the installed unit sends, and `GET /healthz` on the installed port. Each
|
|
+value is held to a plain
|
|
character set the unit can carry (no space, `%`, `$`, quote, backslash,
|
|
`&` or `|`) and refused otherwise. The unit sets no `MOSAIC_*`, `CLAUDE*`,
|
|
`CODEX*` or `PI_*` variable. Serve's exit 2 (usage, config, or a port
|
|
@@ -264,9 +269,11 @@ already in use) is not restarted.
|
|
|
|
There is no start, stop, enable or restart verb. The Console reads the bus
|
|
through the human transport, so whoever starts it holds the human's
|
|
-capability; an agent starting it, even through systemd, works around the
|
|
-human proof (lead decisions 62 and 94). Jason starts it from his own shell:
|
|
-`systemctl --user enable --now mosaic-webui`.
|
|
+capability. Jason starts and restarts it
|
|
+(`systemctl --user enable --now mosaic-webui`), and so may Mos under lead
|
|
+decision 102, which accepts that sends from a Console Mos started enter the
|
|
+trail as Jason (`human-cli`). Any other agent starting it, even through
|
|
+systemd, works around the human proof (lead decisions 62 and 94).
|
|
Exit codes: `0` ok · `1` failed or refused · `4` usage.
|
|
|
|
## Discord connector (`scripts/discord.sh`)
|
|
diff --git a/packages/webui/README.md b/packages/webui/README.md
|
|
index 5ac12d95..4a7cf535 100644
|
|
--- a/packages/webui/README.md
|
|
+++ b/packages/webui/README.md
|
|
@@ -100,8 +100,15 @@ Under `--send` the first of those can also send (next section).
|
|
|
|
To run the Console as a systemd user unit, `scripts/webui-service.sh`
|
|
renders and installs one with these flags (row 75; `docs/TOOLS.md`
|
|
-"Console service"). Jason starts it from his own shell (lead decision 94).
|
|
-The script takes no `--send`, so a unit it installs is read-only.
|
|
+"Console service"). Jason starts and restarts it, and so may Mos under lead
|
|
+decision 102, which accepts that sends from a Console Mos started enter the
|
|
+trail as Jason. Any other agent starting it works around the human proof
|
|
+(lead decisions 62 and 94). Without `--send` the unit is read-only. The script takes `--send` (row 83)
|
|
+only with the three preview flags; otherwise it exits 1 and writes nothing.
|
|
+With it, the unit's ExecStart ends with `--send`, and the Console needs the
|
|
+system config at start: without it serve refuses with `--send needs the bus
|
|
+configured` and exits 2, which the unit doesn't restart. `status` says
|
|
+whether the installed unit sends.
|
|
|
|
## Message a role (row 76, #1570)
|
|
|
|
@@ -113,9 +120,11 @@ node packages/webui/src/cli.mjs serve --port 7332 \
|
|
`--send` is off by default. It goes only with the three preview flags and
|
|
refuses at start without them. It also needs the system config: without it
|
|
the start refuses with `--send needs the bus configured`, where a read-only
|
|
-Console would still serve the board. With `--send` the Inbox has a "Message
|
|
-a role" box: a role picker filled from the agents read, a text box and
|
|
-Send. The message reaches the role as a message from the human, class INFO,
|
|
+Console would still serve the board. A unit that sends comes from
|
|
+`scripts/webui-service.sh install` with `--send` (above). With `--send` the
|
|
+Inbox has a "Message a role" box: a role picker filled from the agents read,
|
|
+a text box and Send. The message reaches the role as a message from the
|
|
+human, class INFO,
|
|
through `message.send` on the human transport. It goes the same way as one
|
|
sent from the CLI, with class INFO where `mosaic talk` uses REQUEST (lead
|
|
decision 95). Decisions are still answered in a
|
|
diff --git a/packages/webui/tests/service.test.mjs b/packages/webui/tests/service.test.mjs
|
|
index d50b6652..da440d6b 100644
|
|
--- a/packages/webui/tests/service.test.mjs
|
|
+++ b/packages/webui/tests/service.test.mjs
|
|
@@ -11,10 +11,35 @@ import { fileURLToPath } from 'node:url';
|
|
// scripts/webui-service.sh (row 75, #1569). Nothing here starts a unit or
|
|
// reaches the real systemd: install and uninstall run with --no-reload or a
|
|
// stand-in systemctl on PATH, and XDG_CONFIG_HOME points into a temp dir.
|
|
+// Row 83 (#1576) adds --send, taken only with the preview set.
|
|
const REPO = fileURLToPath(new URL('../../..', import.meta.url)).replace(/\/$/, '');
|
|
const SCRIPT = join(REPO, 'scripts', 'webui-service.sh');
|
|
const NODE_DIR = dirname(process.execPath);
|
|
const PREVIEW = ['--public-host', 'console.example.com', '--auth-header', 'X-authentik-uid', '--allow-user', 'jason'];
|
|
+const PREVIEW_FLAGS = '--public-host console.example.com --auth-header X-authentik-uid --allow-user jason';
|
|
+const SEND_REFUSAL = /^webui-service: --send needs the preview mode \(--public-host, --auth-header and --allow-user\)$/m;
|
|
+const TEMPLATE = readFileSync(join(REPO, 'packages', 'webui', 'systemd', 'mosaic-webui.service.in'), 'utf8');
|
|
+
|
|
+// The unit as the base script renders it (row 75): the template with its four
|
|
+// placeholders filled in, nothing else. Row 83 adds --send and must leave
|
|
+// every unit rendered without it byte for byte as this.
|
|
+function filled(flags) {
|
|
+ const base = '/usr/local/bin:/usr/bin:/bin';
|
|
+ const path = `:${base}:`.includes(`:${NODE_DIR}:`) ? base : `${NODE_DIR}:${base}`;
|
|
+ return TEMPLATE.replaceAll('@REPO@', REPO).replaceAll('@PATH@', path).replaceAll('@NODE@', process.execPath).replaceAll('@FLAGS@', flags);
|
|
+}
|
|
+
|
|
+// A loopback port with nothing listening, so status's /healthz request
|
|
+// reaches no real Console.
|
|
+async function closedPort() {
|
|
+ const server = createServer();
|
|
+ server.listen(0, '127.0.0.1');
|
|
+ await once(server, 'listening');
|
|
+ const { port } = server.address();
|
|
+ server.close();
|
|
+ await once(server, 'close');
|
|
+ return port;
|
|
+}
|
|
|
|
function tmp(t) {
|
|
const dir = mkdtempSync(join(tmpdir(), 'webui-service-'));
|
|
@@ -114,7 +139,7 @@ test('the rendered unit names none of the human proof agent markers and sets no
|
|
const source = readFileSync(join(REPO, 'packages', 'bus', 'src', 'human.mjs'), 'utf8');
|
|
const markers = [...source.match(/const MARKERS = \[([^\]]*)\]/)[1].matchAll(/'([A-Z_]+)'/g)].map((m) => m[1]);
|
|
assert.ok(markers.length >= 7 && markers.includes('MOSAIC_BUS_CAP') && markers.includes('CLAUDECODE'), markers.join(' '));
|
|
- for (const args of [[], [...PREVIEW, '--business', 'mosaic']]) {
|
|
+ for (const args of [[], [...PREVIEW, '--business', 'mosaic'], [...PREVIEW, '--send']]) {
|
|
const r = await run(dir, ['render', ...args]);
|
|
assert.equal(r.status, 0, r.stderr);
|
|
for (const marker of markers) assert.ok(!r.stdout.includes(marker), marker);
|
|
@@ -212,9 +237,129 @@ test('status prints the unit state and GET /healthz on the installed port', asyn
|
|
|
|
test('usage: an unknown verb or flag, and start or enable, exit 4', async (t) => {
|
|
const dir = tmp(t);
|
|
- for (const args of [[], ['start'], ['stop'], ['enable'], ['restart'], ['render', '--bogus'], ['render', '--port'], ['render', '--dir', dir], ['render', '--no-reload'], ['uninstall', '--port', '7332'], ['status', '--business', 'mosaic'], ['status', '--no-reload']]) {
|
|
+ for (const args of [[], ['start'], ['stop'], ['enable'], ['restart'], ['render', '--bogus'], ['render', '--port'], ['render', '--dir', dir], ['render', '--no-reload'], ['uninstall', '--port', '7332'], ['status', '--business', 'mosaic'], ['status', '--no-reload'], ['render', '--send', 'yes', ...PREVIEW]]) {
|
|
const r = await run(dir, args);
|
|
assert.equal(r.status, 4, args.join(' '));
|
|
assert.match(r.stderr, /^usage: scripts\/webui-service\.sh/);
|
|
}
|
|
});
|
|
+
|
|
+test('without --send, render is the template with its placeholders filled, byte for byte', async (t) => {
|
|
+ const dir = tmp(t);
|
|
+ for (const [args, flags] of [
|
|
+ [[], '--port 7332'],
|
|
+ [['--port', '7340'], '--port 7340'],
|
|
+ [['--business', 'mosaic'], '--port 7332 --business mosaic'],
|
|
+ [PREVIEW, `--port 7332 ${PREVIEW_FLAGS}`],
|
|
+ [['--port', '7340', '--business', 'mosaic.dev_1', '--board', 'http://127.0.0.1:7331', ...PREVIEW], `--port 7340 --business mosaic.dev_1 --board http://127.0.0.1:7331 ${PREVIEW_FLAGS}`],
|
|
+ ]) {
|
|
+ const r = await run(dir, ['render', ...args]);
|
|
+ assert.equal(r.status, 0, r.stderr);
|
|
+ assert.equal(r.stdout, filled(flags), args.join(' '));
|
|
+ }
|
|
+});
|
|
+
|
|
+test('render --send with the preview set ends ExecStart with --send and changes nothing else', async (t) => {
|
|
+ const dir = tmp(t);
|
|
+ const extra = ['--port', '7340', '--business', 'mosaic'];
|
|
+ for (const args of [[...PREVIEW, '--send'], ['--send', ...PREVIEW], [...PREVIEW.slice(0, 2), '--send', ...PREVIEW.slice(2)], ['--send', ...extra, ...PREVIEW]]) {
|
|
+ const plain = args.filter((a) => a !== '--send');
|
|
+ const flags = `${plain.includes('--port') ? '--port 7340 --business mosaic' : '--port 7332'} ${PREVIEW_FLAGS}`;
|
|
+ const r = await run(dir, ['render', ...args]);
|
|
+ assert.equal(r.status, 0, r.stderr);
|
|
+ assert.equal(r.stdout, filled(`${flags} --send`), args.join(' '));
|
|
+ assert.ok(execStart(r.stdout).endsWith(' --allow-user jason --send'), execStart(r.stdout));
|
|
+ const without = await run(dir, ['render', ...plain]);
|
|
+ assert.equal(without.status, 0, without.stderr);
|
|
+ const a = r.stdout.split('\n'), b = without.stdout.split('\n');
|
|
+ assert.equal(a.length, b.length);
|
|
+ assert.deepEqual(a.map((l, i) => (l === b[i] ? null : [l, b[i]])).filter(Boolean), [[`${execStart(without.stdout)} --send`, execStart(without.stdout)]]);
|
|
+ }
|
|
+});
|
|
+
|
|
+test('--send without the whole preview set is refused and writes nothing', async (t) => {
|
|
+ const dir = tmp(t);
|
|
+ const pairs = [PREVIEW.slice(0, 2), PREVIEW.slice(2, 4), PREVIEW.slice(4, 6)];
|
|
+ const partial = [[], ['--port', '7340'], ['--business', 'mosaic'], ...pairs, [...pairs[0], ...pairs[1]], [...pairs[0], ...pairs[2]], [...pairs[1], ...pairs[2]]];
|
|
+ for (const args of partial) {
|
|
+ const r = await run(dir, ['render', '--send', ...args]);
|
|
+ assert.equal(r.status, 1, args.join(' '));
|
|
+ assert.equal(r.stdout, '', args.join(' '));
|
|
+ assert.match(r.stderr, SEND_REFUSAL, args.join(' '));
|
|
+ }
|
|
+ const bin = standIn(dir);
|
|
+ const fresh = join(dir, 'never');
|
|
+ for (const args of partial) {
|
|
+ const r = await run(dir, ['install', '--dir', fresh, '--no-reload', ...args, '--send'], bin);
|
|
+ assert.equal(r.status, 1, args.join(' '));
|
|
+ assert.match(r.stderr, SEND_REFUSAL, args.join(' '));
|
|
+ }
|
|
+ assert.ok(!existsSync(fresh), 'a refusal creates no directory');
|
|
+ const unitDir = join(dir, 'units');
|
|
+ const target = join(unitDir, 'mosaic-webui.service');
|
|
+ assert.equal((await run(dir, ['install', '--dir', unitDir, '--no-reload', ...PREVIEW], bin)).status, 0);
|
|
+ const before = readFileSync(target, 'utf8');
|
|
+ const r = await run(dir, ['install', '--dir', unitDir, '--no-reload', '--send', ...PREVIEW.slice(0, 4)], bin);
|
|
+ assert.equal(r.status, 1);
|
|
+ assert.equal(readFileSync(target, 'utf8'), before, 'the installed unit is left alone');
|
|
+ assert.deepEqual(readdirSync(unitDir), ['mosaic-webui.service']);
|
|
+ assert.ok(!existsSync(join(dir, 'calls.txt')), 'no systemctl call');
|
|
+});
|
|
+
|
|
+test('install --send into --dir with --no-reload writes the sending unit, and status says whether it sends', async (t) => {
|
|
+ const dir = tmp(t);
|
|
+ const unitDir = join(dir, 'units');
|
|
+ const target = join(unitDir, 'mosaic-webui.service');
|
|
+ const port = await closedPort();
|
|
+ const flags = `--port ${port} ${PREVIEW_FLAGS}`;
|
|
+ const r = await run(dir, ['install', '--dir', unitDir, '--no-reload', '--port', String(port), ...PREVIEW, '--send']);
|
|
+ assert.equal(r.status, 0, r.stderr);
|
|
+ assert.match(r.stdout, new RegExp(`^written: ${target}$`, 'm'));
|
|
+ // Lead decision 102: Jason or Mos starts it, no other agent.
|
|
+ assert.match(r.stdout, /^this script starts nothing: .* Jason\nstarts and restarts it, or Mos under lead decision 102 .*\ntrail as Jason\); no other agent does:$/m);
|
|
+ assert.match(r.stdout, new RegExp(`^serve: \\S+ ${REPO.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/packages/webui/src/cli\\.mjs serve ${flags} --send$`, 'm'));
|
|
+ assert.equal(readFileSync(target, 'utf8'), filled(`${flags} --send`));
|
|
+ assert.equal(statSync(target).mode & 0o777, 0o644);
|
|
+ assert.deepEqual(readdirSync(unitDir), ['mosaic-webui.service']);
|
|
+ const bin = standIn(dir);
|
|
+ const sending = await run(dir, ['status', '--dir', unitDir], bin);
|
|
+ assert.equal(sending.status, 0, sending.stderr);
|
|
+ assert.match(sending.stdout, /^send: on \(--send; human messages to roles\)$/m);
|
|
+ assert.match(sending.stdout, /^healthz: \S+ unreachable \(ECONNREFUSED\)$/m);
|
|
+ const again = await run(dir, ['install', '--dir', unitDir, '--no-reload', '--port', String(port), '--send', ...PREVIEW], bin);
|
|
+ assert.match(again.stdout, new RegExp(`^unchanged: ${target}$`, 'm'));
|
|
+ const readOnly = await run(dir, ['install', '--dir', unitDir, '--no-reload', '--port', String(port), ...PREVIEW], bin);
|
|
+ assert.match(readOnly.stdout, new RegExp(`^written: ${target}$`, 'm'));
|
|
+ assert.equal(readFileSync(target, 'utf8'), filled(flags));
|
|
+ const off = await run(dir, ['status', '--dir', unitDir], bin);
|
|
+ assert.match(off.stdout, /^send: off \(read-only\)$/m);
|
|
+ assert.doesNotMatch(off.stdout, /^send: on/m);
|
|
+ // The only systemctl calls are status's reads; install ran none.
|
|
+ assert.deepEqual(readFileSync(join(dir, 'calls.txt'), 'utf8').split('\n').filter(Boolean).filter((c) => !/^--user (is-enabled|is-active|show) mosaic-webui\.service/.test(c)), []);
|
|
+ const none = await run(dir, ['status', '--dir', join(dir, 'empty')], bin);
|
|
+ assert.doesNotMatch(none.stdout, /^send:/m, 'no unit, no send line');
|
|
+ // serve takes --send as an allowed user, so ExecStart can end with
|
|
+ // `--allow-user --send` on a unit that doesn't send.
|
|
+ const oddUser = [...PREVIEW.slice(0, 4), '--allow-user', '--send'];
|
|
+ for (const [args, ending, state] of [[oddUser, '--allow-user --send', 'off'], [[...oddUser, '--send'], '--allow-user --send --send', 'on']]) {
|
|
+ const r = await run(dir, ['install', '--dir', unitDir, '--no-reload', '--port', String(port), ...args]);
|
|
+ assert.equal(r.status, 0, r.stderr);
|
|
+ assert.ok(execStart(readFileSync(target, 'utf8')).endsWith(` ${ending}`), args.join(' '));
|
|
+ assert.match((await run(dir, ['status', '--dir', unitDir], bin)).stdout, new RegExp(`^send: ${state} `, 'm'), args.join(' '));
|
|
+ }
|
|
+});
|
|
+
|
|
+test('uninstall and status take no --send', async (t) => {
|
|
+ const dir = tmp(t);
|
|
+ const bin = standIn(dir);
|
|
+ const unitDir = join(dir, 'units');
|
|
+ assert.equal((await run(dir, ['install', '--dir', unitDir, '--no-reload', ...PREVIEW, '--send'])).status, 0);
|
|
+ for (const args of [['uninstall', '--dir', unitDir, '--send'], ['uninstall', '--dir', unitDir, '--no-reload', '--send'], ['status', '--dir', unitDir, '--send']]) {
|
|
+ const r = await run(dir, args, bin);
|
|
+ assert.equal(r.status, 4, args.join(' '));
|
|
+ assert.match(r.stderr, /^usage: scripts\/webui-service\.sh/);
|
|
+ assert.equal(r.stdout, '', args.join(' '));
|
|
+ }
|
|
+ assert.ok(existsSync(join(unitDir, 'mosaic-webui.service')), 'still installed');
|
|
+ assert.ok(!existsSync(join(dir, 'calls.txt')), 'no systemctl call');
|
|
+});
|
|
diff --git a/scripts/webui-service.sh b/scripts/webui-service.sh
|
|
index 4ff06833..944bb97a 100755
|
|
--- a/scripts/webui-service.sh
|
|
+++ b/scripts/webui-service.sh
|
|
@@ -11,19 +11,24 @@
|
|
# rename) and run `systemctl --user daemon-reload`
|
|
# uninstall [--dir DIR] [--no-reload]
|
|
# remove the unit; refuses while it is active
|
|
-# status [--dir DIR] unit state, then GET /healthz on its port
|
|
+# status [--dir DIR] unit state, whether it sends, then GET
|
|
+# /healthz on its port
|
|
#
|
|
# FLAGS are serve's: --port N (default 7332; 7330 is the operator's own
|
|
# Console), --business ID, --board URL, and the preview set --public-host
|
|
-# NAME --auth-header HEADER --allow-user ID, all three or none. systemd
|
|
+# NAME --auth-header HEADER --allow-user ID, all three or none, and --send
|
|
+# (row 83), taken only with the preview set: the unit's Console then sends
|
|
+# human messages to roles and needs the system config at start. systemd
|
|
# expands % and $ and splits on spaces, so each value is held to a plain
|
|
# character set and refused otherwise. None is a secret.
|
|
#
|
|
# There is no start, stop, enable or restart verb. The Console reads the
|
|
# bus through the human transport, so whoever starts it holds the human's
|
|
-# capability. An agent starting it, even detached through systemd, works
|
|
-# around the human proof (lead decisions 62 and 94). Jason starts it from
|
|
-# his own shell: `systemctl --user enable --now mosaic-webui`.
|
|
+# capability. Jason starts and restarts it
|
|
+# (`systemctl --user enable --now mosaic-webui`), and so may Mos under lead
|
|
+# decision 102, which accepts that sends from a Console Mos started enter
|
|
+# the trail as Jason (human-cli). Any other agent starting it, even detached
|
|
+# through systemd, works around the human proof (lead decisions 62 and 94).
|
|
#
|
|
# Exit codes: 0 ok, 1 operation failed, 4 usage.
|
|
set -euo pipefail
|
|
@@ -31,7 +36,7 @@ REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
TEMPLATE="$REPO/packages/webui/systemd/mosaic-webui.service.in"
|
|
UNIT_NAME="mosaic-webui.service"
|
|
USAGE="usage: scripts/webui-service.sh render [FLAGS] | install [--dir DIR] [--no-reload] [FLAGS] | uninstall [--dir DIR] [--no-reload] | status [--dir DIR]
|
|
- FLAGS: [--port N] [--business ID] [--board URL] [--public-host NAME --auth-header HEADER --allow-user ID]"
|
|
+ FLAGS: [--port N] [--business ID] [--board URL] [--public-host NAME --auth-header HEADER --allow-user ID [--send]]"
|
|
|
|
die() { echo "webui-service: $*" >&2; exit 1; }
|
|
usage() { echo "$USAGE" >&2; exit 4; }
|
|
@@ -45,6 +50,7 @@ BOARD=""
|
|
PUBLIC_HOST=""
|
|
AUTH_HEADER=""
|
|
ALLOW_USER=""
|
|
+SEND=0
|
|
SERVE_SET=0
|
|
|
|
parse_flags() {
|
|
@@ -58,6 +64,7 @@ parse_flags() {
|
|
--public-host) [ $# -ge 2 ] || usage; PUBLIC_HOST="$2"; SERVE_SET=1; shift 2 ;;
|
|
--auth-header) [ $# -ge 2 ] || usage; AUTH_HEADER="$2"; SERVE_SET=1; shift 2 ;;
|
|
--allow-user) [ $# -ge 2 ] || usage; ALLOW_USER="$2"; SERVE_SET=1; shift 2 ;;
|
|
+ --send) SEND=1; SERVE_SET=1; shift ;;
|
|
*) usage ;;
|
|
esac
|
|
done
|
|
@@ -86,6 +93,9 @@ serve_flags() {
|
|
[ -z "$PUBLIC_HOST" ] || preview=$((preview + 1))
|
|
[ -z "$AUTH_HEADER" ] || preview=$((preview + 1))
|
|
[ -z "$ALLOW_USER" ] || preview=$((preview + 1))
|
|
+ # serve refuses --send without the preview set; refuse it here first,
|
|
+ # so no unit is written that would only exit 2 at start.
|
|
+ [ "$SEND" = 0 ] || [ "$preview" -eq 3 ] || die "--send needs the preview mode (--public-host, --auth-header and --allow-user)"
|
|
if [ "$preview" -ne 0 ]; then
|
|
[ "$preview" -eq 3 ] || die "--public-host, --auth-header and --allow-user go together: all three or none"
|
|
case "$PUBLIC_HOST" in [A-Za-z0-9]*) ;; *) die "public host must be a hostname, no port or scheme: $PUBLIC_HOST" ;; esac
|
|
@@ -95,6 +105,7 @@ serve_flags() {
|
|
case "$ALLOW_USER" in *[!A-Za-z0-9._@+-]*) die "allowed user contains a character the unit cannot carry: $ALLOW_USER" ;; esac
|
|
flags="$flags --public-host $PUBLIC_HOST --auth-header $AUTH_HEADER --allow-user $ALLOW_USER"
|
|
fi
|
|
+ [ "$SEND" = 0 ] || flags="$flags --send"
|
|
echo "$flags"
|
|
}
|
|
|
|
@@ -138,8 +149,9 @@ install_unit() {
|
|
echo "daemon-reload done"
|
|
fi
|
|
cat <<MSG
|
|
-this script starts nothing: the Console holds the human's capability, so the
|
|
-human starts it from their own shell:
|
|
+this script starts nothing: the Console holds the human's capability. Jason
|
|
+starts and restarts it, or Mos under lead decision 102 (sends then enter the
|
|
+trail as Jason); no other agent does:
|
|
systemctl --user enable --now mosaic-webui start now and at login
|
|
systemctl --user status mosaic-webui
|
|
journalctl --user -u mosaic-webui -f the Console's log
|
|
@@ -182,6 +194,13 @@ status_unit() {
|
|
echo "healthz: not checked; no unit at $target"
|
|
return 0
|
|
fi
|
|
+ # render puts --send right after the allowed user, which may itself be
|
|
+ # the value --send; a unit that sends ends with both.
|
|
+ if grep -q '^ExecStart=.* --allow-user [^ ][^ ]* --send$' "$target"; then
|
|
+ echo "send: on (--send; human messages to roles)"
|
|
+ else
|
|
+ echo "send: off (read-only)"
|
|
+ fi
|
|
local port
|
|
port="$(sed -n 's/^ExecStart=.* --port \([0-9][0-9]*\).*$/\1/p' "$target")"
|
|
[ -n "$port" ] || { echo "healthz: not checked; no --port in $target"; return 0; }
|