File size: 3,619 Bytes
f778c12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// 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 <idOrNameOrIp>", "Node id, name, or IP")
      .option("--max-age <ms>", "Use cached location newer than this (ms)")
      .option(
        "--accuracy <coarse|balanced|precise>",
        "Desired accuracy (default: balanced/precise depending on node setting)",
      )
      .option("--location-timeout <ms>", "Location fix timeout (ms)", "10000")
      .option("--invoke-timeout <ms>", "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<string, unknown>)
              : {};

          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 },
  );
}