mesh/capabilities answered {words}, an unknown call read as none, the node from MESH_NODE; the finding writer's bundle
transport requires finding-calls; a runtime refusal keeps its code.
Builds: design 47 G §576, §622, §488
Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
78 lines
3.8 KiB
TypeScript
78 lines
3.8 KiB
TypeScript
// The runtime that launched this bundle, as one channel of requests (novox/hq ADR 0193, ADR 0198): what a module's code
|
|
// asks of its node's tool runner beyond emitting, subscribing and its state, each named by the runtime's method
|
|
// (`mesh/seat.publish`, `mesh/provision.get`). The stdio session binds it when the runtime launched the process; a
|
|
// bundle never names a transport.
|
|
|
|
/** What the runtime is asked through: the stdio session provides it. */
|
|
export type RuntimeAsk = (method: string, params: unknown) => Promise<unknown>;
|
|
|
|
let bound: RuntimeAsk | undefined;
|
|
|
|
/** Bind the channel. The stdio session calls this when the runtime launched the process; a test binds its own. */
|
|
export function useRuntime(ask: RuntimeAsk | undefined): void {
|
|
bound = ask;
|
|
words = undefined;
|
|
}
|
|
|
|
/** The call a bundle reads its tool runner's capability words with: answered with `{words}` (design 47 G §576, §622). */
|
|
export const CAPABILITIES_CALL = "mesh/capabilities";
|
|
/** The word of the acknowledged publish and the subscription for new messages the finding writer uses (§559). */
|
|
export const CAP_FINDING_CALLS = "finding-calls";
|
|
/** The code of a runtime's answer to a call it does not know (JSON-RPC's method not found). */
|
|
const CODE_NO_METHOD = -32601;
|
|
|
|
// The words the tool runner states, asked once at the first call that needs one and kept for as long as the bundle runs: a
|
|
// bundle lives no longer than the tool runner that started it (design 47 G §576).
|
|
let words: Promise<string[]> | undefined;
|
|
|
|
async function readWords(): Promise<string[]> {
|
|
try {
|
|
const a = (await askRuntime(CAPABILITIES_CALL, {})) as { words?: string[] } | null;
|
|
return a?.words ?? [];
|
|
} catch (e) {
|
|
// a tool runner of before G answers the call as an unknown one, which reads as no word stated
|
|
if ((e as { code?: number }).code === CODE_NO_METHOD) return [];
|
|
throw new Error(`reading the tool runner's capability words: ${e instanceof Error ? e.message : String(e)}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fail at once, naming the word and the node (from MESH_NODE), when the tool runner this bundle runs under does not state
|
|
* `word`; the words are read once (design 47 G §488, §576). A failed read is asked again at the next call.
|
|
*/
|
|
export async function requireCapability(word: string): Promise<void> {
|
|
const reading = (words ??= readWords());
|
|
let stated: string[];
|
|
try {
|
|
stated = await reading;
|
|
} catch (e) {
|
|
if (words === reading) words = undefined;
|
|
throw e;
|
|
}
|
|
if (stated.includes(word)) return;
|
|
throw new Error(`the tool runner of node ${process.env.MESH_NODE || "unknown"} does not state the capability word "${word}": a call of its family is refused until it does`);
|
|
}
|
|
|
|
/** Ask the runtime one of its methods; refused with the reason when no runtime launched this bundle. */
|
|
export async function askRuntime(method: string, params: unknown): Promise<unknown> {
|
|
if (!bound) {
|
|
throw new Error(`no runtime to ask ${method} of: it is reached through the runtime that launched this bundle (novox/hq ADR 0198)`);
|
|
}
|
|
return bound(method, params);
|
|
}
|
|
|
|
/**
|
|
* Submit an accept, or say an event, on a seat (novox/hq ADR 0259 §3): `subject` whole, as the seat's contract names
|
|
* it (`mesh.seat.<seat>.event.<event>`), de-duplicated by `id` (the same id twice is one message; empty is the
|
|
* runtime's to make). The runtime does it as this module, only as far as its membership lists, and refuses the rest
|
|
* with the reason: the Go side's `stdio.SeatPublish`.
|
|
*/
|
|
export async function seatPublish(subject: string, body: unknown, id = ""): Promise<void> {
|
|
await askRuntime("mesh/seat.publish", { subject, body, id });
|
|
}
|
|
|
|
/** This module's config value `key`, as mesh-config resolved it for this machine (the Go side's `stdio.Config`). */
|
|
export async function config(key: string): Promise<unknown> {
|
|
return askRuntime("mesh/provision.get", { kind: "config", key });
|
|
}
|