mesh/merge-gate pass: builds zsh → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
mesh/delivery-group group withhold-login-shell-execute delivered: every member is delivered
execute runs any command as the operator account, which can become root without a person. The operator withholds it on the control-node until a call needs a person's approval (hq ADR 0268); the other machines keep it for now. The setting execute (serve by default) is written into a root-owned file the tools bundle reads at start, and anything but serve withholds the verb, so the machine's runtime does not offer it.
73 lines
3.7 KiB
TypeScript
73 lines
3.7 KiB
TypeScript
// zsh's tools: its implementation of node-login-shell's one verb, `execute`, and its own
|
|
// `zsh_config` (novox/hq ADR 0176, ADR 0204). The node tools runtime launches this bundle as a
|
|
// process of its own and serves what it registers (ADR 0188, ADR 0193); it runs as the operator
|
|
// account, so the command runs as that account with nothing switched (shell.ts).
|
|
//
|
|
// `execute` is registered only where this machine's setting serves it (novox/hq ADR 0268): the
|
|
// control-node withholds it, so its runtime offers no such verb and a call to it finds nothing.
|
|
|
|
import { userInfo } from "node:os";
|
|
import { join } from "node:path";
|
|
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
|
import { DEFAULT_TIMEOUT_SECONDS, MAX_TIMEOUT_SECONDS, commandEnv, execute, homeOf, readExecuteSetting, timeoutOf, zshFile } from "../shell.js";
|
|
|
|
/** The operator account on this machine, as the mesh told the runtime. */
|
|
function account(env: NodeJS.ProcessEnv): string {
|
|
return env.MESH_OPERATOR_ACCOUNT?.trim() || userInfo().username;
|
|
}
|
|
|
|
function seatVerbs(env: NodeJS.ProcessEnv): ToolDefinition[] {
|
|
const setting = readExecuteSetting(env);
|
|
if (!setting.served) {
|
|
console.error(`[zsh] node-login-shell's execute is not served on this machine: ${setting.why}`);
|
|
return [];
|
|
}
|
|
return [
|
|
{
|
|
name: "execute",
|
|
description:
|
|
`Run one command on this machine as the operator account, in a zsh login shell in the account's home; answers with what it printed (each stream cut at 256 KiB, said in truncated) and how it exited. Ended, with everything it started, after timeout_seconds (default ${DEFAULT_TIMEOUT_SECONDS}, at most ${MAX_TIMEOUT_SECONDS}).`,
|
|
input: {
|
|
type: "object",
|
|
properties: {
|
|
command: { type: "string", description: "the command line, as you would type it" },
|
|
timeout_seconds: { type: "number", description: `give up after this long (default ${DEFAULT_TIMEOUT_SECONDS}, at most ${MAX_TIMEOUT_SECONDS})` },
|
|
},
|
|
required: ["command"],
|
|
},
|
|
run: async (args) => {
|
|
const command = String(args.command ?? "").trim();
|
|
if (!command) throw new Error("execute: a command is required");
|
|
const who = account(env);
|
|
const self = userInfo().username;
|
|
if (who !== self) {
|
|
// The runtime is the account; anything else is a runtime this was not written for, and
|
|
// a command run as the wrong user is worse than one not run.
|
|
throw new Error(`execute runs as ${who}, and this runtime runs as ${self}`);
|
|
}
|
|
return execute(command, { cwd: homeOf(env), env: commandEnv(env), timeoutSeconds: timeoutOf(args.timeout_seconds), account: who });
|
|
},
|
|
},
|
|
];
|
|
}
|
|
|
|
function ownTools(env: NodeJS.ProcessEnv): ToolDefinition[] {
|
|
return [
|
|
{
|
|
name: "zsh_config",
|
|
description: "The operator account's ~/.zshenv and ~/.zshrc on this machine as they are now: each file's lines, how many are the mesh's block, and the content; and whether this machine serves node-login-shell's execute, and why.",
|
|
input: { type: "object", properties: {} },
|
|
run: async () => {
|
|
const home = homeOf(env);
|
|
const setting = readExecuteSetting(env);
|
|
return { account: account(env), execute: { served: setting.served, why: setting.why }, zshenv: await zshFile(join(home, ".zshenv")), zshrc: await zshFile(join(home, ".zshrc")) };
|
|
},
|
|
},
|
|
];
|
|
}
|
|
|
|
// The seat's verb is registered under the seat's name (what the runtime serves on the seat's
|
|
// subject while this module holds it) and the module's own tool under the module's.
|
|
registerModuleTools("node-login-shell", seatVerbs);
|
|
registerModuleTools("zsh", ownTools);
|