// Node location commands: invokes location.get on a paired node and formats the location payload. import { normalizeOptionalLowercaseString } from "@openclaw/normalization-core/string-coerce"; import type { Command } from "commander"; import { defaultRuntime } from "../../runtime.js"; import { runNodesCommand } from "./cli-utils.js"; import { buildNodeInvokeParams, callNodesGatewayCli, nodesCallOpts, parseOptionalNodeNonNegativeInteger, parseOptionalNodePositiveInteger, resolveCliNodeId, } from "./rpc.js"; import type { NodesRpcOpts } from "./types.js"; /** Register node location lookup commands. */ export function registerNodesLocationCommands(nodes: Command) { const location = nodes.command("location").description("Fetch location from a paired node"); nodesCallOpts( location .command("get") .description("Fetch the current location from a node") .requiredOption("--node ", "Node id, name, or IP") .option("--max-age ", "Use cached location newer than this (ms)") .option( "--accuracy ", "Desired accuracy (default: balanced/precise depending on node setting)", ) .option("--location-timeout ", "Location fix timeout (ms)", "10000") .option("--invoke-timeout ", "Node invoke timeout in ms (default 20000)", "20000") .action(async (opts: NodesRpcOpts) => { await runNodesCommand("location get", async () => { const desiredAccuracyRaw = normalizeOptionalLowercaseString(opts.accuracy); const desiredAccuracy = desiredAccuracyRaw === "coarse" || desiredAccuracyRaw === "balanced" || desiredAccuracyRaw === "precise" ? desiredAccuracyRaw : undefined; if (opts.accuracy !== undefined && desiredAccuracy === undefined) { throw new Error("invalid --accuracy (use coarse|balanced|precise)"); } const maxAgeMs = parseOptionalNodeNonNegativeInteger(opts.maxAge, "--max-age"); const timeoutMs = parseOptionalNodePositiveInteger( opts.locationTimeout, "--location-timeout", ); const invokeTimeoutMs = parseOptionalNodePositiveInteger( opts.invokeTimeout, "--invoke-timeout", ); const nodeId = await resolveCliNodeId(opts, opts.node ?? ""); const invokeParams = buildNodeInvokeParams({ nodeId, command: "location.get", params: { maxAgeMs, desiredAccuracy, timeoutMs, }, timeoutMs: invokeTimeoutMs, }); const raw = await callNodesGatewayCli("node.invoke", opts, invokeParams); const res = typeof raw === "object" && raw !== null ? (raw as { payload?: unknown }) : {}; const payload = res.payload && typeof res.payload === "object" ? (res.payload as Record) : {}; if (opts.json) { defaultRuntime.writeJson(payload); return; } const lat = payload.lat; const lon = payload.lon; const acc = payload.accuracyMeters; if (typeof lat === "number" && typeof lon === "number") { const accText = typeof acc === "number" ? ` ±${acc.toFixed(1)}m` : ""; defaultRuntime.log(`${lat},${lon}${accText}`); return; } defaultRuntime.writeJson(payload, 0); }); }), { timeoutMs: 30_000 }, ); }