Build edges are discovered by building; requires and provides are stated by the module about itself. Both belong in the graph and answer different questions — and "what provides postgres-database" needed a sweep over every manifest, which only something holding all of them can do.
79 lines
3.3 KiB
TypeScript
79 lines
3.3 KiB
TypeScript
// mesh-catalog's tools — the module graph's query surface (novox/hq ADR 0070).
|
|
//
|
|
// These are the questions the graph exists to answer, and none of them can be answered anywhere
|
|
// else today: what does this mesh know how to run, what is this module made of, what does a change
|
|
// to this reach, and what is waiting to be rebuilt.
|
|
|
|
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
|
import { Graph } from "../store.js";
|
|
|
|
export function getCatalogueTools(graph: Graph): ToolDefinition[] {
|
|
return [
|
|
{
|
|
name: "catalog_modules",
|
|
description:
|
|
"Every module this mesh holds, with the commit the catalogue considers current and where it came from.",
|
|
input: {},
|
|
run: async () => ({ modules: await graph.modules() }),
|
|
},
|
|
{
|
|
name: "catalog_module",
|
|
description:
|
|
"One module in full — where it came from, what it requires and provides, and what it is made of. The current version unless a commit is named.",
|
|
input: {
|
|
module: { type: "string", description: "the module's name" },
|
|
commit: { type: "string", description: "a particular version (optional)" },
|
|
},
|
|
run: async (args) => {
|
|
const module = String(args.module ?? "");
|
|
if (!module) return { error: "name a module" };
|
|
const found = await graph.show(module, args.commit ? String(args.commit) : undefined);
|
|
return found ? { module: found } : { error: `the catalogue holds no ${module}` };
|
|
},
|
|
},
|
|
{
|
|
name: "catalog_provides",
|
|
description:
|
|
"What provides a provision, and what needs it. Answered from what modules declare about themselves, so it does not require holding every manifest at once.",
|
|
input: { provision: { type: "string", description: "the provision, e.g. postgres-database" } },
|
|
run: async (args) => {
|
|
const provision = String(args.provision ?? "");
|
|
if (!provision) return { error: "name a provision" };
|
|
return await graph.whoProvides(provision);
|
|
},
|
|
},
|
|
{
|
|
name: "catalog_dependents",
|
|
description:
|
|
"What was built against this module — the modules a change to it reaches. Derived from builds, not from a declared list, so it cannot drift from what the code actually uses.",
|
|
input: { module: { type: "string", description: "the module that would change" } },
|
|
run: async (args) => {
|
|
const module = String(args.module ?? "");
|
|
if (!module) return { error: "name a module" };
|
|
return { module, dependents: await graph.dependents(module) };
|
|
},
|
|
},
|
|
{
|
|
name: "catalog_stale",
|
|
description:
|
|
"What must be rebuilt and why — every module built against something that has since moved. `buildable` is the subset waiting on nothing that is itself stale, which is the set that can be built right now.",
|
|
input: {},
|
|
run: async () => ({
|
|
stale: await graph.stale(),
|
|
buildable: await graph.buildable(),
|
|
}),
|
|
},
|
|
];
|
|
}
|
|
|
|
// Opened from the environment when the runtime asks for the module's tools. When it cannot be —
|
|
// no DATABASE_URL — the module contributes no tools rather than taking the whole tool runtime down
|
|
// with it, which is the postgres precedent.
|
|
registerModuleTools("mesh-catalog", (env) => {
|
|
try {
|
|
return getCatalogueTools(Graph.fromEnv(env));
|
|
} catch {
|
|
return [];
|
|
}
|
|
});
|