A registration under a stranger's name is said and skipped, not fatal #26
+8
-1
@@ -59,7 +59,14 @@ export async function runTools(opts: RuntimeOptions): Promise<() => void> {
|
||||
// both: its tools are the module's and the seat's verbs alike.
|
||||
const self = opts.credential?.module;
|
||||
const seatNames = new Set((opts.credential?.claims ?? []).map((c) => c.seat));
|
||||
const ownRegistrations = collectTools().filter(({ module }) => module === self || !seatNames.has(module));
|
||||
// A registration under a name that is neither this module nor a seat it claims is not served:
|
||||
// said, and left out, rather than fatal — on 2026-10-01 the credential of a module that had just
|
||||
// learned to implement a seat did not yet name the claim, and the whole runtime restarted for it.
|
||||
const ownRegistrations = collectTools().filter(({ module }) => {
|
||||
if (module === self || !self || seatNames.has(module)) return module === self || !self;
|
||||
console.log(`[mesh-tools] ${self} registers tools under "${module}", which is neither this module nor a seat its credential claims; not served until the mesh issues the claim`);
|
||||
return false;
|
||||
});
|
||||
const tools = ownRegistrations.flatMap(({ module, tools: own }) => own.map((t) => ({ module, name: t.name })));
|
||||
const stops: Array<() => void> = [];
|
||||
const stop = (): void => stops.splice(0).forEach((s) => s());
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// A module that is both software and a role: postgres's own tools under its name, and its
|
||||
// implementation of the mesh-store seat's verbs under the seat's (novox/hq ADR 0159, 0160).
|
||||
import { registerModuleTools } from "@novox/mesh-sdk/tools";
|
||||
|
||||
registerModuleTools("postgres", () => [
|
||||
{ name: "postgres_create_database", description: "make one", input: {}, run: async () => ({ made: true }) },
|
||||
{ name: "databases", description: "postgres's own listing", input: {}, run: async () => ({ software: "postgres" }) },
|
||||
]);
|
||||
|
||||
registerModuleTools("mesh-store", () => [
|
||||
{ name: "databases", description: "what the store holds", input: {}, run: async () => ({ seat: "mesh-store" }) },
|
||||
]);
|
||||
@@ -195,3 +195,31 @@ test("a mesh that has issued nothing yet gets the derived shape, and says so", a
|
||||
await stream.close();
|
||||
}
|
||||
});
|
||||
|
||||
// A module that implements a seat its credential does not (yet) claim is not served for it and does
|
||||
// not fall over either: the runtime says so and serves the module's own tools.
|
||||
test("a registration under a seat the credential does not claim is said and skipped, not fatal", async (t) => {
|
||||
if (!url) return t.skip("MESH_TEST_NATS unset");
|
||||
resetTools();
|
||||
const stream = await anAssignmentsStream();
|
||||
const credential = { url, node: "anchor", module: "postgres" };
|
||||
const pg = await connectNats(credential);
|
||||
const asker = await connectNats({ url, module: "console", node: "workstation" });
|
||||
const said: string[] = [];
|
||||
const log = console.log;
|
||||
console.log = (...a: unknown[]) => said.push(a.join(" "));
|
||||
let stop = () => {};
|
||||
try {
|
||||
stop = await runTools({ broker: pg, credential, moduleEntrypoints: [fixture("store-seat-unclaimed.mjs")] });
|
||||
console.log = log;
|
||||
assert.ok(said.some((s) => /registers tools under "mesh-store".*not served/.test(s)), said.join("\n"));
|
||||
assert.deepEqual((await callTool(asker, "postgres.databases", {})).result, { software: "postgres" });
|
||||
await assert.rejects(callTool(asker, "seat:mesh-store.databases", {}), /no responders|503/i);
|
||||
} finally {
|
||||
console.log = log;
|
||||
stop();
|
||||
await asker.close();
|
||||
await pg.close();
|
||||
await stream.close();
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user