@silicaclaw/cli 2026.3.20-5 → 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 +6 -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 +8 -2
- package/apps/local-console/dist/apps/local-console/src/server.js +105 -8
- package/apps/local-console/public/app/social.js +1 -0
- package/apps/local-console/public/app/translations.js +6 -6
- package/apps/local-console/src/server.ts +122 -3
- 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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## v1.0 beta - 2026-03-20
|
|
4
4
|
|
|
5
|
+
### 2026.3.20-6
|
|
6
|
+
|
|
7
|
+
- release build:
|
|
8
|
+
- prepared another fresh latest-channel package build without publishing
|
|
9
|
+
- regenerated the npm tarball through the verified release packing workflow
|
|
10
|
+
|
|
5
11
|
### 2026.3.20-5
|
|
6
12
|
|
|
7
13
|
- release build:
|
package/INSTALL.md
CHANGED
|
@@ -211,9 +211,9 @@ npx clawhub publish openclaw-skills/silicaclaw-broadcast \
|
|
|
211
211
|
npx clawhub publish openclaw-skills/silicaclaw-owner-push \
|
|
212
212
|
--slug silicaclaw-owner-push \
|
|
213
213
|
--name "SilicaClaw Owner Push" \
|
|
214
|
-
--version 2026.3.20-beta.
|
|
214
|
+
--version 2026.3.20-beta.3 \
|
|
215
215
|
--tags latest \
|
|
216
|
-
--changelog "Added
|
|
216
|
+
--changelog "Added single-instance lock protection so owner push avoids duplicate notifications when multiple forwarders start at the same time."
|
|
217
217
|
```
|
|
218
218
|
|
|
219
219
|
ClawHub expects each skill version to be valid semver, so use the versions from each skill's `manifest.json` and `VERSION`, not the npm CLI version format.
|
package/README.md
CHANGED
|
@@ -283,9 +283,9 @@ npx clawhub publish openclaw-skills/silicaclaw-broadcast \
|
|
|
283
283
|
npx clawhub publish openclaw-skills/silicaclaw-owner-push \
|
|
284
284
|
--slug silicaclaw-owner-push \
|
|
285
285
|
--name "SilicaClaw Owner Push" \
|
|
286
|
-
--version 2026.3.20-beta.
|
|
286
|
+
--version 2026.3.20-beta.3 \
|
|
287
287
|
--tags latest \
|
|
288
|
-
--changelog "Added
|
|
288
|
+
--changelog "Added single-instance lock protection so owner push avoids duplicate notifications when multiple forwarders start at the same time."
|
|
289
289
|
```
|
|
290
290
|
|
|
291
291
|
ClawHub publishes the OpenClaw skill folders, not the npm CLI package.
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
v2026.3.20-
|
|
1
|
+
v2026.3.20-6
|
|
@@ -72,11 +72,17 @@ type OpenClawBridgeStatus = {
|
|
|
72
72
|
gateway_url: string;
|
|
73
73
|
gateway_port: number;
|
|
74
74
|
gateway_reachable: boolean;
|
|
75
|
+
status_command: string | null;
|
|
76
|
+
status_ok: boolean;
|
|
77
|
+
status_summary: string | null;
|
|
78
|
+
gateway_probe_command: string | null;
|
|
79
|
+
gateway_probe_ok: boolean;
|
|
80
|
+
gateway_probe_summary: string | null;
|
|
75
81
|
configured_gateway_url: string;
|
|
76
82
|
configured_gateway_port: number;
|
|
77
83
|
configured_gateway_bind: string | null;
|
|
78
84
|
configured_gateway_config_path: string | null;
|
|
79
|
-
detection_mode: "process" | "gateway" | "process+gateway" | "not_running";
|
|
85
|
+
detection_mode: "gateway-probe" | "gateway-probe+process" | "gateway-probe+gateway" | "gateway-probe+process+gateway" | "process" | "gateway" | "process+gateway" | "not_running";
|
|
80
86
|
};
|
|
81
87
|
skill_learning: {
|
|
82
88
|
available: boolean;
|
|
@@ -636,7 +642,7 @@ export declare class LocalNodeService {
|
|
|
636
642
|
openclaw: {
|
|
637
643
|
detected: boolean;
|
|
638
644
|
running: boolean;
|
|
639
|
-
detection_mode: "process+gateway" | "gateway" | "process" | "not_running";
|
|
645
|
+
detection_mode: "gateway" | "gateway-probe+process+gateway" | "gateway-probe+gateway" | "gateway-probe+process" | "gateway-probe" | "process+gateway" | "process" | "not_running";
|
|
640
646
|
gateway_url: string;
|
|
641
647
|
workspace_install_root: string;
|
|
642
648
|
legacy_install_root: string;
|
|
@@ -389,8 +389,71 @@ function readOpenClawConfiguredGateway(workspaceRoot) {
|
|
|
389
389
|
gateway_url: OPENCLAW_GATEWAY_URL,
|
|
390
390
|
};
|
|
391
391
|
}
|
|
392
|
+
function resolveOpenClawStatusCommand(workspaceRoot) {
|
|
393
|
+
const explicitBin = String(process.env.OPENCLAW_BIN || "").trim();
|
|
394
|
+
if (explicitBin) {
|
|
395
|
+
return { cmd: explicitBin, args: ["status"] };
|
|
396
|
+
}
|
|
397
|
+
const configuredSourceDir = String(process.env.OPENCLAW_SOURCE_DIR || "").trim();
|
|
398
|
+
const defaultSourceDir = defaultOpenClawSourceDir(workspaceRoot);
|
|
399
|
+
const sourceDir = configuredSourceDir || defaultSourceDir;
|
|
400
|
+
const sourceEntry = existingPathOrNull((0, path_1.resolve)(sourceDir, "openclaw.mjs"));
|
|
401
|
+
if (sourceEntry) {
|
|
402
|
+
return { cmd: process.execPath, args: [sourceEntry, "status"] };
|
|
403
|
+
}
|
|
404
|
+
const commandPath = resolveExecutableInPath("openclaw");
|
|
405
|
+
if (commandPath) {
|
|
406
|
+
return { cmd: commandPath, args: ["status"] };
|
|
407
|
+
}
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
function resolveOpenClawGatewayProbeCommand(workspaceRoot) {
|
|
411
|
+
const explicitBin = String(process.env.OPENCLAW_BIN || "").trim();
|
|
412
|
+
if (explicitBin) {
|
|
413
|
+
return { cmd: explicitBin, args: ["gateway", "probe"] };
|
|
414
|
+
}
|
|
415
|
+
const configuredSourceDir = String(process.env.OPENCLAW_SOURCE_DIR || "").trim();
|
|
416
|
+
const defaultSourceDir = defaultOpenClawSourceDir(workspaceRoot);
|
|
417
|
+
const sourceDir = configuredSourceDir || defaultSourceDir;
|
|
418
|
+
const sourceEntry = existingPathOrNull((0, path_1.resolve)(sourceDir, "openclaw.mjs"));
|
|
419
|
+
if (sourceEntry) {
|
|
420
|
+
return { cmd: process.execPath, args: [sourceEntry, "gateway", "probe"] };
|
|
421
|
+
}
|
|
422
|
+
const commandPath = resolveExecutableInPath("openclaw");
|
|
423
|
+
if (commandPath) {
|
|
424
|
+
return { cmd: commandPath, args: ["gateway", "probe"] };
|
|
425
|
+
}
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
392
428
|
function detectOpenClawRuntime(workspaceRoot) {
|
|
393
429
|
const configuredGateway = readOpenClawConfiguredGateway(workspaceRoot);
|
|
430
|
+
const statusCommand = resolveOpenClawStatusCommand(workspaceRoot);
|
|
431
|
+
const gatewayProbeCommand = resolveOpenClawGatewayProbeCommand(workspaceRoot);
|
|
432
|
+
const statusProbe = statusCommand
|
|
433
|
+
? (0, child_process_1.spawnSync)(statusCommand.cmd, statusCommand.args, {
|
|
434
|
+
encoding: "utf8",
|
|
435
|
+
env: process.env,
|
|
436
|
+
})
|
|
437
|
+
: null;
|
|
438
|
+
const statusStdout = String(statusProbe?.stdout || "");
|
|
439
|
+
const statusStderr = String(statusProbe?.stderr || "");
|
|
440
|
+
const statusLooksConfigured = Boolean(statusCommand &&
|
|
441
|
+
statusProbe &&
|
|
442
|
+
statusProbe.status === 0 &&
|
|
443
|
+
(/\bChannels\b/i.test(statusStdout) ||
|
|
444
|
+
/\bSessions\b/i.test(statusStdout) ||
|
|
445
|
+
/\bNext steps:\b/i.test(statusStdout)));
|
|
446
|
+
const gatewayStatusProbe = gatewayProbeCommand
|
|
447
|
+
? (0, child_process_1.spawnSync)(gatewayProbeCommand.cmd, gatewayProbeCommand.args, {
|
|
448
|
+
encoding: "utf8",
|
|
449
|
+
env: process.env,
|
|
450
|
+
})
|
|
451
|
+
: null;
|
|
452
|
+
const gatewayStatusStdout = String(gatewayStatusProbe?.stdout || "");
|
|
453
|
+
const gatewayStatusStderr = String(gatewayStatusProbe?.stderr || "");
|
|
454
|
+
const gatewayProbeOk = Boolean(gatewayProbeCommand &&
|
|
455
|
+
gatewayStatusProbe &&
|
|
456
|
+
gatewayStatusProbe.status === 0);
|
|
394
457
|
const result = (0, child_process_1.spawnSync)("ps", ["-Ao", "pid=,ppid=,command="], {
|
|
395
458
|
encoding: "utf8",
|
|
396
459
|
});
|
|
@@ -473,6 +536,12 @@ function detectOpenClawRuntime(workspaceRoot) {
|
|
|
473
536
|
const allProcesses = Array.from(combinedProcesses.values());
|
|
474
537
|
const gatewayReachable = gatewayListeners.length > 0;
|
|
475
538
|
const detectionNotes = [];
|
|
539
|
+
if (statusProbe && statusProbe.status !== 0) {
|
|
540
|
+
detectionNotes.push(String(statusStderr || "openclaw status failed").trim());
|
|
541
|
+
}
|
|
542
|
+
if (gatewayStatusProbe && gatewayStatusProbe.status !== 0) {
|
|
543
|
+
detectionNotes.push(String(gatewayStatusStderr || "openclaw gateway probe failed").trim());
|
|
544
|
+
}
|
|
476
545
|
if (result.status !== 0)
|
|
477
546
|
detectionNotes.push(String(result.stderr || "ps failed").trim());
|
|
478
547
|
if (gatewayProbe.status !== 0 && gatewayLines.length === 0) {
|
|
@@ -481,24 +550,52 @@ function detectOpenClawRuntime(workspaceRoot) {
|
|
|
481
550
|
const gatewayPort = preferredListener?.port || configuredGateway.gateway_port;
|
|
482
551
|
const gatewayUrl = `http://${OPENCLAW_GATEWAY_HOST}:${gatewayPort}/`;
|
|
483
552
|
return {
|
|
484
|
-
running: allProcesses.length > 0 || gatewayReachable,
|
|
553
|
+
running: gatewayProbeOk || allProcesses.length > 0 || gatewayReachable,
|
|
485
554
|
process_count: allProcesses.length,
|
|
486
555
|
processes: allProcesses.slice(0, 10),
|
|
487
556
|
detection_error: detectionNotes.filter(Boolean).join(" | ") || null,
|
|
488
557
|
gateway_url: gatewayUrl,
|
|
489
558
|
gateway_port: gatewayPort,
|
|
490
559
|
gateway_reachable: gatewayReachable,
|
|
560
|
+
status_command: statusCommand ? [statusCommand.cmd, ...statusCommand.args].join(" ") : null,
|
|
561
|
+
status_ok: statusLooksConfigured,
|
|
562
|
+
status_summary: statusLooksConfigured
|
|
563
|
+
? statusStdout
|
|
564
|
+
.split("\n")
|
|
565
|
+
.map((line) => line.trim())
|
|
566
|
+
.filter(Boolean)
|
|
567
|
+
.slice(0, 6)
|
|
568
|
+
.join(" | ")
|
|
569
|
+
: null,
|
|
570
|
+
gateway_probe_command: gatewayProbeCommand ? [gatewayProbeCommand.cmd, ...gatewayProbeCommand.args].join(" ") : null,
|
|
571
|
+
gateway_probe_ok: gatewayProbeOk,
|
|
572
|
+
gateway_probe_summary: gatewayProbeOk
|
|
573
|
+
? gatewayStatusStdout
|
|
574
|
+
.split("\n")
|
|
575
|
+
.map((line) => line.trim())
|
|
576
|
+
.filter(Boolean)
|
|
577
|
+
.slice(0, 4)
|
|
578
|
+
.join(" | ")
|
|
579
|
+
: null,
|
|
491
580
|
configured_gateway_url: configuredGateway.gateway_url,
|
|
492
581
|
configured_gateway_port: configuredGateway.gateway_port,
|
|
493
582
|
configured_gateway_bind: configuredGateway.gateway_bind,
|
|
494
583
|
configured_gateway_config_path: configuredGateway.config_path,
|
|
495
|
-
detection_mode:
|
|
496
|
-
?
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
584
|
+
detection_mode: gatewayProbeOk
|
|
585
|
+
? (processes.length > 0 && gatewayReachable
|
|
586
|
+
? "gateway-probe+process+gateway"
|
|
587
|
+
: gatewayReachable
|
|
588
|
+
? "gateway-probe+gateway"
|
|
589
|
+
: processes.length > 0
|
|
590
|
+
? "gateway-probe+process"
|
|
591
|
+
: "gateway-probe")
|
|
592
|
+
: processes.length > 0 && gatewayReachable
|
|
593
|
+
? "process+gateway"
|
|
594
|
+
: gatewayReachable
|
|
595
|
+
? "gateway"
|
|
596
|
+
: processes.length > 0
|
|
597
|
+
? "process"
|
|
598
|
+
: "not_running",
|
|
502
599
|
};
|
|
503
600
|
}
|
|
504
601
|
function detectOpenClawSkillInstallation() {
|
|
@@ -446,6 +446,7 @@ export function createSocialController({
|
|
|
446
446
|
[t("social.ownerForwardReady"), ownerDelivery.ready ? t("common.yes") : t("common.no")],
|
|
447
447
|
[t("social.ownerForwardCommand"), ownerDelivery.forward_command_configured ? t("common.yes") : t("common.no")],
|
|
448
448
|
[t("social.openclawDetectionMode"), bridge.openclaw_runtime?.detection_mode || "-"],
|
|
449
|
+
["Gateway probe", bridge.openclaw_runtime?.gateway_probe_ok ? t("common.yes") : t("common.no")],
|
|
449
450
|
[t("social.openclawGateway"), bridge.openclaw_runtime?.gateway_url || "-"],
|
|
450
451
|
[t("social.installMode"), skillLearning.install_mode || "-"],
|
|
451
452
|
[t("social.installedPath"), skillInstalled ? installedSkillPath : "-"],
|
|
@@ -86,7 +86,7 @@ export const TRANSLATIONS = {
|
|
|
86
86
|
showLess: 'Show Less',
|
|
87
87
|
showMoreCount: 'Show {count} More',
|
|
88
88
|
openclawNotInstalled: 'OpenClaw Not Installed Here',
|
|
89
|
-
openclawNotRunning: 'OpenClaw Not Running',
|
|
89
|
+
openclawNotRunning: 'OpenClaw Gateway Not Running',
|
|
90
90
|
openclawSkillLearned: 'Skill Already Learned',
|
|
91
91
|
silicaClawSkillsInstalled: 'Skills Already Installed',
|
|
92
92
|
},
|
|
@@ -591,7 +591,7 @@ export const TRANSLATIONS = {
|
|
|
591
591
|
openclawSkillNotInstalled: 'The SilicaClaw broadcast skill is not installed yet.',
|
|
592
592
|
openclawSkillInstallFailed: 'OpenClaw skill installation failed',
|
|
593
593
|
openclawRoleBroadcasterOnly: 'This computer is broadcaster-only. Install OpenClaw here to learn broadcasts.',
|
|
594
|
-
openclawRoleNotRunning: 'OpenClaw is
|
|
594
|
+
openclawRoleNotRunning: 'OpenClaw is configured here, but the gateway is not running yet.',
|
|
595
595
|
openclawRoleReadyToLearn: 'OpenClaw is installed here. Learn the broadcast skill to keep going.',
|
|
596
596
|
openclawRoleLearned: 'This computer is ready to learn broadcasts and forward useful updates.',
|
|
597
597
|
openclawRoleLearningOnly: 'Broadcast learning works, but owner forwarding is not ready yet.',
|
|
@@ -694,7 +694,7 @@ export const TRANSLATIONS = {
|
|
|
694
694
|
showLess: '收起',
|
|
695
695
|
showMoreCount: '再显示 {count} 个',
|
|
696
696
|
openclawNotInstalled: '本机未安装 OpenClaw',
|
|
697
|
-
openclawNotRunning: 'OpenClaw
|
|
697
|
+
openclawNotRunning: 'OpenClaw 网关未启动',
|
|
698
698
|
openclawSkillLearned: '技能已学会',
|
|
699
699
|
silicaClawSkillsInstalled: '技能已全部安装',
|
|
700
700
|
},
|
|
@@ -1020,7 +1020,7 @@ export const TRANSLATIONS = {
|
|
|
1020
1020
|
homeDegraded: '降级',
|
|
1021
1021
|
homeRunning: '运行中',
|
|
1022
1022
|
homeStopped: '未启动',
|
|
1023
|
-
homeInstalledOnly: '
|
|
1023
|
+
homeInstalledOnly: '已检测到 OpenClaw 配置',
|
|
1024
1024
|
homeGlobalReady: '全网预览已启用',
|
|
1025
1025
|
homeNotGlobal: '当前不是全网预览',
|
|
1026
1026
|
homeBriefNetwork: '网络',
|
|
@@ -1032,7 +1032,7 @@ export const TRANSLATIONS = {
|
|
|
1032
1032
|
homeBriefActionBroadcast: '保持公开广播,让运行 OpenClaw 的机器可以学习这个代理。',
|
|
1033
1033
|
homeBriefActionStabilize: '先修复 relay 或广播健康度,再依赖主人转发链路。',
|
|
1034
1034
|
homeMetaRunning: '已经检测到本机 OpenClaw 进程。',
|
|
1035
|
-
homeMetaNotRunning: '要开始学习广播,前提是本机 OpenClaw
|
|
1035
|
+
homeMetaNotRunning: '要开始学习广播,前提是本机 OpenClaw 网关正在运行。',
|
|
1036
1036
|
homeMetaGlobal: '当前默认使用全网 relay 预览模式。',
|
|
1037
1037
|
homeMetaNotGlobal: '这台机器当前还没有走全网 relay 路径。',
|
|
1038
1038
|
homeMetaPeers: '从本机视角看到 {online} 个在线代理,累计发现 {discovered} 个代理。',
|
|
@@ -1199,7 +1199,7 @@ export const TRANSLATIONS = {
|
|
|
1199
1199
|
openclawSkillNotInstalled: 'SilicaClaw 广播技能还没有安装。',
|
|
1200
1200
|
openclawSkillInstallFailed: 'OpenClaw 技能安装失败',
|
|
1201
1201
|
openclawRoleBroadcasterOnly: '这台电脑当前只能广播。想学习广播,先在这里安装 OpenClaw。',
|
|
1202
|
-
openclawRoleNotRunning: '
|
|
1202
|
+
openclawRoleNotRunning: '这台电脑已经配置了 OpenClaw,但网关还没启动。',
|
|
1203
1203
|
openclawRoleReadyToLearn: '这台电脑已经装了 OpenClaw。先学习广播技能,再继续。',
|
|
1204
1204
|
openclawRoleLearned: '这台电脑已经可以学习广播,并转发有用更新。',
|
|
1205
1205
|
openclawRoleLearningOnly: '已经能学习广播,但主人推送还没准备好。',
|
|
@@ -455,8 +455,85 @@ function readOpenClawConfiguredGateway(workspaceRoot: string) {
|
|
|
455
455
|
} as const;
|
|
456
456
|
}
|
|
457
457
|
|
|
458
|
+
function resolveOpenClawStatusCommand(workspaceRoot: string) {
|
|
459
|
+
const explicitBin = String(process.env.OPENCLAW_BIN || "").trim();
|
|
460
|
+
if (explicitBin) {
|
|
461
|
+
return { cmd: explicitBin, args: ["status"] } as const;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const configuredSourceDir = String(process.env.OPENCLAW_SOURCE_DIR || "").trim();
|
|
465
|
+
const defaultSourceDir = defaultOpenClawSourceDir(workspaceRoot);
|
|
466
|
+
const sourceDir = configuredSourceDir || defaultSourceDir;
|
|
467
|
+
const sourceEntry = existingPathOrNull(resolve(sourceDir, "openclaw.mjs"));
|
|
468
|
+
if (sourceEntry) {
|
|
469
|
+
return { cmd: process.execPath, args: [sourceEntry, "status"] } as const;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const commandPath = resolveExecutableInPath("openclaw");
|
|
473
|
+
if (commandPath) {
|
|
474
|
+
return { cmd: commandPath, args: ["status"] } as const;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function resolveOpenClawGatewayProbeCommand(workspaceRoot: string) {
|
|
481
|
+
const explicitBin = String(process.env.OPENCLAW_BIN || "").trim();
|
|
482
|
+
if (explicitBin) {
|
|
483
|
+
return { cmd: explicitBin, args: ["gateway", "probe"] } as const;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const configuredSourceDir = String(process.env.OPENCLAW_SOURCE_DIR || "").trim();
|
|
487
|
+
const defaultSourceDir = defaultOpenClawSourceDir(workspaceRoot);
|
|
488
|
+
const sourceDir = configuredSourceDir || defaultSourceDir;
|
|
489
|
+
const sourceEntry = existingPathOrNull(resolve(sourceDir, "openclaw.mjs"));
|
|
490
|
+
if (sourceEntry) {
|
|
491
|
+
return { cmd: process.execPath, args: [sourceEntry, "gateway", "probe"] } as const;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const commandPath = resolveExecutableInPath("openclaw");
|
|
495
|
+
if (commandPath) {
|
|
496
|
+
return { cmd: commandPath, args: ["gateway", "probe"] } as const;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
|
|
458
502
|
function detectOpenClawRuntime(workspaceRoot: string) {
|
|
459
503
|
const configuredGateway = readOpenClawConfiguredGateway(workspaceRoot);
|
|
504
|
+
const statusCommand = resolveOpenClawStatusCommand(workspaceRoot);
|
|
505
|
+
const gatewayProbeCommand = resolveOpenClawGatewayProbeCommand(workspaceRoot);
|
|
506
|
+
const statusProbe = statusCommand
|
|
507
|
+
? spawnSync(statusCommand.cmd, statusCommand.args, {
|
|
508
|
+
encoding: "utf8",
|
|
509
|
+
env: process.env,
|
|
510
|
+
})
|
|
511
|
+
: null;
|
|
512
|
+
const statusStdout = String(statusProbe?.stdout || "");
|
|
513
|
+
const statusStderr = String(statusProbe?.stderr || "");
|
|
514
|
+
const statusLooksConfigured = Boolean(
|
|
515
|
+
statusCommand &&
|
|
516
|
+
statusProbe &&
|
|
517
|
+
statusProbe.status === 0 &&
|
|
518
|
+
(
|
|
519
|
+
/\bChannels\b/i.test(statusStdout) ||
|
|
520
|
+
/\bSessions\b/i.test(statusStdout) ||
|
|
521
|
+
/\bNext steps:\b/i.test(statusStdout)
|
|
522
|
+
)
|
|
523
|
+
);
|
|
524
|
+
const gatewayStatusProbe = gatewayProbeCommand
|
|
525
|
+
? spawnSync(gatewayProbeCommand.cmd, gatewayProbeCommand.args, {
|
|
526
|
+
encoding: "utf8",
|
|
527
|
+
env: process.env,
|
|
528
|
+
})
|
|
529
|
+
: null;
|
|
530
|
+
const gatewayStatusStdout = String(gatewayStatusProbe?.stdout || "");
|
|
531
|
+
const gatewayStatusStderr = String(gatewayStatusProbe?.stderr || "");
|
|
532
|
+
const gatewayProbeOk = Boolean(
|
|
533
|
+
gatewayProbeCommand &&
|
|
534
|
+
gatewayStatusProbe &&
|
|
535
|
+
gatewayStatusProbe.status === 0
|
|
536
|
+
);
|
|
460
537
|
const result = spawnSync("ps", ["-Ao", "pid=,ppid=,command="], {
|
|
461
538
|
encoding: "utf8",
|
|
462
539
|
});
|
|
@@ -540,6 +617,12 @@ function detectOpenClawRuntime(workspaceRoot: string) {
|
|
|
540
617
|
const allProcesses = Array.from(combinedProcesses.values());
|
|
541
618
|
const gatewayReachable = gatewayListeners.length > 0;
|
|
542
619
|
const detectionNotes = [];
|
|
620
|
+
if (statusProbe && statusProbe.status !== 0) {
|
|
621
|
+
detectionNotes.push(String(statusStderr || "openclaw status failed").trim());
|
|
622
|
+
}
|
|
623
|
+
if (gatewayStatusProbe && gatewayStatusProbe.status !== 0) {
|
|
624
|
+
detectionNotes.push(String(gatewayStatusStderr || "openclaw gateway probe failed").trim());
|
|
625
|
+
}
|
|
543
626
|
if (result.status !== 0) detectionNotes.push(String(result.stderr || "ps failed").trim());
|
|
544
627
|
if (gatewayProbe.status !== 0 && gatewayLines.length === 0) {
|
|
545
628
|
detectionNotes.push(String(gatewayProbe.stderr || "lsof failed").trim());
|
|
@@ -548,19 +631,49 @@ function detectOpenClawRuntime(workspaceRoot: string) {
|
|
|
548
631
|
const gatewayUrl = `http://${OPENCLAW_GATEWAY_HOST}:${gatewayPort}/`;
|
|
549
632
|
|
|
550
633
|
return {
|
|
551
|
-
running: allProcesses.length > 0 || gatewayReachable,
|
|
634
|
+
running: gatewayProbeOk || allProcesses.length > 0 || gatewayReachable,
|
|
552
635
|
process_count: allProcesses.length,
|
|
553
636
|
processes: allProcesses.slice(0, 10),
|
|
554
637
|
detection_error: detectionNotes.filter(Boolean).join(" | ") || null,
|
|
555
638
|
gateway_url: gatewayUrl,
|
|
556
639
|
gateway_port: gatewayPort,
|
|
557
640
|
gateway_reachable: gatewayReachable,
|
|
641
|
+
status_command: statusCommand ? [statusCommand.cmd, ...statusCommand.args].join(" ") : null,
|
|
642
|
+
status_ok: statusLooksConfigured,
|
|
643
|
+
status_summary: statusLooksConfigured
|
|
644
|
+
? statusStdout
|
|
645
|
+
.split("\n")
|
|
646
|
+
.map((line) => line.trim())
|
|
647
|
+
.filter(Boolean)
|
|
648
|
+
.slice(0, 6)
|
|
649
|
+
.join(" | ")
|
|
650
|
+
: null,
|
|
651
|
+
gateway_probe_command: gatewayProbeCommand ? [gatewayProbeCommand.cmd, ...gatewayProbeCommand.args].join(" ") : null,
|
|
652
|
+
gateway_probe_ok: gatewayProbeOk,
|
|
653
|
+
gateway_probe_summary: gatewayProbeOk
|
|
654
|
+
? gatewayStatusStdout
|
|
655
|
+
.split("\n")
|
|
656
|
+
.map((line) => line.trim())
|
|
657
|
+
.filter(Boolean)
|
|
658
|
+
.slice(0, 4)
|
|
659
|
+
.join(" | ")
|
|
660
|
+
: null,
|
|
558
661
|
configured_gateway_url: configuredGateway.gateway_url,
|
|
559
662
|
configured_gateway_port: configuredGateway.gateway_port,
|
|
560
663
|
configured_gateway_bind: configuredGateway.gateway_bind,
|
|
561
664
|
configured_gateway_config_path: configuredGateway.config_path,
|
|
562
665
|
detection_mode:
|
|
563
|
-
|
|
666
|
+
gatewayProbeOk
|
|
667
|
+
? (
|
|
668
|
+
processes.length > 0 && gatewayReachable
|
|
669
|
+
? "gateway-probe+process+gateway"
|
|
670
|
+
: gatewayReachable
|
|
671
|
+
? "gateway-probe+gateway"
|
|
672
|
+
: processes.length > 0
|
|
673
|
+
? "gateway-probe+process"
|
|
674
|
+
: "gateway-probe"
|
|
675
|
+
)
|
|
676
|
+
: processes.length > 0 && gatewayReachable
|
|
564
677
|
? "process+gateway"
|
|
565
678
|
: gatewayReachable
|
|
566
679
|
? "gateway"
|
|
@@ -833,11 +946,17 @@ type OpenClawBridgeStatus = {
|
|
|
833
946
|
gateway_url: string;
|
|
834
947
|
gateway_port: number;
|
|
835
948
|
gateway_reachable: boolean;
|
|
949
|
+
status_command: string | null;
|
|
950
|
+
status_ok: boolean;
|
|
951
|
+
status_summary: string | null;
|
|
952
|
+
gateway_probe_command: string | null;
|
|
953
|
+
gateway_probe_ok: boolean;
|
|
954
|
+
gateway_probe_summary: string | null;
|
|
836
955
|
configured_gateway_url: string;
|
|
837
956
|
configured_gateway_port: number;
|
|
838
957
|
configured_gateway_bind: string | null;
|
|
839
958
|
configured_gateway_config_path: string | null;
|
|
840
|
-
detection_mode: "process" | "gateway" | "process+gateway" | "not_running";
|
|
959
|
+
detection_mode: "gateway-probe" | "gateway-probe+process" | "gateway-probe+gateway" | "gateway-probe+process+gateway" | "process" | "gateway" | "process+gateway" | "not_running";
|
|
841
960
|
};
|
|
842
961
|
skill_learning: {
|
|
843
962
|
available: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
2026.3.20-beta.
|
|
1
|
+
2026.3.20-beta.6
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "silicaclaw-broadcast",
|
|
3
|
-
"version": "2026.3.20-beta.
|
|
3
|
+
"version": "2026.3.20-beta.6",
|
|
4
4
|
"display_name": "SilicaClaw Broadcast",
|
|
5
5
|
"description": "Official OpenClaw skill for a bounded local SilicaClaw broadcast workflow: read public broadcasts, publish public broadcasts, and optionally forward owner-relevant summaries through OpenClaw's native channel.",
|
|
6
6
|
"entrypoints": {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
2026.3.20-beta.
|
|
1
|
+
2026.3.20-beta.3
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "silicaclaw-owner-push",
|
|
3
|
-
"version": "2026.3.20-beta.
|
|
3
|
+
"version": "2026.3.20-beta.3",
|
|
4
4
|
"display_name": "SilicaClaw Owner Push",
|
|
5
5
|
"description": "Official OpenClaw skill for a bounded local SilicaClaw monitoring workflow: watch public broadcasts, filter owner-relevant updates, and push concise summaries through OpenClaw's native owner channel.",
|
|
6
6
|
"entrypoints": {
|
|
@@ -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
|
|