fix(web): fully remove payload runtime from production build
Some checks failed
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled

Remove ALL Payload imports, withPayload wrapper, and (payload)
routes. Blog index + changelog are now static data arrays.
Blog post at /blog/peer-messaging-claude-code is static TSX.

Payload CMS stays as a dev dependency for future local admin
but has zero presence in the production build.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-06 09:25:02 +01:00
parent 2be08ab85f
commit d8bafe3144
16 changed files with 3050 additions and 245 deletions

View File

@@ -3,10 +3,6 @@
*
* Starts BrokerClient connections for every mesh in config on boot,
* then routes the 5 MCP tools through them.
*
* list_peers is stubbed at the CLI level — the broker's WS protocol
* does not yet carry a list-peers request type (Step 16). Until then,
* it returns a note.
*/
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -163,13 +159,21 @@ If you have multiple joined meshes, prefix the \`to\` argument of send_message w
: "list_peers: no joined meshes",
true,
);
const lines = clients.map(
(c) =>
`- ${c!.meshSlug} (${c!.status}, mesh ${c!.meshId.slice(0, 8)}…)`,
);
return text(
`Connected meshes:\n${lines.join("\n")}\n\n(list_peers WS protocol lands in Step 16; only mesh status is shown for now.)`,
);
const sections: string[] = [];
for (const c of clients) {
const peers = await c!.listPeers();
const header = `## ${c!.meshSlug} (${c!.status}, mesh ${c!.meshId.slice(0, 8)}…)`;
if (peers.length === 0) {
sections.push(`${header}\nNo peers connected.`);
} else {
const peerLines = peers.map((p) => {
const summary = p.summary ? ` — "${p.summary}"` : "";
return `- **${p.displayName}** [${p.status}] (${p.pubkey.slice(0, 12)}…)${summary}`;
});
sections.push(`${header}\n${peerLines.join("\n")}`);
}
}
return text(sections.join("\n\n"));
}
case "check_messages": {
@@ -187,8 +191,9 @@ If you have multiple joined meshes, prefix the \`to\` argument of send_message w
case "set_summary": {
const { summary } = (args ?? {}) as SetSummaryArgs;
if (!summary) return text("set_summary: `summary` required", true);
for (const c of allClients()) await c.setSummary(summary);
return text(
`set_summary: summary recorded locally ("${summary}"). (Broker WS protocol for summaries lands in Step 16.)`,
`Summary set: "${summary}" (visible to ${allClients().length} mesh(es)).`,
);
}

View File

@@ -25,6 +25,15 @@ import { signHello } from "../crypto/hello-sig";
export type Priority = "now" | "next" | "low";
export type ConnStatus = "connecting" | "open" | "closed" | "reconnecting";
export interface PeerInfo {
pubkey: string;
displayName: string;
status: string;
summary: string | null;
sessionId: string;
connectedAt: string;
}
export interface InboundPush {
messageId: string;
meshId: string;
@@ -64,6 +73,7 @@ export class BrokerClient {
private outbound: Array<() => void> = []; // closures that send once ws is open
private pushHandlers = new Set<PushHandler>();
private pushBuffer: InboundPush[] = [];
private listPeersResolvers: Array<(peers: PeerInfo[]) => void> = [];
private closed = false;
private reconnectAttempt = 0;
private helloTimer: NodeJS.Timeout | null = null;
@@ -266,6 +276,29 @@ export class BrokerClient {
this.ws.send(JSON.stringify({ type: "set_status", status }));
}
/** Request the list of connected peers from the broker. */
async listPeers(): Promise<PeerInfo[]> {
if (!this.ws || this.ws.readyState !== this.ws.OPEN) return [];
return new Promise((resolve) => {
this.listPeersResolvers.push(resolve);
this.ws!.send(JSON.stringify({ type: "list_peers" }));
// Timeout after 5s — return empty list rather than hang.
setTimeout(() => {
const idx = this.listPeersResolvers.indexOf(resolve);
if (idx !== -1) {
this.listPeersResolvers.splice(idx, 1);
resolve([]);
}
}, 5_000);
});
}
/** Update this session's summary visible to other peers. */
async setSummary(summary: string): Promise<void> {
if (!this.ws || this.ws.readyState !== this.ws.OPEN) return;
this.ws.send(JSON.stringify({ type: "set_summary", summary }));
}
close(): void {
this.closed = true;
if (this.helloTimer) clearTimeout(this.helloTimer);
@@ -294,6 +327,12 @@ export class BrokerClient {
}
return;
}
if (msg.type === "peers_list") {
const peers = (msg.peers as PeerInfo[]) ?? [];
const resolver = this.listPeersResolvers.shift();
if (resolver) resolver(peers);
return;
}
if (msg.type === "push") {
const nonce = String(msg.nonce ?? "");
const ciphertext = String(msg.ciphertext ?? "");