chore(cli-v2): un-ignore CLI source tree for binary release workflow
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

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>
This commit is contained in:
Alejandro Gutiérrez
2026-04-15 02:45:44 +01:00
parent 5b69de08da
commit d37516213a
243 changed files with 14507 additions and 1 deletions

View File

@@ -0,0 +1,96 @@
import { createInterface } from "node:readline";
import { getStoredToken } from "~/services/auth/facade.js";
import { generateInvite } from "~/services/invite/generate.js";
import { readConfig } from "~/services/config/facade.js";
import { writeClipboard } from "~/services/clipboard/facade.js";
import { green, bold, dim, icons } from "~/ui/styles.js";
import { renderQrAsync } from "~/ui/qr.js";
import { EXIT } from "~/constants/exit-codes.js";
function prompt(question: string): Promise<string> {
const rl = createInterface({ input: process.stdin, output: process.stdout });
return new Promise((resolve) => {
rl.question(question, (a) => { rl.close(); resolve(a.trim()); });
});
}
export async function invite(
email?: string,
opts: { mesh?: string; expires?: string; uses?: number; role?: string; json?: boolean } = {},
): Promise<number> {
const auth = getStoredToken();
if (!auth) {
console.error(" Not signed in. Run `claudemesh login` first.");
return EXIT.AUTH_FAILED;
}
const config = readConfig();
if (config.meshes.length === 0) {
console.error(" No meshes. Create one with `claudemesh mesh create <name>`.");
return EXIT.NOT_FOUND;
}
// Resolve which mesh to share
let meshSlug = opts.mesh;
if (!meshSlug) {
if (config.meshes.length === 1) {
meshSlug = config.meshes[0]!.slug;
} else {
// Show picker
console.log("\n Select mesh to share:\n");
config.meshes.forEach((m, i) => {
console.log(` ${bold(String(i + 1) + ")")} ${m.slug} ${dim("(" + m.name + ")")}`);
});
console.log("");
const choice = await prompt(" Choice [1]: ") || "1";
const idx = parseInt(choice, 10) - 1;
meshSlug = config.meshes[idx >= 0 && idx < config.meshes.length ? idx : 0]!.slug;
}
}
try {
const result = await generateInvite(meshSlug, {
email,
expires_in: opts.expires ?? "7d",
max_uses: opts.uses,
role: opts.role,
});
const copied = writeClipboard(result.url);
if (opts.json) {
console.log(JSON.stringify({ schema_version: "1.0", ...result, copied }, null, 2));
} else {
if (email) {
if (result.emailed) {
console.log(`\n ${green(icons.check)} Invite sent to ${bold(email)}`);
if (copied) console.log(` ${green(icons.check)} Link also copied to clipboard`);
} else {
console.log(`\n ${icons.cross} Email to ${bold(email)} was NOT sent (server did not send).`);
console.log(` ${dim("Share the link manually:")}`);
console.log(` ${result.url}`);
if (copied) console.log(` ${green(icons.check)} Link copied to clipboard`);
}
} else {
console.log(`\n ${green(icons.check)} Invite link${copied ? " copied to clipboard" : ""}:`);
console.log(` ${result.url}`);
// Print QR for phone→laptop pairing. Small variant is ~17 lines tall.
const qr = await renderQrAsync(result.url, { small: true });
console.log("");
for (const line of qr.split("\n")) console.log(` ${line}`);
}
console.log(`\n ${dim("Expires " + result.expires_at + ". Anyone with this link can join \"" + meshSlug + "\".")}\n`);
}
return EXIT.SUCCESS;
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
if (msg.includes("403") || msg.includes("permission")) {
console.error(` ${icons.cross} You don't have permission to invite to "${meshSlug}".`);
console.error(` ${dim("Ask the mesh owner to grant you invite permissions.")}`);
} else {
console.error(` ${icons.cross} Failed: ${msg}`);
}
return EXIT.INTERNAL_ERROR;
}
}