@phren/cli
Version:
Knowledge layer for AI agents - CLI, MCP server, and data layer
80 lines (79 loc) • 5.15 kB
JavaScript
import { z } from "zod";
import { askPeers, buildSets, indirectMembers, localCaller } from "./conductor-group.js";
import { readSetName, saveSetName } from "./conductor-role.js";
import { computerLabel, readComputers } from "./computer-identity.js";
import { localNames } from "./computer-names.js";
import { listsCaller } from "./health.js";
import { optionalHookPeers, peerRequest } from "./peers.js";
import { object } from "./protocol.js";
import { localConductor } from "./server-launch.js";
const callerSchema = z.object({ name: z.string().max(253).optional(), hostKey: z.string().max(512).optional() });
/** `GET /v1/conductor`: this computer's live conductor and what a peer needs
* to place it in a set. Asked with the caller's name or host key, it also
* says whether this computer lists the caller back. */
export async function conductorAnswer(info, url) {
const caller = callerSchema.parse({ name: url.searchParams.get("name") ?? undefined, hostKey: url.searchParams.get("hostKey") ?? undefined });
const [conductor, links, set] = await Promise.all([localConductor(), listsCaller(caller), readSetName()]);
return { computer: info.computer, conductor: conductor ?? null, peers: links.computers,
...(caller.name || caller.hostKey ? { knowsCaller: links.knowsCaller } : {}), ...(set ? { set } : {}) };
}
/** `GET /v1/sets`: this computer's set and every set its one-way peers are in, plus unlinked computers. */
export async function readSets(info) {
const [{ peers, peerError }, conductor, set, { computers }] = await Promise.all([optionalHookPeers(), localConductor(), readSetName(),
readComputers({ local: { id: info.computer.id } })]);
const answers = await askPeers(peers, await localCaller());
// Old /v1/conductor replies list peer names without ids. Resolve unknown
// names through the existing identity directory, also on older Hooks.
// Skip this extra round when names already identify every member.
const indirect = new Set(indirectMembers(answers, [info.computer.name, ...(info.computer.aliases ?? []), ...localNames()]).map(computerLabel));
const reportedComputers = (await Promise.all(answers.map(async (answer) => {
if (!answer.ok || answer.knowsCaller === false || !answer.peers?.some(name => indirect.has(computerLabel(name))))
return [];
try {
const directory = await peerRequest(answer.peer, "/v1/computers", undefined, 12_000);
return (Array.isArray(directory.computers) ? directory.computers.slice(0, 64) : []).flatMap(value => {
const row = object(value);
return typeof row.id === "string" && typeof row.name === "string" ? [{ id: row.id, name: row.name,
aliases: (Array.isArray(row.aliases) ? row.aliases : []).filter((name) => typeof name === "string").slice(0, 64) }] : [];
});
}
catch {
return [];
} // Missing or offline identity directory keeps the name-only view.
}))).flat();
const view = buildSets({ local: { name: info.computer.name, id: info.computer.id, ...(conductor ? { conductor } : {}), ...(set ? { set } : {}),
names: [info.computer.name, ...(info.computer.aliases ?? []), ...localNames()] }, answers, computers, reportedComputers });
return { ...view, ...(peerError ? { peerError } : {}) };
}
const setNameValue = z.string().trim().min(1).max(60).refine(t => !/[\x00-\x1f\x7f]/.test(t)).nullable();
/** `POST /v1/sets/name`: names this computer's set. The owner's call is sent
* on to every reachable member; a member's relay (with `namedAt`) is kept
* when newer than the name held, and goes no further. */
export async function nameSet(data) {
const input = z.object({ name: setNameValue, namedAt: z.string().datetime().optional() }).strict().parse(data);
if (input.namedAt)
return { ok: true, name: input.name, changed: await saveSetName(input.name, input.namedAt) };
const namedAt = new Date().toISOString();
await saveSetName(input.name, namedAt);
const { peers } = await optionalHookPeers();
const answers = await askPeers(peers, await localCaller());
const told = [], unreachable = [];
await Promise.all(answers.map(async (answer) => {
// A one-way peer is in another set; a peer that could not answer is named so the owner knows it missed the name.
if (answer.ok && answer.knowsCaller === false)
return;
if (!answer.ok) {
if (!answer.disabled)
unreachable.push({ computer: answer.peer.name, error: answer.error });
return;
}
try {
await peerRequest(answer.peer, "/v1/sets/name", { name: input.name, namedAt }, 8_000);
told.push(answer.peer.name);
}
catch (error) {
unreachable.push({ computer: answer.peer.name, error: error instanceof Error ? error.message.slice(0, 300) : "Unreachable." });
}
}));
return { ok: true, name: input.name, namedAt, told: told.sort(), unreachable: unreachable.sort((a, b) => a.computer.localeCompare(b.computer)) };
}