The TypeScript runtime announces what it serves too, in the same services format (hq ADR 0197)
The per-module containers still run this runtime; their tools and the seats they hold (the store's, the catalogue's) must be found by the console the same way as the node runtime's. It answers $SRV.PING, $SRV.INFO and $SRV.STATS with one service per process, one endpoint per tool per subject and per seat verb served, the metadata as the Go runtime writes it.
This commit is contained in:
@@ -16,6 +16,7 @@ import { collectTools, toolKey, type ToolDefinition } from "@novox/mesh-sdk/tool
|
||||
import type { Broker } from "@novox/mesh-sdk/messaging";
|
||||
import { atWork, seatToolSubject, type Credential, type RuntimeBroker } from "./broker-nats.js";
|
||||
import { launch, launches } from "./launch.js";
|
||||
import { announce, endpointsOf, type ServedSeatVerb } from "./announce.js";
|
||||
|
||||
/**
|
||||
* The one verb every module's runtime answers for it (novox/hq ADR 0152, design 34 §3): the
|
||||
@@ -259,7 +260,16 @@ export async function runTools(opts: RuntimeOptions): Promise<() => void> {
|
||||
|
||||
console.log(`[mesh-tools] serving ${names.length} tool(s) for ${served.size} module(s): ${names.join(", ") || "(none)"}` +
|
||||
(failed.size ? `; not serving ${[...failed.keys()].join(", ")}, whose bundle(s) failed to load` : ""));
|
||||
stops.push(await serveClaimedSeats(runtime, [...served.keys()], self, opts.credential, registrations));
|
||||
const seats = await serveClaimedSeats(runtime, [...served.keys()], self, opts.credential, registrations);
|
||||
stops.push(seats.stop);
|
||||
// **What it serves, it announces** (novox/hq ADR 0197), asked at the moment of the request.
|
||||
if (typeof runtime.raw === "function") {
|
||||
stops.push(announce(runtime, {
|
||||
name: self ?? "runtime", id: runtime.node ?? self ?? "runtime",
|
||||
description: `the tool runtime of ${self ?? "a module"}${runtime.node ? ` on ${runtime.node}` : ""}`,
|
||||
metadata: runtime.node ? { node: runtime.node } : {},
|
||||
}, () => endpointsOf(runtime, ownRegistrations, seats.serving())));
|
||||
}
|
||||
return () => stop();
|
||||
}
|
||||
|
||||
@@ -303,11 +313,17 @@ async function serveClaimedSeats(
|
||||
self: string | undefined,
|
||||
credential: Credential | undefined,
|
||||
registrations: { module: string; owner: string; tools: ToolDefinition[] }[],
|
||||
): Promise<() => void> {
|
||||
if (typeof broker.handleSubject !== "function") return () => {};
|
||||
): Promise<{ stop: () => void; serving: () => ServedSeatVerb[] }> {
|
||||
if (typeof broker.handleSubject !== "function") return { stop: () => {}, serving: () => [] };
|
||||
// A seat's verbs are the role's, not the software's (ADR 0159): implemented under the seat's
|
||||
// name — `registerModuleTools("mesh-store", …)` — and never confused with the module's own tools.
|
||||
const implementations = new Map<string, Map<string, (args: Record<string, unknown>) => Promise<unknown>>>();
|
||||
const definitions = new Map<string, Map<string, ToolDefinition>>();
|
||||
for (const { module, tools } of registrations) {
|
||||
const defs = definitions.get(module) ?? new Map<string, ToolDefinition>();
|
||||
for (const t of tools) defs.set(t.name, t);
|
||||
definitions.set(module, defs);
|
||||
}
|
||||
for (const { module, owner, tools } of registrations) {
|
||||
const verbs = implementations.get(module) ?? new Map<string, (args: Record<string, unknown>) => Promise<unknown>>();
|
||||
for (const t of tools) verbs.set(t.name, (args) => atWork.run({ module: owner }, () => t.run(args)));
|
||||
@@ -341,21 +357,25 @@ async function serveClaimedSeats(
|
||||
};
|
||||
|
||||
let stops: (() => void)[] = [];
|
||||
let servingNow: ServedSeatVerb[] = [];
|
||||
const serve = async (): Promise<void> => {
|
||||
stops.forEach((s) => s());
|
||||
stops = [];
|
||||
servingNow = [];
|
||||
for (const v of wanted()) {
|
||||
const run = implementations.get(v.seat)?.get(v.verb);
|
||||
const tool = definitions.get(v.seat)?.get(v.verb);
|
||||
if (!run) {
|
||||
console.log(`[mesh-tools] ${v.holder} claims ${v.seat} and implements no ${v.verb}, which that seat promises; not served`);
|
||||
continue;
|
||||
}
|
||||
stops.push(await broker.handleSubject(v.subject, run));
|
||||
if (tool) servingNow.push({ ...v, tool });
|
||||
console.log(`[mesh-tools] serving ${v.seat}'s ${v.verb} on ${v.subject}, admitted where ${v.holder} holds the seat`);
|
||||
}
|
||||
};
|
||||
await serve();
|
||||
// A membership issued to any served module may add, move or withdraw a seat's verbs.
|
||||
if (typeof broker.onMembership === "function") broker.onMembership(() => void serve());
|
||||
return () => stops.forEach((s) => s());
|
||||
return { stop: () => stops.forEach((s) => s()), serving: () => servingNow };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user