@silicaclaw/cli 2026.3.19-6 → 2026.3.19-7

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.
@@ -0,0 +1,144 @@
1
+ export function createShellController({ t, resolveThemeMode }) {
2
+ function peerStatusText(status) {
3
+ if (status === "online") return t("overview.online");
4
+ if (status === "offline") return t("overview.offline");
5
+ if (status === "stale") return t("network.stale");
6
+ return status || "-";
7
+ }
8
+
9
+ function readUiCache(key) {
10
+ try {
11
+ const raw = localStorage.getItem(key);
12
+ return raw ? JSON.parse(raw) : null;
13
+ } catch {
14
+ return null;
15
+ }
16
+ }
17
+
18
+ function writeUiCache(key, value) {
19
+ try {
20
+ localStorage.setItem(key, JSON.stringify(value));
21
+ } catch {
22
+ // ignore cache write failures
23
+ }
24
+ }
25
+
26
+ function hydrateCachedShell() {
27
+ const overview = readUiCache("silicaclaw_ui_overview");
28
+ if (overview) {
29
+ if (overview.overviewCardsHtml) document.getElementById("overviewCards").innerHTML = overview.overviewCardsHtml;
30
+ if (overview.snapshotText) document.getElementById("snapshot").innerHTML = overview.snapshotText;
31
+ if (overview.heroModeText) document.getElementById("heroMode").textContent = overview.heroModeText;
32
+ if (overview.pillBroadcastText) document.getElementById("pillBroadcast").textContent = overview.pillBroadcastText;
33
+ if (overview.pillBroadcastClassName) document.getElementById("pillBroadcast").className = overview.pillBroadcastClassName;
34
+ if (overview.agentsCountHintText) document.getElementById("agentsCountHint").textContent = overview.agentsCountHintText;
35
+ if (overview.agentsWrapHtml) document.getElementById("agentsWrap").innerHTML = overview.agentsWrapHtml;
36
+ }
37
+ const network = readUiCache("silicaclaw_ui_network");
38
+ if (network) {
39
+ if (network.heroAdapterText) document.getElementById("heroAdapter").textContent = network.heroAdapterText;
40
+ if (network.heroRelayText) document.getElementById("heroRelay").textContent = network.heroRelayText;
41
+ if (network.heroRoomText) document.getElementById("heroRoom").textContent = network.heroRoomText;
42
+ if (network.pillAdapterText) document.getElementById("pillAdapter").textContent = network.pillAdapterText;
43
+ }
44
+ const social = readUiCache("silicaclaw_ui_social");
45
+ if (social) {
46
+ if (social.integrationStatusText) document.getElementById("integrationStatusBar").textContent = social.integrationStatusText;
47
+ if (social.integrationStatusClassName) document.getElementById("integrationStatusBar").className = social.integrationStatusClassName;
48
+ if (social.socialStatusLineText) document.getElementById("socialStatusLine").textContent = social.socialStatusLineText;
49
+ if (social.socialStatusSublineText) document.getElementById("socialStatusSubline").textContent = social.socialStatusSublineText;
50
+ }
51
+ }
52
+
53
+ function toast(msg) {
54
+ const toastEl = document.getElementById("toast");
55
+ toastEl.textContent = msg;
56
+ toastEl.classList.add("show");
57
+ setTimeout(() => toastEl.classList.remove("show"), 2000);
58
+ }
59
+
60
+ function flashButton(btn, doneText = "Done") {
61
+ if (!btn) return;
62
+ const oldText = btn.textContent || "";
63
+ btn.disabled = true;
64
+ btn.textContent = doneText;
65
+ setTimeout(() => {
66
+ btn.textContent = oldText;
67
+ btn.disabled = false;
68
+ }, 900);
69
+ }
70
+
71
+ function setFeedback(id, text, level = "info") {
72
+ const el = document.getElementById(id);
73
+ el.textContent = text;
74
+ el.style.color = level === "error" ? "#ff6b81" : level === "warn" ? "#ffb454" : "#9aa7c3";
75
+ }
76
+
77
+ function setProfileNextStepVisible(show) {
78
+ const banner = document.getElementById("profileNextStepBanner");
79
+ banner.classList.toggle("show", Boolean(show));
80
+ }
81
+
82
+ function renderSocialModeHint(selected, effective, restartRequired, pending = false) {
83
+ const restartText = restartRequired ? t("hints.restartRequiredHint") : t("hints.restartNotRequiredHint");
84
+ const pendingText = pending ? ` ${t("hints.pendingRuntimeModeHint")}` : "";
85
+ document.getElementById("socialModeHint").textContent = `${t("hints.socialModeHint", {
86
+ selected,
87
+ effective,
88
+ restart: restartText,
89
+ })}${pendingText}`;
90
+ }
91
+
92
+ function setSocialModePendingState(pending) {
93
+ const applyBtn = document.getElementById("socialModeApplyBtn");
94
+ applyBtn.classList.toggle("button-pending", Boolean(pending));
95
+ }
96
+
97
+ function pulseOverviewBroadcastStep() {
98
+ const step = document.getElementById("overviewStepBroadcast");
99
+ const body = document.getElementById("overviewStepBroadcastBody");
100
+ const status = document.getElementById("overviewStepBroadcastStatus");
101
+ if (!step || !body || !status) return;
102
+ const previousBody = body.textContent;
103
+ const previousStatus = status.textContent;
104
+ step.classList.add("is-done", "is-highlighted");
105
+ status.textContent = t("overview.stepDone");
106
+ body.textContent = t("overview.stepBroadcastSuccess");
107
+ setTimeout(() => {
108
+ step.classList.remove("is-highlighted");
109
+ if (body.textContent === t("overview.stepBroadcastSuccess")) {
110
+ body.textContent = previousBody;
111
+ }
112
+ if (status.textContent === t("overview.stepDone")) {
113
+ status.textContent = previousStatus;
114
+ }
115
+ }, 2200);
116
+ }
117
+
118
+ function applyTheme(mode) {
119
+ const raw = mode === "system" ? "system" : mode === "light" ? "light" : "dark";
120
+ const next = resolveThemeMode(raw);
121
+ document.documentElement.setAttribute("data-theme-mode", next);
122
+ localStorage.setItem("silicaclaw_theme_mode", raw);
123
+ document.querySelectorAll("[data-theme-choice]").forEach((btn) => {
124
+ btn.classList.toggle("active", btn.dataset.themeChoice === raw);
125
+ btn.classList.toggle("topbar-theme-mode__btn--active", btn.dataset.themeChoice === raw);
126
+ btn.setAttribute("aria-checked", btn.dataset.themeChoice === raw ? "true" : "false");
127
+ btn.setAttribute("aria-pressed", btn.dataset.themeChoice === raw ? "true" : "false");
128
+ });
129
+ }
130
+
131
+ return {
132
+ applyTheme,
133
+ flashButton,
134
+ hydrateCachedShell,
135
+ peerStatusText,
136
+ pulseOverviewBroadcastStep,
137
+ renderSocialModeHint,
138
+ setFeedback,
139
+ setProfileNextStepVisible,
140
+ setSocialModePendingState,
141
+ toast,
142
+ writeUiCache,
143
+ };
144
+ }
@@ -0,0 +1,485 @@
1
+ export function createSocialController({
2
+ api,
3
+ escapeHtml,
4
+ formatMessageBody,
5
+ getActiveTab,
6
+ getLogLevelFilter,
7
+ getLogsCache,
8
+ getSocialMessageFilter,
9
+ getSocialMessageGovernance,
10
+ getSocialMessagesCache,
11
+ getSocialModeDirty,
12
+ getSocialModePending,
13
+ getSocialTemplate,
14
+ getVisibleRemotePublicCount,
15
+ renderSocialModeHint,
16
+ setLogLevelFilter,
17
+ setLogsCache,
18
+ setSocialMessageGovernance,
19
+ setSocialMessagesCache,
20
+ setSocialModePendingState,
21
+ setSocialTemplate,
22
+ shortId,
23
+ t,
24
+ toPrettyJson,
25
+ writeUiCache,
26
+ }) {
27
+ function renderSocialMessages() {
28
+ const listEl = document.getElementById("socialMessageList");
29
+ const metaEl = document.getElementById("socialMessageMeta");
30
+ const hintEl = document.getElementById("socialMessageHint");
31
+ const governance = getSocialMessageGovernance();
32
+ const socialMessagesCache = getSocialMessagesCache();
33
+ const socialMessageFilter = getSocialMessageFilter();
34
+ const governanceHint = governance
35
+ ? `${t("overview.messageHint")} ${t("overview.governanceSummary", {
36
+ max: String(governance.send_limit?.max || "-"),
37
+ seconds: String(Math.floor((governance.send_limit?.window_ms || 60000) / 1000)),
38
+ })}`
39
+ : t("overview.messageHint");
40
+ hintEl.textContent = governanceHint;
41
+ if (!socialMessagesCache.length) {
42
+ metaEl.textContent = t("overview.noMessagesMeta");
43
+ listEl.innerHTML = `<div class="empty-state">${t("overview.noMessagesEmpty")}</div>`;
44
+ return;
45
+ }
46
+
47
+ const filteredMessages = socialMessagesCache.filter((item) => {
48
+ if (socialMessageFilter === "self") return Boolean(item.is_self);
49
+ if (socialMessageFilter === "remote") return !item.is_self;
50
+ return true;
51
+ });
52
+
53
+ const baseMeta = socialMessageFilter === "all"
54
+ ? t("overview.cachedMessages", { count: String(socialMessagesCache.length) })
55
+ : t("overview.filteredMessages", { count: String(filteredMessages.length) });
56
+ const governanceMeta = governance
57
+ ? ` · ${t("overview.governanceSummary", {
58
+ max: String(governance.send_limit?.max || "-"),
59
+ seconds: String(Math.floor((governance.send_limit?.window_ms || 60000) / 1000)),
60
+ })}`
61
+ : "";
62
+ metaEl.textContent = `${baseMeta}${governanceMeta}`;
63
+
64
+ if (!filteredMessages.length) {
65
+ listEl.innerHTML = `<div class="empty-state">${t("overview.noMessagesEmpty")}</div>`;
66
+ return;
67
+ }
68
+
69
+ listEl.innerHTML = filteredMessages
70
+ .map((item) => {
71
+ const selfStatusChips = item.is_self
72
+ ? `
73
+ <span class="tag-chip" style="margin-left:8px;">${t("overview.selfMessagePublished")}</span>
74
+ <span class="tag-chip" style="margin-left:8px;">${t("overview.selfMessageConfirmed")}</span>
75
+ <span class="tag-chip" style="margin-left:8px;">${
76
+ item.remote_observation_count > 0
77
+ ? t("overview.selfMessageRemoteObserved", { count: String(item.remote_observation_count) })
78
+ : t("overview.selfMessageAwaitingObservation")
79
+ }</span>
80
+ <span class="tag-chip" style="margin-left:8px;">${t("overview.selfMessageRemoteVisible", { count: String(getVisibleRemotePublicCount()) })}</span>
81
+ `
82
+ : "";
83
+ const observationChip = item.remote_observation_count > 0
84
+ ? `<span class="tag-chip" style="margin-left:8px;">${t("overview.messageObservedBy", { count: String(item.remote_observation_count) })}</span>`
85
+ : "";
86
+ return `
87
+ <div class="log-item">
88
+ <div style="display:flex; align-items:center; justify-content:space-between; gap:12px;">
89
+ <div>
90
+ <strong>${escapeHtml(item.display_name || t("overview.unnamed"))}</strong>
91
+ <span class="mono" style="color:#90a2c3; margin-left:8px;">${escapeHtml(shortId(item.agent_id || ""))}</span>
92
+ <span class="tag-chip" style="margin-left:8px;">${escapeHtml(item.topic || t("labels.globalTopic"))}</span>
93
+ ${item.is_self ? `<span class="tag-chip" style="margin-left:8px;">${t("overview.messageFilterSelf")}</span>` : ""}
94
+ <span class="tag-chip" style="margin-left:8px;">${item.online ? t("overview.online") : t("overview.offline")}</span>
95
+ ${observationChip}
96
+ ${selfStatusChips}
97
+ </div>
98
+ <div class="mono" style="color:#90a2c3;">${new Date(item.created_at).toLocaleString()}</div>
99
+ </div>
100
+ <div style="margin-top:8px; line-height:1.6;">${formatMessageBody(item.body || "")}</div>
101
+ </div>
102
+ `;
103
+ })
104
+ .join("");
105
+ }
106
+
107
+ async function refreshMessages() {
108
+ const result = await api("/api/messages?limit=50");
109
+ setSocialMessagesCache(Array.isArray(result.data?.items) ? result.data.items : []);
110
+ setSocialMessageGovernance(result.data?.governance || null);
111
+ renderSocialMessages();
112
+ }
113
+
114
+ async function refreshSocial() {
115
+ const [socialRes, summaryRes, statusRes, networkCfgRes, governanceRes] = await Promise.all([
116
+ api("/api/social/config"),
117
+ api("/api/social/integration-summary"),
118
+ api("/api/integration/status"),
119
+ api("/api/network/config"),
120
+ api("/api/social/message-governance"),
121
+ ]);
122
+ const bridgeRes = await api("/api/openclaw/bridge");
123
+ const social = socialRes.data || {};
124
+ const summary = summaryRes.data || {};
125
+ const status = statusRes.data || {};
126
+ const networkCfg = networkCfgRes.data || {};
127
+ const bridge = bridgeRes.data || {};
128
+ const governance = governanceRes.data || {};
129
+ const runtime = social.runtime || {};
130
+ const config = social.social_config || {};
131
+ const network = config.network || {};
132
+ const runtimeNetwork = runtime.resolved_network || {};
133
+ const effectiveAdapterExtra = networkCfg.adapter_extra || {};
134
+ const effectiveMode = networkCfg.mode || status.network_mode || summary.current_network_mode || runtimeNetwork.mode || network.mode || "-";
135
+ const selectedMode = network.mode || runtimeNetwork.mode || status.network_mode || effectiveMode || "-";
136
+ const effectiveAdapter = networkCfg.adapter || runtimeNetwork.adapter || summary.current_adapter || "-";
137
+ const effectiveNamespace = networkCfg.namespace || runtimeNetwork.namespace || summary.current_namespace || "-";
138
+ const effectiveRoom = effectiveAdapterExtra.room || runtimeNetwork.room || network.room || "-";
139
+ const effectiveRelay = effectiveAdapterExtra.signaling_url || runtimeNetwork.signaling_url || network.signaling_url || "-";
140
+ const discoverable = status.discoverable === true;
141
+ const mode = effectiveMode;
142
+ const summaryLine = status.status_line || summary.summary_line || `${summary.connected ? t("social.connectedToSilicaClaw") : t("social.notConnectedToSilicaClaw")} · ${discoverable ? t("social.discoverableInCurrentMode") : t("social.notDiscoverableInCurrentMode")} · ${t("social.usingMode", { mode })}`;
143
+ const publicDiscoveryText = status.public_enabled ? t("social.publicDiscoveryEnabled") : t("social.publicDiscoveryDisabled");
144
+
145
+ const namespaceText = `${status.connected_to_silicaclaw ? t("social.connectedToSilicaClaw") : t("social.notConnected")} · ${publicDiscoveryText} · ${t("social.mode")} ${mode}`;
146
+ document.getElementById("socialStatusLine").textContent = summaryLine;
147
+ document.getElementById("socialStatusSubline").textContent = namespaceText;
148
+ const bar = document.getElementById("integrationStatusBar");
149
+ bar.className = `integration-strip ${status.connected_to_silicaclaw && status.public_enabled ? "ok" : "warn"}${getActiveTab() === "overview" ? "" : " hidden"}`;
150
+ bar.textContent = t("social.barStatus", {
151
+ connected: status.connected_to_silicaclaw ? t("common.yes") : t("common.no"),
152
+ mode,
153
+ public: status.public_enabled ? t("common.on") : t("common.off"),
154
+ });
155
+ document.getElementById("brandStatusDot").className = `sidebar-version__status ${status.connected_to_silicaclaw ? "ok" : "warn"}`;
156
+ writeUiCache("silicaclaw_ui_social", {
157
+ integrationStatusText: bar.textContent,
158
+ integrationStatusClassName: bar.className,
159
+ socialStatusLineText: summaryLine,
160
+ socialStatusSublineText: namespaceText,
161
+ });
162
+ const reasons = [];
163
+ if (!status.configured && status.configured_reason) reasons.push(t("social.configuredReason", { reason: status.configured_reason }));
164
+ if (!status.running && status.running_reason) reasons.push(t("social.runningReason", { reason: status.running_reason }));
165
+ if (!status.discoverable && status.discoverable_reason) reasons.push(t("social.discoverableReasonFull", { reason: status.discoverable_reason }));
166
+ document.getElementById("socialStateHint").textContent = reasons.length ? reasons.join(" · ") : t("hints.allIntegrationChecksPassed");
167
+ const modeSelect = document.getElementById("socialModeSelect");
168
+ const displayedSelectedMode = getSocialModeDirty() && getSocialModePending() ? getSocialModePending() : selectedMode;
169
+ if (modeSelect && displayedSelectedMode !== "-") modeSelect.value = displayedSelectedMode;
170
+ renderSocialModeHint(displayedSelectedMode, mode, !!social.network_requires_restart, getSocialModeDirty());
171
+ setSocialModePendingState(getSocialModeDirty());
172
+
173
+ document.getElementById("socialPrimaryCards").innerHTML = [
174
+ [t("social.configured"), status.configured ? t("common.yes") : t("common.no")],
175
+ [t("social.running"), status.running ? t("common.yes") : t("common.no")],
176
+ [t("social.discoverable"), discoverable ? t("common.yes") : t("common.no")],
177
+ [t("social.publicDiscovery"), status.public_enabled ? t("common.on") : t("common.off")],
178
+ [t("social.networkMode"), mode],
179
+ [t("labels.adapter"), effectiveAdapter],
180
+ [t("social.discoverableReason"), status.discoverable_reason || "-"],
181
+ ].map(([k, v]) => `<div class="card"><div class="label">${k}</div><div class="value" style="font-size:17px;">${v}</div></div>`).join("");
182
+
183
+ document.getElementById("socialIntegrationCards").innerHTML = [
184
+ [t("social.connected"), bridge.connected_to_silicaclaw ? t("common.yes") : t("common.no")],
185
+ [t("social.messageBroadcast"), bridge.message_broadcast_enabled ? t("common.on") : t("common.off")],
186
+ [t("social.displayName"), status.display_name || t("overview.unnamed")],
187
+ [t("social.agentId"), shortId(status.agent_id || "")],
188
+ [t("social.socialFound"), summary.social_md_found ? t("common.yes") : t("common.no")],
189
+ [t("social.socialSource"), summary.social_md_source_path || "-"],
190
+ [t("social.reuseOpenClawIdentity"), summary.reused_openclaw_identity ? t("common.yes") : t("common.no")],
191
+ ].map(([k, v]) => `<div class="card"><div class="label">${k}</div><div class="value" style="font-size:17px;">${v}</div></div>`).join("");
192
+
193
+ const skillLearning = bridge.skill_learning || {};
194
+ const ownerDelivery = bridge.owner_delivery || {};
195
+ const installAction = skillLearning.install_action || {};
196
+ const openclawRunning = !!bridge.openclaw_runtime?.running;
197
+ const openclawDetected = !!bridge.openclaw_installation?.detected || openclawRunning || !!bridge.openclaw_runtime?.gateway_reachable;
198
+ const skillInstalled = !!skillLearning.installed;
199
+ const installedSkillPath = skillLearning.installed_skill_path || "-";
200
+ let ownerDeliveryHeadline = t("feedback.openclawRoleBroadcasterOnly");
201
+ let ownerDeliveryBody = ownerDelivery.reason || "-";
202
+ let ownerDeliveryTone = "warn";
203
+ if (ownerDelivery.ready) {
204
+ ownerDeliveryHeadline = t("feedback.openclawRoleOwnerReady");
205
+ ownerDeliveryBody = t("hints.ownerDeliveryReadyBody");
206
+ ownerDeliveryTone = "info";
207
+ } else if (ownerDelivery.bridge_messages_readable) {
208
+ if (ownerDelivery.forward_command_configured) {
209
+ ownerDeliveryHeadline = t("feedback.openclawRoleNeedsOwnerRoute");
210
+ ownerDeliveryBody = t("hints.ownerDeliveryNeedsRouteBody");
211
+ } else {
212
+ ownerDeliveryHeadline = t("feedback.openclawRoleLearningOnly");
213
+ ownerDeliveryBody = t("hints.ownerDeliveryLearningBody");
214
+ }
215
+ } else if (!openclawRunning) {
216
+ ownerDeliveryHeadline = t("feedback.openclawRoleNotRunning");
217
+ ownerDeliveryBody = ownerDelivery.reason || "-";
218
+ }
219
+ document.getElementById("socialOwnerDeliveryStatus").className = `feedback ${ownerDeliveryTone}`;
220
+ document.getElementById("socialOwnerDeliveryStatus").textContent = ownerDeliveryHeadline;
221
+ document.getElementById("socialOwnerDeliverySubline").textContent = [
222
+ `${t("social.broadcastReadable")}: ${ownerDelivery.bridge_messages_readable ? t("common.yes") : t("common.no")}`,
223
+ `${t("social.ownerForwardCommand")}: ${ownerDelivery.forward_command_configured ? t("common.yes") : t("common.no")}`,
224
+ `${t("social.ownerForwardReady")}: ${ownerDelivery.ready ? t("common.yes") : t("common.no")}`,
225
+ ].join(" · ");
226
+ document.getElementById("socialOwnerDeliveryReason").textContent = ownerDeliveryBody;
227
+ document.getElementById("openclawSkillCards").innerHTML = [
228
+ [t("social.openclawInstalled"), openclawDetected ? t("common.yes") : t("common.no")],
229
+ [t("social.running"), openclawRunning ? t("common.yes") : t("common.no")],
230
+ [t("social.skillInstalled"), skillInstalled ? t("common.yes") : t("common.no")],
231
+ [t("social.broadcastReadable"), ownerDelivery.bridge_messages_readable ? t("common.yes") : t("common.no")],
232
+ [t("social.ownerForwardReady"), ownerDelivery.ready ? t("common.yes") : t("common.no")],
233
+ [t("social.ownerForwardCommand"), ownerDelivery.forward_command_configured ? t("common.yes") : t("common.no")],
234
+ [t("social.openclawDetectionMode"), bridge.openclaw_runtime?.detection_mode || "-"],
235
+ [t("social.openclawGateway"), bridge.openclaw_runtime?.gateway_url || "-"],
236
+ [t("social.installMode"), skillLearning.install_mode || "-"],
237
+ [t("social.installedPath"), skillInstalled ? installedSkillPath : "-"],
238
+ ].map(([k, v]) => `<div class="card"><div class="label">${k}</div><div class="value" style="font-size:17px;">${escapeHtml(v)}</div></div>`).join("");
239
+ document.getElementById("openclawSkillPath").textContent = ownerDelivery.forward_command
240
+ ? `${ownerDelivery.forward_command}${ownerDelivery.owner_channel ? ` · ${ownerDelivery.owner_channel}` : ""}${ownerDelivery.owner_target ? ` · ${ownerDelivery.owner_target}` : ""}`
241
+ : skillInstalled
242
+ ? installedSkillPath
243
+ : `${installAction.recommended_command || "-"}${bridge.openclaw_runtime?.gateway_url ? ` · detect ${bridge.openclaw_runtime.gateway_url}` : ""}`;
244
+ document.getElementById("openclawSkillHint").textContent = !openclawDetected
245
+ ? t("feedback.openclawRoleBroadcasterOnly")
246
+ : !openclawRunning
247
+ ? t("feedback.openclawRoleNotRunning")
248
+ : !skillInstalled
249
+ ? t("feedback.openclawRoleReadyToLearn")
250
+ : ownerDelivery.ready
251
+ ? t("feedback.openclawRoleOwnerReady")
252
+ : ownerDelivery.bridge_messages_readable && !ownerDelivery.forward_command_configured
253
+ ? t("feedback.openclawRoleLearningOnly")
254
+ : ownerDelivery.bridge_messages_readable
255
+ ? t("feedback.openclawRoleNeedsOwnerRoute")
256
+ : t("feedback.openclawRoleLearned");
257
+ const skillInstallBtn = document.getElementById("openclawSkillInstallBtn");
258
+ skillInstallBtn.textContent = !openclawDetected
259
+ ? t("actions.openclawNotInstalled")
260
+ : !openclawRunning
261
+ ? t("actions.openclawNotRunning")
262
+ : skillInstalled
263
+ ? t("actions.openclawSkillLearned")
264
+ : t("actions.learnOpenClawSkill");
265
+ skillInstallBtn.disabled = !openclawDetected || !openclawRunning || skillInstalled;
266
+
267
+ const policy = governance.policy || {};
268
+ const blockedAgentIds = Array.isArray(policy.blocked_agent_ids) ? policy.blocked_agent_ids : [];
269
+ const blockedTerms = Array.isArray(policy.blocked_terms) ? policy.blocked_terms : [];
270
+ document.getElementById("governanceSendLimitInput").value = String(policy.send_limit?.max ?? 5);
271
+ document.getElementById("governanceSendWindowInput").value = String(Math.floor((policy.send_limit?.window_ms ?? 60000) / 1000));
272
+ document.getElementById("governanceReceiveLimitInput").value = String(policy.receive_limit?.max ?? 8);
273
+ document.getElementById("governanceReceiveWindowInput").value = String(Math.floor((policy.receive_limit?.window_ms ?? 60000) / 1000));
274
+ document.getElementById("governanceDuplicateWindowInput").value = String(Math.floor((policy.duplicate_window_ms ?? 180000) / 1000));
275
+ document.getElementById("governanceBlockedAgentsInput").value = blockedAgentIds.join(", ");
276
+ document.getElementById("governanceBlockedTermsInput").value = blockedTerms.join(", ");
277
+ document.getElementById("socialGovernanceCards").innerHTML = [
278
+ [t("labels.sendLimit"), `${policy.send_limit?.max ?? "-"} / ${Math.floor((policy.send_limit?.window_ms ?? 60000) / 1000)}s`],
279
+ [t("labels.receiveLimit"), `${policy.receive_limit?.max ?? "-"} / ${Math.floor((policy.receive_limit?.window_ms ?? 60000) / 1000)}s`],
280
+ [t("labels.duplicateWindowSeconds"), `${Math.floor((policy.duplicate_window_ms ?? 0) / 1000)}s`],
281
+ [t("labels.blockedAgentIds"), blockedAgentIds.length],
282
+ [t("labels.blockedTerms"), blockedTerms.length],
283
+ ].map(([k, v]) => `<div class="card"><div class="label">${k}</div><div class="value" style="font-size:17px;">${v}</div></div>`).join("");
284
+
285
+ const moderationEvents = Array.isArray(governance.recent_events) ? governance.recent_events : [];
286
+ document.getElementById("socialModerationList").innerHTML = moderationEvents.length === 0
287
+ ? `<div class="empty-state">${t("network.noModerationEvents")}</div>`
288
+ : moderationEvents.map((event) => `
289
+ <div class="log-item">
290
+ <div class="log-${event.level || "warn"}">[${String(event.level || "warn").toUpperCase()}] ${escapeHtml(event.message || "-")}</div>
291
+ <div class="mono" style="color:#90a2c3;">${new Date(event.timestamp).toLocaleString()}</div>
292
+ </div>
293
+ `).join("");
294
+
295
+ document.getElementById("socialAdvancedCards").innerHTML = [
296
+ [t("labels.adapter"), effectiveAdapter],
297
+ [t("social.namespace"), effectiveNamespace],
298
+ [t("labels.room"), effectiveRoom],
299
+ [t("social.bridgeStatus"), bridge.connected_to_silicaclaw ? t("common.yes") : t("common.no")],
300
+ [t("social.messageBroadcast"), bridge.message_broadcast_enabled ? t("common.on") : t("common.off")],
301
+ [t("social.restartRequired"), social.network_requires_restart ? t("common.yes") : t("common.no")],
302
+ ].map(([k, v]) => `<div class="card"><div class="label">${k}</div><div class="value" style="font-size:17px;">${v}</div></div>`).join("");
303
+ document.getElementById("socialAdvancedWrap").textContent = toPrettyJson({
304
+ effective_runtime: {
305
+ mode: effectiveMode,
306
+ adapter: effectiveAdapter,
307
+ namespace: effectiveNamespace,
308
+ relay: effectiveRelay,
309
+ room: effectiveRoom,
310
+ restart_required: social.network_requires_restart,
311
+ },
312
+ openclaw_bridge: {
313
+ enabled: bridge.enabled,
314
+ connected_to_silicaclaw: bridge.connected_to_silicaclaw,
315
+ public_enabled: bridge.public_enabled,
316
+ message_broadcast_enabled: bridge.message_broadcast_enabled,
317
+ openclaw_runtime: bridge.openclaw_runtime || {},
318
+ skill_learning: bridge.skill_learning || {},
319
+ endpoints: bridge.endpoints || {},
320
+ },
321
+ source: {
322
+ social_md_found: summary.social_md_found,
323
+ social_md_source_path: summary.social_md_source_path,
324
+ },
325
+ });
326
+
327
+ document.getElementById("socialSourceWrap").textContent = toPrettyJson({
328
+ found: social.found,
329
+ source_path: social.source_path,
330
+ parse_error: social.parse_error,
331
+ });
332
+ document.getElementById("socialRawWrap").textContent = toPrettyJson({
333
+ raw_frontmatter: social.raw_frontmatter || null,
334
+ });
335
+ document.getElementById("socialRuntimeWrap").textContent = toPrettyJson(runtime);
336
+ }
337
+
338
+ async function exportSocialTemplate() {
339
+ const payload = (await api("/api/social/export-template")).data || {};
340
+ setSocialTemplate(String(payload.content || ""));
341
+ document.getElementById("socialTemplateWrap").textContent = getSocialTemplate() || "-";
342
+ return payload;
343
+ }
344
+
345
+ function renderLogs() {
346
+ const logsCache = getLogsCache();
347
+ const logLevelFilter = getLogLevelFilter();
348
+ const el = document.getElementById("logList");
349
+ if (!logsCache.length) {
350
+ el.innerHTML = `<div class="empty-state">${t("network.noLogsYet")}</div>`;
351
+ return;
352
+ }
353
+ const filtered = logLevelFilter === "all" ? logsCache : logsCache.filter((item) => String(item.level || "").toLowerCase() === logLevelFilter);
354
+ if (!filtered.length) {
355
+ el.innerHTML = `<div class="empty-state">${t("network.noLogsForLevel", { level: logLevelFilter })}</div>`;
356
+ return;
357
+ }
358
+ el.innerHTML = filtered.map((item) => `
359
+ <div class="log-item">
360
+ <div class="log-${item.level}">[${String(item.level).toUpperCase()}] ${item.message}</div>
361
+ <div class="mono" style="color:#90a2c3;">${new Date(item.timestamp).toLocaleString()}</div>
362
+ </div>
363
+ `).join("");
364
+ }
365
+
366
+ async function refreshLogs() {
367
+ setLogsCache((await api("/api/logs")).data || []);
368
+ renderLogs();
369
+ }
370
+
371
+ function renderSkillCard(skill, options = {}) {
372
+ const capabilities = Array.isArray(skill.capabilities) ? skill.capabilities.slice(0, 6) : [];
373
+ const statusText = options.statusText || (skill.installed_in_openclaw ? t("common.yes") : t("hints.skillsNotInstalled"));
374
+ const versionText = String(skill.version || "-");
375
+ const bodyText = escapeHtml(String(skill.description || "-"));
376
+ const installTone = skill.installed_in_openclaw || skill.install_mode === "workspace" || skill.install_mode === "legacy"
377
+ ? "tag-chip emphasis"
378
+ : "tag-chip muted";
379
+ const sourceText = escapeHtml(String(skill.source_path || skill.bundled_source_path || skill.installed_path || "-"));
380
+ const locationText = escapeHtml(String(skill.installed_path || skill.manifest_path || "-"));
381
+ return `
382
+ <div class="skill-card">
383
+ <div class="skill-card__top">
384
+ <div>
385
+ <div class="skill-card__eyebrow">${escapeHtml(options.eyebrow || skill.install_mode || "skill")}</div>
386
+ <div class="skill-card__title">${escapeHtml(skill.display_name || skill.name || "Skill")}</div>
387
+ </div>
388
+ <div class="skill-card__version mono">${escapeHtml(versionText)}</div>
389
+ </div>
390
+ <div class="skill-card__body">${bodyText}</div>
391
+ <div class="skill-card__tags">
392
+ <span class="${installTone}">${escapeHtml(statusText)}</span>
393
+ ${capabilities.map((item) => `<span class="tag-chip">${escapeHtml(String(item))}</span>`).join("")}
394
+ </div>
395
+ <div class="skill-card__meta">
396
+ <div class="skill-card__meta-item">
397
+ <div class="skill-card__meta-label">${t("labels.skillsSource")}</div>
398
+ <div class="skill-card__meta-value mono">${sourceText}</div>
399
+ </div>
400
+ <div class="skill-card__meta-item">
401
+ <div class="skill-card__meta-label">${t("labels.skillsLocation")}</div>
402
+ <div class="skill-card__meta-value mono">${locationText}</div>
403
+ </div>
404
+ </div>
405
+ </div>
406
+ `;
407
+ }
408
+
409
+ async function refreshSkills() {
410
+ const payload = (await api("/api/skills")).data || {};
411
+ const bundled = Array.isArray(payload.bundled_skills) ? payload.bundled_skills : [];
412
+ const installed = Array.isArray(payload.installed_skills) ? payload.installed_skills : [];
413
+ const openclaw = payload.openclaw || {};
414
+ const summary = payload.summary || {};
415
+ const installAction = payload.install_action || {};
416
+ const featured = bundled[0] || null;
417
+ const openclawDetected = !!openclaw.detected;
418
+ const openclawRunning = !!openclaw.running;
419
+
420
+ document.getElementById("skillsBannerRuntimeValue").textContent = t("hints.skillsRuntimeSummary", {
421
+ runtime: openclawRunning ? t("common.yes") : t("common.no"),
422
+ bundled: String(summary.bundled_count || 0),
423
+ installed: String(summary.installed_count || 0),
424
+ });
425
+
426
+ document.getElementById("skillsSummaryCards").innerHTML = [
427
+ [t("labels.skillsBundled"), String(summary.bundled_count || 0)],
428
+ [t("labels.skillsInstalled"), String(summary.installed_count || 0)],
429
+ [t("social.openclawInstalled"), openclawDetected ? t("common.yes") : t("common.no")],
430
+ [t("social.running"), openclawRunning ? t("common.yes") : t("common.no")],
431
+ ].map(([k, v]) => `<div class="card"><div class="label">${k}</div><div class="value" style="font-size:17px;">${escapeHtml(v)}</div></div>`).join("");
432
+
433
+ document.getElementById("skillsFeaturedSpotlight").innerHTML = featured
434
+ ? `
435
+ <div class="skills-spotlight">
436
+ <div class="skill-card__eyebrow">${escapeHtml(openclaw.gateway_url || "OpenClaw skill")}</div>
437
+ <div class="skills-spotlight__title">${escapeHtml(featured.display_name || featured.name)}</div>
438
+ <div class="skills-spotlight__body">${escapeHtml(featured.description || "-")}</div>
439
+ <div class="skill-card__tags">
440
+ <span class="${featured.installed_in_openclaw ? "tag-chip emphasis" : "tag-chip muted"}">${featured.installed_in_openclaw ? t("common.yes") : t("hints.skillsNotInstalled")}</span>
441
+ ${(featured.capabilities || []).slice(0, 8).map((item) => `<span class="tag-chip">${escapeHtml(String(item))}</span>`).join("")}
442
+ </div>
443
+ </div>
444
+ `
445
+ : `<div class="skills-empty">${t("hints.skillsNoBundled")}</div>`;
446
+
447
+ document.getElementById("skillsBundledGrid").innerHTML = bundled.length
448
+ ? bundled.map((skill) => renderSkillCard(skill, {
449
+ eyebrow: skill.install_mode === "workspace" || skill.install_mode === "legacy" ? skill.install_mode : "bundled",
450
+ statusText: skill.installed_in_openclaw ? `${t("labels.skillsStatus")}: ${t("common.yes")}` : t("hints.skillsNotInstalled"),
451
+ })).join("")
452
+ : `<div class="skills-empty">${t("hints.skillsNoBundled")}</div>`;
453
+
454
+ document.getElementById("skillsInstalledGrid").innerHTML = installed.length
455
+ ? installed.map((skill) => renderSkillCard(skill, {
456
+ eyebrow: skill.install_mode || "installed",
457
+ statusText: `${t("labels.skillsStatus")}: ${escapeHtml(String(skill.install_mode || "-"))}`,
458
+ })).join("")
459
+ : `<div class="skills-empty">${t("hints.skillsNoInstalled")}</div>`;
460
+
461
+ const installBtn = document.getElementById("skillsInstallBtn");
462
+ installBtn.textContent = !openclawDetected
463
+ ? t("actions.openclawNotInstalled")
464
+ : !openclawRunning
465
+ ? t("actions.openclawNotRunning")
466
+ : featured?.installed_in_openclaw
467
+ ? t("actions.openclawSkillLearned")
468
+ : t("actions.learnOpenClawSkill");
469
+ installBtn.disabled = !openclawDetected || !openclawRunning || !!featured?.installed_in_openclaw;
470
+ document.getElementById("skillsFeedback").textContent = featured?.installed_in_openclaw
471
+ ? t("feedback.openclawSkillInstalled")
472
+ : installAction.recommended_command || t("common.ready");
473
+ }
474
+
475
+ return {
476
+ exportSocialTemplate,
477
+ refreshLogs,
478
+ refreshMessages,
479
+ refreshSkills,
480
+ refreshSocial,
481
+ renderLogs,
482
+ renderSocialMessages,
483
+ setLogLevelFilter,
484
+ };
485
+ }