@matelink/cli 2026.4.17 → 2026.4.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bin/matecli.mjs +152 -10
  2. package/package.json +1 -1
package/bin/matecli.mjs CHANGED
@@ -53,6 +53,102 @@ const DEFAULT_GATEWAY_SCOPES = [
53
53
  ].join(",");
54
54
  const SESSION_CONTEXT_MIN_TOKENS = 1024;
55
55
  const CLI_ENTRY = fileURLToPath(import.meta.url);
56
+
57
+
58
+ function buildPathString(values) {
59
+ const entries = [];
60
+ const seen = new Set();
61
+ for (const value of values) {
62
+ const raw = String(value ?? "").trim();
63
+ if (!raw) continue;
64
+ for (const part of raw.split(":")) {
65
+ const normalized = part.trim();
66
+ if (!normalized || seen.has(normalized)) continue;
67
+ seen.add(normalized);
68
+ entries.push(normalized);
69
+ }
70
+ }
71
+ return entries.join(":");
72
+ }
73
+
74
+ function resolveSiblingBinDirs(executablePath) {
75
+ const normalized = String(executablePath ?? "").trim();
76
+ if (!normalized) return [];
77
+ const binDir = path.dirname(normalized);
78
+ return [binDir];
79
+ }
80
+
81
+ function resolveOpenClawBinaryCandidates() {
82
+ const homeDir = os.homedir();
83
+ const candidates = new Set();
84
+ const addCandidate = (candidate) => {
85
+ const normalized = String(candidate ?? "").trim();
86
+ if (!normalized) {
87
+ return;
88
+ }
89
+ try {
90
+ if (fs.existsSync(normalized)) {
91
+ candidates.add(fs.realpathSync(normalized));
92
+ }
93
+ } catch {
94
+ if (fs.existsSync(normalized)) {
95
+ candidates.add(normalized);
96
+ }
97
+ }
98
+ };
99
+
100
+ for (const nodeCandidate of resolveNodeCandidates()) {
101
+ addCandidate(path.join(path.dirname(nodeCandidate), "openclaw"));
102
+ }
103
+ addCandidate(path.join(homeDir, ".nvm", "current", "bin", "openclaw"));
104
+ addCandidate(path.join(homeDir, ".volta", "bin", "openclaw"));
105
+ addCandidate("/opt/homebrew/bin/openclaw");
106
+ addCandidate("/usr/local/bin/openclaw");
107
+ addCandidate("/usr/bin/openclaw");
108
+
109
+ const whichOpenClaw = runCommand("which", ["openclaw"]);
110
+ if (whichOpenClaw.status === 0) {
111
+ const resolved = String(whichOpenClaw.stdout ?? "").trim();
112
+ if (resolved && fs.existsSync(resolved)) {
113
+ addCandidate(resolved);
114
+ }
115
+ }
116
+
117
+ return [...candidates];
118
+ }
119
+
120
+ function resolveOpenClawBinaryPath() {
121
+ for (const candidate of resolveOpenClawBinaryCandidates()) {
122
+ if (candidate && fs.existsSync(candidate)) {
123
+ return candidate;
124
+ }
125
+ }
126
+ return "openclaw";
127
+ }
128
+
129
+ function buildBridgeRuntimeEnv(extraEnv = {}) {
130
+ const homeDir = os.homedir();
131
+ const nodePath = resolveBridgeServiceNodePath();
132
+ const openclawPath = resolveOpenClawBinaryPath();
133
+ const pathValue = buildPathString([
134
+ ...resolveSiblingBinDirs(nodePath),
135
+ ...resolveSiblingBinDirs(openclawPath),
136
+ "/opt/homebrew/bin",
137
+ "/usr/local/bin",
138
+ "/usr/bin",
139
+ "/bin",
140
+ "/usr/sbin",
141
+ "/sbin",
142
+ path.join(homeDir, ".volta", "bin"),
143
+ path.join(homeDir, ".nvm", "current", "bin"),
144
+ process.env.PATH ?? "",
145
+ ]);
146
+ return {
147
+ ...process.env,
148
+ ...extraEnv,
149
+ PATH: pathValue,
150
+ };
151
+ }
56
152
  const CLI_LANGUAGE = detectCliLanguage();
57
153
  const CLI_I18N = {
58
154
  zh: {
@@ -728,6 +824,7 @@ function listFixedMemoryFiles(workspaceRoot) {
728
824
  workspaceRoot,
729
825
  relativePath,
730
826
  absolutePath,
827
+ includeContent: true,
731
828
  });
732
829
  });
733
830
  }
@@ -1375,6 +1472,40 @@ function extractErrorMessage(decoded, fallback) {
1375
1472
  return fallback;
1376
1473
  }
1377
1474
 
1475
+ function formatErrorDetails(error) {
1476
+ if (!(error instanceof Error)) {
1477
+ return String(error ?? "");
1478
+ }
1479
+ const details = [];
1480
+ if (error.name && error.name !== "Error") {
1481
+ details.push(`name=${error.name}`);
1482
+ }
1483
+ if (error.code) {
1484
+ details.push(`code=${String(error.code)}`);
1485
+ }
1486
+ if (error.errno) {
1487
+ details.push(`errno=${String(error.errno)}`);
1488
+ }
1489
+ if (error.type) {
1490
+ details.push(`type=${String(error.type)}`);
1491
+ }
1492
+ if (error.cause && typeof error.cause === "object") {
1493
+ const cause = error.cause;
1494
+ if (cause?.code) {
1495
+ details.push(`cause.code=${String(cause.code)}`);
1496
+ }
1497
+ if (cause?.errno) {
1498
+ details.push(`cause.errno=${String(cause.errno)}`);
1499
+ }
1500
+ if (cause?.message) {
1501
+ details.push(`cause=${String(cause.message)}`);
1502
+ }
1503
+ }
1504
+ return details.length > 0
1505
+ ? `${error.message} [${details.join(", ")}]`
1506
+ : error.message;
1507
+ }
1508
+
1378
1509
  async function fetchWithTimeout(url, options = {}, timeoutMs = DEFAULT_NETWORK_TIMEOUT_MS) {
1379
1510
  const controller = new AbortController();
1380
1511
  const timer = setTimeout(() => controller.abort(new Error(`fetch timeout after ${timeoutMs}ms`)), timeoutMs);
@@ -1383,6 +1514,8 @@ async function fetchWithTimeout(url, options = {}, timeoutMs = DEFAULT_NETWORK_T
1383
1514
  ...options,
1384
1515
  signal: controller.signal,
1385
1516
  });
1517
+ } catch (error) {
1518
+ throw new Error(formatErrorDetails(error));
1386
1519
  } finally {
1387
1520
  clearTimeout(timer);
1388
1521
  }
@@ -1756,7 +1889,9 @@ function resolveBridgeServiceNodePath() {
1756
1889
  function writeBridgeLaunchScript(installConfig) {
1757
1890
  const scriptPath = resolveBridgeLaunchScriptPath();
1758
1891
  fs.mkdirSync(path.dirname(scriptPath), { recursive: true });
1759
- const defaultPath = [
1892
+ const defaultPath = buildPathString([
1893
+ ...resolveSiblingBinDirs(installConfig.nodePath),
1894
+ ...resolveSiblingBinDirs(installConfig.openclawPath),
1760
1895
  "/opt/homebrew/bin",
1761
1896
  "/usr/local/bin",
1762
1897
  "/usr/bin",
@@ -1765,11 +1900,16 @@ function writeBridgeLaunchScript(installConfig) {
1765
1900
  "/sbin",
1766
1901
  path.join(os.homedir(), ".volta", "bin"),
1767
1902
  path.join(os.homedir(), ".nvm", "current", "bin"),
1768
- ].join(":");
1769
- const knownNodeCandidates = resolveNodeCandidates();
1770
- const candidateChecks = knownNodeCandidates
1771
- .map((candidate) => `if [ -x '${escapePosixShellSingleQuoted(candidate)}' ]; then NODE_BIN='${escapePosixShellSingleQuoted(candidate)}'; fi`)
1772
- .join("\n");
1903
+ ]);
1904
+ const knownNodeCandidates = [installConfig.nodePath, ...resolveNodeCandidates()]
1905
+ .filter(Boolean)
1906
+ .filter((candidate, index, values) => values.indexOf(candidate) === index);
1907
+ const candidateChecks = knownNodeCandidates.length > 0
1908
+ ? `${knownNodeCandidates
1909
+ .map((candidate, index) => `${index === 0 ? 'if' : 'elif'} [ -x '${escapePosixShellSingleQuoted(candidate)}' ]; then NODE_BIN='${escapePosixShellSingleQuoted(candidate)}';`)
1910
+ .join("\n")}
1911
+ fi`
1912
+ : "";
1773
1913
  const fallbackLine = buildPosixShellExec("node", installConfig.args);
1774
1914
  const script = [
1775
1915
  "#!/bin/sh",
@@ -1806,6 +1946,7 @@ function ensureBridgeServiceInstallConfig({ relayUrl, gatewayBaseUrl }) {
1806
1946
  stdoutLog: path.join(logDir, "bridge.stdout.log"),
1807
1947
  stderrLog: path.join(logDir, "bridge.stderr.log"),
1808
1948
  nodePath: resolveBridgeServiceNodePath(),
1949
+ openclawPath: resolveOpenClawBinaryPath(),
1809
1950
  scriptPath: CLI_ENTRY,
1810
1951
  args: bridgeInvocationArgs({ relayUrl, gatewayBaseUrl }),
1811
1952
  };
@@ -2610,7 +2751,7 @@ async function readRelayNextGatewayRequest({
2610
2751
  if (message.includes("timeout")) {
2611
2752
  return null;
2612
2753
  }
2613
- throw error;
2754
+ throw new Error(`relay gateway poll transport failed: GET ${url} -> ${message}`);
2614
2755
  }
2615
2756
 
2616
2757
  if (response.status === 204) {
@@ -3199,8 +3340,9 @@ async function callGatewayRpcLocalViaCli({
3199
3340
  args.push("--token", gatewayAuthToken);
3200
3341
  }
3201
3342
 
3202
- const child = spawn("openclaw", args, {
3343
+ const child = spawn(resolveOpenClawBinaryPath(), args, {
3203
3344
  stdio: ["ignore", "pipe", "pipe"],
3345
+ env: buildBridgeRuntimeEnv(),
3204
3346
  });
3205
3347
  let timedOut = false;
3206
3348
  const timeout = setTimeout(() => {
@@ -3629,12 +3771,12 @@ function startRelayBridgeDetached({
3629
3771
  }
3630
3772
 
3631
3773
  const child = spawn(
3632
- process.execPath,
3774
+ resolveBridgeServiceNodePath(),
3633
3775
  [CLI_ENTRY, "bridge", "--relay", relayUrl, "--gateway", gatewayBaseUrl],
3634
3776
  {
3635
3777
  detached: true,
3636
3778
  stdio: "ignore",
3637
- env: { ...process.env },
3779
+ env: buildBridgeRuntimeEnv(),
3638
3780
  windowsHide: process.platform === "win32",
3639
3781
  },
3640
3782
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matelink/cli",
3
- "version": "2026.4.17",
3
+ "version": "2026.4.18",
4
4
  "private": false,
5
5
  "description": "Relay-first CLI for pairing and bridging OpenClaw gateway traffic",
6
6
  "type": "module",