@silicaclaw/cli 2026.3.20-4 → 2026.3.20-6
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/CHANGELOG.md +12 -0
- package/INSTALL.md +2 -2
- package/README.md +2 -2
- package/VERSION +1 -1
- package/apps/local-console/dist/apps/local-console/src/server.d.ts +13 -2
- package/apps/local-console/dist/apps/local-console/src/server.js +149 -20
- package/apps/local-console/dist/packages/network/src/relayPreview.d.ts +4 -0
- package/apps/local-console/dist/packages/network/src/relayPreview.js +37 -6
- package/apps/local-console/public/app/app.js +45 -2
- package/apps/local-console/public/app/network.js +35 -4
- package/apps/local-console/public/app/social.js +1 -0
- package/apps/local-console/public/app/styles.css +35 -0
- package/apps/local-console/public/app/template.js +1 -0
- package/apps/local-console/public/app/translations.js +18 -6
- package/apps/local-console/src/server.ts +175 -16
- package/node_modules/@silicaclaw/network/dist/packages/network/src/relayPreview.d.ts +4 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/relayPreview.js +37 -6
- package/node_modules/@silicaclaw/network/src/relayPreview.ts +41 -6
- package/openclaw-skills/silicaclaw-broadcast/VERSION +1 -1
- package/openclaw-skills/silicaclaw-broadcast/manifest.json +1 -1
- package/openclaw-skills/silicaclaw-owner-push/VERSION +1 -1
- package/openclaw-skills/silicaclaw-owner-push/manifest.json +1 -1
- package/openclaw-skills/silicaclaw-owner-push/scripts/owner-push-forwarder.mjs +84 -1
- package/package.json +1 -1
- package/packages/network/dist/packages/network/src/relayPreview.d.ts +4 -0
- package/packages/network/dist/packages/network/src/relayPreview.js +37 -6
- package/packages/network/src/relayPreview.ts +41 -6
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { dirname, resolve } from "node:path";
|
|
6
6
|
import { spawn } from "node:child_process";
|
|
@@ -12,9 +12,11 @@ const OWNER_FORWARD_CMD = String(process.env.OPENCLAW_OWNER_FORWARD_CMD || "").t
|
|
|
12
12
|
const STATE_PATH = resolve(
|
|
13
13
|
String(process.env.OPENCLAW_OWNER_FORWARD_STATE_PATH || resolve(homedir(), ".openclaw", "workspace", "state", "silicaclaw-owner-push.json"))
|
|
14
14
|
);
|
|
15
|
+
const LOCK_PATH = `${STATE_PATH}.lock`;
|
|
15
16
|
const LATEST_ONLY = String(process.env.OPENCLAW_FORWARD_LATEST_ONLY || "true").trim().toLowerCase() !== "false";
|
|
16
17
|
const ONCE = process.argv.includes("--once");
|
|
17
18
|
const VERBOSE = process.argv.includes("--verbose");
|
|
19
|
+
let lockFd = null;
|
|
18
20
|
|
|
19
21
|
function parseListEnv(name) {
|
|
20
22
|
return String(process.env[name] || "")
|
|
@@ -89,6 +91,85 @@ function saveState(state) {
|
|
|
89
91
|
writeFileSync(STATE_PATH, JSON.stringify(state, null, 2), "utf8");
|
|
90
92
|
}
|
|
91
93
|
|
|
94
|
+
function isPidRunning(pid) {
|
|
95
|
+
if (!pid || !Number.isFinite(pid) || pid <= 0) return false;
|
|
96
|
+
try {
|
|
97
|
+
process.kill(pid, 0);
|
|
98
|
+
return true;
|
|
99
|
+
} catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function releaseLock() {
|
|
105
|
+
if (lockFd !== null) {
|
|
106
|
+
try {
|
|
107
|
+
closeSync(lockFd);
|
|
108
|
+
} catch {
|
|
109
|
+
// ignore
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
rmSync(LOCK_PATH, { force: true });
|
|
113
|
+
} catch {
|
|
114
|
+
// ignore
|
|
115
|
+
}
|
|
116
|
+
lockFd = null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function acquireLock() {
|
|
121
|
+
mkdirSync(dirname(LOCK_PATH), { recursive: true });
|
|
122
|
+
try {
|
|
123
|
+
lockFd = openSync(LOCK_PATH, "wx");
|
|
124
|
+
writeFileSync(lockFd, JSON.stringify({
|
|
125
|
+
pid: process.pid,
|
|
126
|
+
started_at: new Date().toISOString(),
|
|
127
|
+
state_path: STATE_PATH,
|
|
128
|
+
}, null, 2), "utf8");
|
|
129
|
+
process.on("exit", releaseLock);
|
|
130
|
+
process.on("SIGINT", () => {
|
|
131
|
+
releaseLock();
|
|
132
|
+
process.exit(130);
|
|
133
|
+
});
|
|
134
|
+
process.on("SIGTERM", () => {
|
|
135
|
+
releaseLock();
|
|
136
|
+
process.exit(143);
|
|
137
|
+
});
|
|
138
|
+
return;
|
|
139
|
+
} catch {
|
|
140
|
+
// fall through
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
const existing = JSON.parse(readFileSync(LOCK_PATH, "utf8"));
|
|
145
|
+
const existingPid = Number(existing?.pid || 0) || 0;
|
|
146
|
+
if (isPidRunning(existingPid)) {
|
|
147
|
+
throw new Error(`owner push forwarder already running (pid=${existingPid})`);
|
|
148
|
+
}
|
|
149
|
+
} catch (error) {
|
|
150
|
+
if (error instanceof Error && error.message.includes("already running")) {
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
rmSync(LOCK_PATH, { force: true });
|
|
156
|
+
lockFd = openSync(LOCK_PATH, "wx");
|
|
157
|
+
writeFileSync(lockFd, JSON.stringify({
|
|
158
|
+
pid: process.pid,
|
|
159
|
+
started_at: new Date().toISOString(),
|
|
160
|
+
state_path: STATE_PATH,
|
|
161
|
+
}, null, 2), "utf8");
|
|
162
|
+
process.on("exit", releaseLock);
|
|
163
|
+
process.on("SIGINT", () => {
|
|
164
|
+
releaseLock();
|
|
165
|
+
process.exit(130);
|
|
166
|
+
});
|
|
167
|
+
process.on("SIGTERM", () => {
|
|
168
|
+
releaseLock();
|
|
169
|
+
process.exit(143);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
92
173
|
function trimState(state) {
|
|
93
174
|
const recentIds = Array.isArray(state.seen_ids) ? state.seen_ids.slice(-500) : [];
|
|
94
175
|
const pushedEntries = Object.entries(state.pushed_at || {}).slice(-500);
|
|
@@ -253,10 +334,12 @@ async function pollOnce(state) {
|
|
|
253
334
|
}
|
|
254
335
|
|
|
255
336
|
async function main() {
|
|
337
|
+
acquireLock();
|
|
256
338
|
const state = loadState();
|
|
257
339
|
if (VERBOSE) {
|
|
258
340
|
console.log(`SilicaClaw owner push watching ${API_BASE}`);
|
|
259
341
|
console.log(`State file: ${STATE_PATH}`);
|
|
342
|
+
console.log(`Lock file: ${LOCK_PATH}`);
|
|
260
343
|
console.log(`Latest-only mode: ${LATEST_ONLY ? "on" : "off"}`);
|
|
261
344
|
}
|
|
262
345
|
|
package/package.json
CHANGED
|
@@ -109,7 +109,6 @@ class RelayPreviewAdapter {
|
|
|
109
109
|
try {
|
|
110
110
|
await this.joinRoom("start");
|
|
111
111
|
this.started = true;
|
|
112
|
-
await this.refreshPeers();
|
|
113
112
|
await this.pollOnce();
|
|
114
113
|
this.scheduleNextPoll(this.pollIntervalMs);
|
|
115
114
|
this.recordDiscovery("signaling_connected", { endpoint: this.activeEndpoint });
|
|
@@ -258,8 +257,10 @@ class RelayPreviewAdapter {
|
|
|
258
257
|
const payload = await this.get(`/peers?room=${encodeURIComponent(this.room)}`);
|
|
259
258
|
this.lastPeerRefreshAt = Date.now();
|
|
260
259
|
this.stats.peers_refresh_succeeded += 1;
|
|
261
|
-
const
|
|
262
|
-
|
|
260
|
+
const peerItems = Array.isArray(payload?.peer_details) && payload.peer_details.length
|
|
261
|
+
? payload.peer_details
|
|
262
|
+
: Array.isArray(payload?.peers) ? payload.peers : [];
|
|
263
|
+
this.updatePeersFromList(peerItems);
|
|
263
264
|
}
|
|
264
265
|
onEnvelope(envelope) {
|
|
265
266
|
this.stats.received_total += 1;
|
|
@@ -340,9 +341,13 @@ class RelayPreviewAdapter {
|
|
|
340
341
|
}
|
|
341
342
|
async joinRoom(reason) {
|
|
342
343
|
this.stats.join_attempted += 1;
|
|
343
|
-
await this.post("/join", { room: this.room, peer_id: this.peerId });
|
|
344
|
+
const payload = await this.post("/join", { room: this.room, peer_id: this.peerId });
|
|
344
345
|
this.lastJoinAt = Date.now();
|
|
345
346
|
this.stats.join_succeeded += 1;
|
|
347
|
+
if (Array.isArray(payload?.peers)) {
|
|
348
|
+
this.updatePeersFromList(payload.peers);
|
|
349
|
+
this.lastPeerRefreshAt = this.lastJoinAt;
|
|
350
|
+
}
|
|
346
351
|
this.recordDiscovery("join_ok", { endpoint: this.activeEndpoint, detail: reason });
|
|
347
352
|
}
|
|
348
353
|
async maybeRefreshJoin(reason) {
|
|
@@ -407,13 +412,38 @@ class RelayPreviewAdapter {
|
|
|
407
412
|
throw new Error(errors.join(" | "));
|
|
408
413
|
}
|
|
409
414
|
updatePeersFromList(values) {
|
|
410
|
-
const
|
|
415
|
+
const parsedPeers = [];
|
|
416
|
+
for (const value of values) {
|
|
417
|
+
if (typeof value === "string") {
|
|
418
|
+
const peerId = String(value || "").trim();
|
|
419
|
+
if (peerId) {
|
|
420
|
+
parsedPeers.push({ peer_id: peerId });
|
|
421
|
+
}
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
if (value && typeof value === "object") {
|
|
425
|
+
const raw = value;
|
|
426
|
+
const peerId = String(raw.peer_id || "").trim();
|
|
427
|
+
if (!peerId) {
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
parsedPeers.push({
|
|
431
|
+
peer_id: peerId,
|
|
432
|
+
meta: {
|
|
433
|
+
signal_queue_size: Number(raw.signal_queue_size ?? 0),
|
|
434
|
+
relay_queue_size: Number(raw.relay_queue_size ?? 0),
|
|
435
|
+
},
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
const peerIds = parsedPeers.map((peer) => peer.peer_id);
|
|
411
440
|
if (!peerIds.includes(this.peerId)) {
|
|
412
441
|
void this.joinRoom("self_missing_from_peers").catch(() => { });
|
|
413
442
|
}
|
|
414
443
|
const now = Date.now();
|
|
415
444
|
const next = new Map();
|
|
416
|
-
for (const
|
|
445
|
+
for (const peerInfo of parsedPeers) {
|
|
446
|
+
const peerId = peerInfo.peer_id;
|
|
417
447
|
if (peerId === this.peerId)
|
|
418
448
|
continue;
|
|
419
449
|
const existing = this.peers.get(peerId);
|
|
@@ -427,6 +457,7 @@ class RelayPreviewAdapter {
|
|
|
427
457
|
last_seen_at: now,
|
|
428
458
|
messages_seen: existing?.messages_seen ?? 0,
|
|
429
459
|
reconnect_attempts: existing?.reconnect_attempts ?? 0,
|
|
460
|
+
meta: peerInfo.meta || existing?.meta,
|
|
430
461
|
});
|
|
431
462
|
}
|
|
432
463
|
for (const peerId of this.peers.keys()) {
|
|
@@ -34,6 +34,10 @@ type RelayPeer = {
|
|
|
34
34
|
last_seen_at: number;
|
|
35
35
|
messages_seen: number;
|
|
36
36
|
reconnect_attempts: number;
|
|
37
|
+
meta?: {
|
|
38
|
+
signal_queue_size?: number;
|
|
39
|
+
relay_queue_size?: number;
|
|
40
|
+
};
|
|
37
41
|
};
|
|
38
42
|
|
|
39
43
|
type RelayDiagnostics = {
|
|
@@ -227,7 +231,6 @@ export class RelayPreviewAdapter implements NetworkAdapter {
|
|
|
227
231
|
try {
|
|
228
232
|
await this.joinRoom("start");
|
|
229
233
|
this.started = true;
|
|
230
|
-
await this.refreshPeers();
|
|
231
234
|
await this.pollOnce();
|
|
232
235
|
this.scheduleNextPoll(this.pollIntervalMs);
|
|
233
236
|
this.recordDiscovery("signaling_connected", { endpoint: this.activeEndpoint });
|
|
@@ -375,8 +378,10 @@ export class RelayPreviewAdapter implements NetworkAdapter {
|
|
|
375
378
|
const payload = await this.get(`/peers?room=${encodeURIComponent(this.room)}`);
|
|
376
379
|
this.lastPeerRefreshAt = Date.now();
|
|
377
380
|
this.stats.peers_refresh_succeeded += 1;
|
|
378
|
-
const
|
|
379
|
-
|
|
381
|
+
const peerItems = Array.isArray(payload?.peer_details) && payload.peer_details.length
|
|
382
|
+
? payload.peer_details
|
|
383
|
+
: Array.isArray(payload?.peers) ? payload.peers : [];
|
|
384
|
+
this.updatePeersFromList(peerItems);
|
|
380
385
|
}
|
|
381
386
|
|
|
382
387
|
private onEnvelope(envelope: unknown): void {
|
|
@@ -457,9 +462,13 @@ export class RelayPreviewAdapter implements NetworkAdapter {
|
|
|
457
462
|
|
|
458
463
|
private async joinRoom(reason: string): Promise<void> {
|
|
459
464
|
this.stats.join_attempted += 1;
|
|
460
|
-
await this.post("/join", { room: this.room, peer_id: this.peerId });
|
|
465
|
+
const payload = await this.post("/join", { room: this.room, peer_id: this.peerId });
|
|
461
466
|
this.lastJoinAt = Date.now();
|
|
462
467
|
this.stats.join_succeeded += 1;
|
|
468
|
+
if (Array.isArray(payload?.peers)) {
|
|
469
|
+
this.updatePeersFromList(payload.peers);
|
|
470
|
+
this.lastPeerRefreshAt = this.lastJoinAt;
|
|
471
|
+
}
|
|
463
472
|
this.recordDiscovery("join_ok", { endpoint: this.activeEndpoint, detail: reason });
|
|
464
473
|
}
|
|
465
474
|
|
|
@@ -528,13 +537,38 @@ export class RelayPreviewAdapter implements NetworkAdapter {
|
|
|
528
537
|
}
|
|
529
538
|
|
|
530
539
|
private updatePeersFromList(values: unknown[]): void {
|
|
531
|
-
const
|
|
540
|
+
const parsedPeers: Array<{ peer_id: string; meta?: RelayPeer["meta"] }> = [];
|
|
541
|
+
for (const value of values) {
|
|
542
|
+
if (typeof value === "string") {
|
|
543
|
+
const peerId = String(value || "").trim();
|
|
544
|
+
if (peerId) {
|
|
545
|
+
parsedPeers.push({ peer_id: peerId });
|
|
546
|
+
}
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
if (value && typeof value === "object") {
|
|
550
|
+
const raw = value as Record<string, unknown>;
|
|
551
|
+
const peerId = String(raw.peer_id || "").trim();
|
|
552
|
+
if (!peerId) {
|
|
553
|
+
continue;
|
|
554
|
+
}
|
|
555
|
+
parsedPeers.push({
|
|
556
|
+
peer_id: peerId,
|
|
557
|
+
meta: {
|
|
558
|
+
signal_queue_size: Number(raw.signal_queue_size ?? 0),
|
|
559
|
+
relay_queue_size: Number(raw.relay_queue_size ?? 0),
|
|
560
|
+
},
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
const peerIds = parsedPeers.map((peer) => peer.peer_id);
|
|
532
565
|
if (!peerIds.includes(this.peerId)) {
|
|
533
566
|
void this.joinRoom("self_missing_from_peers").catch(() => {});
|
|
534
567
|
}
|
|
535
568
|
const now = Date.now();
|
|
536
569
|
const next = new Map<string, RelayPeer>();
|
|
537
|
-
for (const
|
|
570
|
+
for (const peerInfo of parsedPeers) {
|
|
571
|
+
const peerId = peerInfo.peer_id;
|
|
538
572
|
if (peerId === this.peerId) continue;
|
|
539
573
|
const existing = this.peers.get(peerId);
|
|
540
574
|
if (!existing) {
|
|
@@ -547,6 +581,7 @@ export class RelayPreviewAdapter implements NetworkAdapter {
|
|
|
547
581
|
last_seen_at: now,
|
|
548
582
|
messages_seen: existing?.messages_seen ?? 0,
|
|
549
583
|
reconnect_attempts: existing?.reconnect_attempts ?? 0,
|
|
584
|
+
meta: peerInfo.meta || existing?.meta,
|
|
550
585
|
});
|
|
551
586
|
}
|
|
552
587
|
for (const peerId of this.peers.keys()) {
|