Files
claudemesh/apps/cli-v2/src/commands/connect.ts
Alejandro Gutiérrez d37516213a
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
chore(cli-v2): un-ignore CLI source tree for binary release workflow
The CLI source (242 files, ~14k lines) was gitignored during the
earlier cli→cli-v2 reorg so only the published npm package carried it.
That blocks the GitHub Actions release workflow (release-cli.yml),
which clones the repo fresh on each runner and needs the source to
compile binaries via `bun build --compile`.

Moves the gitignore from root-level to `apps/cli-v2/.gitignore` with
only the usual build artefacts excluded (node_modules, dist, .turbo,
.cache). Source is now in git at apps/cli-v2/src/.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 02:45:44 +01:00

82 lines
2.4 KiB
TypeScript

/**
* Short-lived WS connection helper for CLI commands (peers, send, inbox, state).
*
* Opens a connection to one mesh, runs a callback, then closes cleanly.
* The caller never deals with connect/close lifecycle.
*/
import { hostname } from "node:os";
import { createInterface } from "node:readline";
import { BrokerClient } from "~/services/broker/facade.js";
import { readConfig } from "~/services/config/facade.js";
import type { JoinedMesh } from "~/services/config/facade.js";
export interface ConnectOpts {
/** Mesh slug to connect to. Auto-selects if only one mesh joined. */
meshSlug?: string | null;
/** Display name for this session. Defaults to hostname-pid. */
displayName?: string;
/** Connect to all meshes and run fn for each. */
all?: boolean;
}
async function pickMesh(meshes: JoinedMesh[]): Promise<JoinedMesh> {
console.log("\n Select mesh:");
meshes.forEach((m, i) => {
console.log(` ${i + 1}) ${m.slug}`);
});
console.log("");
const rl = createInterface({ input: process.stdin, output: process.stdout });
return new Promise((resolve) => {
rl.question(" Choice [1]: ", (answer) => {
rl.close();
const idx = parseInt(answer || "1", 10) - 1;
if (idx >= 0 && idx < meshes.length) {
resolve(meshes[idx]!);
} else {
console.error(" Invalid choice, using first mesh.");
resolve(meshes[0]!);
}
});
});
}
export async function withMesh<T>(
opts: ConnectOpts,
fn: (client: BrokerClient, mesh: JoinedMesh) => Promise<T>,
): Promise<T> {
const config = readConfig();
if (config.meshes.length === 0) {
console.error("No meshes joined. Run `claudemesh join <url>` first.");
process.exit(1);
}
let mesh: JoinedMesh;
if (opts.meshSlug) {
const found = config.meshes.find((m) => m.slug === opts.meshSlug);
if (!found) {
console.error(
`Mesh "${opts.meshSlug}" not found. Joined: ${config.meshes.map((m) => m.slug).join(", ")}`,
);
process.exit(1);
}
mesh = found;
} else if (config.meshes.length === 1) {
mesh = config.meshes[0]!;
} else {
mesh = await pickMesh(config.meshes);
}
const displayName = opts.displayName ?? config.displayName ?? `${hostname()}-${process.pid}`;
const client = new BrokerClient(mesh, { displayName });
try {
await client.connect();
const result = await fn(client, mesh);
return result;
} finally {
client.close();
}
}