Bifrost/tools/provider-advertisement.mjs
2026-08-22 06:16:39 +02:00

1316 lines
51 KiB
JavaScript

#!/usr/bin/env node
import { createRequire } from "node:module";
import { spawnSync } from "node:child_process";
import { mkdir } from "node:fs/promises";
import { existsSync, statSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import {
crossingReceiptDefinition,
crossingReceiptDocumentType,
crossingReceiptSchemaId,
openCrossingReceiptStore,
resolveCrossingReceiptStorePath,
} from "./bifrost-crossing-documents.mjs";
import {
discordPostCommandDefinition,
discordPostCommandDocumentType,
discordPostCommandSchemaId,
discordPostReceiptDefinition,
discordPostReceiptDocumentType,
discordPostReceiptSchemaId,
} from "./bifrost-discord-command-documents.mjs";
const scriptDir = dirname(fileURLToPath(import.meta.url));
const repoRoot = resolve(scriptDir, "..");
const projectsRoot = resolve(repoRoot, "..");
const cultLibRoot = resolve(process.env.VOIDBOT_CULTLIB_ROOT || resolve(projectsRoot, "CultLib"));
const defaultStorePath = resolve(repoRoot, ".bifrost", "provider-store.cc");
const defaultOdinCultMeshUri = "cultmesh://odin/rendezvous/provider-catalog";
const cultCacheRequire = createRequire(resolveCultCachePackagePath());
const cultMeshRequire = createRequire(resolveCultMeshPackagePath());
const cultNetRequire = createRequire(resolveCultNetPackagePath());
const cultCacheRuntime = loadCultCacheRuntime();
const cultMeshRuntime = loadCultMeshRuntime();
const cultNetRuntime = loadCultNetRuntime();
const {
CultCache,
SingleFileMessagePackBackingStore,
defineDocumentType,
} = cultCacheRuntime;
const { CultMesh } = cultMeshRuntime;
const { defineCultNetDocumentBinding } = cultNetRuntime;
function loadCultCacheRuntime() {
const candidates = [
resolve(cultLibRoot, "packages", "cultcache-ts", "dist", "index.js"),
resolve(projectsRoot, "CultCacheTS", "dist", "index.js"),
];
for (const candidate of candidates) {
try {
const runtime = cultCacheRequire(candidate);
if (runtime.CultCache && runtime.SingleFileMessagePackBackingStore && runtime.defineDocumentType) {
return runtime;
}
} catch {
// Try the next local CultCache runtime candidate.
}
}
throw new Error("CultCache TypeScript runtime with document APIs is unavailable.");
}
function resolveCultCachePackagePath() {
const candidates = [
resolve(cultLibRoot, "packages", "cultcache-ts", "package.json"),
resolve(projectsRoot, "CultCacheTS", "package.json"),
resolve(projectsRoot, "CultCacheTS", "node_modules", "cultcache-ts", "package.json"),
];
for (const candidate of candidates) {
if (existsSync(candidate)) {
return candidate;
}
}
throw new Error(`CultCache TypeScript runtime is unavailable. Tried: ${candidates.join(", ")}`);
}
function loadCultMeshRuntime() {
const candidates = [
resolve(cultLibRoot, "packages", "cultmesh-ts", "dist", "index.js"),
resolve(projectsRoot, "CultMeshTS", "dist", "index.js"),
];
for (const candidate of candidates) {
try {
const runtime = cultMeshRequire(candidate);
if (runtime.CultMesh) {
return runtime;
}
} catch {
// Try the next local CultMesh runtime candidate.
}
}
throw new Error("CultMesh TypeScript runtime is unavailable.");
}
function resolveCultMeshPackagePath() {
const candidates = [
resolve(cultLibRoot, "packages", "cultmesh-ts", "package.json"),
resolve(projectsRoot, "CultMeshTS", "package.json"),
resolve(projectsRoot, "CultMeshTS", "node_modules", "cultmesh-ts", "package.json"),
];
for (const candidate of candidates) {
if (existsSync(candidate)) {
return candidate;
}
}
throw new Error(`CultMesh TypeScript runtime is unavailable. Tried: ${candidates.join(", ")}`);
}
function loadCultNetRuntime() {
const candidates = [
resolve(cultLibRoot, "packages", "cultnet-ts", "dist", "index.js"),
resolve(projectsRoot, "CultNetTS", "dist", "index.js"),
];
for (const candidate of candidates) {
try {
const runtime = cultNetRequire(candidate);
if (runtime.defineCultNetDocumentBinding) {
return runtime;
}
} catch {
// Try the next local CultNet runtime candidate.
}
}
throw new Error("CultNet TypeScript runtime is unavailable.");
}
function resolveCultNetPackagePath() {
const candidates = [
resolve(cultLibRoot, "packages", "cultnet-ts", "package.json"),
resolve(projectsRoot, "CultNetTS", "package.json"),
resolve(projectsRoot, "CultNetTS", "node_modules", "cultnet-ts", "package.json"),
];
for (const candidate of candidates) {
if (existsSync(candidate)) {
return candidate;
}
}
throw new Error(`CultNet TypeScript runtime is unavailable. Tried: ${candidates.join(", ")}`);
}
const documentType = "gamecult.eve.provider_advertisement";
const schemaId = "gamecult.eve.provider_advertisement.v1";
const documentId = "bifrost";
const surfaceDocumentType = "gamecult.eve.surface_state";
const surfaceSchemaId = "gamecult.eve.surface_state.v1";
const interfaceBindingDocumentType = "gamecult.eve.interface_binding";
const interfaceBindingSchemaId = "gamecult.eve.interface_binding.v1";
const verseId = "bifrost.local";
const rootVerse = requiredDeploymentIdentity("BIFROST_ROOT_VERSE");
const currentMachine = requiredDeploymentIdentity("BIFROST_MACHINE_ID");
const canonicalService = `${rootVerse}.bifrost`;
const locatedService = `${rootVerse}.${currentMachine}.bifrost`;
const motionSurfaceCultMeshUri = `cultmesh://${locatedService}/eve/governance/surface`;
const motionCommandCultMeshUri = `cultmesh://${locatedService}/commands/motion`;
const advertisementDefinition = defineDocumentType({
type: documentType,
schemaId,
schemaName: documentType,
schemaVersion: schemaId,
schema: {
// Persisted provider stores can contain an older advertisement revision
// beside live command and receipt documents. Decode that generated
// discovery metadata so export can replace it without discarding the
// command ledger; only newly built advertisements must satisfy the current
// contract below.
parse: (input) => parseAdvertisement(input, { requireCurrentContract: false }),
},
name: "providerId",
indexes: {
serviceName: "serviceName",
},
members: [
{ slot: 1, memberName: "providerId", typeName: "string", isName: true },
{ slot: 2, memberName: "serviceName", typeName: "string", indexAlias: "serviceName" },
{ slot: 3, memberName: "contractPath", typeName: "string" },
{ slot: 4, memberName: "generatedAt", typeName: "string" },
{ slot: 5, memberName: "authority", typeName: "object" },
{ slot: 6, memberName: "namespaces", typeName: "object", isMany: true },
{ slot: 7, memberName: "schemas", typeName: "object", isMany: true },
{ slot: 8, memberName: "witnesses", typeName: "object", isMany: true },
{ slot: 9, memberName: "surfaces", typeName: "object", isMany: true },
{ slot: 10, memberName: "commandBoundaries", typeName: "object", isMany: true },
{ slot: 11, memberName: "styleCapabilities", typeName: "object", isMany: true },
{ slot: 12, memberName: "demotions", typeName: "string", isMany: true },
{ slot: 13, memberName: "rootVerse", typeName: "string" },
{ slot: 14, memberName: "canonicalService", typeName: "string" },
{ slot: 15, memberName: "locatedService", typeName: "string" },
{ slot: 16, memberName: "cultMeshAddress", typeName: "string" },
{ slot: 17, memberName: "endpoints", typeName: "object", isMany: true },
{ slot: 18, memberName: "routes", typeName: "object", isMany: true },
],
});
const surfaceDefinition = defineDocumentType({
type: surfaceDocumentType,
schemaId: surfaceSchemaId,
schemaName: surfaceDocumentType,
schemaVersion: surfaceSchemaId,
schema: { parse: parseObjectDocument("Eve surface state") },
name: "providerId",
members: [
{ slot: 0, memberName: "providerId", typeName: "string", isName: true },
{ slot: 1, memberName: "title", typeName: "string" },
{ slot: 2, memberName: "version", typeName: "long" },
{ slot: 3, memberName: "updatedAt", typeName: "string" },
{ slot: 4, memberName: "surface", typeName: "object" },
],
});
const interfaceBindingDefinition = defineDocumentType({
type: interfaceBindingDocumentType,
schemaId: interfaceBindingSchemaId,
schemaName: interfaceBindingDocumentType,
schemaVersion: interfaceBindingSchemaId,
schema: { parse: parseObjectDocument("Eve interface binding") },
name: "bindingId",
});
async function main() {
const [command, ...rawArgs] = process.argv.slice(2);
const options = parseArgs(rawArgs);
if (!command || command === "help" || command === "--help" || command === "-h") {
printHelp();
return;
}
rejectRemovedHttpProbeConfig(options);
switch (command) {
case "export":
await exportAdvertisement(options);
return;
case "publish-odin":
await publishOdinAdvertisement(options);
return;
case "print":
printJson(buildAdvertisement(options));
return;
case "print-surface":
printJson(buildOperatorSurface(await collectStats(options)));
return;
case "print-binding":
{
const stats = await collectStats(options);
printJson(buildInterfaceBinding(buildOperatorSurface(stats), stats));
}
return;
case "schema":
printJson({
documentType,
schemaId,
documentId,
members: advertisementDefinition.members,
});
return;
default:
throw new Error(`Unknown command "${command}". Run "node tools/provider-advertisement.mjs help".`);
}
}
async function exportAdvertisement(options) {
const storePath = resolveOptionPath(options.out ?? options.store ?? defaultStorePath);
await mkdir(dirname(storePath), { recursive: true });
const cache = CultCache.builder()
.withDocumentType(advertisementDefinition)
.withDocumentType(surfaceDefinition)
.withDocumentType(interfaceBindingDefinition)
.withDocumentType(discordPostCommandDefinition(defineDocumentType))
.withDocumentType(discordPostReceiptDefinition(defineDocumentType))
.withDocumentType(crossingReceiptDefinition(defineDocumentType))
.withGenericStore(new SingleFileMessagePackBackingStore(storePath))
.build();
await cache.pullAllBackingStores();
const advertisement = buildAdvertisement(options);
const stats = await collectStats(options);
const surface = buildOperatorSurface(stats, options);
const binding = buildInterfaceBinding(surface, stats, options);
await cache.put(advertisementDefinition, advertisement.providerId, advertisement);
await cache.put(surfaceDefinition, surface.providerId, surface);
await cache.put(interfaceBindingDefinition, binding.bindingId, binding);
printJson({
ok: true,
documentType,
schemaId,
providerId: advertisement.providerId,
out: storePath,
surfaces: advertisement.surfaces.map((surface) => surface.id),
witnesses: advertisement.witnesses.map((witness) => witness.path),
stats: stats.summary,
});
}
function buildAdvertisement(options) {
return parseAdvertisement({
providerId: "bifrost",
serviceName: "Bifrost",
contractPath: "docs/verse-service-contract.md",
generatedAt: options["generated-at"] ?? new Date().toISOString(),
rootVerse,
canonicalService,
locatedService,
cultMeshAddress: locatedService,
endpoints: [
endpoint("operator-tui", `${locatedService}/eve/tui`, "gamecult.eve.surface.v1", ["tui", "nightwing-tui"]),
endpoint("operator-gui", `${locatedService}/eve/gui`, "gamecult.eve.surface.v1", ["gui", "browser", "eve-native"]),
endpoint("operator-commands", `${locatedService}/commands`, "bifrost.bridge_action.v0", ["command"]),
endpoint("crossing-receipts", `${locatedService}/receipts/crossings`, crossingReceiptSchemaId, ["receipt", "bridge", "cultmesh"]),
endpoint("discord-post-commands", `${locatedService}/commands/discord-post`, discordPostCommandSchemaId, ["command", "discord", "cultmesh"]),
endpoint("discord-post-receipts", `${locatedService}/receipts/discord-post`, discordPostReceiptSchemaId, ["receipt", "discord", "cultmesh"]),
endpoint("patron-support-intake", `${locatedService}/heimdall/patron-support/events`, "bifrost.patron_support_event.v0", ["heimdall", "patronage", "intake"]),
endpoint("github-webhooks", `${locatedService}/github/webhooks`, "bifrost.work_item.v0", ["github", "work-sync", "review-sync"]),
endpoint("motion-surface", motionSurfaceCultMeshUri, "gamecult.eve.surface.v1", ["cultmesh", "governance", "motion"]),
endpoint("motion-commands", motionCommandCultMeshUri, "bifrost.motion_command.v0", ["command", "cultmesh", "governance", "motion"]),
],
routes: [
route("cultmesh-store", ".bifrost/provider-store.cc", "local-cultmesh", true),
],
authority: {
owner: "Bifrost",
role: "GameCult labor, governance, patron pressure, project work, account membership, and governed-public-crossing provider",
presentationOwner: "Eve/CultUI",
discoveryOwner: "Odin through CultMesh",
stateOwner: "Bifrost typed state with CultCache .cc witnesses or export paths",
runtimeMigration: `deployment identity ${locatedService}`,
},
namespaces: [
namespace("gamecult.bifrost.service", "service registration, build/version, schema catalog, and command discovery"),
namespace("gamecult.bifrost.governance", "motions, topic threads, comments, votes, approvals, objections, and policy receipts"),
namespace("gamecult.bifrost.work", "projects, work items, claims, review state, completion artifacts, and maintainer acceptance"),
namespace("gamecult.bifrost.economics", "patron pressure, contributor credit, ledger entries, payout proposal batches, and revenue-share inputs"),
namespace("gamecult.bifrost.bridge", "GitHub, Discord, Reddit, CultNet/CultCache, and future collaboration crossings plus receipts"),
namespace("gamecult.bifrost.surface.product", "member, patron, contributor, and project-facing Eve product surfaces"),
namespace("gamecult.bifrost.surface.operator", "readiness, witness, bridge, schema, migration, and deploy operator surfaces"),
],
schemas: [
schema(schemaId, documentType, "this provider advertisement"),
schema("gamecult.eve.surface.v1", "gamecult.eve.surface", "Eve/CultUI surface compositions lowered by Eve runtimes"),
schema("bifrost.governance.topic.v0", "bifrost.governance.topic", "existing CultCache governance topic witness"),
schema("bifrost.governance.topic_comment.v0", "bifrost.governance.topic-comment", "existing CultCache governance comment witness"),
schema("bifrost.agent-transport.update-request.v0", "bifrost.agent-transport.update-request", "existing CultCache/CultNet agent intake witness"),
schema("bifrost.work_item.v0", "bifrost.work-item", "planned Postgres-to-CultCache work item witness"),
schema("bifrost.motion.v0", "bifrost.motion", "app-native motion witness"),
schema("bifrost.vote.v0", "bifrost.vote", "motion vote witness"),
schema("bifrost.motion_command.v0", "bifrost.motion-command", "Eve Motion Verse command envelope for create, vote, and close"),
schema("bifrost.ledger_entry.v0", "bifrost.ledger-entry", "planned contributor/patron ledger witness"),
schema("bifrost.bridge_action.v0", "bifrost.bridge-action", "current hosted governed crossing command witness"),
schema("bifrost.bridge_receipt.v0", "bifrost.bridge-receipt", "current hosted governed crossing result witness"),
schema(crossingReceiptSchemaId, crossingReceiptDocumentType, "canonical Bifrost-owned public crossing receipt"),
schema(discordPostCommandSchemaId, discordPostCommandDocumentType, "CultMesh command request for Bifrost-owned Discord persona/bot posts"),
schema(discordPostReceiptSchemaId, discordPostReceiptDocumentType, "CultMesh receipt for Bifrost-owned Discord persona/bot posts"),
schema("bifrost.patron_support_event.v0", "bifrost.patron-support-event", "current hosted Heimdall-signed patron support fact consumed by Bifrost"),
schema("bifrost.member_capability_snapshot.v0", "bifrost.member-capability-snapshot", "planned Heimdall-consumed membership capability witness"),
],
witnesses: [
witness(".bifrost/provider-store.cc", `${schemaId}; ${surfaceSchemaId}; ${interfaceBindingSchemaId}; ${discordPostCommandSchemaId}; ${discordPostReceiptSchemaId}`, "current", "typed provider advertisement, operator surface, Eve interface binding, and Bifrost Discord command request/receipt store"),
witness(".bifrost/governance-threads.cc", "bifrost.governance.topic.v0; bifrost.governance.topic_comment.v0", "current", "governance discussion, approvals, and dispatch promotion topics"),
witness(".bifrost/agent-transport.cc", "bifrost.agent-transport.update-request.v0", "current", "repo Persona update requests and dispatch queue state"),
witness(".bifrost/work-items.cc", "bifrost.work_item.v0", "planned-export", "work items exported from the alpha transactional store"),
witness(".bifrost/motions.cc", "bifrost.motion.v0; bifrost.vote.v0", "planned-export", "app-native motions and votes exported from the alpha transactional store"),
witness(".bifrost/ledger.cc", "bifrost.ledger_entry.v0", "planned-export", "patron and contributor ledger entries exported from the alpha transactional store"),
witness(".bifrost/member-capabilities.cc", "bifrost.member_capability_snapshot.v0", "planned-export", "membership and account capability snapshots consumed by Bifrost"),
witness(".bifrost/bridge-receipts.cc", crossingReceiptSchemaId, "current", "canonical governed public crossing receipt history"),
witness(".bifrost/eve-surfaces.cc", "gamecult.eve.surface.v1", "planned-export", "future product Eve/CultUI surfaces not yet in the provider store"),
],
surfaces: [
surface("bifrost", "Bifrost Operator Dashboard", "gamecult.bifrost.surface.operator", "gamecult.eve.surface_state.v1", ".bifrost/provider-store.cc", "eve", [
"service health",
"compact service status",
"topic and request status",
"dispatch activity by source channel",
"bridge capability status",
]),
surface("bifrost.account", "Account Verse", "gamecult.bifrost.surface.product", "gamecult.eve.surface.v1", ".bifrost/eve-surfaces.cc", "account/eve", [
"membership status",
"Heimdall-linked account projection",
"grant consumption",
"audit trail lowerings",
]),
surface("bifrost.patron", "Patron Verse", "gamecult.bifrost.surface.product", "gamecult.eve.surface.v1", ".bifrost/eve-surfaces.cc", "patron/eve", [
"patron standing",
"priority pressure",
"pledge/reward influence",
"receipts",
]),
surface("bifrost.project", "Project Verse", "gamecult.bifrost.surface.product", "gamecult.eve.surface.v1", ".bifrost/eve-surfaces.cc", "project/eve", [
"project membership",
"repository links",
"maintainer authority",
"work boards",
]),
surface("bifrost.work", "Work Verse", "gamecult.bifrost.surface.product", "gamecult.eve.surface.v1", ".bifrost/eve-surfaces.cc", "work/eve", [
"work items",
"claims",
"review",
"blockers",
"completion artifacts",
]),
surface("bifrost.motion", "Motion Verse", "gamecult.bifrost.surface.product", "gamecult.eve.surface.v1", "/eve/governance/surface", "motion/eve", [
"motions",
"topic threads",
"votes",
"approvals",
"objections",
]),
surface("bifrost.operator", "Bifrost Operator Verse", "gamecult.bifrost.surface.operator", "gamecult.eve.surface.v1", ".bifrost/eve-surfaces.cc", "operator/eve", [
"readiness",
"store freshness",
"bridge queues",
"schema catalog",
"migration drift",
]),
],
commandBoundaries: [
boundary("work", "Bifrost", [
"claim work",
"submit completion",
"record blockers",
"maintainer accept/reject",
], [
"does not execute payout",
"does not let GitHub issue state override Bifrost work authority",
]),
boundary("motion", "Bifrost", [
"open motion",
"comment",
"vote",
"approve",
"object",
"promote to dispatch",
], [
"Discord and Reddit mirrors cannot become canonical governance without a committed Bifrost document",
]),
boundary("patron", "Bifrost", [
"record patron pressure",
"consume Heimdall-signed Patreon and PayPal support facts",
"surface reward influence",
"emit standing receipts",
], [
"does not charge cards",
"does not store Patreon or PayPal provider tokens",
"does not execute external payout rails",
]),
boundary("project", "Bifrost plus project maintainers", [
"link repositories",
"publish work boards",
"surface maintainer authority",
], [
"does not seize repo Persona cognition or project-local ownership",
]),
boundary("account", "Bifrost consumes Heimdall claims", [
"display membership status",
"consume linked-account grants",
"record Bifrost audit trails",
], [
"does not mint identity grants",
"does not own OAuth provider truth",
]),
boundary("bridge", "Bifrost", [
"prepare governed public crossings",
"execute approved handoffs",
"record receipts",
"own canonical bifrost.crossing_receipt.v1 documents for every crossing attempt",
"open GitHub draft PRs and PR comments through Bifrost gate",
"post Discord messages and DMs through Bifrost gate with Heimdall-linked actor capability",
"record receipt-only future-surface requests with named-surface Heimdall capability matching before named actuators exist",
"post Persona-flaired Reddit organizing threads",
"accept typed CultMesh Discord post command requests and write typed Discord post receipts",
], [
"does not treat local protocol JSON as work authority",
"surface-specific receipts cannot decide crossing completion",
"does not touch secrets in this advertisement",
]),
],
styleCapabilities: [
style("density", ["compact TUI grids", "operator scan tables", "member-facing board/list/detail lowerings"]),
style("visualEncoding", ["status badges", "priority swatches", "role/tier labels", "receipt/audit timelines"]),
style("interaction", ["tabs", "filters", "segmented modes", "claim/review/vote command affordances", "read-only witness inspection"]),
style("loweringTargets", ["Eve native", "browser/Razor transitional lowering", "compact TUI", "future room/overlay projections"]),
style("tone", ["quiet operational product", "governance/labor clarity", "no crypto/DAO styling", "no separate marketing dashboard truth"]),
],
demotions: [
"Razor Pages are browser lowerings, not the canonical presentation owner.",
"HTTP health/readiness JSON is product smoke output only; provider readiness comes from typed CultMesh/Idunn state.",
"Discord messages are mirrors and input surfaces until Bifrost commits typed state.",
"Reddit threads are organizing surfaces until Bifrost commits typed votes, priority signals, comments, or receipts.",
"Local dispatch JSON is evidence for receipts, not command authority.",
"This advertisement is read-only discovery metadata and does not migrate runtime state.",
],
commands: [
command("discord.post", "write", "Write a bifrost.bridge.discord_post_command.v1 document into the provider CultMesh store and wait for a bifrost.bridge.discord_post_receipt.v1 receipt.", {
transport: "cultmesh-command-document",
commandDocumentType: discordPostCommandDocumentType,
commandSchemaId: discordPostCommandSchemaId,
receiptDocumentType: discordPostReceiptDocumentType,
receiptSchemaId: discordPostReceiptSchemaId,
storeRoute: ".bifrost/provider-store.cc",
cultMeshUri: `${locatedService}/commands/discord-post`,
}),
],
});
}
async function collectStats(options) {
const generatedAt = options["generated-at"] ?? new Date().toISOString();
const transport = runJsonTool(["tools/agent-transport.mjs", "list", "--json"]);
const governance = runJsonTool(["tools/governance-threads.mjs", "list", "--json"]);
const crossingReceipts = await readCrossingReceipts(options);
const docker = dockerContainers();
const witnesses = [
witnessStat(".bifrost/provider-store.cc"),
witnessStat(".bifrost/governance-threads.cc"),
witnessStat(".bifrost/agent-transport.cc"),
witnessStat(".bifrost/bridge-receipts.cc"),
witnessStat(".bifrost/discord-webhook-cache.json"),
];
const topics = Array.isArray(governance.value) ? governance.value : [];
const requests = Array.isArray(transport.value) ? transport.value : [];
const topicCounts = countBy(topics, "status");
const requestCounts = countBy(requests, "status");
const receiptCounts = countBy(crossingReceipts.value, "status");
const recentWindowHours = 24;
const dockerHealthy = docker.items.filter((item) => /healthy/i.test(item.status)).length;
const dockerRunning = docker.items.filter((item) => /^Up\b/i.test(item.status)).length;
const bridge = buildBridgeStats();
const service = buildTypedServiceStatus({ witnesses, transport, governance, bridge });
return {
generatedAt,
health: service.health,
ready: service.ready,
docker,
governance: {
ok: governance.ok,
error: governance.error,
count: topics.length,
statusCounts: topicCounts,
recentCount: countRecent(topics, recentWindowHours),
channelCounts: countBy(topics, "sourceKind"),
recentChannelCounts: countRecentBy(topics, "sourceKind", recentWindowHours),
latestUpdatedAt: latestTimestamp(topics.map((item) => item.updatedAt)),
},
transport: {
ok: transport.ok,
error: transport.error,
count: requests.length,
statusCounts: requestCounts,
recentCount: countRecent(requests, recentWindowHours),
channelCounts: countBy(requests, "sourceKind"),
recentChannelCounts: countRecentBy(requests, "sourceKind", recentWindowHours),
latestUpdatedAt: latestTimestamp(requests.map((item) => item.updatedAt)),
},
crossingReceipts: {
ok: crossingReceipts.ok,
error: crossingReceipts.error,
count: crossingReceipts.value.length,
statusCounts: receiptCounts,
recentCount: countRecent(crossingReceipts.value, recentWindowHours),
gapRows: buildReceiptGapRows(crossingReceipts.value),
latestUpdatedAt: latestTimestamp(crossingReceipts.value.map((item) => item.completedAt || item.startedAt || item.requestedAt)),
},
witnesses,
bridge,
summary: {
status: service.ready.ok ? "ready" : "degraded",
health: service.health.value,
ready: service.ready.value,
dockerRunning,
dockerHealthy,
governanceTopics: topics.length,
transportRequests: requests.length,
crossingReceipts: crossingReceipts.value.length,
crossingReceiptGaps: buildReceiptGapRows(crossingReceipts.value).length,
recentGovernanceTopics: countRecent(topics, recentWindowHours),
recentTransportRequests: countRecent(requests, recentWindowHours),
openTopics: topicCounts.open ?? 0,
queuedRequests: requestCounts.queued ?? 0,
recentWindowHours,
},
};
}
async function readCrossingReceipts(options) {
try {
const storePath = resolveCrossingReceiptStorePath(options);
if (!existsSync(storePath)) {
return { ok: true, error: "", value: [] };
}
const store = await openCrossingReceiptStore(storePath);
return { ok: true, error: "", value: store.getAll() };
} catch (error) {
return {
ok: false,
error: error instanceof Error ? error.message : String(error),
value: [],
};
}
}
function buildReceiptGapRows(receipts) {
return receipts
.filter((receipt) => receipt.status === "failed" || receipt.status === "running" || receipt.status === "requested")
.sort((left, right) => String(right.requestedAt).localeCompare(String(left.requestedAt)))
.slice(0, 8)
.map((receipt) =>
`${receipt.receiptId}: ${receipt.crossingKind} ${receipt.status}; source=${receipt.source?.kind || "unknown"}/${receipt.source?.id || "unknown"}; target=${receipt.target?.surface || "unknown"} ${receipt.target?.locator || ""}`.trim(),
);
}
function buildTypedServiceStatus({ witnesses, transport, governance, bridge }) {
const providerStore = witnesses.find((item) => item.path === ".bifrost/provider-store.cc");
const storesReady = witnesses.every((item) => item.exists);
const healthOk = Boolean(providerStore?.exists) && transport.ok && governance.ok;
const readyOk = healthOk && storesReady && bridge.ready;
return {
health: {
ok: healthOk,
value: healthOk
? `typed-store ${providerStore.path} / command stores readable`
: `typed-store ${providerStore?.exists ? "present" : "missing"} / command stores ${transport.ok && governance.ok ? "readable" : "degraded"}`,
},
ready: {
ok: readyOk,
value: readyOk
? "CultMesh command/surface store ready"
: `CultMesh command/surface store ${storesReady ? "present" : "incomplete"} / bridge ${bridge.ready ? "ready" : "not-ready"}`,
},
};
}
function rejectRemovedHttpProbeConfig(options) {
const removed = [
["--health-url", options["health-url"]],
["--ready-url", options["ready-url"]],
["BIFROST_HEALTH_URL", process.env.BIFROST_HEALTH_URL],
["BIFROST_READY_URL", process.env.BIFROST_READY_URL],
].filter(([, value]) => optionalString(value));
if (removed.length > 0) {
throw new Error(`${removed.map(([name]) => name).join(" / ")} were removed from provider advertisement status. Publish Bifrost readiness through typed CultMesh/Idunn state instead of HTTP probes.`);
}
}
function runJsonTool(args) {
const result = spawnSync(process.execPath, args, {
cwd: repoRoot,
encoding: "utf8",
windowsHide: true,
});
if (result.status !== 0) {
return {
ok: false,
error: (result.stderr || result.stdout || `exit ${result.status}`).trim(),
value: null,
};
}
try {
return {
ok: true,
value: JSON.parse(result.stdout),
};
} catch (error) {
return {
ok: false,
error: error instanceof Error ? error.message : String(error),
value: null,
};
}
}
function dockerContainers() {
const result = spawnSync("docker", ["ps", "--filter", "name=bifrost", "--format", "{{json .}}"], {
cwd: repoRoot,
encoding: "utf8",
windowsHide: true,
});
if (result.status !== 0) {
return {
ok: false,
error: (result.stderr || result.stdout || `exit ${result.status}`).trim(),
items: [],
};
}
const items = result.stdout
.split(/\r?\n/)
.map((line) => line.trim())
.filter(Boolean)
.map((line) => {
try {
const parsed = JSON.parse(line);
return {
id: parsed.ID,
name: parsed.Names,
image: parsed.Image,
status: parsed.Status,
ports: parsed.Ports,
};
} catch {
return null;
}
})
.filter(Boolean);
return { ok: true, items };
}
function witnessStat(relativePath) {
const fullPath = resolve(repoRoot, relativePath);
if (!existsSync(fullPath)) {
return {
path: relativePath,
exists: false,
updatedAt: null,
};
}
const stat = statSync(fullPath);
return {
path: relativePath,
exists: true,
updatedAt: stat.mtime.toISOString(),
};
}
async function publishOdinAdvertisement(options) {
if (options["odin-cultmesh-rudp"] || process.env.BIFROST_ODIN_CULTMESH_RUDP) {
throw new Error("--odin-cultmesh-rudp and BIFROST_ODIN_CULTMESH_RUDP were removed. Configure --odin-cultmesh-uri cultmesh://odin/rendezvous/provider-catalog and let CultMesh resolve Odin's transport.");
}
const odinCultMeshUri = options["odin-cultmesh-uri"] ?? options.odin ?? process.env.BIFROST_ODIN_CULTMESH_URI ?? defaultOdinCultMeshUri;
if (!odinCultMeshUri) {
throw new Error("Bifrost Odin publication requires an Odin CultMesh URI.");
}
const advertisement = buildAdvertisement(options);
await CultMesh.publishRudpDocumentOnce(
"bifrost",
0x0d1d0002,
odinCultMeshUri,
defineCultNetDocumentBinding({ definition: advertisementDefinition }),
advertisement.providerId,
advertisement,
{
sourceRole: "bifrost.provider",
tags: ["startup-respect", "odin-verse-discovery"],
},
);
printJson({
ok: true,
documentType,
schemaId,
providerId: advertisement.providerId,
odinCultMeshUri,
});
}
function buildBridgeStats() {
const discordCredentialSource = process.env.BIFROST_DISCORD_BOT_TOKEN
? "BIFROST_DISCORD_BOT_TOKEN"
: process.env.DISCORD_BOT_TOKEN
? "DISCORD_BOT_TOKEN-fallback"
: "missing";
const redditCredentialSource = process.env.BIFROST_REDDIT_CLIENT_ID && process.env.BIFROST_REDDIT_REFRESH_TOKEN
? "BIFROST_REDDIT_CLIENT_ID+BIFROST_REDDIT_REFRESH_TOKEN"
: "missing";
const surfaces = [
{
id: "github",
label: "GitHub",
prepared: true,
ready: true,
authority: "Bifrost typed CultMesh command gate plus GitHub webhook sync",
credentialSource: "GitHub app/OAuth/gh runtime",
note: "draft PRs, PR comments, and webhook work sync are hooked",
},
{
id: "discord",
label: "Discord",
prepared: true,
ready: discordCredentialSource !== "missing",
authority: "Bifrost typed CultMesh command gate plus Heimdall-linked account/capability reference",
credentialSource: discordCredentialSource,
note: discordCredentialSource === "missing"
? "transport actuator token is not visible to this process"
: "transport actuator credential is visible",
},
{
id: "reddit",
label: "Reddit",
prepared: true,
ready: redditCredentialSource !== "missing",
authority: "Bifrost typed CultMesh command gate plus Heimdall-linked reddit capability reference",
credentialSource: redditCredentialSource,
note: redditCredentialSource === "missing"
? "reddit transport credentials are not visible to this process"
: "reddit transport credentials are visible",
},
{
id: "other",
label: "Other",
prepared: true,
ready: false,
authority: "typed CultMesh command contract required before use",
credentialSource: "not-configured",
note: "the old receipt-only HTTP bridge hatch is removed",
},
{
id: "patron",
label: "Patron",
prepared: true,
ready: true,
authority: "Heimdall HMAC via /heimdall/patron-support/events",
credentialSource: "Heimdall:PatronSupportIntakeSecret",
note: "consumes Heimdall-signed Patreon/PayPal support facts; Bifrost stores no provider tokens",
},
];
return {
discordPost: true,
discordDm: true,
redditPost: redditCredentialSource !== "missing",
githubDraftPr: true,
githubPrComment: true,
githubWebhookSync: true,
otherRequest: true,
patronSupportIntake: true,
credentialSource: discordCredentialSource,
redditCredentialSource,
bridgeLedgerConfigured: false,
bridgeLedgerCredentialSource: "removed-http-bridge",
patronSupportAuthority: "Heimdall HMAC via /heimdall/patron-support/events",
prepared: surfaces.every((surface) => surface.prepared),
ready: surfaces.every((surface) => surface.ready),
surfaces,
};
}
function buildOperatorSurface(stats) {
const statusTone = stats.summary.status === "ready" ? "ok" : "warn";
const bridgeLine = stats.bridge.surfaces
.map((surface) => `${surface.label} ${surface.ready ? "live" : surface.prepared ? "prepared" : "no"}`)
.join(" / ");
const children = [
panelNode("service", "Service", [
metricNode("status", "Status", stats.summary.status, statusTone),
metricNode("daemon", "Daemon", `health ${stats.summary.health} / ready ${stats.summary.ready}`, stats.health.ok && stats.ready.ok ? "ok" : "warn"),
metricNode("containers", "Containers", `${stats.summary.dockerRunning} up / ${stats.summary.dockerHealthy} healthy`, stats.summary.dockerHealthy > 0 ? "ok" : "warn"),
metricNode("stores", "Stores", witnessHealthLine(stats.witnesses), witnessHealthTone(stats.witnesses)),
metricNode(
"bridge",
"Bridge",
bridgeLine,
stats.bridge.ready ? "ok" : "warn",
),
listNode(
"bridge-readiness",
"Bridge Readiness",
stats.bridge.surfaces.map((surface) =>
`${surface.id}: ${surface.ready ? "live" : surface.prepared ? "prepared" : "missing"}; authority=${surface.authority}; credential=${surface.credentialSource}; note=${surface.note}`,
),
),
]),
panelNode("activity", "Activity", [
metricNode("topics", "Topics", `${stats.summary.governanceTopics} total / ${stats.summary.openTopics} open / ${stats.summary.recentGovernanceTopics} in ${stats.summary.recentWindowHours}h`, stats.governance.ok ? "ok" : "warn"),
metricNode("requests", "Requests", `${stats.summary.transportRequests} total / ${stats.summary.queuedRequests} queued / ${stats.summary.recentTransportRequests} in ${stats.summary.recentWindowHours}h`, stats.transport.ok ? "ok" : "warn"),
metricNode("crossing-receipts", "Crossing Receipts", `${stats.summary.crossingReceipts} total / ${stats.summary.crossingReceiptGaps} gaps`, stats.crossingReceipts.ok && stats.summary.crossingReceiptGaps === 0 ? "ok" : "warn"),
listNode("status", "Status", [
`topics: ${compactCounts(stats.governance.statusCounts)}`,
`requests: ${compactCounts(stats.transport.statusCounts)}`,
`crossing receipts: ${compactCounts(stats.crossingReceipts.statusCounts)}`,
]),
listNode("receipt-gaps", "Receipt Gaps", stats.crossingReceipts.gapRows),
listNode("channels", "Dispatch Channels", compactChannelLines(stats.transport.channelCounts, stats.transport.recentChannelCounts, stats.summary.recentWindowHours)),
]),
];
return {
providerId: "bifrost",
title: "Bifrost Operator Dashboard",
version: Date.parse(stats.generatedAt) || Date.now(),
updatedAt: stats.generatedAt,
stats,
surface: {
schema: "gamecult.eve.surface.v1",
id: "bifrost-operator-dashboard",
title: "Bifrost Operator Dashboard",
commands: [
command("discord.post", "write", "Post to Discord through Bifrost's CultMesh command request/receipt documents.", {
transport: "cultmesh-command-document",
commandDocumentType: discordPostCommandDocumentType,
commandSchemaId: discordPostCommandSchemaId,
receiptDocumentType: discordPostReceiptDocumentType,
receiptSchemaId: discordPostReceiptSchemaId,
storeRoute: ".bifrost/provider-store.cc",
cultMeshUri: `${locatedService}/commands/discord-post`,
}),
],
root: {
id: "bifrost-root",
kind: "dashboard",
props: {
title: "Bifrost",
subtitle: "Governance, labor, and public crossing bridge",
status: stats.summary.status,
generatedAt: stats.generatedAt,
},
children,
},
},
};
}
function buildInterfaceBinding(surface, stats) {
return {
bindingId: "bifrost",
providerId: "bifrost",
title: surface.title,
kind: "operator-dashboard",
updatedAt: surface.updatedAt,
provider: {
id: "bifrost",
title: "Bifrost",
description: "Bifrost-owned operator stats and bridge health surface.",
version: String(surface.version),
endpoint: `${locatedService}/eve/tui`,
cultMeshAddress: `${locatedService}/eve/tui`,
canonicalService,
locatedService,
endpoints: [
endpoint("operator-tui", `${locatedService}/eve/tui`, "gamecult.eve.surface.v1", ["tui", "nightwing-tui"]),
endpoint("operator-gui", `${locatedService}/eve/gui`, "gamecult.eve.surface.v1", ["gui", "browser", "eve-native"]),
],
routes: [
route("cultmesh-store", ".bifrost/provider-store.cc", "local-cultmesh", true),
],
capabilities: [
"operator-stats",
"bridge-health",
"governance-counts",
"agent-transport-counts",
"motion-surface",
"motion-commands",
"github-bridge",
"github-work-sync",
"discord-bridge",
"reddit-bridge",
"future-surface-bridge",
"heimdall-patron-support-intake",
],
usesCultMesh: true,
status: stats.summary.status,
transport: "CultMesh Eve interface binding.",
},
surface: surface.surface,
rendererHints: {
preferredLowerings: ["nightwing-tui", "eve-native", "browser"],
tileId: "bifrost",
minWidth: 80,
minHeight: 18,
preferredWidth: 120,
preferredHeight: 32,
density: "operator",
},
};
}
function panelNode(id, title, children) {
return {
id: `panel-${id}`,
kind: "panel",
props: { title },
children,
};
}
function metricNode(id, label, value, tone) {
return {
id: `metric-${id}`,
kind: "metric",
props: { label, value, tone },
};
}
function listNode(id, title, items) {
return {
id: `list-${id}`,
kind: "list",
props: { title },
children: items.length > 0
? items.map((item, index) => ({
id: `list-${id}-${index}`,
kind: "text",
props: { text: item },
}))
: [{ id: `list-${id}-empty`, kind: "text", props: { text: "none" } }],
};
}
function compactCounts(value) {
const entries = Object.entries(value).sort((left, right) => left[0].localeCompare(right[0]));
return entries.length > 0 ? entries.map(([key, count]) => `${key} ${count}`).join(" / ") : "none";
}
function compactChannelLines(totalCounts, recentCounts, windowHours) {
const keys = new Set([...Object.keys(totalCounts), ...Object.keys(recentCounts)]);
const lines = [...keys]
.sort((left, right) => left.localeCompare(right))
.map((key) => `${key}: ${totalCounts[key] || 0} total / ${recentCounts[key] || 0} in ${windowHours}h`);
return lines.length > 0 ? lines : ["none"];
}
function countBy(items, field) {
const counts = {};
for (const item of items) {
const key = String(item?.[field] || "unknown");
counts[key] = (counts[key] || 0) + 1;
}
return counts;
}
function countRecent(items, hours) {
const cutoff = Date.now() - hours * 60 * 60 * 1000;
return items.filter((item) => {
const timestamp = Date.parse(item?.updatedAt || item?.createdAt || "");
return Number.isFinite(timestamp) && timestamp >= cutoff;
}).length;
}
function countRecentBy(items, field, hours) {
const cutoff = Date.now() - hours * 60 * 60 * 1000;
const counts = {};
for (const item of items) {
const timestamp = Date.parse(item?.updatedAt || item?.createdAt || "");
if (!Number.isFinite(timestamp) || timestamp < cutoff) {
continue;
}
const key = String(item?.[field] || "unknown");
counts[key] = (counts[key] || 0) + 1;
}
return counts;
}
function latestTimestamp(values) {
return values
.filter((value) => typeof value === "string" && value.trim().length > 0)
.sort()
.at(-1) || null;
}
function witnessHealthLine(witnesses) {
const missing = witnesses.filter((item) => !item.exists).length;
if (missing > 0) {
return `${witnesses.length - missing}/${witnesses.length} present`;
}
const latest = latestTimestamp(witnesses.map((item) => item.updatedAt));
return `all present / latest ${latest ? shortTime(latest) : "unknown"}`;
}
function witnessHealthTone(witnesses) {
return witnesses.every((item) => item.exists) ? "ok" : "warn";
}
function shortTime(value) {
const date = new Date(value);
if (Number.isNaN(date.getTime())) {
return "unknown";
}
return date.toISOString().slice(5, 16).replace("T", " ");
}
function yesNo(value) {
return value ? "yes" : "no";
}
function namespace(id, purpose) {
return { id, purpose };
}
function schema(id, type, purpose) {
return { id, type, purpose };
}
function witness(path, schemaIds, status, purpose) {
return { path, schemaIds, status, purpose };
}
function surface(id, name, namespace, schemaId, witnessPath, resourceBase, capabilities) {
return {
id,
name,
namespace,
schemaId,
witnessPath,
capabilities,
cultMeshAddress: `${locatedService}/${resourceBase}/tui`,
graphicalAddress: `${locatedService}/${resourceBase}/gui`,
endpoints: [
endpoint("tui", `${locatedService}/${resourceBase}/tui`, schemaId, ["tui"]),
endpoint("gui", `${locatedService}/${resourceBase}/gui`, schemaId, ["gui"]),
],
};
}
function boundary(area, owner, commands, forbiddenAuthority) {
return { area, owner, commands, forbiddenAuthority };
}
function command(id, mode, purpose, metadata = {}) {
return {
id,
command: id,
mode,
purpose,
...metadata,
};
}
function style(area, capabilities) {
return { area, capabilities };
}
function endpoint(id, address, schemaId, lowerings) {
return { id, address, schemaId, lowerings };
}
function route(id, address, transport, demoted) {
return { id, address, transport, demoted };
}
function parseAdvertisement(input, { requireCurrentContract = true } = {}) {
if (!input || typeof input !== "object") {
throw new Error("Provider advertisement must be an object.");
}
const advertisement = {
providerId: requireString(input.providerId, "providerId"),
serviceName: requireString(input.serviceName, "serviceName"),
contractPath: requireString(input.contractPath, "contractPath"),
generatedAt: requireString(input.generatedAt, "generatedAt"),
rootVerse: optionalString(input.rootVerse, rootVerse),
canonicalService: optionalString(input.canonicalService, canonicalService),
locatedService: optionalString(input.locatedService, locatedService),
cultMeshAddress: optionalString(input.cultMeshAddress, locatedService),
endpoints: Array.isArray(input.endpoints) ? requireObjectArray(input.endpoints, "endpoints") : [],
routes: Array.isArray(input.routes) ? requireObjectArray(input.routes, "routes") : [],
authority: requireObject(input.authority, "authority"),
namespaces: requireObjectArray(input.namespaces, "namespaces"),
schemas: requireObjectArray(input.schemas, "schemas"),
witnesses: requireObjectArray(input.witnesses, "witnesses"),
surfaces: requireObjectArray(input.surfaces, "surfaces"),
commandBoundaries: requireObjectArray(input.commandBoundaries, "commandBoundaries"),
styleCapabilities: requireObjectArray(input.styleCapabilities, "styleCapabilities"),
demotions: requireStringArray(input.demotions, "demotions"),
};
if (!requireCurrentContract) {
return advertisement;
}
const requiredSurfaces = new Set(["bifrost.work", "bifrost.motion", "bifrost.patron", "bifrost.project", "bifrost.account"]);
const surfaceIds = new Set(advertisement.surfaces.map((surface) => surface.id));
for (const id of requiredSurfaces) {
if (!surfaceIds.has(id)) {
throw new Error(`Provider advertisement must name ${id}.`);
}
}
const requiredEndpoints = new Set(["crossing-receipts", "discord-post-commands", "discord-post-receipts", "patron-support-intake", "github-webhooks"]);
const endpointIds = new Set(advertisement.endpoints.map((endpoint) => endpoint.id));
for (const id of requiredEndpoints) {
if (!endpointIds.has(id)) {
throw new Error(`Provider advertisement must name endpoint ${id}.`);
}
}
const requiredSchemas = new Set([crossingReceiptSchemaId, "bifrost.bridge_action.v0", "bifrost.bridge_receipt.v0", "bifrost.patron_support_event.v0"]);
const schemaIds = new Set(advertisement.schemas.map((schema) => schema.id));
for (const id of requiredSchemas) {
if (!schemaIds.has(id)) {
throw new Error(`Provider advertisement must name schema ${id}.`);
}
}
return advertisement;
}
function parseObjectDocument(label) {
return (input) => {
if (!input || typeof input !== "object" || Array.isArray(input)) {
throw new Error(`${label} must be an object.`);
}
return input;
};
}
function parseArgs(args) {
const options = {};
for (let index = 0; index < args.length; index += 1) {
const token = args[index];
if (!token.startsWith("--")) {
throw new Error(`Unexpected argument "${token}".`);
}
const key = token.slice(2);
const next = args[index + 1];
if (!next || next.startsWith("--")) {
options[key] = "true";
continue;
}
options[key] = next;
index += 1;
}
return options;
}
function requireString(value, field) {
if (typeof value !== "string" || value.trim().length === 0) {
throw new Error(`Provider advertisement field "${field}" must be a non-empty string.`);
}
return value.trim();
}
function optionalString(value, fallback) {
if (typeof value === "string" && value.trim().length > 0) {
return value.trim();
}
return fallback;
}
function requireObject(value, field) {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new Error(`Provider advertisement field "${field}" must be an object.`);
}
return value;
}
function requireObjectArray(value, field) {
if (!Array.isArray(value) || value.some((item) => !item || typeof item !== "object" || Array.isArray(item))) {
throw new Error(`Provider advertisement field "${field}" must be an array of objects.`);
}
return value;
}
function requireStringArray(value, field) {
if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || item.trim().length === 0)) {
throw new Error(`Provider advertisement field "${field}" must be an array of non-empty strings.`);
}
return value.map((item) => item.trim());
}
function resolveOptionPath(path) {
return resolve(process.cwd(), path);
}
function requiredDeploymentIdentity(name) {
const value = process.env[name];
if (typeof value !== "string" || !/^[a-z0-9][a-z0-9-]*$/.test(value)) {
throw new Error(`${name} must explicitly name this deployment using lowercase letters, digits, or hyphens.`);
}
return value;
}
function printJson(value) {
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
}
function printHelp() {
process.stdout.write(`Bifrost Eve provider advertisement
Commands:
export Write advertisement, operator surface, and interface binding to the Bifrost provider store
publish-odin Publish the provider advertisement once to Odin through its CultMesh rendezvous URI
print Print the advertisement as protocol-debug JSON without writing state
print-surface Print the current operator Eve surface JSON without writing state
print-binding Print the current Eve interface binding JSON without writing state
schema Print document type metadata
Options:
--out <path> Override export path; defaults to .bifrost/provider-store.cc
--odin-cultmesh-uri <cultmesh-uri>
Odin CultMesh rendezvous URI; also reads BIFROST_ODIN_CULTMESH_URI
--generated-at <iso> Pin generatedAt for deterministic fixture checks
Examples:
node tools/provider-advertisement.mjs print
node tools/provider-advertisement.mjs print-binding
node tools/provider-advertisement.mjs export --out .bifrost/provider-store.cc
node tools/provider-advertisement.mjs print-surface
$env:BIFROST_ODIN_CULTMESH_URI="cultmesh://odin/rendezvous/provider-catalog"; node tools/provider-advertisement.mjs publish-odin
`);
}
main().catch((error) => {
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
process.exitCode = 1;
});