1. Stale bus host lock refusals from bus status, start, stop and restart name mosaic bus recover ORG. 2. org definitions: unknown org, unknown or malformed name and bad id give one refusal or usage line, no stack trace, no host path. 3. Pack, board and workflow refusals print their hint as template save does; tested with the real CliError transport shape. 4. console start and stop clear a console/start.lock whose named supervisor (pid and start time) is gone; a live or unnamed holder refuses. 5. Template save keeps free text verbatim and writes no placeholders; the preview lists each field that mentions an org-specific name. 6. A request the shim never sent (no socket, connect refused) exits 5 and the CLI reports bus-not-running with the start and recover line. 7. autonomy-precondition carries the check that failed, including on the hire paths. 8. Saving under an existing template name refuses template-exists with a next step; refusal table entry added, channel-proof pins moved.
46 lines
2.0 KiB
JavaScript
46 lines
2.0 KiB
JavaScript
// Transport shim used by S4, not a second product CLI. Input and proof never carry service tokens.
|
|
import { randomBytes } from 'node:crypto';
|
|
import { spawnSync } from 'node:child_process';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { readFileSync } from 'node:fs';
|
|
import { Client, NOT_SENT_EXIT } from './client.mjs';
|
|
import { readProcess } from './human.mjs';
|
|
const file = fileURLToPath(import.meta.url);
|
|
if (!process.env.MOSAIC_BUS_CLI_NONCE) {
|
|
const child = spawnSync(process.execPath, [file, ...process.argv.slice(2)], {
|
|
stdio: 'inherit',
|
|
env: { ...process.env, MOSAIC_BUS_CLI_NONCE: randomBytes(32).toString('hex') },
|
|
});
|
|
process.exit(child.status ?? 2);
|
|
}
|
|
try {
|
|
const request = JSON.parse(readFileSync(0, 'utf8'));
|
|
if (
|
|
!request ||
|
|
typeof request !== 'object' ||
|
|
Object.keys(request).some((k) => !['business', 'verb', 'args'].includes(k))
|
|
)
|
|
throw Error();
|
|
const me = readProcess(process.pid);
|
|
const client = new Client({
|
|
path: process.argv[2],
|
|
// Row 254: library.sync fetches for up to 100 s and the server holds its socket for 120 s,
|
|
// so wait past that for the server's own answer. Row 236: the grants verbs call Gitea and get the
|
|
// same wait. Every other verb keeps the 5 s default.
|
|
timeout: request.verb === 'library.sync' || request.verb.startsWith('grants.') ? 130_000 : undefined,
|
|
human: {
|
|
business: request.business,
|
|
pid: me.pid,
|
|
startTime: me.startTime,
|
|
nonce: process.env.MOSAIC_BUS_CLI_NONCE,
|
|
},
|
|
});
|
|
const result = await client.call(request.verb, request.args ?? {});
|
|
process.stdout.write(JSON.stringify(result) + '\n');
|
|
} catch (e) {
|
|
process.stderr.write((/^[a-z-]{1,64}$/.test(e.code ?? '') ? e.code : 'invalid-request') + '\n');
|
|
// E2E6 fix 6: a request that never left (no socket, or the connect refused) keeps its code and
|
|
// exits NOT_SENT_EXIT, so `mosaic` can say the bus is not running. Every other failure exits 2.
|
|
process.exitCode = e?.notSent === true ? NOT_SENT_EXIT : 2;
|
|
}
|