import { createInterface } from "node:readline"; import { apiFetch } from "../api.mjs"; import { emit } from "../output.mjs"; import { t } from "../i18n.mjs"; function fmtTs(v) { if (!v) return "-"; return new Date(typeof v === "number" ? v * 1000 : v).toLocaleString(); } async function confirm(q) { const rl = createInterface({ input: process.stdin, output: process.stdout }); return new Promise((resolve) => { rl.question(`${q} [y/N] `, (a) => { rl.close(); resolve(a.trim().toLowerCase() === "y"); }); }); } function parseHeader(kv) { const eq = kv.indexOf("="); if (eq < 0) return { name: kv, value: "" }; return { name: kv.slice(0, eq), value: kv.slice(eq + 1) }; } function getRootCommand(cmd) { let curr = cmd; while (curr.parent) curr = curr.parent; return curr; } function resolveNodeEndpoint(opts, cmd) { if (opts.endpoint) { return { endpoint: opts.endpoint, apiFetchOpts: cmd.optsWithGlobals() }; } if (opts.nodeUrl) { return { endpoint: opts.nodeUrl, apiFetchOpts: cmd.optsWithGlobals() }; } // Check if --base-url, --endpoint, or --node-url was explicitly passed after the subcommand const root = getRootCommand(cmd); const rawArgs = root.rawArgs || process.argv; const cmdName = cmd.name(); let subArgsStart = -1; for (let i = 0; i < rawArgs.length - 1; i++) { if (rawArgs[i] === "nodes" || rawArgs[i] === "provider-nodes") { if (rawArgs[i + 1] === cmdName) { subArgsStart = i + 2; break; } } } let explicitSubcommandBaseUrl = undefined; let serverBaseUrl = undefined; if (subArgsStart !== -1) { const preArgs = rawArgs.slice(0, subArgsStart); for (let i = 0; i < preArgs.length; i++) { if (preArgs[i] === "--base-url" && i + 1 < preArgs.length) { serverBaseUrl = preArgs[i + 1]; } else if (preArgs[i].startsWith("--base-url=")) { serverBaseUrl = preArgs[i].slice("--base-url=".length); } } const subArgs = rawArgs.slice(subArgsStart); for (let i = 0; i < subArgs.length; i++) { const arg = subArgs[i]; if ( (arg === "--base-url" || arg === "--endpoint" || arg === "--node-url") && i + 1 < subArgs.length ) { explicitSubcommandBaseUrl = subArgs[i + 1]; } else if ( arg.startsWith("--base-url=") || arg.startsWith("--endpoint=") || arg.startsWith("--node-url=") ) { explicitSubcommandBaseUrl = arg.slice(arg.indexOf("=") + 1); } } } if (explicitSubcommandBaseUrl !== undefined) { const globals = cmd.optsWithGlobals?.() ?? {}; const apiFetchOpts = { ...globals }; if (serverBaseUrl) { apiFetchOpts.baseUrl = serverBaseUrl; } else { delete apiFetchOpts.baseUrl; } return { endpoint: explicitSubcommandBaseUrl, apiFetchOpts }; } return { endpoint: undefined, apiFetchOpts: cmd.optsWithGlobals() }; } const nodeSchema = [ { key: "id", header: "Node ID", width: 22 }, { key: "provider", header: "Provider", width: 16 }, { key: "name", header: "Name", width: 24 }, { key: "baseUrl", header: "Base URL", width: 38 }, { key: "region", header: "Region", width: 14 }, { key: "weight", header: "Weight" }, { key: "enabled", header: "Enabled", formatter: (v) => (v ? "✓" : "✗") }, { key: "lastLatencyMs", header: "Latency", formatter: (v) => (v ? `${v}ms` : "-") }, ]; export function registerNodes(program) { const nodes = program .command("nodes") .alias("provider-nodes") .description(t("nodes.description")); nodes .command("list") .option("--provider
", t("nodes.list.provider"))
.option("--enabled", t("nodes.list.enabled"))
.action(async (opts, cmd) => {
const params = new URLSearchParams();
if (opts.provider) params.set("provider", opts.provider);
if (opts.enabled) params.set("enabled", "true");
const res = await apiFetch(`/api/provider-nodes?${params}`);
if (!res.ok) {
process.stderr.write(`Error: ${res.status}\n`);
process.exit(1);
}
const data = await res.json();
emit(data.items ?? data, cmd.optsWithGlobals(), nodeSchema);
});
nodes.command("get ", t("nodes.add.provider"))
.option("--endpoint ", t("nodes.validate.provider"))
.action(async (opts, cmd) => {
const { endpoint, apiFetchOpts } = resolveNodeEndpoint(opts, cmd);
if (!endpoint) {
process.stderr.write(`error: required option '--endpoint ", t("nodes.metrics.period"), "24h")
.action(async (id, opts, cmd) => {
const res = await apiFetch(`/api/provider-nodes/${id}?metrics=true&period=${opts.period}`);
if (!res.ok) {
process.stderr.write(`Error: ${res.status}\n`);
process.exit(1);
}
emit(await res.json(), cmd.optsWithGlobals());
});
}