@silicaclaw/cli 2026.3.20-7 → 2026.3.20-9
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/VERSION +1 -1
- package/apps/local-console/dist/apps/local-console/src/server.d.ts +9 -0
- package/apps/local-console/dist/apps/local-console/src/server.js +73 -93
- package/apps/local-console/public/app/app.js +2 -14
- package/apps/local-console/public/app/overview.js +5 -6
- package/apps/local-console/src/server.ts +73 -98
- package/openclaw-skills/silicaclaw-broadcast/VERSION +1 -1
- package/openclaw-skills/silicaclaw-broadcast/manifest.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## v1.0 beta - 2026-03-20
|
|
4
4
|
|
|
5
|
+
### 2026.3.20-9
|
|
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
|
+
|
|
11
|
+
### 2026.3.20-8
|
|
12
|
+
|
|
13
|
+
- release build:
|
|
14
|
+
- prepared another fresh latest-channel package build without publishing
|
|
15
|
+
- regenerated the npm tarball through the verified release packing workflow
|
|
16
|
+
|
|
5
17
|
### 2026.3.20-7
|
|
6
18
|
|
|
7
19
|
- release build:
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
v2026.3.20-
|
|
1
|
+
v2026.3.20-9
|
|
@@ -245,6 +245,15 @@ export declare class LocalNodeService {
|
|
|
245
245
|
can_enable_public_discovery: boolean;
|
|
246
246
|
next_steps: string[];
|
|
247
247
|
};
|
|
248
|
+
openclaw: {
|
|
249
|
+
detected: boolean;
|
|
250
|
+
running: boolean;
|
|
251
|
+
detection_mode: "gateway" | "gateway-probe+process+gateway" | "gateway-probe+gateway" | "gateway-probe+process" | "gateway-probe" | "process+gateway" | "process" | "not_running";
|
|
252
|
+
gateway_url: string;
|
|
253
|
+
gateway_probe_ok: boolean;
|
|
254
|
+
status_ok: boolean;
|
|
255
|
+
skill_installed: boolean;
|
|
256
|
+
};
|
|
248
257
|
social: {
|
|
249
258
|
found: boolean;
|
|
250
259
|
enabled: boolean;
|
|
@@ -433,68 +433,17 @@ function resolveOpenClawGatewayProbeCommand(workspaceRoot) {
|
|
|
433
433
|
function detectOpenClawRuntime(workspaceRoot) {
|
|
434
434
|
const configuredGateway = readOpenClawConfiguredGateway(workspaceRoot);
|
|
435
435
|
const statusCommand = resolveOpenClawStatusCommand(workspaceRoot);
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
})
|
|
442
|
-
: null;
|
|
443
|
-
const statusStdout = String(statusProbe?.stdout || "");
|
|
444
|
-
const statusStderr = String(statusProbe?.stderr || "");
|
|
445
|
-
const statusLooksConfigured = Boolean(statusCommand &&
|
|
446
|
-
statusProbe &&
|
|
447
|
-
statusProbe.status === 0 &&
|
|
448
|
-
(/\bChannels\b/i.test(statusStdout) ||
|
|
449
|
-
/\bSessions\b/i.test(statusStdout) ||
|
|
450
|
-
/\bNext steps:\b/i.test(statusStdout)));
|
|
451
|
-
const gatewayStatusProbe = gatewayProbeCommand
|
|
452
|
-
? (0, child_process_1.spawnSync)(gatewayProbeCommand.cmd, gatewayProbeCommand.args, {
|
|
453
|
-
encoding: "utf8",
|
|
454
|
-
env: process.env,
|
|
455
|
-
})
|
|
456
|
-
: null;
|
|
457
|
-
const gatewayStatusStdout = String(gatewayStatusProbe?.stdout || "");
|
|
458
|
-
const gatewayStatusStderr = String(gatewayStatusProbe?.stderr || "");
|
|
459
|
-
const gatewayProbeOk = Boolean(gatewayProbeCommand &&
|
|
460
|
-
gatewayStatusProbe &&
|
|
461
|
-
gatewayStatusProbe.status === 0);
|
|
462
|
-
const result = (0, child_process_1.spawnSync)("ps", ["-Ao", "pid=,ppid=,command="], {
|
|
463
|
-
encoding: "utf8",
|
|
464
|
-
});
|
|
465
|
-
const stdout = String(result.stdout || "");
|
|
466
|
-
const lines = stdout
|
|
467
|
-
.split("\n")
|
|
468
|
-
.map((line) => line.trim())
|
|
469
|
-
.filter(Boolean);
|
|
470
|
-
const processes = lines
|
|
471
|
-
.map((line) => {
|
|
472
|
-
const match = line.match(/^(\d+)\s+(\d+)\s+(.+)$/);
|
|
473
|
-
if (!match)
|
|
474
|
-
return null;
|
|
475
|
-
const command = match[3] || "";
|
|
476
|
-
const lower = command.toLowerCase();
|
|
477
|
-
const isOpenClaw = lower.includes(" openclaw ") ||
|
|
478
|
-
lower.endsWith(" openclaw") ||
|
|
479
|
-
lower.includes("/openclaw ") ||
|
|
480
|
-
lower.includes("openclaw.mjs") ||
|
|
481
|
-
lower.includes("openclaw gateway") ||
|
|
482
|
-
lower.includes("openclaw agent") ||
|
|
483
|
-
lower.includes("openclaw message");
|
|
484
|
-
if (!isOpenClaw)
|
|
485
|
-
return null;
|
|
486
|
-
return {
|
|
487
|
-
pid: Number(match[1]),
|
|
488
|
-
ppid: Number(match[2]),
|
|
489
|
-
command,
|
|
490
|
-
};
|
|
491
|
-
})
|
|
492
|
-
.filter((item) => Boolean(item));
|
|
493
|
-
const openclawPids = new Set(processes.map((item) => item.pid));
|
|
494
|
-
const gatewayProbe = (0, child_process_1.spawnSync)("lsof", ["-nP", "-iTCP", "-sTCP:LISTEN"], {
|
|
436
|
+
const statusLooksConfigured = Boolean(statusCommand ||
|
|
437
|
+
configuredGateway.config_path ||
|
|
438
|
+
detectOpenClawInstallation(workspaceRoot).detected);
|
|
439
|
+
const gatewayProbeCommand = ["lsof", "-nP", `-iTCP:${configuredGateway.gateway_port}`, "-sTCP:LISTEN"];
|
|
440
|
+
const gatewayProbe = (0, child_process_1.spawnSync)(gatewayProbeCommand[0], gatewayProbeCommand.slice(1), {
|
|
495
441
|
encoding: "utf8",
|
|
442
|
+
timeout: 1200,
|
|
496
443
|
});
|
|
497
|
-
const
|
|
444
|
+
const gatewayStatusStdout = String(gatewayProbe.stdout || "");
|
|
445
|
+
const gatewayStatusStderr = String(gatewayProbe.stderr || "");
|
|
446
|
+
const gatewayLines = gatewayStatusStdout
|
|
498
447
|
.split("\n")
|
|
499
448
|
.map((line) => line.trim())
|
|
500
449
|
.filter(Boolean);
|
|
@@ -504,15 +453,10 @@ function detectOpenClawRuntime(workspaceRoot) {
|
|
|
504
453
|
const parts = line.split(/\s+/);
|
|
505
454
|
const pid = Number(parts[1] || 0);
|
|
506
455
|
const command = parts[0] || "";
|
|
507
|
-
const lowerCommand = command.toLowerCase();
|
|
508
456
|
const endpoint = parts[8] || parts[parts.length - 1] || "";
|
|
509
457
|
const portMatch = endpoint.match(/:(\d+)(?:\s*\(|$)/);
|
|
510
458
|
if (!pid || !command || !portMatch)
|
|
511
459
|
return null;
|
|
512
|
-
const isOpenClawListener = openclawPids.has(pid) ||
|
|
513
|
-
lowerCommand.includes("openclaw");
|
|
514
|
-
if (!isOpenClawListener)
|
|
515
|
-
return null;
|
|
516
460
|
const port = Number(portMatch[1]);
|
|
517
461
|
if (!Number.isFinite(port) || port <= 0)
|
|
518
462
|
return null;
|
|
@@ -524,33 +468,58 @@ function detectOpenClawRuntime(workspaceRoot) {
|
|
|
524
468
|
};
|
|
525
469
|
})
|
|
526
470
|
.filter((item) => Boolean(item));
|
|
471
|
+
const gatewayProbeOk = gatewayListeners.length > 0;
|
|
472
|
+
let processes = gatewayListeners.map((item) => ({
|
|
473
|
+
pid: item.pid,
|
|
474
|
+
ppid: item.ppid,
|
|
475
|
+
command: item.command,
|
|
476
|
+
}));
|
|
477
|
+
let processResult = null;
|
|
478
|
+
if (!gatewayProbeOk) {
|
|
479
|
+
processResult = (0, child_process_1.spawnSync)("ps", ["-Ao", "pid=,ppid=,command="], {
|
|
480
|
+
encoding: "utf8",
|
|
481
|
+
timeout: 1200,
|
|
482
|
+
});
|
|
483
|
+
const stdout = String(processResult.stdout || "");
|
|
484
|
+
const lines = stdout
|
|
485
|
+
.split("\n")
|
|
486
|
+
.map((line) => line.trim())
|
|
487
|
+
.filter(Boolean);
|
|
488
|
+
processes = lines
|
|
489
|
+
.map((line) => {
|
|
490
|
+
const match = line.match(/^(\d+)\s+(\d+)\s+(.+)$/);
|
|
491
|
+
if (!match)
|
|
492
|
+
return null;
|
|
493
|
+
const command = match[3] || "";
|
|
494
|
+
const lower = command.toLowerCase();
|
|
495
|
+
const isOpenClaw = lower.includes(" openclaw ") ||
|
|
496
|
+
lower.endsWith(" openclaw") ||
|
|
497
|
+
lower.includes("/openclaw ") ||
|
|
498
|
+
lower.includes("openclaw.mjs") ||
|
|
499
|
+
lower.includes("openclaw gateway") ||
|
|
500
|
+
lower.includes("openclaw agent") ||
|
|
501
|
+
lower.includes("openclaw message");
|
|
502
|
+
if (!isOpenClaw)
|
|
503
|
+
return null;
|
|
504
|
+
return {
|
|
505
|
+
pid: Number(match[1]),
|
|
506
|
+
ppid: Number(match[2]),
|
|
507
|
+
command,
|
|
508
|
+
};
|
|
509
|
+
})
|
|
510
|
+
.filter((item) => Boolean(item));
|
|
511
|
+
}
|
|
527
512
|
const preferredListener = gatewayListeners.find((item) => item.port === configuredGateway.gateway_port) ||
|
|
528
513
|
gatewayListeners[0] ||
|
|
529
514
|
null;
|
|
530
|
-
const
|
|
531
|
-
|
|
532
|
-
if (!combinedProcesses.has(process.pid)) {
|
|
533
|
-
combinedProcesses.set(process.pid, process);
|
|
534
|
-
continue;
|
|
535
|
-
}
|
|
536
|
-
const current = combinedProcesses.get(process.pid);
|
|
537
|
-
if (current && current.command.length < process.command.length) {
|
|
538
|
-
combinedProcesses.set(process.pid, process);
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
const allProcesses = Array.from(combinedProcesses.values());
|
|
542
|
-
const gatewayReachable = gatewayListeners.length > 0;
|
|
515
|
+
const allProcesses = processes.slice(0, 10);
|
|
516
|
+
const gatewayReachable = gatewayProbeOk;
|
|
543
517
|
const detectionNotes = [];
|
|
544
|
-
if (
|
|
545
|
-
detectionNotes.push(String(statusStderr || "openclaw status failed").trim());
|
|
546
|
-
}
|
|
547
|
-
if (gatewayStatusProbe && gatewayStatusProbe.status !== 0) {
|
|
518
|
+
if (gatewayProbe.status !== 0 && gatewayLines.length === 0) {
|
|
548
519
|
detectionNotes.push(String(gatewayStatusStderr || "openclaw gateway probe failed").trim());
|
|
549
520
|
}
|
|
550
|
-
if (
|
|
551
|
-
detectionNotes.push(String(
|
|
552
|
-
if (gatewayProbe.status !== 0 && gatewayLines.length === 0) {
|
|
553
|
-
detectionNotes.push(String(gatewayProbe.stderr || "lsof failed").trim());
|
|
521
|
+
if (processResult && processResult.status !== 0) {
|
|
522
|
+
detectionNotes.push(String(processResult.stderr || "ps failed").trim());
|
|
554
523
|
}
|
|
555
524
|
const gatewayPort = preferredListener?.port || configuredGateway.gateway_port;
|
|
556
525
|
const gatewayUrl = `http://${OPENCLAW_GATEWAY_HOST}:${gatewayPort}/`;
|
|
@@ -565,14 +534,13 @@ function detectOpenClawRuntime(workspaceRoot) {
|
|
|
565
534
|
status_command: statusCommand ? [statusCommand.cmd, ...statusCommand.args].join(" ") : null,
|
|
566
535
|
status_ok: statusLooksConfigured,
|
|
567
536
|
status_summary: statusLooksConfigured
|
|
568
|
-
?
|
|
569
|
-
.
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
.join(" | ")
|
|
537
|
+
? configuredGateway.config_path
|
|
538
|
+
? `configured via ${configuredGateway.config_path}`
|
|
539
|
+
: statusCommand
|
|
540
|
+
? `command available: ${[statusCommand.cmd, ...statusCommand.args].join(" ")}`
|
|
541
|
+
: "OpenClaw environment detected"
|
|
574
542
|
: null,
|
|
575
|
-
gateway_probe_command: gatewayProbeCommand
|
|
543
|
+
gateway_probe_command: gatewayProbeCommand.join(" "),
|
|
576
544
|
gateway_probe_ok: gatewayProbeOk,
|
|
577
545
|
gateway_probe_summary: gatewayProbeOk
|
|
578
546
|
? gatewayStatusStdout
|
|
@@ -944,6 +912,9 @@ class LocalNodeService {
|
|
|
944
912
|
getOverview() {
|
|
945
913
|
const discovered = this.search("");
|
|
946
914
|
const onlineCount = discovered.filter((profile) => profile.online).length;
|
|
915
|
+
const openclawInstallation = detectOpenClawInstallation(this.projectRoot);
|
|
916
|
+
const openclawRuntime = this.getCachedOpenClawRuntime();
|
|
917
|
+
const openclawSkillInstallation = detectOpenClawSkillInstallation();
|
|
947
918
|
return {
|
|
948
919
|
app_version: this.appVersion,
|
|
949
920
|
agent_id: this.identity?.agent_id ?? "",
|
|
@@ -959,6 +930,15 @@ class LocalNodeService {
|
|
|
959
930
|
init_state: this.initState,
|
|
960
931
|
presence_ttl_ms: PRESENCE_TTL_MS,
|
|
961
932
|
onboarding: this.getOnboardingSummary(),
|
|
933
|
+
openclaw: {
|
|
934
|
+
detected: openclawInstallation.detected,
|
|
935
|
+
running: openclawRuntime.running,
|
|
936
|
+
detection_mode: openclawRuntime.detection_mode,
|
|
937
|
+
gateway_url: openclawRuntime.gateway_url,
|
|
938
|
+
gateway_probe_ok: openclawRuntime.gateway_probe_ok,
|
|
939
|
+
status_ok: openclawRuntime.status_ok,
|
|
940
|
+
skill_installed: openclawSkillInstallation.installed,
|
|
941
|
+
},
|
|
962
942
|
social: {
|
|
963
943
|
found: this.socialFound,
|
|
964
944
|
enabled: this.socialConfig.enabled,
|
|
@@ -690,7 +690,7 @@ const APP_UPDATE_SESSION_KEY = 'silicaclaw_pending_updated_version';
|
|
|
690
690
|
async function refreshActiveView() {
|
|
691
691
|
const tasks = [refreshPublicProfilePreview(), refreshRelayQueueStatus()];
|
|
692
692
|
if (activeTab === 'overview') {
|
|
693
|
-
tasks.push(refreshOverview(), refreshMessages()
|
|
693
|
+
tasks.push(refreshOverview(), refreshMessages());
|
|
694
694
|
} else if (activeTab === 'agent') {
|
|
695
695
|
tasks.push(refreshOverview());
|
|
696
696
|
} else if (activeTab === 'chat') {
|
|
@@ -724,19 +724,7 @@ const APP_UPDATE_SESSION_KEY = 'silicaclaw_pending_updated_version';
|
|
|
724
724
|
}
|
|
725
725
|
|
|
726
726
|
async function refreshAll() {
|
|
727
|
-
|
|
728
|
-
if (activeTab === 'network') {
|
|
729
|
-
tasks.push(refreshPeers(), refreshDiscovery(), refreshLogs());
|
|
730
|
-
}
|
|
731
|
-
const shouldRefreshProfile = !(activeTab === 'profile' && profileController.isDirty());
|
|
732
|
-
if (shouldRefreshProfile) {
|
|
733
|
-
tasks.push(refreshProfile());
|
|
734
|
-
}
|
|
735
|
-
const results = await Promise.allSettled(tasks);
|
|
736
|
-
const firstError = results.find((result) => result.status === 'rejected');
|
|
737
|
-
if (firstError && firstError.status === 'rejected') {
|
|
738
|
-
setFeedback('networkFeedback', firstError.reason instanceof Error ? firstError.reason.message : t('common.unknownError'), 'error');
|
|
739
|
-
}
|
|
727
|
+
await refreshActiveView();
|
|
740
728
|
}
|
|
741
729
|
|
|
742
730
|
bindAppEvents({
|
|
@@ -66,12 +66,11 @@ export function createOverviewController({
|
|
|
66
66
|
setOverviewMode,
|
|
67
67
|
setVisibleRemotePublicCount,
|
|
68
68
|
}) {
|
|
69
|
-
const [overviewRes, discoveredRes, peerRes, profileRes,
|
|
69
|
+
const [overviewRes, discoveredRes, peerRes, profileRes, networkCfgRes, networkStatsRes] = await Promise.allSettled([
|
|
70
70
|
api("/api/overview"),
|
|
71
71
|
api("/api/search?q="),
|
|
72
72
|
api("/api/peers"),
|
|
73
73
|
api("/api/profile"),
|
|
74
|
-
api("/api/openclaw/bridge"),
|
|
75
74
|
api("/api/network/config"),
|
|
76
75
|
api("/api/network/stats"),
|
|
77
76
|
]);
|
|
@@ -82,7 +81,6 @@ export function createOverviewController({
|
|
|
82
81
|
const allProfiles = discoveredRes.status === "fulfilled" ? discoveredRes.value.data || [] : [];
|
|
83
82
|
const peers = peerRes.status === "fulfilled" ? peerRes.value.data || {} : {};
|
|
84
83
|
const currentProfile = profileRes.status === "fulfilled" ? profileRes.value.data || {} : {};
|
|
85
|
-
const bridge = bridgeRes.status === "fulfilled" ? bridgeRes.value.data || {} : {};
|
|
86
84
|
const networkCfg = networkCfgRes.status === "fulfilled" ? networkCfgRes.value.data || {} : {};
|
|
87
85
|
const networkStats = networkStatsRes.status === "fulfilled" ? networkStatsRes.value.data || {} : {};
|
|
88
86
|
const all = Array.isArray(allProfiles) ? allProfiles.slice() : [];
|
|
@@ -145,9 +143,10 @@ export function createOverviewController({
|
|
|
145
143
|
document.getElementById("pillBroadcast").textContent = pillBroadcastText;
|
|
146
144
|
document.getElementById("pillBroadcast").className = pillBroadcastClassName;
|
|
147
145
|
|
|
148
|
-
const
|
|
149
|
-
const
|
|
150
|
-
const
|
|
146
|
+
const openclaw = o.openclaw || {};
|
|
147
|
+
const openclawRunning = !!openclaw.running;
|
|
148
|
+
const openclawDetected = !!openclaw.detected || openclawRunning;
|
|
149
|
+
const skillInstalled = !!openclaw.skill_installed;
|
|
151
150
|
const globalMode = heroModeText === "global-preview";
|
|
152
151
|
const lastNetworkError = String(networkDiag.last_error || o.last_broadcast_error || "").trim();
|
|
153
152
|
const broadcastHealthy = o.broadcast_enabled && !lastNetworkError;
|
|
@@ -508,75 +508,19 @@ function resolveOpenClawGatewayProbeCommand(workspaceRoot: string) {
|
|
|
508
508
|
function detectOpenClawRuntime(workspaceRoot: string) {
|
|
509
509
|
const configuredGateway = readOpenClawConfiguredGateway(workspaceRoot);
|
|
510
510
|
const statusCommand = resolveOpenClawStatusCommand(workspaceRoot);
|
|
511
|
-
const gatewayProbeCommand = resolveOpenClawGatewayProbeCommand(workspaceRoot);
|
|
512
|
-
const statusProbe = statusCommand
|
|
513
|
-
? spawnSync(statusCommand.cmd, statusCommand.args, {
|
|
514
|
-
encoding: "utf8",
|
|
515
|
-
env: process.env,
|
|
516
|
-
})
|
|
517
|
-
: null;
|
|
518
|
-
const statusStdout = String(statusProbe?.stdout || "");
|
|
519
|
-
const statusStderr = String(statusProbe?.stderr || "");
|
|
520
511
|
const statusLooksConfigured = Boolean(
|
|
521
|
-
statusCommand
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
(
|
|
525
|
-
/\bChannels\b/i.test(statusStdout) ||
|
|
526
|
-
/\bSessions\b/i.test(statusStdout) ||
|
|
527
|
-
/\bNext steps:\b/i.test(statusStdout)
|
|
528
|
-
)
|
|
529
|
-
);
|
|
530
|
-
const gatewayStatusProbe = gatewayProbeCommand
|
|
531
|
-
? spawnSync(gatewayProbeCommand.cmd, gatewayProbeCommand.args, {
|
|
532
|
-
encoding: "utf8",
|
|
533
|
-
env: process.env,
|
|
534
|
-
})
|
|
535
|
-
: null;
|
|
536
|
-
const gatewayStatusStdout = String(gatewayStatusProbe?.stdout || "");
|
|
537
|
-
const gatewayStatusStderr = String(gatewayStatusProbe?.stderr || "");
|
|
538
|
-
const gatewayProbeOk = Boolean(
|
|
539
|
-
gatewayProbeCommand &&
|
|
540
|
-
gatewayStatusProbe &&
|
|
541
|
-
gatewayStatusProbe.status === 0
|
|
512
|
+
statusCommand ||
|
|
513
|
+
configuredGateway.config_path ||
|
|
514
|
+
detectOpenClawInstallation(workspaceRoot).detected
|
|
542
515
|
);
|
|
543
|
-
const
|
|
544
|
-
|
|
545
|
-
});
|
|
546
|
-
const stdout = String(result.stdout || "");
|
|
547
|
-
const lines = stdout
|
|
548
|
-
.split("\n")
|
|
549
|
-
.map((line) => line.trim())
|
|
550
|
-
.filter(Boolean);
|
|
551
|
-
|
|
552
|
-
const processes = lines
|
|
553
|
-
.map((line) => {
|
|
554
|
-
const match = line.match(/^(\d+)\s+(\d+)\s+(.+)$/);
|
|
555
|
-
if (!match) return null;
|
|
556
|
-
const command = match[3] || "";
|
|
557
|
-
const lower = command.toLowerCase();
|
|
558
|
-
const isOpenClaw =
|
|
559
|
-
lower.includes(" openclaw ") ||
|
|
560
|
-
lower.endsWith(" openclaw") ||
|
|
561
|
-
lower.includes("/openclaw ") ||
|
|
562
|
-
lower.includes("openclaw.mjs") ||
|
|
563
|
-
lower.includes("openclaw gateway") ||
|
|
564
|
-
lower.includes("openclaw agent") ||
|
|
565
|
-
lower.includes("openclaw message");
|
|
566
|
-
if (!isOpenClaw) return null;
|
|
567
|
-
return {
|
|
568
|
-
pid: Number(match[1]),
|
|
569
|
-
ppid: Number(match[2]),
|
|
570
|
-
command,
|
|
571
|
-
};
|
|
572
|
-
})
|
|
573
|
-
.filter((item): item is { pid: number; ppid: number; command: string } => Boolean(item));
|
|
574
|
-
|
|
575
|
-
const openclawPids = new Set(processes.map((item) => item.pid));
|
|
576
|
-
const gatewayProbe = spawnSync("lsof", ["-nP", "-iTCP", "-sTCP:LISTEN"], {
|
|
516
|
+
const gatewayProbeCommand = ["lsof", "-nP", `-iTCP:${configuredGateway.gateway_port}`, "-sTCP:LISTEN"];
|
|
517
|
+
const gatewayProbe = spawnSync(gatewayProbeCommand[0], gatewayProbeCommand.slice(1), {
|
|
577
518
|
encoding: "utf8",
|
|
519
|
+
timeout: 1200,
|
|
578
520
|
});
|
|
579
|
-
const
|
|
521
|
+
const gatewayStatusStdout = String(gatewayProbe.stdout || "");
|
|
522
|
+
const gatewayStatusStderr = String(gatewayProbe.stderr || "");
|
|
523
|
+
const gatewayLines = gatewayStatusStdout
|
|
580
524
|
.split("\n")
|
|
581
525
|
.map((line) => line.trim())
|
|
582
526
|
.filter(Boolean);
|
|
@@ -586,14 +530,9 @@ function detectOpenClawRuntime(workspaceRoot: string) {
|
|
|
586
530
|
const parts = line.split(/\s+/);
|
|
587
531
|
const pid = Number(parts[1] || 0);
|
|
588
532
|
const command = parts[0] || "";
|
|
589
|
-
const lowerCommand = command.toLowerCase();
|
|
590
533
|
const endpoint = parts[8] || parts[parts.length - 1] || "";
|
|
591
534
|
const portMatch = endpoint.match(/:(\d+)(?:\s*\(|$)/);
|
|
592
535
|
if (!pid || !command || !portMatch) return null;
|
|
593
|
-
const isOpenClawListener =
|
|
594
|
-
openclawPids.has(pid) ||
|
|
595
|
-
lowerCommand.includes("openclaw");
|
|
596
|
-
if (!isOpenClawListener) return null;
|
|
597
536
|
const port = Number(portMatch[1]);
|
|
598
537
|
if (!Number.isFinite(port) || port <= 0) return null;
|
|
599
538
|
return {
|
|
@@ -604,34 +543,59 @@ function detectOpenClawRuntime(workspaceRoot: string) {
|
|
|
604
543
|
};
|
|
605
544
|
})
|
|
606
545
|
.filter((item): item is { pid: number; ppid: number; port: number; command: string } => Boolean(item));
|
|
546
|
+
const gatewayProbeOk = gatewayListeners.length > 0;
|
|
547
|
+
let processes: Array<{ pid: number; ppid: number; command: string }> = gatewayListeners.map((item) => ({
|
|
548
|
+
pid: item.pid,
|
|
549
|
+
ppid: item.ppid,
|
|
550
|
+
command: item.command,
|
|
551
|
+
}));
|
|
552
|
+
let processResult: ReturnType<typeof spawnSync> | null = null;
|
|
553
|
+
if (!gatewayProbeOk) {
|
|
554
|
+
processResult = spawnSync("ps", ["-Ao", "pid=,ppid=,command="], {
|
|
555
|
+
encoding: "utf8",
|
|
556
|
+
timeout: 1200,
|
|
557
|
+
});
|
|
558
|
+
const stdout = String(processResult.stdout || "");
|
|
559
|
+
const lines = stdout
|
|
560
|
+
.split("\n")
|
|
561
|
+
.map((line) => line.trim())
|
|
562
|
+
.filter(Boolean);
|
|
563
|
+
processes = lines
|
|
564
|
+
.map((line) => {
|
|
565
|
+
const match = line.match(/^(\d+)\s+(\d+)\s+(.+)$/);
|
|
566
|
+
if (!match) return null;
|
|
567
|
+
const command = match[3] || "";
|
|
568
|
+
const lower = command.toLowerCase();
|
|
569
|
+
const isOpenClaw =
|
|
570
|
+
lower.includes(" openclaw ") ||
|
|
571
|
+
lower.endsWith(" openclaw") ||
|
|
572
|
+
lower.includes("/openclaw ") ||
|
|
573
|
+
lower.includes("openclaw.mjs") ||
|
|
574
|
+
lower.includes("openclaw gateway") ||
|
|
575
|
+
lower.includes("openclaw agent") ||
|
|
576
|
+
lower.includes("openclaw message");
|
|
577
|
+
if (!isOpenClaw) return null;
|
|
578
|
+
return {
|
|
579
|
+
pid: Number(match[1]),
|
|
580
|
+
ppid: Number(match[2]),
|
|
581
|
+
command,
|
|
582
|
+
};
|
|
583
|
+
})
|
|
584
|
+
.filter((item): item is { pid: number; ppid: number; command: string } => Boolean(item));
|
|
585
|
+
}
|
|
586
|
+
|
|
607
587
|
const preferredListener =
|
|
608
588
|
gatewayListeners.find((item) => item.port === configuredGateway.gateway_port) ||
|
|
609
589
|
gatewayListeners[0] ||
|
|
610
590
|
null;
|
|
611
|
-
|
|
612
|
-
const
|
|
613
|
-
for (const process of [...processes, ...gatewayListeners]) {
|
|
614
|
-
if (!combinedProcesses.has(process.pid)) {
|
|
615
|
-
combinedProcesses.set(process.pid, process);
|
|
616
|
-
continue;
|
|
617
|
-
}
|
|
618
|
-
const current = combinedProcesses.get(process.pid);
|
|
619
|
-
if (current && current.command.length < process.command.length) {
|
|
620
|
-
combinedProcesses.set(process.pid, process);
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
const allProcesses = Array.from(combinedProcesses.values());
|
|
624
|
-
const gatewayReachable = gatewayListeners.length > 0;
|
|
591
|
+
const allProcesses = processes.slice(0, 10);
|
|
592
|
+
const gatewayReachable = gatewayProbeOk;
|
|
625
593
|
const detectionNotes = [];
|
|
626
|
-
if (
|
|
627
|
-
detectionNotes.push(String(statusStderr || "openclaw status failed").trim());
|
|
628
|
-
}
|
|
629
|
-
if (gatewayStatusProbe && gatewayStatusProbe.status !== 0) {
|
|
594
|
+
if (gatewayProbe.status !== 0 && gatewayLines.length === 0) {
|
|
630
595
|
detectionNotes.push(String(gatewayStatusStderr || "openclaw gateway probe failed").trim());
|
|
631
596
|
}
|
|
632
|
-
if (
|
|
633
|
-
|
|
634
|
-
detectionNotes.push(String(gatewayProbe.stderr || "lsof failed").trim());
|
|
597
|
+
if (processResult && processResult.status !== 0) {
|
|
598
|
+
detectionNotes.push(String(processResult.stderr || "ps failed").trim());
|
|
635
599
|
}
|
|
636
600
|
const gatewayPort = preferredListener?.port || configuredGateway.gateway_port;
|
|
637
601
|
const gatewayUrl = `http://${OPENCLAW_GATEWAY_HOST}:${gatewayPort}/`;
|
|
@@ -647,14 +611,13 @@ function detectOpenClawRuntime(workspaceRoot: string) {
|
|
|
647
611
|
status_command: statusCommand ? [statusCommand.cmd, ...statusCommand.args].join(" ") : null,
|
|
648
612
|
status_ok: statusLooksConfigured,
|
|
649
613
|
status_summary: statusLooksConfigured
|
|
650
|
-
?
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
.
|
|
654
|
-
|
|
655
|
-
.join(" | ")
|
|
614
|
+
? configuredGateway.config_path
|
|
615
|
+
? `configured via ${configuredGateway.config_path}`
|
|
616
|
+
: statusCommand
|
|
617
|
+
? `command available: ${[statusCommand.cmd, ...statusCommand.args].join(" ")}`
|
|
618
|
+
: "OpenClaw environment detected"
|
|
656
619
|
: null,
|
|
657
|
-
gateway_probe_command: gatewayProbeCommand
|
|
620
|
+
gateway_probe_command: gatewayProbeCommand.join(" "),
|
|
658
621
|
gateway_probe_ok: gatewayProbeOk,
|
|
659
622
|
gateway_probe_summary: gatewayProbeOk
|
|
660
623
|
? gatewayStatusStdout
|
|
@@ -1201,6 +1164,9 @@ export class LocalNodeService {
|
|
|
1201
1164
|
getOverview() {
|
|
1202
1165
|
const discovered = this.search("");
|
|
1203
1166
|
const onlineCount = discovered.filter((profile) => profile.online).length;
|
|
1167
|
+
const openclawInstallation = detectOpenClawInstallation(this.projectRoot);
|
|
1168
|
+
const openclawRuntime = this.getCachedOpenClawRuntime();
|
|
1169
|
+
const openclawSkillInstallation = detectOpenClawSkillInstallation();
|
|
1204
1170
|
|
|
1205
1171
|
return {
|
|
1206
1172
|
app_version: this.appVersion,
|
|
@@ -1217,6 +1183,15 @@ export class LocalNodeService {
|
|
|
1217
1183
|
init_state: this.initState,
|
|
1218
1184
|
presence_ttl_ms: PRESENCE_TTL_MS,
|
|
1219
1185
|
onboarding: this.getOnboardingSummary(),
|
|
1186
|
+
openclaw: {
|
|
1187
|
+
detected: openclawInstallation.detected,
|
|
1188
|
+
running: openclawRuntime.running,
|
|
1189
|
+
detection_mode: openclawRuntime.detection_mode,
|
|
1190
|
+
gateway_url: openclawRuntime.gateway_url,
|
|
1191
|
+
gateway_probe_ok: openclawRuntime.gateway_probe_ok,
|
|
1192
|
+
status_ok: openclawRuntime.status_ok,
|
|
1193
|
+
skill_installed: openclawSkillInstallation.installed,
|
|
1194
|
+
},
|
|
1220
1195
|
social: {
|
|
1221
1196
|
found: this.socialFound,
|
|
1222
1197
|
enabled: this.socialConfig.enabled,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
2026.3.20-beta.
|
|
1
|
+
2026.3.20-beta.9
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "silicaclaw-broadcast",
|
|
3
|
-
"version": "2026.3.20-beta.
|
|
3
|
+
"version": "2026.3.20-beta.9",
|
|
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": {
|