End-to-end join: user runs `claudemesh join ic://join/<base64>` and walks away with a signed member record + persistent keypair. new modules: - src/crypto/keypair.ts: libsodium ed25519 keypair generation. Format is crypto_sign_keypair raw bytes, hex-encoded (32-byte pub, 64-byte secret = seed || pub). Same format libsodium will need in Step 18 for sign/verify. - src/invite/parse.ts: ic://join/<base64url(JSON)> parser with Zod shape validation + expiry check. encodeInviteLink helper for tests. - src/invite/enroll.ts: POST /join to broker, converts ws:// to http:// transparently. rewritten join command wires them together: 1. parse invite → 2. generate keypair → 3. POST /join → 4. persist config → 5. print success. state/config.ts: saveConfig now chmods the file to 0600 after write, since it holds ed25519 secret keys. No-op on Windows. signature verification (step 18) + invite-token one-time-use tracking are deferred. For now the invite link is a plain bearer token; any client with the link can join. verified end-to-end via apps/cli/scripts/join-roundtrip.ts: build invite → run join subprocess → load new config → connect as new member → send A→B → receive push. Flow passes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
79 lines
2.1 KiB
TypeScript
79 lines
2.1 KiB
TypeScript
#!/usr/bin/env bun
|
|
/**
|
|
* @claudemesh/cli entry point.
|
|
*
|
|
* Dispatches between two modes:
|
|
* - `claudemesh mcp` → MCP server (stdio transport)
|
|
* - `claudemesh <subcommand>` → CLI subcommand
|
|
*
|
|
* Claude Code invokes the `mcp` mode via stdio. Humans use all others.
|
|
*/
|
|
|
|
import { startMcpServer } from "./mcp/server";
|
|
import { runInstall } from "./commands/install";
|
|
import { runJoin } from "./commands/join";
|
|
import { runList } from "./commands/list";
|
|
import { runLeave } from "./commands/leave";
|
|
import { runSeedTestMesh } from "./commands/seed-test-mesh";
|
|
|
|
const HELP = `claudemesh — peer mesh for Claude Code sessions
|
|
|
|
Usage:
|
|
claudemesh <command> [args]
|
|
|
|
Commands:
|
|
install Print Claude Code MCP registration instructions
|
|
join <link> Join a mesh via invite link (ic://join/...)
|
|
list Show all joined meshes
|
|
leave <slug> Leave a joined mesh
|
|
seed-test-mesh Dev-only: inject a mesh into config (skips invite flow)
|
|
mcp Start MCP server (stdio) — invoked by Claude Code
|
|
--help, -h Show this help
|
|
|
|
Environment:
|
|
CLAUDEMESH_BROKER_URL Override broker URL (default: wss://ic.claudemesh.com/ws)
|
|
CLAUDEMESH_CONFIG_DIR Override config directory (default: ~/.claudemesh/)
|
|
CLAUDEMESH_DEBUG=1 Verbose logging
|
|
`;
|
|
|
|
const cmd = process.argv[2];
|
|
const args = process.argv.slice(3);
|
|
|
|
async function main(): Promise<void> {
|
|
switch (cmd) {
|
|
case "mcp":
|
|
await startMcpServer();
|
|
return;
|
|
case "install":
|
|
runInstall();
|
|
return;
|
|
case "join":
|
|
await runJoin(args);
|
|
return;
|
|
case "list":
|
|
runList();
|
|
return;
|
|
case "leave":
|
|
runLeave(args);
|
|
return;
|
|
case "seed-test-mesh":
|
|
runSeedTestMesh(args);
|
|
return;
|
|
case "--help":
|
|
case "-h":
|
|
case "help":
|
|
case undefined:
|
|
console.log(HELP);
|
|
return;
|
|
default:
|
|
console.error(`Unknown command: ${cmd}`);
|
|
console.error("Run `claudemesh --help` for usage.");
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
main().catch((e) => {
|
|
console.error(`claudemesh: ${e instanceof Error ? e.message : String(e)}`);
|
|
process.exit(1);
|
|
});
|