// mint-invite.mjs — mint one agent invite via the admin API and print its secret. // // Used by `lchat add-bot` to provision a sidecar identity. It reuses // web/static/crypto.js so the id + claim_hash it derives from the secret T match // exactly what the sidecar (provision.mjs) computes from the same T — the crypto // never diverges (the whole reason the bot is Node). Reads LCHAT_BASE + // LCHAT_ADMIN_TOKEN from the env; takes --label / --description / --model / // --listen. Prints the bare secret T (the sidecar's LCHAT_*_INVITE) to stdout; // all diagnostics go to stderr. import { randomSecret, deriveInvite } from "../web/static/crypto.js"; const BASE = (process.env.LCHAT_BASE || "").replace(/\/$/, ""); const ADMIN = (process.env.LCHAT_ADMIN_TOKEN || "").trim(); if (!BASE || !ADMIN) { console.error("mint-invite: set LCHAT_BASE and LCHAT_ADMIN_TOKEN"); process.exit(1); } function arg(name, def = "") { const i = process.argv.indexOf(`--${name}`); return i >= 0 && i + 1 < process.argv.length ? process.argv[i + 1] : def; } const label = arg("label"); if (!label) { console.error("mint-invite: --label is required"); process.exit(1); } const T = randomSecret(); const { id, claimHash } = await deriveInvite(T); const res = await fetch(`${BASE}/api/admin/key-invites`, { method: "POST", headers: { "content-type": "application/json", authorization: `Bearer ${ADMIN}` }, body: JSON.stringify({ label, id, wrapped: "", claim_hash: claimHash, agent: true, available_to_members: true, // in the Helpers catalog, so Eleven's roster baseline sees members description: arg("description"), model: arg("model"), listen: arg("listen"), // "", "mention", or "name" }), }); if (!res.ok) { const detail = await res.text().catch(() => ""); console.error(`mint-invite: POST /api/admin/key-invites -> HTTP ${res.status} ${detail}`); process.exit(1); } process.stdout.write(T); // the bare secret; add-bot writes it as the sidecar's LCHAT_*_INVITE