@silicaclaw/cli 2026.3.20-3 → 2026.3.20-5
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 +39 -0
- package/apps/local-console/dist/apps/local-console/src/server.js +229 -12
- 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 +293 -2
- package/apps/local-console/public/app/network.js +144 -32
- package/apps/local-console/public/app/overview.js +43 -15
- package/apps/local-console/public/app/social.js +135 -53
- package/apps/local-console/public/app/styles.css +86 -0
- package/apps/local-console/public/app/template.js +7 -1
- package/apps/local-console/public/app/translations.js +44 -0
- package/apps/local-console/src/server.ts +262 -14
- 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/references/runtime-setup.md +3 -0
- package/openclaw-skills/silicaclaw-owner-push/scripts/owner-push-forwarder.mjs +67 -8
- 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
- package/scripts/silicaclaw-cli.mjs +4 -1
- package/scripts/silicaclaw-gateway.mjs +108 -0
|
@@ -27,6 +27,7 @@ if (!root) {
|
|
|
27
27
|
throw new Error("Missing root element: app-root");
|
|
28
28
|
}
|
|
29
29
|
root.innerHTML = appTemplate;
|
|
30
|
+
const APP_UPDATE_SESSION_KEY = 'silicaclaw_pending_updated_version';
|
|
30
31
|
|
|
31
32
|
const i18n = createI18n(TRANSLATIONS);
|
|
32
33
|
const DEFAULT_LOCALE = i18n.DEFAULT_LOCALE;
|
|
@@ -70,6 +71,10 @@ root.innerHTML = appTemplate;
|
|
|
70
71
|
document.getElementById('sidebarToggleBtn').setAttribute('aria-label', t('labels.collapseSidebar'));
|
|
71
72
|
document.querySelector('.sidebar-version').title = t('common.version');
|
|
72
73
|
setText('.sidebar-version__label', t('common.version'));
|
|
74
|
+
document.getElementById('brandUpdateHint').textContent = t('labels.versionChecking');
|
|
75
|
+
document.getElementById('brandRelayHint').textContent = t('labels.relayQueuesHealthy');
|
|
76
|
+
document.getElementById('brandCheckUpdateBtn').textContent = t('actions.checkUpdate');
|
|
77
|
+
document.getElementById('brandUpdateBtn').textContent = t('actions.updateNow');
|
|
73
78
|
document.getElementById('integrationStatusBar').textContent = t('social.barStatus', {
|
|
74
79
|
connected: '-',
|
|
75
80
|
mode: '-',
|
|
@@ -319,6 +324,216 @@ root.innerHTML = appTemplate;
|
|
|
319
324
|
toast,
|
|
320
325
|
writeUiCache,
|
|
321
326
|
} = shell;
|
|
327
|
+
let appUpdatePollTimer = null;
|
|
328
|
+
let appUpdateCheckInFlight = false;
|
|
329
|
+
let relayQueueCheckInFlight = false;
|
|
330
|
+
let lastRelayQueueCheckAt = 0;
|
|
331
|
+
|
|
332
|
+
function setAppUpdateUi({
|
|
333
|
+
hint,
|
|
334
|
+
buttonVisible = false,
|
|
335
|
+
buttonDisabled = false,
|
|
336
|
+
buttonText = t('actions.updateNow'),
|
|
337
|
+
checkVisible = false,
|
|
338
|
+
checkDisabled = false,
|
|
339
|
+
}) {
|
|
340
|
+
const hintEl = document.getElementById('brandUpdateHint');
|
|
341
|
+
const buttonEl = document.getElementById('brandUpdateBtn');
|
|
342
|
+
const checkEl = document.getElementById('brandCheckUpdateBtn');
|
|
343
|
+
if (hintEl) {
|
|
344
|
+
hintEl.textContent = hint;
|
|
345
|
+
hintEl.classList.toggle('hidden', !hint);
|
|
346
|
+
}
|
|
347
|
+
if (checkEl) {
|
|
348
|
+
checkEl.textContent = t('actions.checkUpdate');
|
|
349
|
+
checkEl.classList.toggle('hidden', !checkVisible);
|
|
350
|
+
checkEl.disabled = checkDisabled;
|
|
351
|
+
}
|
|
352
|
+
if (buttonEl) {
|
|
353
|
+
buttonEl.textContent = buttonText;
|
|
354
|
+
buttonEl.classList.toggle('hidden', !buttonVisible);
|
|
355
|
+
buttonEl.disabled = buttonDisabled;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function platformUpdateHint(platform) {
|
|
360
|
+
if (platform === 'darwin') return t('labels.versionPlatformMac');
|
|
361
|
+
if (platform === 'linux') return t('labels.versionPlatformLinux');
|
|
362
|
+
return t('labels.versionPlatformOther');
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function setRelayQueueUi({ hint = '', tone = 'ok', visible = false }) {
|
|
366
|
+
const hintEl = document.getElementById('brandRelayHint');
|
|
367
|
+
if (!hintEl) return;
|
|
368
|
+
hintEl.textContent = hint;
|
|
369
|
+
hintEl.classList.toggle('hidden', !visible || !hint);
|
|
370
|
+
hintEl.classList.remove('warn', 'danger');
|
|
371
|
+
if (tone === 'warn' || tone === 'danger') {
|
|
372
|
+
hintEl.classList.add(tone);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
async function refreshRelayQueueStatus({ force = false } = {}) {
|
|
377
|
+
const now = Date.now();
|
|
378
|
+
if (relayQueueCheckInFlight) return null;
|
|
379
|
+
if (!force && now - lastRelayQueueCheckAt < 15_000) return null;
|
|
380
|
+
relayQueueCheckInFlight = true;
|
|
381
|
+
try {
|
|
382
|
+
const result = await api('/api/peers');
|
|
383
|
+
const peers = result.data || {};
|
|
384
|
+
const peerItems = Array.isArray(peers.items) ? peers.items : [];
|
|
385
|
+
const relayQueueMax = peerItems.reduce((max, peer) => Math.max(max, Number(peer?.meta?.relay_queue_size || 0)), 0);
|
|
386
|
+
const signalQueueMax = peerItems.reduce((max, peer) => Math.max(max, Number(peer?.meta?.signal_queue_size || 0)), 0);
|
|
387
|
+
const queueMax = Math.max(relayQueueMax, signalQueueMax);
|
|
388
|
+
if (queueMax >= 100) {
|
|
389
|
+
setRelayQueueUi({ hint: t('labels.relayQueuesHigh'), tone: 'danger', visible: true });
|
|
390
|
+
} else if (queueMax >= 20) {
|
|
391
|
+
setRelayQueueUi({ hint: t('labels.relayQueuesWatch'), tone: 'warn', visible: true });
|
|
392
|
+
} else {
|
|
393
|
+
setRelayQueueUi({ hint: t('labels.relayQueuesHealthy'), tone: 'ok', visible: true });
|
|
394
|
+
}
|
|
395
|
+
lastRelayQueueCheckAt = now;
|
|
396
|
+
return { relayQueueMax, signalQueueMax };
|
|
397
|
+
} catch (_error) {
|
|
398
|
+
return null;
|
|
399
|
+
} finally {
|
|
400
|
+
relayQueueCheckInFlight = false;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
async function refreshAppUpdateStatus({ silent = false } = {}) {
|
|
405
|
+
if (appUpdateCheckInFlight) return null;
|
|
406
|
+
appUpdateCheckInFlight = true;
|
|
407
|
+
try {
|
|
408
|
+
const result = await api('/api/app/update-status');
|
|
409
|
+
const status = result.data || {};
|
|
410
|
+
const currentVersion = String(status.current_version || '').trim();
|
|
411
|
+
const latestVersion = String(status.latest_version || '').trim();
|
|
412
|
+
const platformHint = platformUpdateHint(String(status.platform || ''));
|
|
413
|
+
if (currentVersion) {
|
|
414
|
+
document.getElementById('brandVersion').textContent = currentVersion.startsWith('v') ? currentVersion : `v${currentVersion}`;
|
|
415
|
+
}
|
|
416
|
+
if (status.update_available && status.latest_version) {
|
|
417
|
+
setAppUpdateUi({
|
|
418
|
+
hint: `${t('labels.versionUpdateReady', { version: `v${status.latest_version}` })} · ${platformHint}`,
|
|
419
|
+
buttonVisible: true,
|
|
420
|
+
buttonDisabled: false,
|
|
421
|
+
buttonText: t('actions.updateNowVersion', { version: latestVersion.startsWith('v') ? latestVersion : `v${latestVersion}` }),
|
|
422
|
+
checkVisible: true,
|
|
423
|
+
checkDisabled: false,
|
|
424
|
+
});
|
|
425
|
+
} else if (status.check_error) {
|
|
426
|
+
setAppUpdateUi({
|
|
427
|
+
hint: t('labels.versionCheckFailed'),
|
|
428
|
+
buttonVisible: false,
|
|
429
|
+
buttonDisabled: false,
|
|
430
|
+
buttonText: t('actions.updateNow'),
|
|
431
|
+
checkVisible: true,
|
|
432
|
+
checkDisabled: false,
|
|
433
|
+
});
|
|
434
|
+
if (!silent) {
|
|
435
|
+
setFeedback('networkFeedback', t('feedback.appUpdateCheckFailed'), 'warn');
|
|
436
|
+
}
|
|
437
|
+
} else {
|
|
438
|
+
setAppUpdateUi({
|
|
439
|
+
hint: `${t('labels.versionCurrent')} · ${platformHint}`,
|
|
440
|
+
buttonVisible: false,
|
|
441
|
+
buttonDisabled: false,
|
|
442
|
+
buttonText: t('actions.updateNow'),
|
|
443
|
+
checkVisible: true,
|
|
444
|
+
checkDisabled: false,
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
return status;
|
|
448
|
+
} catch (_error) {
|
|
449
|
+
setAppUpdateUi({
|
|
450
|
+
hint: t('labels.versionCheckFailed'),
|
|
451
|
+
buttonVisible: false,
|
|
452
|
+
buttonDisabled: false,
|
|
453
|
+
buttonText: t('actions.updateNow'),
|
|
454
|
+
checkVisible: true,
|
|
455
|
+
checkDisabled: false,
|
|
456
|
+
});
|
|
457
|
+
if (!silent) {
|
|
458
|
+
setFeedback('networkFeedback', t('feedback.appUpdateCheckFailed'), 'warn');
|
|
459
|
+
}
|
|
460
|
+
return null;
|
|
461
|
+
} finally {
|
|
462
|
+
appUpdateCheckInFlight = false;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function startAppUpdatePolling(targetVersion) {
|
|
467
|
+
if (appUpdatePollTimer) {
|
|
468
|
+
window.clearInterval(appUpdatePollTimer);
|
|
469
|
+
}
|
|
470
|
+
let attempts = 0;
|
|
471
|
+
appUpdatePollTimer = window.setInterval(async () => {
|
|
472
|
+
attempts += 1;
|
|
473
|
+
const status = await refreshAppUpdateStatus({ silent: true });
|
|
474
|
+
if (status && !status.update_available && String(status.current_version || '') === String(targetVersion || '')) {
|
|
475
|
+
window.clearInterval(appUpdatePollTimer);
|
|
476
|
+
appUpdatePollTimer = null;
|
|
477
|
+
if (targetVersion) {
|
|
478
|
+
window.sessionStorage.setItem(APP_UPDATE_SESSION_KEY, String(targetVersion));
|
|
479
|
+
}
|
|
480
|
+
window.location.reload();
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
if (attempts >= 24) {
|
|
484
|
+
window.clearInterval(appUpdatePollTimer);
|
|
485
|
+
appUpdatePollTimer = null;
|
|
486
|
+
setAppUpdateUi({
|
|
487
|
+
hint: t('labels.versionCurrent'),
|
|
488
|
+
buttonVisible: false,
|
|
489
|
+
buttonDisabled: false,
|
|
490
|
+
buttonText: t('actions.updateNow'),
|
|
491
|
+
checkVisible: true,
|
|
492
|
+
checkDisabled: false,
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
}, 5000);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
async function triggerAppUpdate() {
|
|
499
|
+
const buttonEl = document.getElementById('brandUpdateBtn');
|
|
500
|
+
setAppUpdateUi({
|
|
501
|
+
hint: t('labels.versionUpdating'),
|
|
502
|
+
buttonVisible: true,
|
|
503
|
+
buttonDisabled: true,
|
|
504
|
+
buttonText: t('labels.versionUpdating'),
|
|
505
|
+
checkVisible: true,
|
|
506
|
+
checkDisabled: true,
|
|
507
|
+
});
|
|
508
|
+
try {
|
|
509
|
+
const result = await api('/api/app/update', { method: 'POST' });
|
|
510
|
+
const data = result.data || {};
|
|
511
|
+
if (!data.started) {
|
|
512
|
+
setAppUpdateUi({
|
|
513
|
+
hint: t('labels.versionCurrent'),
|
|
514
|
+
buttonVisible: false,
|
|
515
|
+
buttonDisabled: false,
|
|
516
|
+
buttonText: t('actions.updateNow'),
|
|
517
|
+
checkVisible: true,
|
|
518
|
+
checkDisabled: false,
|
|
519
|
+
});
|
|
520
|
+
toast(t('feedback.appUpdateLatest'));
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
toast(t('feedback.appUpdateStarted'));
|
|
524
|
+
startAppUpdatePolling(String(data.target_version || ''));
|
|
525
|
+
} catch (error) {
|
|
526
|
+
setAppUpdateUi({
|
|
527
|
+
hint: t('labels.versionCheckFailed'),
|
|
528
|
+
buttonVisible: true,
|
|
529
|
+
buttonDisabled: false,
|
|
530
|
+
buttonText: t('actions.updateNow'),
|
|
531
|
+
checkVisible: true,
|
|
532
|
+
checkDisabled: false,
|
|
533
|
+
});
|
|
534
|
+
setFeedback('networkFeedback', error instanceof Error ? error.message : t('feedback.appUpdateFailed'), 'error');
|
|
535
|
+
}
|
|
536
|
+
}
|
|
322
537
|
setLocale(currentLocale);
|
|
323
538
|
applyStaticTranslations();
|
|
324
539
|
|
|
@@ -427,6 +642,23 @@ root.innerHTML = appTemplate;
|
|
|
427
642
|
document.getElementById('publicDiscoveryHint')?.classList.toggle('hidden', tab !== 'overview');
|
|
428
643
|
if (tab === 'profile' && !profileController.isDirty() && !profileController.isSaving()) {
|
|
429
644
|
refreshProfile().catch(() => {});
|
|
645
|
+
} else if (tab === 'overview') {
|
|
646
|
+
refreshOverview().catch(() => {});
|
|
647
|
+
refreshMessages().catch(() => {});
|
|
648
|
+
} else if (tab === 'agent') {
|
|
649
|
+
refreshOverview().catch(() => {});
|
|
650
|
+
} else if (tab === 'chat') {
|
|
651
|
+
refreshMessages().catch(() => {});
|
|
652
|
+
} else if (tab === 'skills') {
|
|
653
|
+
refreshSkills().catch(() => {});
|
|
654
|
+
} else if (tab === 'network') {
|
|
655
|
+
refreshNetwork().catch(() => {});
|
|
656
|
+
refreshPeers().catch(() => {});
|
|
657
|
+
refreshDiscovery().catch(() => {});
|
|
658
|
+
refreshLogs().catch(() => {});
|
|
659
|
+
} else if (tab === 'social') {
|
|
660
|
+
refreshSocial().catch(() => {});
|
|
661
|
+
refreshMessages().catch(() => {});
|
|
430
662
|
}
|
|
431
663
|
}
|
|
432
664
|
|
|
@@ -453,9 +685,46 @@ root.innerHTML = appTemplate;
|
|
|
453
685
|
const renderLogs = socialController.renderLogs;
|
|
454
686
|
const refreshLogs = socialController.refreshLogs;
|
|
455
687
|
const refreshSkills = socialController.refreshSkills;
|
|
688
|
+
let autoRefreshInFlight = false;
|
|
689
|
+
|
|
690
|
+
async function refreshActiveView() {
|
|
691
|
+
const tasks = [refreshPublicProfilePreview(), refreshRelayQueueStatus()];
|
|
692
|
+
if (activeTab === 'overview') {
|
|
693
|
+
tasks.push(refreshOverview(), refreshMessages(), refreshSocial());
|
|
694
|
+
} else if (activeTab === 'agent') {
|
|
695
|
+
tasks.push(refreshOverview());
|
|
696
|
+
} else if (activeTab === 'chat') {
|
|
697
|
+
tasks.push(refreshMessages());
|
|
698
|
+
} else if (activeTab === 'skills') {
|
|
699
|
+
tasks.push(refreshSkills());
|
|
700
|
+
} else if (activeTab === 'network') {
|
|
701
|
+
tasks.push(refreshNetwork(), refreshPeers(), refreshDiscovery(), refreshLogs());
|
|
702
|
+
} else if (activeTab === 'social') {
|
|
703
|
+
tasks.push(refreshSocial(), refreshMessages());
|
|
704
|
+
} else if (activeTab === 'profile' && !profileController.isDirty() && !profileController.isSaving()) {
|
|
705
|
+
tasks.push(refreshProfile());
|
|
706
|
+
}
|
|
707
|
+
const results = await Promise.allSettled(tasks);
|
|
708
|
+
const firstError = results.find((result) => result.status === 'rejected');
|
|
709
|
+
if (firstError && firstError.status === 'rejected') {
|
|
710
|
+
setFeedback('networkFeedback', firstError.reason instanceof Error ? firstError.reason.message : t('common.unknownError'), 'error');
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
async function refreshAuto() {
|
|
715
|
+
if (document.hidden || autoRefreshInFlight) {
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
autoRefreshInFlight = true;
|
|
719
|
+
try {
|
|
720
|
+
await refreshActiveView();
|
|
721
|
+
} finally {
|
|
722
|
+
autoRefreshInFlight = false;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
456
725
|
|
|
457
726
|
async function refreshAll() {
|
|
458
|
-
const tasks = [refreshOverview(), refreshNetwork(), refreshSocial(), refreshSkills(), refreshPublicProfilePreview(), refreshMessages()];
|
|
727
|
+
const tasks = [refreshOverview(), refreshNetwork(), refreshSocial(), refreshSkills(), refreshPublicProfilePreview(), refreshMessages(), refreshRelayQueueStatus({ force: true })];
|
|
459
728
|
if (activeTab === 'network') {
|
|
460
729
|
tasks.push(refreshPeers(), refreshDiscovery(), refreshLogs());
|
|
461
730
|
}
|
|
@@ -511,6 +780,28 @@ root.innerHTML = appTemplate;
|
|
|
511
780
|
|
|
512
781
|
applyTheme(localStorage.getItem('silicaclaw_theme_mode') || 'dark');
|
|
513
782
|
hydrateCachedShell();
|
|
783
|
+
document.getElementById('brandUpdateBtn').addEventListener('click', () => {
|
|
784
|
+
triggerAppUpdate().catch(() => {});
|
|
785
|
+
});
|
|
786
|
+
document.getElementById('brandCheckUpdateBtn').addEventListener('click', () => {
|
|
787
|
+
refreshAppUpdateStatus().catch(() => {});
|
|
788
|
+
});
|
|
514
789
|
refreshAll();
|
|
790
|
+
refreshAppUpdateStatus({ silent: true }).catch(() => {});
|
|
791
|
+
const updatedVersion = window.sessionStorage.getItem(APP_UPDATE_SESSION_KEY);
|
|
792
|
+
if (updatedVersion) {
|
|
793
|
+
window.sessionStorage.removeItem(APP_UPDATE_SESSION_KEY);
|
|
794
|
+
toast(t('feedback.appUpdatedTo', { version: updatedVersion.startsWith('v') ? updatedVersion : `v${updatedVersion}` }));
|
|
795
|
+
}
|
|
515
796
|
exportSocialTemplate().catch(() => {});
|
|
516
|
-
|
|
797
|
+
document.addEventListener('visibilitychange', () => {
|
|
798
|
+
if (!document.hidden) {
|
|
799
|
+
refreshAuto().catch(() => {});
|
|
800
|
+
refreshAppUpdateStatus({ silent: true }).catch(() => {});
|
|
801
|
+
refreshRelayQueueStatus({ force: true }).catch(() => {});
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
setInterval(refreshAuto, 4000);
|
|
805
|
+
setInterval(() => {
|
|
806
|
+
refreshAppUpdateStatus({ silent: true }).catch(() => {});
|
|
807
|
+
}, 15 * 60 * 1000);
|
|
@@ -13,6 +13,21 @@ export function createNetworkController({
|
|
|
13
13
|
room: "",
|
|
14
14
|
};
|
|
15
15
|
let quickConnectDefaults = { ...fallbackQuickConnectDefaults };
|
|
16
|
+
let lastNetworkRenderKey = "";
|
|
17
|
+
let lastPeersRenderKey = "";
|
|
18
|
+
let lastDiscoveryRenderKey = "";
|
|
19
|
+
|
|
20
|
+
function queueState(value) {
|
|
21
|
+
const n = Number(value || 0);
|
|
22
|
+
if (n >= 100) return { tone: "danger", label: t("labels.queueHigh") };
|
|
23
|
+
if (n >= 20) return { tone: "warn", label: t("labels.queueWatch") };
|
|
24
|
+
return { tone: "ok", label: t("labels.queueHealthy") };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function queueBadge(value) {
|
|
28
|
+
const state = queueState(value);
|
|
29
|
+
return `<span class="pill ${state.tone}">${Number(value || 0)} · ${state.label}</span>`;
|
|
30
|
+
}
|
|
16
31
|
|
|
17
32
|
async function refreshNetwork() {
|
|
18
33
|
const [cfg, sts, rtp] = await Promise.all([api("/api/network/config"), api("/api/network/stats"), api("/api/runtime/paths")]);
|
|
@@ -25,6 +40,10 @@ export function createNetworkController({
|
|
|
25
40
|
const transportStats = s.adapter_transport_stats || {};
|
|
26
41
|
const d = s.adapter_discovery_stats || {};
|
|
27
42
|
const dx = s.adapter_diagnostics_summary || {};
|
|
43
|
+
const runtimeDiag = s.runtime_diagnostics || {};
|
|
44
|
+
const runtimeMemory = runtimeDiag.memory_mib || {};
|
|
45
|
+
const runtimeDirectory = runtimeDiag.directory || {};
|
|
46
|
+
const runtimeSocial = runtimeDiag.social || {};
|
|
28
47
|
const ac = s.adapter_config || c.adapter_config || {};
|
|
29
48
|
quickConnectDefaults = {
|
|
30
49
|
signalingUrl: String(
|
|
@@ -59,7 +78,7 @@ export function createNetworkController({
|
|
|
59
78
|
heroRoomText: dx.room || "-",
|
|
60
79
|
pillAdapterText: `${t("labels.adapter")}: ${c.adapter || "-"}`,
|
|
61
80
|
});
|
|
62
|
-
|
|
81
|
+
const networkCardsHtml = [
|
|
63
82
|
[t("labels.adapter"), c.adapter],
|
|
64
83
|
[t("labels.namespace"), c.namespace || "-"],
|
|
65
84
|
[t("labels.port"), c.port ?? "-"],
|
|
@@ -75,12 +94,18 @@ export function createNetworkController({
|
|
|
75
94
|
[t("network.sent"), msg.broadcast_total ?? 0],
|
|
76
95
|
[t("network.peers"), p.total ?? 0],
|
|
77
96
|
[t("network.onlinePeers"), p.online ?? 0],
|
|
97
|
+
["RSS", runtimeMemory.rss ?? "-"],
|
|
98
|
+
["Heap", runtimeMemory.heap_used ?? "-"],
|
|
99
|
+
["Profiles", runtimeDirectory.profile_count ?? "-"],
|
|
100
|
+
["Index keys", runtimeDirectory.index_key_count ?? "-"],
|
|
101
|
+
["Messages", runtimeSocial.message_count ?? "-"],
|
|
102
|
+
["Observations", runtimeSocial.observation_count ?? "-"],
|
|
78
103
|
[t("network.activeWebrtcPeers"), dx.active_webrtc_peers ?? "-"],
|
|
79
104
|
[t("network.reconnectAttempts"), dx.reconnect_attempts_total ?? "-"],
|
|
80
105
|
[t("network.lastInbound"), ago(msg.last_message_at)],
|
|
81
106
|
[t("network.lastOutbound"), ago(msg.last_broadcast_at)],
|
|
82
107
|
].map(([k, v]) => `<div class="card"><div class="label">${k}</div><div class="value" style="font-size:17px;">${v}</div></div>`).join("");
|
|
83
|
-
|
|
108
|
+
const networkSummaryHtml = [
|
|
84
109
|
[t("labels.mode"), describeCurrentMode(t, c.mode || "lan")],
|
|
85
110
|
[t("network.relayHealth"), relayHealth],
|
|
86
111
|
[t("network.currentRelay"), dx.signaling_url || "-"],
|
|
@@ -93,7 +118,7 @@ export function createNetworkController({
|
|
|
93
118
|
|
|
94
119
|
const comp = c.components || {};
|
|
95
120
|
const lim = c.limits || {};
|
|
96
|
-
|
|
121
|
+
const networkComponentsText = [
|
|
97
122
|
`demo_mode: ${c.demo_mode || "-"}`,
|
|
98
123
|
`transport: ${comp.transport || "-"}`,
|
|
99
124
|
`discovery: ${comp.discovery || "-"}`,
|
|
@@ -130,12 +155,39 @@ export function createNetworkController({
|
|
|
130
155
|
`last_discovery_event_at: ${dx.last_discovery_event_at ? new Date(dx.last_discovery_event_at).toISOString() : "-"}`,
|
|
131
156
|
].join("\n");
|
|
132
157
|
|
|
133
|
-
|
|
158
|
+
const networkConfigSnapshotText = toPrettyJson({
|
|
134
159
|
config: c,
|
|
135
160
|
adapter_config: ac,
|
|
136
161
|
runtime_paths: runtimePaths,
|
|
137
162
|
});
|
|
138
|
-
|
|
163
|
+
const networkStatsSnapshotText = toPrettyJson({ stats: s });
|
|
164
|
+
const renderKey = JSON.stringify({
|
|
165
|
+
adapter: c.adapter || "",
|
|
166
|
+
mode: c.mode || "",
|
|
167
|
+
namespace: c.namespace || "",
|
|
168
|
+
port: c.port ?? null,
|
|
169
|
+
signaling_url: dx.signaling_url || "",
|
|
170
|
+
room: dx.room || "",
|
|
171
|
+
relay_health: relayHealth,
|
|
172
|
+
last_poll_at: dx.last_poll_at || 0,
|
|
173
|
+
last_publish_at: dx.last_publish_at || 0,
|
|
174
|
+
last_error: dx.last_error || "",
|
|
175
|
+
msg_received_total: msg.received_total ?? 0,
|
|
176
|
+
msg_broadcast_total: msg.broadcast_total ?? 0,
|
|
177
|
+
peers_total: p.total ?? 0,
|
|
178
|
+
peers_online: p.online ?? 0,
|
|
179
|
+
reconnect_attempts_total: dx.reconnect_attempts_total ?? 0,
|
|
180
|
+
active_webrtc_peers: dx.active_webrtc_peers ?? 0,
|
|
181
|
+
});
|
|
182
|
+
if (renderKey === lastNetworkRenderKey) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
document.getElementById("networkCards").innerHTML = networkCardsHtml;
|
|
186
|
+
document.getElementById("networkSummaryList").innerHTML = networkSummaryHtml;
|
|
187
|
+
document.getElementById("networkComponents").textContent = networkComponentsText;
|
|
188
|
+
document.getElementById("networkConfigSnapshot").textContent = networkConfigSnapshotText;
|
|
189
|
+
document.getElementById("networkStatsSnapshot").textContent = networkStatsSnapshotText;
|
|
190
|
+
lastNetworkRenderKey = renderKey;
|
|
139
191
|
}
|
|
140
192
|
|
|
141
193
|
async function refreshPeers() {
|
|
@@ -143,8 +195,12 @@ export function createNetworkController({
|
|
|
143
195
|
const peers = peerRes.data || {};
|
|
144
196
|
const ds = statsRes.data?.adapter_discovery_stats || {};
|
|
145
197
|
const summary = peers.diagnostics_summary || {};
|
|
146
|
-
|
|
147
|
-
|
|
198
|
+
const peerItems = Array.isArray(peers.items) ? peers.items : [];
|
|
199
|
+
const relayQueueTotal = peerItems.reduce((sum, peer) => sum + Number(peer?.meta?.relay_queue_size || 0), 0);
|
|
200
|
+
const relayQueueMax = peerItems.reduce((max, peer) => Math.max(max, Number(peer?.meta?.relay_queue_size || 0)), 0);
|
|
201
|
+
const signalQueueTotal = peerItems.reduce((sum, peer) => sum + Number(peer?.meta?.signal_queue_size || 0), 0);
|
|
202
|
+
const signalQueueMax = peerItems.reduce((max, peer) => Math.max(max, Number(peer?.meta?.signal_queue_size || 0)), 0);
|
|
203
|
+
const peerCardsHtml = [
|
|
148
204
|
[t("network.total"), peers.total || 0],
|
|
149
205
|
[t("overview.online"), peers.online || 0],
|
|
150
206
|
[t("network.stale"), peers.stale || 0],
|
|
@@ -159,26 +215,28 @@ export function createNetworkController({
|
|
|
159
215
|
[t("network.seedPeers"), summary.seed_peers_count ?? 0],
|
|
160
216
|
[t("network.discoveryEvents"), summary.discovery_events_total ?? 0],
|
|
161
217
|
[t("network.activeWebrtcPeers"), summary.active_webrtc_peers ?? "-"],
|
|
218
|
+
["Relay queue", queueBadge(relayQueueTotal)],
|
|
219
|
+
["Max relay queue", queueBadge(relayQueueMax)],
|
|
220
|
+
["Signal queue", queueBadge(signalQueueTotal)],
|
|
221
|
+
["Max signal queue", queueBadge(signalQueueMax)],
|
|
162
222
|
[t("network.observeCalls"), ds.observe_calls || 0],
|
|
163
223
|
[t("network.heartbeats"), ds.heartbeat_sent || 0],
|
|
164
224
|
[t("network.peersAdded"), ds.peers_added || 0],
|
|
165
225
|
[t("network.peersRemoved"), ds.peers_removed || 0],
|
|
166
226
|
].map(([k, v]) => `<div class="card"><div class="label">${k}</div><div class="value" style="font-size:17px;">${v}</div></div>`).join("");
|
|
227
|
+
const peerStatsText = toPrettyJson({
|
|
228
|
+
discovery_stats: ds,
|
|
229
|
+
diagnostics_summary: summary,
|
|
230
|
+
adapter_stats: statsRes.data?.adapter_stats || {},
|
|
231
|
+
});
|
|
167
232
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
discovery_stats: ds,
|
|
172
|
-
diagnostics_summary: summary,
|
|
173
|
-
});
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
document.getElementById("peerTableWrap").innerHTML = `
|
|
233
|
+
const peerTableHtml = !peers.items || !peers.items.length
|
|
234
|
+
? `<div class="empty-state">${t("network.noPeersDiscovered")}</div>`
|
|
235
|
+
: `
|
|
178
236
|
<table class="table">
|
|
179
|
-
<thead><tr><th>${t("network.peer")}</th><th>${t("network.status")}</th><th>${t("network.lastSeen")}</th><th>${t("network.staleSince")}</th><th>${t("network.messages")}</th><th>${t("network.firstSeen")}</th><th>${t("network.meta")}</th></tr></thead>
|
|
237
|
+
<thead><tr><th>${t("network.peer")}</th><th>${t("network.status")}</th><th>${t("network.lastSeen")}</th><th>${t("network.staleSince")}</th><th>${t("network.messages")}</th><th>${t("network.firstSeen")}</th><th>Relay Q</th><th>Signal Q</th><th>${t("network.meta")}</th></tr></thead>
|
|
180
238
|
<tbody>
|
|
181
|
-
${
|
|
239
|
+
${peerItems.map((peer) => `
|
|
182
240
|
<tr>
|
|
183
241
|
<td class="mono">${shortId(peer.peer_id)}</td>
|
|
184
242
|
<td class="${peer.status === "online" ? "online" : peer.status === "offline" ? "offline" : "stale"}">${peerStatusText(peer.status)}</td>
|
|
@@ -186,25 +244,67 @@ export function createNetworkController({
|
|
|
186
244
|
<td>${peer.stale_since_at ? ago(peer.stale_since_at) : "-"}</td>
|
|
187
245
|
<td>${peer.messages_seen || 0}</td>
|
|
188
246
|
<td>${new Date(peer.first_seen_at).toLocaleTimeString()}</td>
|
|
247
|
+
<td>${queueBadge(Number(peer?.meta?.relay_queue_size || 0))}</td>
|
|
248
|
+
<td>${queueBadge(Number(peer?.meta?.signal_queue_size || 0))}</td>
|
|
189
249
|
<td class="mono">${peer.meta ? JSON.stringify(peer.meta) : "-"}</td>
|
|
190
250
|
</tr>
|
|
191
251
|
`).join("")}
|
|
192
252
|
</tbody>
|
|
193
253
|
</table>
|
|
194
254
|
`;
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
255
|
+
const renderKey = JSON.stringify({
|
|
256
|
+
total: peers.total || 0,
|
|
257
|
+
online: peers.online || 0,
|
|
258
|
+
stale: peers.stale || 0,
|
|
259
|
+
namespace: peers.namespace || "",
|
|
260
|
+
summary: {
|
|
261
|
+
signaling_url: summary.signaling_url || "",
|
|
262
|
+
room: summary.room || "",
|
|
263
|
+
last_join_at: summary.last_join_at || 0,
|
|
264
|
+
last_poll_at: summary.last_poll_at || 0,
|
|
265
|
+
last_publish_at: summary.last_publish_at || 0,
|
|
266
|
+
last_error: summary.last_error || "",
|
|
267
|
+
},
|
|
268
|
+
ds: {
|
|
269
|
+
observe_calls: ds.observe_calls || 0,
|
|
270
|
+
heartbeat_sent: ds.heartbeat_sent || 0,
|
|
271
|
+
peers_added: ds.peers_added || 0,
|
|
272
|
+
peers_removed: ds.peers_removed || 0,
|
|
273
|
+
},
|
|
274
|
+
queues: {
|
|
275
|
+
relay_total: relayQueueTotal,
|
|
276
|
+
relay_max: relayQueueMax,
|
|
277
|
+
signal_total: signalQueueTotal,
|
|
278
|
+
signal_max: signalQueueMax,
|
|
279
|
+
},
|
|
280
|
+
items: peerItems
|
|
281
|
+
? peerItems.map((peer) => [
|
|
282
|
+
peer.peer_id,
|
|
283
|
+
peer.status || "",
|
|
284
|
+
peer.last_seen_at || 0,
|
|
285
|
+
peer.stale_since_at || 0,
|
|
286
|
+
peer.messages_seen || 0,
|
|
287
|
+
peer.first_seen_at || 0,
|
|
288
|
+
Number(peer?.meta?.relay_queue_size || 0),
|
|
289
|
+
Number(peer?.meta?.signal_queue_size || 0),
|
|
290
|
+
peer.meta ? JSON.stringify(peer.meta) : "",
|
|
291
|
+
])
|
|
292
|
+
: [],
|
|
199
293
|
});
|
|
294
|
+
if (renderKey === lastPeersRenderKey) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
document.getElementById("peerCards").innerHTML = peerCardsHtml;
|
|
298
|
+
document.getElementById("peerTableWrap").innerHTML = peerTableHtml;
|
|
299
|
+
document.getElementById("peerStatsWrap").textContent = peerStatsText;
|
|
300
|
+
lastPeersRenderKey = renderKey;
|
|
200
301
|
}
|
|
201
302
|
|
|
202
303
|
async function refreshDiscovery() {
|
|
203
304
|
const eventsRes = await api("/api/discovery/events");
|
|
204
305
|
const payload = eventsRes.data || {};
|
|
205
306
|
const items = Array.isArray(payload.items) ? payload.items : [];
|
|
206
|
-
|
|
207
|
-
document.getElementById("discoveryCards").innerHTML = [
|
|
307
|
+
const discoveryCardsHtml = [
|
|
208
308
|
[t("labels.adapter"), payload.adapter || "-"],
|
|
209
309
|
[t("labels.namespace"), payload.namespace || "-"],
|
|
210
310
|
[t("network.eventsTotal"), payload.total ?? 0],
|
|
@@ -212,11 +312,9 @@ export function createNetworkController({
|
|
|
212
312
|
[t("network.signalingEndpoints"), (payload.signaling_endpoints || []).length || 0],
|
|
213
313
|
[t("network.seedPeers"), payload.seed_peers_count ?? 0],
|
|
214
314
|
].map(([k, v]) => `<div class="card"><div class="label">${k}</div><div class="value" style="font-size:17px;">${v}</div></div>`).join("");
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
} else {
|
|
219
|
-
document.getElementById("discoveryEventList").innerHTML = items
|
|
315
|
+
const discoveryEventListHtml = !items.length
|
|
316
|
+
? `<div class="empty-state">${t("network.noDiscoveryEvents")}</div>`
|
|
317
|
+
: items
|
|
220
318
|
.slice()
|
|
221
319
|
.reverse()
|
|
222
320
|
.map((event) => `
|
|
@@ -227,9 +325,23 @@ export function createNetworkController({
|
|
|
227
325
|
</div>
|
|
228
326
|
`)
|
|
229
327
|
.join("");
|
|
328
|
+
const discoverySnapshotText = toPrettyJson(payload);
|
|
329
|
+
const renderKey = JSON.stringify({
|
|
330
|
+
adapter: payload.adapter || "",
|
|
331
|
+
namespace: payload.namespace || "",
|
|
332
|
+
total: payload.total ?? 0,
|
|
333
|
+
last_event_at: payload.last_event_at || 0,
|
|
334
|
+
signaling_endpoints: payload.signaling_endpoints || [],
|
|
335
|
+
seed_peers_count: payload.seed_peers_count ?? 0,
|
|
336
|
+
items: items.map((event) => [event.type || "", event.peer_id || "", event.endpoint || "", event.detail || "", event.at || 0]),
|
|
337
|
+
});
|
|
338
|
+
if (renderKey === lastDiscoveryRenderKey) {
|
|
339
|
+
return;
|
|
230
340
|
}
|
|
231
|
-
|
|
232
|
-
document.getElementById("
|
|
341
|
+
document.getElementById("discoveryCards").innerHTML = discoveryCardsHtml;
|
|
342
|
+
document.getElementById("discoveryEventList").innerHTML = discoveryEventListHtml;
|
|
343
|
+
document.getElementById("discoverySnapshot").textContent = discoverySnapshotText;
|
|
344
|
+
lastDiscoveryRenderKey = renderKey;
|
|
233
345
|
}
|
|
234
346
|
|
|
235
347
|
return {
|