Spaces:
Configuration error
Configuration error
| import type { Command } from "commander"; | |
| import { callGateway } from "../../gateway/call.js"; | |
| import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../../utils/message-channel.js"; | |
| import { withProgress } from "../progress.js"; | |
| import { parseNodeList, parsePairingList } from "./format.js"; | |
| import type { NodeListNode, NodesRpcOpts } from "./types.js"; | |
| export const nodesCallOpts = (cmd: Command, defaults?: { timeoutMs?: number }) => | |
| cmd | |
| .option("--url <url>", "Gateway WebSocket URL (defaults to gateway.remote.url when configured)") | |
| .option("--token <token>", "Gateway token (if required)") | |
| .option("--timeout <ms>", "Timeout in ms", String(defaults?.timeoutMs ?? 10_000)) | |
| .option("--json", "Output JSON", false); | |
| export const callGatewayCli = async (method: string, opts: NodesRpcOpts, params?: unknown) => | |
| withProgress( | |
| { | |
| label: `Nodes ${method}`, | |
| indeterminate: true, | |
| enabled: opts.json !== true, | |
| }, | |
| async () => | |
| await callGateway({ | |
| url: opts.url, | |
| token: opts.token, | |
| method, | |
| params, | |
| timeoutMs: Number(opts.timeout ?? 10_000), | |
| clientName: GATEWAY_CLIENT_NAMES.CLI, | |
| mode: GATEWAY_CLIENT_MODES.CLI, | |
| }), | |
| ); | |
| export function unauthorizedHintForMessage(message: string): string | null { | |
| const haystack = message.toLowerCase(); | |
| if ( | |
| haystack.includes("unauthorizedclient") || | |
| haystack.includes("bridge client is not authorized") || | |
| haystack.includes("unsigned bridge clients are not allowed") | |
| ) { | |
| return [ | |
| "peekaboo bridge rejected the client.", | |
| "sign the peekaboo CLI (TeamID Y5PE65HELJ) or launch the host with", | |
| "PEEKABOO_ALLOW_UNSIGNED_SOCKET_CLIENTS=1 for local dev.", | |
| ].join(" "); | |
| } | |
| return null; | |
| } | |
| function normalizeNodeKey(value: string) { | |
| return value | |
| .toLowerCase() | |
| .replace(/[^a-z0-9]+/g, "-") | |
| .replace(/^-+/, "") | |
| .replace(/-+$/, ""); | |
| } | |
| export async function resolveNodeId(opts: NodesRpcOpts, query: string) { | |
| const q = String(query ?? "").trim(); | |
| if (!q) throw new Error("node required"); | |
| let nodes: NodeListNode[] = []; | |
| try { | |
| const res = (await callGatewayCli("node.list", opts, {})) as unknown; | |
| nodes = parseNodeList(res); | |
| } catch { | |
| const res = (await callGatewayCli("node.pair.list", opts, {})) as unknown; | |
| const { paired } = parsePairingList(res); | |
| nodes = paired.map((n) => ({ | |
| nodeId: n.nodeId, | |
| displayName: n.displayName, | |
| platform: n.platform, | |
| version: n.version, | |
| remoteIp: n.remoteIp, | |
| })); | |
| } | |
| const qNorm = normalizeNodeKey(q); | |
| const matches = nodes.filter((n) => { | |
| if (n.nodeId === q) return true; | |
| if (typeof n.remoteIp === "string" && n.remoteIp === q) return true; | |
| const name = typeof n.displayName === "string" ? n.displayName : ""; | |
| if (name && normalizeNodeKey(name) === qNorm) return true; | |
| if (q.length >= 6 && n.nodeId.startsWith(q)) return true; | |
| return false; | |
| }); | |
| if (matches.length === 1) return matches[0].nodeId; | |
| if (matches.length === 0) { | |
| const known = nodes | |
| .map((n) => n.displayName || n.remoteIp || n.nodeId) | |
| .filter(Boolean) | |
| .join(", "); | |
| throw new Error(`unknown node: ${q}${known ? ` (known: ${known})` : ""}`); | |
| } | |
| throw new Error( | |
| `ambiguous node: ${q} (matches: ${matches | |
| .map((n) => n.displayName || n.remoteIp || n.nodeId) | |
| .join(", ")})`, | |
| ); | |
| } | |