@matelink/cli 2026.4.21 → 2026.4.23
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.
- package/README.md +28 -0
- package/bin/matecli.mjs +132 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -178,3 +178,31 @@ API:
|
|
|
178
178
|
- `GET /v1/gateways/{gatewayId}/requests/next` gateway bridge polls pending chat requests
|
|
179
179
|
- `POST /v1/gateways/{gatewayId}/requests/{requestId}/events` gateway bridge streams relay events
|
|
180
180
|
- `POST /v1/openclaw/responses` app chat endpoint (supports `gatewayId + clientToken`)
|
|
181
|
+
|
|
182
|
+
## 9) CLI Bridge Logging
|
|
183
|
+
|
|
184
|
+
Bridge runs as a systemd user service on server 80, logs are written to files.
|
|
185
|
+
|
|
186
|
+
### Viewing logs (server 80)
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
tail -f /root/.openclaw/matecli/logs/bridge.stdout.log # normal logs (requests, relay status)
|
|
190
|
+
tail -f /root/.openclaw/matecli/logs/bridge.stderr.log # error logs (poll failures, retries)
|
|
191
|
+
|
|
192
|
+
# last 100 lines
|
|
193
|
+
tail -100 /root/.openclaw/matecli/logs/bridge.stderr.log
|
|
194
|
+
|
|
195
|
+
# search for specific request
|
|
196
|
+
grep "Bridge request" /root/.openclaw/matecli/logs/bridge.stdout.log | tail -20
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Service management
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
systemctl --user status matelink-matecli-bridge # check status
|
|
203
|
+
systemctl --user restart matelink-matecli-bridge # restart
|
|
204
|
+
systemctl --user stop matelink-matecli-bridge # stop
|
|
205
|
+
journalctl --user -u matelink-matecli-bridge -f # systemd journal (if enabled)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Service config: `~/.config/systemd/user/matelink-matecli-bridge.service`
|
package/bin/matecli.mjs
CHANGED
|
@@ -18,7 +18,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
18
18
|
|
|
19
19
|
const NUMERIC_CODE_LENGTH = 4;
|
|
20
20
|
const GROUP_SIZE = 4;
|
|
21
|
-
const DEFAULT_RELAY_WORKER_WAIT_SECONDS =
|
|
21
|
+
const DEFAULT_RELAY_WORKER_WAIT_SECONDS = 90;
|
|
22
22
|
const DEFAULT_NETWORK_TIMEOUT_MS = 600000;
|
|
23
23
|
const BRIDGE_RETRY_DELAY_MS = 2000;
|
|
24
24
|
const DEFAULT_RELAY_URL = "https://test-mate.clipzap.ai";
|
|
@@ -54,6 +54,15 @@ const DEFAULT_GATEWAY_SCOPES = [
|
|
|
54
54
|
const SESSION_CONTEXT_MIN_TOKENS = 1024;
|
|
55
55
|
const CLI_ENTRY = fileURLToPath(import.meta.url);
|
|
56
56
|
|
|
57
|
+
function readCliVersion() {
|
|
58
|
+
try {
|
|
59
|
+
const pkgPath = path.resolve(path.dirname(CLI_ENTRY), "..", "package.json");
|
|
60
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
61
|
+
return pkg.version || "unknown";
|
|
62
|
+
} catch {
|
|
63
|
+
return "unknown";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
57
66
|
|
|
58
67
|
function buildPathString(values) {
|
|
59
68
|
const entries = [];
|
|
@@ -2744,6 +2753,13 @@ async function readRelayNextGatewayRequest({
|
|
|
2744
2753
|
method: "GET",
|
|
2745
2754
|
headers: {
|
|
2746
2755
|
...relayGatewayHeaders(gatewayToken),
|
|
2756
|
+
"x-bridge-version": CLI_PACKAGE_NAME + "/" + readCliVersion(),
|
|
2757
|
+
"x-bridge-os": os.platform() + "/" + os.release(),
|
|
2758
|
+
"x-bridge-arch": os.arch(),
|
|
2759
|
+
"x-bridge-hostname": os.hostname(),
|
|
2760
|
+
"x-bridge-node": process.version,
|
|
2761
|
+
"x-bridge-pid": String(process.pid),
|
|
2762
|
+
"x-bridge-gateway-id": gatewayId ?? "",
|
|
2747
2763
|
},
|
|
2748
2764
|
}, (Math.max(waitSeconds, 1) + 15) * 1000);
|
|
2749
2765
|
} catch (error) {
|
|
@@ -3673,6 +3689,22 @@ async function proxyGatewayRequestLocal({
|
|
|
3673
3689
|
}
|
|
3674
3690
|
}
|
|
3675
3691
|
|
|
3692
|
+
async function reportBridgeMetaToRelay({ relayUrl, gatewayId, gatewayToken, meta }) {
|
|
3693
|
+
const url = `${relayUrl}/v1/gateways/${encodeURIComponent(gatewayId)}/meta`;
|
|
3694
|
+
try {
|
|
3695
|
+
await fetchWithTimeout(url, {
|
|
3696
|
+
method: "POST",
|
|
3697
|
+
headers: {
|
|
3698
|
+
"content-type": "application/json",
|
|
3699
|
+
...relayGatewayHeaders(gatewayToken),
|
|
3700
|
+
},
|
|
3701
|
+
body: JSON.stringify(meta),
|
|
3702
|
+
}, 15000);
|
|
3703
|
+
} catch {
|
|
3704
|
+
// best-effort, don't fail bridge startup
|
|
3705
|
+
}
|
|
3706
|
+
}
|
|
3707
|
+
|
|
3676
3708
|
async function runRelayBridge({
|
|
3677
3709
|
relayUrl,
|
|
3678
3710
|
gatewayId,
|
|
@@ -3680,6 +3712,7 @@ async function runRelayBridge({
|
|
|
3680
3712
|
gatewayBaseUrl,
|
|
3681
3713
|
gatewayAuthToken,
|
|
3682
3714
|
json,
|
|
3715
|
+
bridgeMeta,
|
|
3683
3716
|
}) {
|
|
3684
3717
|
if (!relayUrl) {
|
|
3685
3718
|
fail("relay URL is required for bridge mode");
|
|
@@ -3692,6 +3725,18 @@ async function runRelayBridge({
|
|
|
3692
3725
|
console.log(`Relay bridge online. gatewayId=${gatewayId}`);
|
|
3693
3726
|
console.log(`Relay URL: ${relayUrl}`);
|
|
3694
3727
|
console.log(`Local Gateway: ${gatewayBaseUrl}`);
|
|
3728
|
+
console.log(`Bridge version: ${bridgeMeta.bridgeVersion}`);
|
|
3729
|
+
console.log(`OS: ${bridgeMeta.osType} ${bridgeMeta.osRelease} (${bridgeMeta.arch})`);
|
|
3730
|
+
console.log(`Hostname: ${bridgeMeta.hostname} | User: ${bridgeMeta.username}`);
|
|
3731
|
+
console.log(`Node: ${bridgeMeta.nodeVersion} | PID: ${bridgeMeta.pid}`);
|
|
3732
|
+
console.log(`CPU: ${bridgeMeta.cpus}x ${bridgeMeta.cpuModel ?? "unknown"}`);
|
|
3733
|
+
console.log(`Memory: ${Math.round(bridgeMeta.freeMemory / 1024 / 1024)}MB free / ${Math.round(bridgeMeta.totalMemory / 1024 / 1024)}MB total`);
|
|
3734
|
+
if (bridgeMeta.linkedUserId) console.log(`Linked user: ${bridgeMeta.linkedUserId}`);
|
|
3735
|
+
if (bridgeMeta.accountId) console.log(`Account: ${bridgeMeta.accountId}`);
|
|
3736
|
+
if (bridgeMeta.deviceId) console.log(`Device: ${bridgeMeta.deviceId}`);
|
|
3737
|
+
if (bridgeMeta.pairCode) console.log(`Last pair code: ${bridgeMeta.pairCode}`);
|
|
3738
|
+
console.log(`Workspace: ${bridgeMeta.workspacePath ?? "default"}`);
|
|
3739
|
+
console.log(`Transport: ${bridgeMeta.transportMode}`);
|
|
3695
3740
|
}
|
|
3696
3741
|
|
|
3697
3742
|
setGatewayWsBridgePublisher(async (event, payload) => {
|
|
@@ -3708,6 +3753,8 @@ async function runRelayBridge({
|
|
|
3708
3753
|
});
|
|
3709
3754
|
});
|
|
3710
3755
|
|
|
3756
|
+
await reportBridgeMetaToRelay({ relayUrl, gatewayId, gatewayToken, meta: bridgeMeta });
|
|
3757
|
+
|
|
3711
3758
|
const activeRequests = new Set();
|
|
3712
3759
|
|
|
3713
3760
|
while (true) {
|
|
@@ -4506,6 +4553,88 @@ async function runBridge({ json, relay, noRelay, gateway }) {
|
|
|
4506
4553
|
process.exit(0);
|
|
4507
4554
|
});
|
|
4508
4555
|
|
|
4556
|
+
const linkedUserId = typeof section?.linkedUserId === "string" && section.linkedUserId.trim()
|
|
4557
|
+
? section.linkedUserId.trim()
|
|
4558
|
+
: null;
|
|
4559
|
+
const pairCode = typeof section?.lastPairCode === "string" ? section.lastPairCode.trim() : null;
|
|
4560
|
+
|
|
4561
|
+
// Collect OpenClaw config (sanitized — strip sensitive tokens).
|
|
4562
|
+
const openclawConfig = (() => {
|
|
4563
|
+
try {
|
|
4564
|
+
const raw = readOptionalJsonFile(configPath);
|
|
4565
|
+
// Redact secrets but keep structure.
|
|
4566
|
+
const sanitized = JSON.parse(JSON.stringify(raw));
|
|
4567
|
+
if (sanitized?.gateway?.auth?.token) sanitized.gateway.auth.token = "***";
|
|
4568
|
+
if (sanitized?.channels?.testnextim?.appSecret) sanitized.channels.testnextim.appSecret = "***";
|
|
4569
|
+
if (sanitized?.channels?.["custom-im"]?.appSecret) sanitized.channels["custom-im"].appSecret = "***";
|
|
4570
|
+
return sanitized;
|
|
4571
|
+
} catch {
|
|
4572
|
+
return null;
|
|
4573
|
+
}
|
|
4574
|
+
})();
|
|
4575
|
+
|
|
4576
|
+
// Resolve workspace path.
|
|
4577
|
+
const workspacePath = (() => {
|
|
4578
|
+
try { return resolveConfiguredWorkspaceRoot(); } catch { return null; }
|
|
4579
|
+
})();
|
|
4580
|
+
|
|
4581
|
+
// Read device identity id (if available).
|
|
4582
|
+
const deviceId = (() => {
|
|
4583
|
+
try {
|
|
4584
|
+
const identity = readJsonFileIfExists(GATEWAY_CLIENT_IDENTITY_PATH);
|
|
4585
|
+
return identity?.deviceId ?? null;
|
|
4586
|
+
} catch { return null; }
|
|
4587
|
+
})();
|
|
4588
|
+
|
|
4589
|
+
|
|
4590
|
+
const bridgeMeta = {
|
|
4591
|
+
gatewayId: creds.gatewayId,
|
|
4592
|
+
relayUrl,
|
|
4593
|
+
gatewayBaseUrl,
|
|
4594
|
+
bridgeVersion: readCliVersion(),
|
|
4595
|
+
// System info
|
|
4596
|
+
os: os.platform() + "/" + os.release(),
|
|
4597
|
+
osPlatform: os.platform(),
|
|
4598
|
+
osRelease: os.release(),
|
|
4599
|
+
osType: os.type(),
|
|
4600
|
+
arch: os.arch(),
|
|
4601
|
+
hostname: os.hostname(),
|
|
4602
|
+
nodeVersion: process.version,
|
|
4603
|
+
pid: process.pid,
|
|
4604
|
+
uptime: os.uptime(),
|
|
4605
|
+
totalMemory: os.totalmem(),
|
|
4606
|
+
freeMemory: os.freemem(),
|
|
4607
|
+
cpus: os.cpus().length,
|
|
4608
|
+
cpuModel: os.cpus()[0]?.model ?? null,
|
|
4609
|
+
username: os.userInfo().username,
|
|
4610
|
+
homeDir: os.homedir(),
|
|
4611
|
+
shell: os.userInfo().shell ?? null,
|
|
4612
|
+
// Pairing & user info
|
|
4613
|
+
linkedUserId: linkedUserId,
|
|
4614
|
+
pairCode: pairCode,
|
|
4615
|
+
accountId: section?.accountId ?? null,
|
|
4616
|
+
clientUserId: section?.clientUserId ?? null,
|
|
4617
|
+
deviceId: deviceId,
|
|
4618
|
+
// Config paths
|
|
4619
|
+
configPath,
|
|
4620
|
+
statePath: resolveTestNextIMStatePath(),
|
|
4621
|
+
openclawHome: resolveOpenClawHome(),
|
|
4622
|
+
workspacePath: workspacePath,
|
|
4623
|
+
// OpenClaw config (sanitized)
|
|
4624
|
+
openclawConfig: openclawConfig,
|
|
4625
|
+
// Gateway details
|
|
4626
|
+
gatewayPort: resolveGatewayPort(config) ?? null,
|
|
4627
|
+
gatewayHost: resolveGatewayHost(config),
|
|
4628
|
+
hasGatewayAuthToken: Boolean(gatewayAuthToken),
|
|
4629
|
+
// Channel/transport
|
|
4630
|
+
transportMode: normalizeChatTransportMode(section?.chatTransportMode, "relay"),
|
|
4631
|
+
channelEnabled: section?.enabled !== false,
|
|
4632
|
+
channelName: section?.name ?? "TestNextIM",
|
|
4633
|
+
bindPublicBaseUrl: typeof section?.bindPublicBaseUrl === "string" ? section.bindPublicBaseUrl : null,
|
|
4634
|
+
// Timestamps
|
|
4635
|
+
startedAt: new Date().toISOString(),
|
|
4636
|
+
};
|
|
4637
|
+
|
|
4509
4638
|
try {
|
|
4510
4639
|
if (json) {
|
|
4511
4640
|
console.log(
|
|
@@ -4513,9 +4642,7 @@ async function runBridge({ json, relay, noRelay, gateway }) {
|
|
|
4513
4642
|
{
|
|
4514
4643
|
command: "bridge",
|
|
4515
4644
|
status: "running",
|
|
4516
|
-
|
|
4517
|
-
gatewayId: creds.gatewayId,
|
|
4518
|
-
gatewayBaseUrl,
|
|
4645
|
+
...bridgeMeta,
|
|
4519
4646
|
lockPath: bridgeLock.lockPath,
|
|
4520
4647
|
},
|
|
4521
4648
|
null,
|
|
@@ -4531,6 +4658,7 @@ async function runBridge({ json, relay, noRelay, gateway }) {
|
|
|
4531
4658
|
gatewayBaseUrl,
|
|
4532
4659
|
gatewayAuthToken,
|
|
4533
4660
|
json,
|
|
4661
|
+
bridgeMeta,
|
|
4534
4662
|
});
|
|
4535
4663
|
} finally {
|
|
4536
4664
|
cleanupBridgeLock();
|