fix(web): correct LinkedIn URL on about page
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

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-09 13:17:24 +01:00
parent 05e3c43e29
commit 0661e6223a
28 changed files with 3409 additions and 8 deletions

View File

@@ -0,0 +1,90 @@
/**
* Localhost HTTP callback listener for CLI-to-browser sync flow.
*
* Endpoints:
* GET /ping → reachability check (web page preflight)
* GET /callback → receives sync token via ?token= query param
* OPTIONS * → CORS preflight for claudemesh.com
*/
import { createServer, type Server } from "node:http";
export interface CallbackListener {
/** Port the server is listening on. */
port: number;
/** Resolves when the /callback endpoint receives a token. */
token: Promise<string>;
/** Shut down the server. */
close: () => void;
}
/**
* Start a localhost HTTP server on a random OS-assigned port.
* Returns the port and a promise that resolves with the sync token.
*/
export function startCallbackListener(): Promise<CallbackListener> {
return new Promise((resolveStart) => {
let resolveToken: (token: string) => void;
const tokenPromise = new Promise<string>((r) => {
resolveToken = r;
});
const server: Server = createServer((req, res) => {
const url = new URL(req.url!, "http://localhost");
// CORS preflight
if (req.method === "OPTIONS") {
res.writeHead(204, {
"Access-Control-Allow-Origin": "https://claudemesh.com",
"Access-Control-Allow-Methods": "GET",
"Access-Control-Allow-Headers": "Content-Type",
});
res.end();
return;
}
// Reachability check — web page calls this before redirecting
if (url.pathname === "/ping") {
res.writeHead(200, {
"Content-Type": "text/plain",
"Access-Control-Allow-Origin": "https://claudemesh.com",
});
res.end("ok");
return;
}
// Sync token callback
if (url.pathname === "/callback") {
const token = url.searchParams.get("token");
if (token) {
res.writeHead(200, {
"Content-Type": "text/html",
"Access-Control-Allow-Origin": "https://claudemesh.com",
});
res.end(
"<html><body><h2>Done! You can close this tab.</h2><p>Launching claudemesh...</p></body></html>",
);
resolveToken(token);
// Close server after a short delay to ensure response is sent
setTimeout(() => server.close(), 500);
} else {
res.writeHead(400, { "Content-Type": "text/plain" });
res.end("Missing token");
}
return;
}
res.writeHead(404);
res.end();
});
server.listen(0, "127.0.0.1", () => {
const addr = server.address() as { port: number };
resolveStart({
port: addr.port,
token: tokenPromise,
close: () => server.close(),
});
});
});
}

View File

@@ -0,0 +1,4 @@
export { startCallbackListener, type CallbackListener } from "./callback-listener";
export { openBrowser } from "./open-browser";
export { generatePairingCode } from "./pairing-code";
export { syncWithBroker, type SyncResult } from "./sync-with-broker";

View File

@@ -0,0 +1,33 @@
/**
* Cross-platform browser opener.
* Respects BROWSER env var. Falls back to platform-specific launcher.
*/
import { exec } from "node:child_process";
/**
* Open a URL in the user's default browser.
* Returns true if the command succeeded, false otherwise.
* Non-fatal — callers should show the URL as fallback.
*/
export function openBrowser(url: string): Promise<boolean> {
// Validate URL
if (!url.startsWith("http://") && !url.startsWith("https://")) {
return Promise.resolve(false);
}
const quoted = JSON.stringify(url);
const browserCmd = process.env.BROWSER;
const cmd = browserCmd
? `${browserCmd} ${quoted}`
: process.platform === "darwin"
? `open ${quoted}`
: process.platform === "win32"
? `rundll32 url.dll,FileProtocolHandler ${quoted}`
: `xdg-open ${quoted}`;
return new Promise((resolve) => {
exec(cmd, (err) => resolve(!err));
});
}

View File

@@ -0,0 +1,17 @@
/**
* Generate a short pairing code for CLI-to-browser visual confirmation.
* Excludes ambiguous characters (0/O, 1/l/I) for readability.
*/
import { randomBytes } from "node:crypto";
const CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789";
/**
* Generate a 4-character alphanumeric pairing code.
* Example output: "A3Kx", "Hn7v", "pQ4m"
*/
export function generatePairingCode(): string {
const bytes = randomBytes(4);
return Array.from(bytes, (b) => CHARS[b % CHARS.length]).join("");
}

View File

@@ -0,0 +1,83 @@
/**
* Call the broker's POST /cli-sync endpoint to sync dashboard meshes.
*
* Takes a sync JWT (from the browser callback) and a freshly generated
* ed25519 keypair. The broker creates member rows and returns mesh details.
*/
export interface SyncResult {
account_id: string;
meshes: Array<{
mesh_id: string;
slug: string;
broker_url: string;
member_id: string;
role: "admin" | "member";
}>;
}
/**
* Sync meshes from dashboard via broker.
*
* @param syncToken - JWT from the browser sync flow
* @param peerPubkey - ed25519 public key hex (64 chars)
* @param displayName - display name for the new member
* @param brokerBaseUrl - HTTPS base URL of the broker (derived from WSS URL)
*/
export async function syncWithBroker(
syncToken: string,
peerPubkey: string,
displayName: string,
brokerBaseUrl?: string,
): Promise<SyncResult> {
// Default broker URL — derive HTTPS from WSS
const base = brokerBaseUrl ?? deriveHttpUrl(
process.env.CLAUDEMESH_BROKER_URL ?? "wss://ic.claudemesh.com/ws",
);
const res = await fetch(`${base}/cli-sync`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
sync_token: syncToken,
peer_pubkey: peerPubkey,
display_name: displayName,
}),
});
if (!res.ok) {
const body = await res.text();
let msg: string;
try {
msg = JSON.parse(body).error ?? body;
} catch {
msg = body;
}
throw new Error(`Broker sync failed (${res.status}): ${msg}`);
}
const body = (await res.json()) as { ok: boolean; account_id?: string; meshes?: SyncResult["meshes"]; error?: string };
if (!body.ok) {
throw new Error(`Broker sync failed: ${body.error ?? "unknown error"}`);
}
return {
account_id: body.account_id!,
meshes: body.meshes!,
};
}
/**
* Convert a WSS broker URL to an HTTPS base URL.
* wss://ic.claudemesh.com/ws → https://ic.claudemesh.com
* ws://localhost:3001/ws → http://localhost:3001
*/
function deriveHttpUrl(wssUrl: string): string {
const url = new URL(wssUrl);
url.protocol = url.protocol === "wss:" ? "https:" : "http:";
// Remove /ws path suffix
url.pathname = url.pathname.replace(/\/ws\/?$/, "");
// Remove trailing slash
return url.toString().replace(/\/$/, "");
}