@silicaclaw/cli 2026.3.20-13 → 2026.3.20-14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/VERSION +1 -1
- package/apps/local-console/dist/apps/local-console/src/server.d.ts +5 -1
- package/apps/local-console/dist/apps/local-console/src/server.js +39 -23
- package/apps/local-console/public/app/app.js +129 -5
- package/apps/local-console/public/app/social.js +29 -2
- package/apps/local-console/public/app/styles.css +3 -2
- package/apps/local-console/public/app/template.js +108 -77
- package/apps/local-console/public/app/translations.js +6 -0
- package/apps/local-console/src/server.ts +33 -14
- 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,12 @@
|
|
|
2
2
|
|
|
3
3
|
## v1.0 beta - 2026-03-20
|
|
4
4
|
|
|
5
|
+
### 2026.3.20-14
|
|
6
|
+
|
|
7
|
+
- release build:
|
|
8
|
+
- prepared another fresh latest-channel package build without publishing
|
|
9
|
+
- regenerated the npm tarball through the verified release packing workflow
|
|
10
|
+
|
|
5
11
|
### 2026.3.20-13
|
|
6
12
|
|
|
7
13
|
- release build:
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
v2026.3.20-
|
|
1
|
+
v2026.3.20-14
|
|
@@ -623,6 +623,7 @@ export declare class LocalNodeService {
|
|
|
623
623
|
getIdentity(): AgentIdentity | null;
|
|
624
624
|
getSocialMessages(limit?: number, options?: {
|
|
625
625
|
agent_id?: string | null;
|
|
626
|
+
offset?: number | null;
|
|
626
627
|
}): {
|
|
627
628
|
total: number;
|
|
628
629
|
items: SocialMessageView[];
|
|
@@ -656,7 +657,10 @@ export declare class LocalNodeService {
|
|
|
656
657
|
last_message_at: number | null;
|
|
657
658
|
unread_count: number;
|
|
658
659
|
}>;
|
|
659
|
-
getPrivateMessages(conversationId: string, limit?: number):
|
|
660
|
+
getPrivateMessages(conversationId: string, limit?: number, offset?: number): {
|
|
661
|
+
total: number;
|
|
662
|
+
items: PrivateMessageView[];
|
|
663
|
+
};
|
|
660
664
|
sendPrivateMessage(input: {
|
|
661
665
|
to_agent_id: string;
|
|
662
666
|
recipient_encryption_public_key: string;
|
|
@@ -1580,6 +1580,7 @@ class LocalNodeService {
|
|
|
1580
1580
|
}
|
|
1581
1581
|
getSocialMessages(limit = 50, options) {
|
|
1582
1582
|
const resolvedLimit = Math.max(1, Math.min(200, Number(limit) || 50));
|
|
1583
|
+
const resolvedOffset = Math.max(0, Number(options?.offset) || 0);
|
|
1583
1584
|
this.ensureLocalDirectoryBaseline();
|
|
1584
1585
|
this.compactCacheInMemory();
|
|
1585
1586
|
const agentId = String(options?.agent_id || "").trim();
|
|
@@ -1588,7 +1589,7 @@ class LocalNodeService {
|
|
|
1588
1589
|
: this.socialMessages;
|
|
1589
1590
|
return {
|
|
1590
1591
|
total: filtered.length,
|
|
1591
|
-
items: filtered.slice(
|
|
1592
|
+
items: filtered.slice(resolvedOffset, resolvedOffset + resolvedLimit).map((message) => {
|
|
1592
1593
|
const profile = this.directory.profiles[message.agent_id];
|
|
1593
1594
|
const lastSeenAt = this.directory.presence[message.agent_id] ?? 0;
|
|
1594
1595
|
const observations = this.socialMessageObservations.filter((item) => item.message_id === message.message_id);
|
|
@@ -1650,11 +1651,12 @@ class LocalNodeService {
|
|
|
1650
1651
|
}
|
|
1651
1652
|
return Array.from(conversations.values()).sort((a, b) => (b.last_message_at || 0) - (a.last_message_at || 0));
|
|
1652
1653
|
}
|
|
1653
|
-
getPrivateMessages(conversationId, limit = PRIVATE_MESSAGE_QUERY_LIMIT) {
|
|
1654
|
+
getPrivateMessages(conversationId, limit = PRIVATE_MESSAGE_QUERY_LIMIT, offset = 0) {
|
|
1654
1655
|
const normalizedConversationId = String(conversationId || "").trim();
|
|
1655
1656
|
const resolvedLimit = Math.max(1, Math.min(PRIVATE_MESSAGE_QUERY_LIMIT, Number(limit) || PRIVATE_MESSAGE_QUERY_LIMIT));
|
|
1657
|
+
const resolvedOffset = Math.max(0, Number(offset) || 0);
|
|
1656
1658
|
const receiptsByMessageId = new Map(this.privateMessageReceipts.map((receipt) => [receipt.message_id, receipt.status]));
|
|
1657
|
-
|
|
1659
|
+
const filtered = this.privateMessages
|
|
1658
1660
|
.filter((message) => {
|
|
1659
1661
|
if (message.from_agent_id === message.to_agent_id) {
|
|
1660
1662
|
return false;
|
|
@@ -1665,20 +1667,24 @@ class LocalNodeService {
|
|
|
1665
1667
|
}
|
|
1666
1668
|
return !normalizedConversationId || message.conversation_id === normalizedConversationId;
|
|
1667
1669
|
})
|
|
1668
|
-
.sort((a, b) => a.created_at - b.created_at)
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1670
|
+
.sort((a, b) => a.created_at - b.created_at);
|
|
1671
|
+
const total = filtered.length;
|
|
1672
|
+
const paged = filtered.slice(Math.max(0, total - resolvedOffset - resolvedLimit), Math.max(0, total - resolvedOffset));
|
|
1673
|
+
return {
|
|
1674
|
+
total,
|
|
1675
|
+
items: paged.map((message) => ({
|
|
1676
|
+
message_id: message.message_id,
|
|
1677
|
+
conversation_id: message.conversation_id,
|
|
1678
|
+
from_agent_id: message.from_agent_id,
|
|
1679
|
+
to_agent_id: message.to_agent_id,
|
|
1680
|
+
body: this.decryptPrivateMessageBody(message),
|
|
1681
|
+
created_at: message.created_at,
|
|
1682
|
+
is_self: message.from_agent_id === this.identity?.agent_id,
|
|
1683
|
+
delivery_status: receiptsByMessageId.get(message.message_id) ||
|
|
1684
|
+
this.privateMessageDeliveryStatusCache.get(message.message_id) ||
|
|
1685
|
+
(message.from_agent_id === this.identity?.agent_id ? "fallback-sent" : "sent"),
|
|
1686
|
+
})),
|
|
1687
|
+
};
|
|
1682
1688
|
}
|
|
1683
1689
|
async sendPrivateMessage(input) {
|
|
1684
1690
|
if (!this.identity || !this.privateEncryptionKeyPair) {
|
|
@@ -1727,7 +1733,7 @@ class LocalNodeService {
|
|
|
1727
1733
|
await this.publish(PRIVATE_MESSAGE_TOPIC, message);
|
|
1728
1734
|
}
|
|
1729
1735
|
this.privateMessageDeliveryStatusCache.set(message.message_id, reason);
|
|
1730
|
-
const view = this.getPrivateMessages(message.conversation_id).find((item) => item.message_id === message.message_id);
|
|
1736
|
+
const view = this.getPrivateMessages(message.conversation_id, PRIVATE_MESSAGE_QUERY_LIMIT, 0).items.find((item) => item.message_id === message.message_id);
|
|
1731
1737
|
if (view) {
|
|
1732
1738
|
view.delivery_status = reason;
|
|
1733
1739
|
}
|
|
@@ -3714,7 +3720,7 @@ async function main() {
|
|
|
3714
3720
|
catch {
|
|
3715
3721
|
// best effort after response has been sent
|
|
3716
3722
|
}
|
|
3717
|
-
},
|
|
3723
|
+
}, 1200);
|
|
3718
3724
|
}));
|
|
3719
3725
|
app.put("/api/profile", asyncRoute(async (req, res) => {
|
|
3720
3726
|
const body = req.body;
|
|
@@ -3811,8 +3817,9 @@ async function main() {
|
|
|
3811
3817
|
}));
|
|
3812
3818
|
app.get("/api/messages", (req, res) => {
|
|
3813
3819
|
const limit = Number(req.query.limit ?? 50);
|
|
3820
|
+
const offset = Number(req.query.offset ?? 0);
|
|
3814
3821
|
const agentId = String(req.query.agent_id ?? "").trim();
|
|
3815
|
-
sendOk(res, node.getSocialMessages(limit, { agent_id: agentId || null }));
|
|
3822
|
+
sendOk(res, node.getSocialMessages(limit, { agent_id: agentId || null, offset }));
|
|
3816
3823
|
});
|
|
3817
3824
|
app.get("/api/private/state", (_req, res) => {
|
|
3818
3825
|
sendOk(res, node.getPrivateMessagingState());
|
|
@@ -3823,7 +3830,8 @@ async function main() {
|
|
|
3823
3830
|
app.get("/api/private/messages", (req, res) => {
|
|
3824
3831
|
const conversationId = String(req.query.conversation_id ?? "").trim();
|
|
3825
3832
|
const limit = Number(req.query.limit ?? PRIVATE_MESSAGE_QUERY_LIMIT);
|
|
3826
|
-
|
|
3833
|
+
const offset = Number(req.query.offset ?? 0);
|
|
3834
|
+
sendOk(res, node.getPrivateMessages(conversationId, limit, offset));
|
|
3827
3835
|
});
|
|
3828
3836
|
app.post("/api/private/messages/send", asyncRoute(async (req, res) => {
|
|
3829
3837
|
const result = await node.sendPrivateMessage({
|
|
@@ -3850,8 +3858,9 @@ async function main() {
|
|
|
3850
3858
|
});
|
|
3851
3859
|
app.get("/api/openclaw/bridge/messages", (req, res) => {
|
|
3852
3860
|
const limit = Number(req.query.limit ?? 50);
|
|
3861
|
+
const offset = Number(req.query.offset ?? 0);
|
|
3853
3862
|
const agentId = String(req.query.agent_id ?? "").trim();
|
|
3854
|
-
sendOk(res, node.getSocialMessages(limit, { agent_id: agentId || null }));
|
|
3863
|
+
sendOk(res, node.getSocialMessages(limit, { agent_id: agentId || null, offset }));
|
|
3855
3864
|
});
|
|
3856
3865
|
app.post("/api/openclaw/bridge/message", asyncRoute(async (req, res) => {
|
|
3857
3866
|
const body = String(req.body?.body || "");
|
|
@@ -3967,9 +3976,16 @@ async function main() {
|
|
|
3967
3976
|
let html = (0, fs_1.readFileSync)(staticIndexFile, "utf8");
|
|
3968
3977
|
html = html.replace("</body>", `${renderBootstrapScript(payload)}\n</body>`);
|
|
3969
3978
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
3979
|
+
res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
|
|
3970
3980
|
res.send(html);
|
|
3971
3981
|
});
|
|
3972
|
-
app.use(express_1.default.static(staticDir
|
|
3982
|
+
app.use(express_1.default.static(staticDir, {
|
|
3983
|
+
etag: false,
|
|
3984
|
+
lastModified: false,
|
|
3985
|
+
setHeaders: (res) => {
|
|
3986
|
+
res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
|
|
3987
|
+
},
|
|
3988
|
+
}));
|
|
3973
3989
|
app.use((error, _req, res, _next) => {
|
|
3974
3990
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
3975
3991
|
sendError(res, 500, "INTERNAL_ERROR", message);
|
|
@@ -28,6 +28,7 @@ if (!root) {
|
|
|
28
28
|
}
|
|
29
29
|
root.innerHTML = appTemplate;
|
|
30
30
|
const APP_UPDATE_SESSION_KEY = 'silicaclaw_pending_updated_version';
|
|
31
|
+
const PAGING_STATE_STORAGE_KEY = 'silicaclaw_ui_paging_state';
|
|
31
32
|
|
|
32
33
|
const i18n = createI18n(TRANSLATIONS);
|
|
33
34
|
const DEFAULT_LOCALE = i18n.DEFAULT_LOCALE;
|
|
@@ -52,7 +53,9 @@ const APP_UPDATE_SESSION_KEY = 'silicaclaw_pending_updated_version';
|
|
|
52
53
|
summary.setAttribute('data-i18n-closed-label', t('labels.show'));
|
|
53
54
|
summary.setAttribute('data-i18n-open-label', t('labels.hide'));
|
|
54
55
|
});
|
|
55
|
-
setText('.nav-section__label', t('common.
|
|
56
|
+
setText('.nav-section__label', t('common.workspace'), 0);
|
|
57
|
+
setText('.nav-section__label', t('common.messages'), 1);
|
|
58
|
+
setText('.nav-section__label', t('common.networkGroup'), 2);
|
|
56
59
|
setText('[data-tab="overview"] .tab-title', t('pageMeta.overview.title'));
|
|
57
60
|
setText('[data-tab="overview"] .tab-copy', t('labels.overviewTabCopy'));
|
|
58
61
|
setText('[data-tab="agent"] .tab-title', t('pageMeta.agent.title'));
|
|
@@ -120,6 +123,12 @@ const APP_UPDATE_SESSION_KEY = 'silicaclaw_pending_updated_version';
|
|
|
120
123
|
document.getElementById('privateTargetIdLabel').textContent = t('social.agentId');
|
|
121
124
|
document.getElementById('privateMessageSendBtn').textContent = t('actions.sendPrivateMessage');
|
|
122
125
|
document.getElementById('privateRefreshBtn').textContent = t('actions.refreshPrivate');
|
|
126
|
+
document.getElementById('socialMessagePrevPageBtn').textContent = t('overview.prevPage');
|
|
127
|
+
document.getElementById('socialMessageNextPageBtn').textContent = t('overview.nextPage');
|
|
128
|
+
document.getElementById('privateConversationPrevPageBtn').textContent = t('overview.prevPage');
|
|
129
|
+
document.getElementById('privateConversationNextPageBtn').textContent = t('overview.nextPage');
|
|
130
|
+
document.getElementById('privateMessagePrevPageBtn').textContent = t('overview.prevPage');
|
|
131
|
+
document.getElementById('privateMessageNextPageBtn').textContent = t('overview.nextPage');
|
|
123
132
|
document.getElementById('chatFeedHint').textContent = t('hints.chatFeedHint');
|
|
124
133
|
document.getElementById('overviewGuideTitle').textContent = t('overview.guideTitle');
|
|
125
134
|
document.getElementById('overviewGuideBody').textContent = t('overview.guideBody');
|
|
@@ -552,6 +561,7 @@ const APP_UPDATE_SESSION_KEY = 'silicaclaw_pending_updated_version';
|
|
|
552
561
|
let activeTab = 'overview';
|
|
553
562
|
let logsCache = [];
|
|
554
563
|
let socialMessagesCache = [];
|
|
564
|
+
let socialMessagePage = 1;
|
|
555
565
|
let logLevelFilter = 'all';
|
|
556
566
|
let socialTemplate = '';
|
|
557
567
|
let socialModeDirty = false;
|
|
@@ -562,6 +572,11 @@ const APP_UPDATE_SESSION_KEY = 'silicaclaw_pending_updated_version';
|
|
|
562
572
|
let privateState = null;
|
|
563
573
|
let privateConversations = [];
|
|
564
574
|
let privateMessages = [];
|
|
575
|
+
let privateConversationPage = 1;
|
|
576
|
+
const PRIVATE_CONVERSATION_PAGE_SIZE = 12;
|
|
577
|
+
let privateMessagesTotal = 0;
|
|
578
|
+
let privateMessagePage = 1;
|
|
579
|
+
const PRIVATE_MESSAGE_PAGE_SIZE = 20;
|
|
565
580
|
let privateTarget = null;
|
|
566
581
|
let selectedPrivateConversationId = '';
|
|
567
582
|
let overviewMode = 'lan';
|
|
@@ -570,6 +585,29 @@ const APP_UPDATE_SESSION_KEY = 'silicaclaw_pending_updated_version';
|
|
|
570
585
|
const AGENTS_PAGE_SIZE = 10;
|
|
571
586
|
const pageMeta = TRANSLATIONS[currentLocale].pageMeta || TRANSLATIONS[DEFAULT_LOCALE].pageMeta;
|
|
572
587
|
|
|
588
|
+
function loadPagingState() {
|
|
589
|
+
try {
|
|
590
|
+
const raw = localStorage.getItem(PAGING_STATE_STORAGE_KEY);
|
|
591
|
+
if (!raw) return null;
|
|
592
|
+
return JSON.parse(raw);
|
|
593
|
+
} catch {
|
|
594
|
+
return null;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
function savePagingState() {
|
|
599
|
+
try {
|
|
600
|
+
localStorage.setItem(PAGING_STATE_STORAGE_KEY, JSON.stringify({
|
|
601
|
+
socialMessagePage,
|
|
602
|
+
privateConversationPage,
|
|
603
|
+
privateMessagePage,
|
|
604
|
+
selectedPrivateConversationId,
|
|
605
|
+
}));
|
|
606
|
+
} catch {
|
|
607
|
+
// ignore localStorage failures
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
573
611
|
async function api(path, options = {}) {
|
|
574
612
|
const res = await fetch(path, { headers: { 'Content-Type': 'application/json' }, ...options });
|
|
575
613
|
const json = await res.json().catch(() => null);
|
|
@@ -704,8 +742,12 @@ const APP_UPDATE_SESSION_KEY = 'silicaclaw_pending_updated_version';
|
|
|
704
742
|
: 'Private messaging unavailable';
|
|
705
743
|
document.getElementById('privateTargetName').value = privateTarget?.display_name || '';
|
|
706
744
|
document.getElementById('privateTargetAgentId').value = privateTarget?.agent_id || '';
|
|
707
|
-
|
|
708
|
-
|
|
745
|
+
const conversationTotalPages = Math.max(1, Math.ceil((privateConversations.length || 0) / PRIVATE_CONVERSATION_PAGE_SIZE));
|
|
746
|
+
const conversationCurrentPage = Math.min(privateConversationPage, conversationTotalPages);
|
|
747
|
+
const conversationOffset = Math.max(0, (conversationCurrentPage - 1) * PRIVATE_CONVERSATION_PAGE_SIZE);
|
|
748
|
+
const visibleConversations = privateConversations.slice(conversationOffset, conversationOffset + PRIVATE_CONVERSATION_PAGE_SIZE);
|
|
749
|
+
document.getElementById('privateConversationList').innerHTML = visibleConversations.length
|
|
750
|
+
? visibleConversations.map((item) => `
|
|
709
751
|
<button class="agent-card" type="button" data-private-conversation="${escapeHtml(item.conversation_id)}">
|
|
710
752
|
<div class="agent-card__avatar-fallback">${escapeHtml(((item.peer_display_name || item.peer_agent_id || '?')[0] || '?').toUpperCase())}</div>
|
|
711
753
|
<div class="agent-card__main">
|
|
@@ -717,6 +759,12 @@ const APP_UPDATE_SESSION_KEY = 'silicaclaw_pending_updated_version';
|
|
|
717
759
|
</button>
|
|
718
760
|
`).join('')
|
|
719
761
|
: `<div class="empty-state">No private conversations yet.</div>`;
|
|
762
|
+
document.getElementById('privateConversationPageMeta').textContent = t('overview.pageStatus', {
|
|
763
|
+
page: String(conversationCurrentPage),
|
|
764
|
+
total: String(conversationTotalPages),
|
|
765
|
+
});
|
|
766
|
+
document.getElementById('privateConversationPrevPageBtn').disabled = conversationCurrentPage <= 1;
|
|
767
|
+
document.getElementById('privateConversationNextPageBtn').disabled = conversationCurrentPage >= conversationTotalPages;
|
|
720
768
|
document.getElementById('privateMessageList').innerHTML = privateMessages.length
|
|
721
769
|
? privateMessages.map((item) => `
|
|
722
770
|
<div class="log-item">
|
|
@@ -731,6 +779,14 @@ const APP_UPDATE_SESSION_KEY = 'silicaclaw_pending_updated_version';
|
|
|
731
779
|
</div>
|
|
732
780
|
`).join('')
|
|
733
781
|
: `<div class="empty-state">No private messages yet.</div>`;
|
|
782
|
+
const totalPages = Math.max(1, Math.ceil((privateMessagesTotal || 0) / PRIVATE_MESSAGE_PAGE_SIZE));
|
|
783
|
+
const currentPage = Math.min(privateMessagePage, totalPages);
|
|
784
|
+
document.getElementById('privateMessagePageMeta').textContent = t('overview.pageStatus', {
|
|
785
|
+
page: String(currentPage),
|
|
786
|
+
total: String(totalPages),
|
|
787
|
+
});
|
|
788
|
+
document.getElementById('privateMessagePrevPageBtn').disabled = currentPage <= 1;
|
|
789
|
+
document.getElementById('privateMessageNextPageBtn').disabled = currentPage >= totalPages;
|
|
734
790
|
}
|
|
735
791
|
|
|
736
792
|
async function refreshPrivate() {
|
|
@@ -740,6 +796,8 @@ const APP_UPDATE_SESSION_KEY = 'silicaclaw_pending_updated_version';
|
|
|
740
796
|
]);
|
|
741
797
|
privateState = stateRes.data || null;
|
|
742
798
|
privateConversations = Array.isArray(conversationsRes.data) ? conversationsRes.data : [];
|
|
799
|
+
const conversationTotalPages = Math.max(1, Math.ceil((privateConversations.length || 0) / PRIVATE_CONVERSATION_PAGE_SIZE));
|
|
800
|
+
privateConversationPage = Math.min(privateConversationPage, conversationTotalPages);
|
|
743
801
|
if ((!privateTarget || privateTarget.agent_id === privateState?.agent_id) && privateConversations[0]) {
|
|
744
802
|
const first = privateConversations[0];
|
|
745
803
|
privateTarget = {
|
|
@@ -758,16 +816,22 @@ const APP_UPDATE_SESSION_KEY = 'silicaclaw_pending_updated_version';
|
|
|
758
816
|
};
|
|
759
817
|
}
|
|
760
818
|
if (selectedPrivateConversationId) {
|
|
761
|
-
const
|
|
762
|
-
|
|
819
|
+
const offset = Math.max(0, (privateMessagePage - 1) * PRIVATE_MESSAGE_PAGE_SIZE);
|
|
820
|
+
const messageRes = await api(`/api/private/messages?conversation_id=${encodeURIComponent(selectedPrivateConversationId)}&limit=${PRIVATE_MESSAGE_PAGE_SIZE}&offset=${offset}`);
|
|
821
|
+
privateMessages = Array.isArray(messageRes.data?.items) ? messageRes.data.items : [];
|
|
822
|
+
privateMessagesTotal = Number(messageRes.data?.total || 0);
|
|
763
823
|
} else {
|
|
764
824
|
privateMessages = [];
|
|
825
|
+
privateMessagesTotal = 0;
|
|
765
826
|
}
|
|
766
827
|
renderPrivate();
|
|
767
828
|
}
|
|
768
829
|
|
|
769
830
|
const renderSocialMessages = socialController.renderSocialMessages;
|
|
770
831
|
const refreshMessages = socialController.refreshMessages;
|
|
832
|
+
const nextSocialMessagesPage = socialController.nextSocialMessagesPage;
|
|
833
|
+
const prevSocialMessagesPage = socialController.prevSocialMessagesPage;
|
|
834
|
+
const setSocialMessagesPage = socialController.setSocialMessagesPage;
|
|
771
835
|
|
|
772
836
|
const refreshNetwork = networkController.refreshNetwork;
|
|
773
837
|
const refreshPeers = networkController.refreshPeers;
|
|
@@ -870,6 +934,9 @@ const APP_UPDATE_SESSION_KEY = 'silicaclaw_pending_updated_version';
|
|
|
870
934
|
display_name: String(button.getAttribute('data-private-name') || ''),
|
|
871
935
|
private_encryption_public_key: String(button.getAttribute('data-private-key') || ''),
|
|
872
936
|
};
|
|
937
|
+
privateConversationPage = 1;
|
|
938
|
+
privateMessagePage = 1;
|
|
939
|
+
savePagingState();
|
|
873
940
|
selectedPrivateConversationId = [privateState?.agent_id || '', privateTarget.agent_id].sort().join(':');
|
|
874
941
|
switchTab('private');
|
|
875
942
|
});
|
|
@@ -886,6 +953,8 @@ const APP_UPDATE_SESSION_KEY = 'silicaclaw_pending_updated_version';
|
|
|
886
953
|
private_encryption_public_key: selectedConversation.peer_public_key,
|
|
887
954
|
};
|
|
888
955
|
}
|
|
956
|
+
privateMessagePage = 1;
|
|
957
|
+
savePagingState();
|
|
889
958
|
await refreshPrivate();
|
|
890
959
|
});
|
|
891
960
|
|
|
@@ -893,6 +962,36 @@ const APP_UPDATE_SESSION_KEY = 'silicaclaw_pending_updated_version';
|
|
|
893
962
|
await refreshPrivate();
|
|
894
963
|
});
|
|
895
964
|
|
|
965
|
+
document.getElementById('privateConversationPrevPageBtn').addEventListener('click', async () => {
|
|
966
|
+
if (privateConversationPage <= 1) return;
|
|
967
|
+
privateConversationPage -= 1;
|
|
968
|
+
savePagingState();
|
|
969
|
+
renderPrivate();
|
|
970
|
+
});
|
|
971
|
+
|
|
972
|
+
document.getElementById('privateConversationNextPageBtn').addEventListener('click', async () => {
|
|
973
|
+
const totalPages = Math.max(1, Math.ceil((privateConversations.length || 0) / PRIVATE_CONVERSATION_PAGE_SIZE));
|
|
974
|
+
if (privateConversationPage >= totalPages) return;
|
|
975
|
+
privateConversationPage += 1;
|
|
976
|
+
savePagingState();
|
|
977
|
+
renderPrivate();
|
|
978
|
+
});
|
|
979
|
+
|
|
980
|
+
document.getElementById('privateMessagePrevPageBtn').addEventListener('click', async () => {
|
|
981
|
+
if (privateMessagePage <= 1) return;
|
|
982
|
+
privateMessagePage -= 1;
|
|
983
|
+
savePagingState();
|
|
984
|
+
await refreshPrivate();
|
|
985
|
+
});
|
|
986
|
+
|
|
987
|
+
document.getElementById('privateMessageNextPageBtn').addEventListener('click', async () => {
|
|
988
|
+
const totalPages = Math.max(1, Math.ceil((privateMessagesTotal || 0) / PRIVATE_MESSAGE_PAGE_SIZE));
|
|
989
|
+
if (privateMessagePage >= totalPages) return;
|
|
990
|
+
privateMessagePage += 1;
|
|
991
|
+
savePagingState();
|
|
992
|
+
await refreshPrivate();
|
|
993
|
+
});
|
|
994
|
+
|
|
896
995
|
document.getElementById('privateMessageSendBtn').addEventListener('click', async () => {
|
|
897
996
|
const body = String(document.getElementById('privateMessageInput').value || '').trim();
|
|
898
997
|
if (!privateTarget?.agent_id || !privateTarget?.private_encryption_public_key) {
|
|
@@ -919,12 +1018,37 @@ const APP_UPDATE_SESSION_KEY = 'silicaclaw_pending_updated_version';
|
|
|
919
1018
|
});
|
|
920
1019
|
document.getElementById('privateMessageInput').value = '';
|
|
921
1020
|
setFeedback('privateFeedback', result.meta?.message || 'Private message sent.');
|
|
1021
|
+
privateMessagePage = 1;
|
|
1022
|
+
savePagingState();
|
|
922
1023
|
await refreshPrivate();
|
|
923
1024
|
} catch (error) {
|
|
924
1025
|
setFeedback('privateFeedback', error instanceof Error ? error.message : 'Private message failed.', 'error');
|
|
925
1026
|
}
|
|
926
1027
|
});
|
|
927
1028
|
|
|
1029
|
+
document.getElementById('socialMessagePrevPageBtn').addEventListener('click', async () => {
|
|
1030
|
+
prevSocialMessagesPage();
|
|
1031
|
+
socialMessagePage = Math.max(1, socialMessagePage - 1);
|
|
1032
|
+
savePagingState();
|
|
1033
|
+
await refreshMessages();
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
document.getElementById('socialMessageNextPageBtn').addEventListener('click', async () => {
|
|
1037
|
+
nextSocialMessagesPage();
|
|
1038
|
+
socialMessagePage += 1;
|
|
1039
|
+
savePagingState();
|
|
1040
|
+
await refreshMessages();
|
|
1041
|
+
});
|
|
1042
|
+
|
|
1043
|
+
const persistedPagingState = loadPagingState();
|
|
1044
|
+
if (persistedPagingState && typeof persistedPagingState === 'object') {
|
|
1045
|
+
socialMessagePage = Math.max(1, Number(persistedPagingState.socialMessagePage) || 1);
|
|
1046
|
+
privateConversationPage = Math.max(1, Number(persistedPagingState.privateConversationPage) || 1);
|
|
1047
|
+
privateMessagePage = Math.max(1, Number(persistedPagingState.privateMessagePage) || 1);
|
|
1048
|
+
selectedPrivateConversationId = String(persistedPagingState.selectedPrivateConversationId || '').trim();
|
|
1049
|
+
setSocialMessagesPage(socialMessagePage);
|
|
1050
|
+
}
|
|
1051
|
+
|
|
928
1052
|
applyTheme(localStorage.getItem('silicaclaw_theme_mode') || 'dark');
|
|
929
1053
|
hydrateCachedShell();
|
|
930
1054
|
document.getElementById('brandUpdateBtn').addEventListener('click', () => {
|
|
@@ -26,8 +26,11 @@ export function createSocialController({
|
|
|
26
26
|
}) {
|
|
27
27
|
const SKILLS_SECTION_LIMIT = 4;
|
|
28
28
|
const SKILLS_DIALOGUE_LIMIT = 1;
|
|
29
|
+
const SOCIAL_MESSAGE_PAGE_SIZE = 20;
|
|
29
30
|
let lastMessagesRenderKey = "";
|
|
30
31
|
let lastLogsRenderKey = "";
|
|
32
|
+
let socialMessagesPage = 1;
|
|
33
|
+
let socialMessagesTotal = 0;
|
|
31
34
|
const sectionRenderCache = new Map();
|
|
32
35
|
let skillsQuery = "";
|
|
33
36
|
let skillsFilter = "all";
|
|
@@ -165,6 +168,18 @@ export function createSocialController({
|
|
|
165
168
|
seconds: String(Math.floor((governance.send_limit?.window_ms || 60000) / 1000)),
|
|
166
169
|
})}`
|
|
167
170
|
: t("overview.messageHint");
|
|
171
|
+
const totalPages = Math.max(1, Math.ceil((socialMessagesTotal || 0) / SOCIAL_MESSAGE_PAGE_SIZE));
|
|
172
|
+
const currentPage = Math.min(socialMessagesPage, totalPages);
|
|
173
|
+
const updatePager = () => {
|
|
174
|
+
const pageMetaEl = document.getElementById("socialMessagePageMeta");
|
|
175
|
+
const prevBtn = document.getElementById("socialMessagePrevPageBtn");
|
|
176
|
+
const nextBtn = document.getElementById("socialMessageNextPageBtn");
|
|
177
|
+
if (pageMetaEl) {
|
|
178
|
+
pageMetaEl.textContent = t("overview.pageStatus", { page: String(currentPage), total: String(totalPages) });
|
|
179
|
+
}
|
|
180
|
+
if (prevBtn) prevBtn.disabled = currentPage <= 1;
|
|
181
|
+
if (nextBtn) nextBtn.disabled = currentPage >= totalPages;
|
|
182
|
+
};
|
|
168
183
|
if (!socialMessagesCache.length) {
|
|
169
184
|
const nextMeta = t("overview.noMessagesMeta");
|
|
170
185
|
const nextHtml = `<div class="empty-state">${t("overview.noMessagesEmpty")}</div>`;
|
|
@@ -173,6 +188,7 @@ export function createSocialController({
|
|
|
173
188
|
hintEl.textContent = governanceHint;
|
|
174
189
|
metaEl.textContent = nextMeta;
|
|
175
190
|
listEl.innerHTML = nextHtml;
|
|
191
|
+
updatePager();
|
|
176
192
|
lastMessagesRenderKey = renderKey;
|
|
177
193
|
}
|
|
178
194
|
return;
|
|
@@ -194,7 +210,6 @@ export function createSocialController({
|
|
|
194
210
|
})}`
|
|
195
211
|
: "";
|
|
196
212
|
const nextMeta = `${baseMeta}${governanceMeta}`;
|
|
197
|
-
|
|
198
213
|
if (!filteredMessages.length) {
|
|
199
214
|
const nextHtml = `<div class="empty-state">${t("overview.noMessagesEmpty")}</div>`;
|
|
200
215
|
const renderKey = JSON.stringify({ hint: governanceHint, meta: nextMeta, html: nextHtml });
|
|
@@ -202,6 +217,7 @@ export function createSocialController({
|
|
|
202
217
|
hintEl.textContent = governanceHint;
|
|
203
218
|
metaEl.textContent = nextMeta;
|
|
204
219
|
listEl.innerHTML = nextHtml;
|
|
220
|
+
updatePager();
|
|
205
221
|
lastMessagesRenderKey = renderKey;
|
|
206
222
|
}
|
|
207
223
|
return;
|
|
@@ -285,13 +301,16 @@ export function createSocialController({
|
|
|
285
301
|
hintEl.textContent = governanceHint;
|
|
286
302
|
metaEl.textContent = nextMeta;
|
|
287
303
|
listEl.innerHTML = nextHtml;
|
|
304
|
+
updatePager();
|
|
288
305
|
lastMessagesRenderKey = renderKey;
|
|
289
306
|
}
|
|
290
307
|
|
|
291
308
|
async function refreshMessages() {
|
|
292
|
-
const
|
|
309
|
+
const offset = Math.max(0, (socialMessagesPage - 1) * SOCIAL_MESSAGE_PAGE_SIZE);
|
|
310
|
+
const result = await api(`/api/messages?limit=${SOCIAL_MESSAGE_PAGE_SIZE}&offset=${offset}`);
|
|
293
311
|
setSocialMessagesCache(Array.isArray(result.data?.items) ? result.data.items : []);
|
|
294
312
|
setSocialMessageGovernance(result.data?.governance || null);
|
|
313
|
+
socialMessagesTotal = Number(result.data?.total || 0);
|
|
295
314
|
renderSocialMessages();
|
|
296
315
|
}
|
|
297
316
|
|
|
@@ -879,6 +898,14 @@ export function createSocialController({
|
|
|
879
898
|
refreshSocial,
|
|
880
899
|
renderLogs,
|
|
881
900
|
renderSocialMessages,
|
|
901
|
+
nextSocialMessagesPage: () => {
|
|
902
|
+
const totalPages = Math.max(1, Math.ceil((socialMessagesTotal || 0) / SOCIAL_MESSAGE_PAGE_SIZE));
|
|
903
|
+
socialMessagesPage = Math.min(totalPages, socialMessagesPage + 1);
|
|
904
|
+
},
|
|
905
|
+
prevSocialMessagesPage: () => {
|
|
906
|
+
socialMessagesPage = Math.max(1, socialMessagesPage - 1);
|
|
907
|
+
},
|
|
908
|
+
setSocialMessagesPage: (page) => { socialMessagesPage = Math.max(1, Number(page) || 1); },
|
|
882
909
|
setSkillsFilter,
|
|
883
910
|
setSkillsQuery,
|
|
884
911
|
setLogLevelFilter,
|
|
@@ -394,8 +394,8 @@
|
|
|
394
394
|
.nav-section {
|
|
395
395
|
display: grid;
|
|
396
396
|
align-content: start;
|
|
397
|
-
gap:
|
|
398
|
-
margin-bottom:
|
|
397
|
+
gap: 8px;
|
|
398
|
+
margin-bottom: 14px;
|
|
399
399
|
}
|
|
400
400
|
.nav-section:last-child {
|
|
401
401
|
margin-bottom: 0;
|
|
@@ -418,6 +418,7 @@
|
|
|
418
418
|
display: grid;
|
|
419
419
|
align-content: start;
|
|
420
420
|
gap: 4px;
|
|
421
|
+
padding: 2px 0 0;
|
|
421
422
|
}
|
|
422
423
|
.nav button {
|
|
423
424
|
position: relative;
|
|
@@ -23,84 +23,94 @@ export const appTemplate = String.raw`<div class="app" id="appShell">
|
|
|
23
23
|
<div class="sidebar-shell__body">
|
|
24
24
|
<nav class="sidebar-nav nav">
|
|
25
25
|
<section class="nav-section">
|
|
26
|
-
<div class="nav-section__label">
|
|
26
|
+
<div class="nav-section__label">Workspace</div>
|
|
27
27
|
<div class="nav-section__items">
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
</
|
|
60
|
-
<
|
|
61
|
-
<
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
<
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
<
|
|
98
|
-
<
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
28
|
+
<button class="tab nav-item active" data-tab="overview">
|
|
29
|
+
<span class="tab-icon" aria-hidden="true">
|
|
30
|
+
<svg viewBox="0 0 24 24">
|
|
31
|
+
<line x1="12" x2="12" y1="20" y2="10"></line>
|
|
32
|
+
<line x1="18" x2="18" y1="20" y2="4"></line>
|
|
33
|
+
<line x1="6" x2="6" y1="20" y2="16"></line>
|
|
34
|
+
</svg>
|
|
35
|
+
</span>
|
|
36
|
+
<span class="tab-labels"><span class="tab-title">Overview</span><span class="tab-copy">Agent health, visibility, and next steps</span></span>
|
|
37
|
+
</button>
|
|
38
|
+
<button class="tab nav-item" data-tab="profile">
|
|
39
|
+
<span class="tab-icon" aria-hidden="true">
|
|
40
|
+
<svg viewBox="0 0 24 24">
|
|
41
|
+
<path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path>
|
|
42
|
+
<polyline points="14 2 14 8 20 8"></polyline>
|
|
43
|
+
<line x1="16" x2="8" y1="13" y2="13"></line>
|
|
44
|
+
<line x1="16" x2="8" y1="17" y2="17"></line>
|
|
45
|
+
<line x1="10" x2="8" y1="9" y2="9"></line>
|
|
46
|
+
</svg>
|
|
47
|
+
</span>
|
|
48
|
+
<span class="tab-labels"><span class="tab-title">Profile</span><span class="tab-copy">Public card, visibility, and saved identity</span></span>
|
|
49
|
+
</button>
|
|
50
|
+
<button class="tab nav-item" data-tab="skills">
|
|
51
|
+
<span class="tab-icon" aria-hidden="true">
|
|
52
|
+
<svg viewBox="0 0 24 24">
|
|
53
|
+
<path d="M12 3l2.5 5 5.5.8-4 3.9.9 5.5-4.9-2.6-4.9 2.6.9-5.5-4-3.9 5.5-.8z"></path>
|
|
54
|
+
</svg>
|
|
55
|
+
</span>
|
|
56
|
+
<span class="tab-labels"><span class="tab-title">Skills</span><span class="tab-copy">Bundled skills and OpenClaw-installed skills</span></span>
|
|
57
|
+
</button>
|
|
58
|
+
</div>
|
|
59
|
+
</section>
|
|
60
|
+
<section class="nav-section">
|
|
61
|
+
<div class="nav-section__label">Messages</div>
|
|
62
|
+
<div class="nav-section__items">
|
|
63
|
+
<button class="tab nav-item" data-tab="chat">
|
|
64
|
+
<span class="tab-icon" aria-hidden="true">
|
|
65
|
+
<svg viewBox="0 0 24 24">
|
|
66
|
+
<path d="M21 15a2 2 0 0 1-2 2H8l-5 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
|
|
67
|
+
<path d="M8 9h8"></path>
|
|
68
|
+
<path d="M8 13h6"></path>
|
|
69
|
+
</svg>
|
|
70
|
+
</span>
|
|
71
|
+
<span class="tab-labels"><span class="tab-title">Messages</span><span class="tab-copy">Public message composer and observed feed</span></span>
|
|
72
|
+
</button>
|
|
73
|
+
<button class="tab nav-item" data-tab="private">
|
|
74
|
+
<span class="tab-icon" aria-hidden="true">
|
|
75
|
+
<svg viewBox="0 0 24 24">
|
|
76
|
+
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
|
|
77
|
+
<rect x="4" y="11" width="16" height="10" rx="2"></rect>
|
|
78
|
+
<circle cx="12" cy="16" r="1"></circle>
|
|
79
|
+
</svg>
|
|
80
|
+
</span>
|
|
81
|
+
<span class="tab-labels"><span class="tab-title">Private</span><span class="tab-copy">Private messages between visible agents</span></span>
|
|
82
|
+
</button>
|
|
83
|
+
</div>
|
|
84
|
+
</section>
|
|
85
|
+
<section class="nav-section">
|
|
86
|
+
<div class="nav-section__label">Network</div>
|
|
87
|
+
<div class="nav-section__items">
|
|
88
|
+
<button class="tab nav-item" data-tab="agent">
|
|
89
|
+
<span class="tab-icon" aria-hidden="true">
|
|
90
|
+
<svg viewBox="0 0 24 24">
|
|
91
|
+
<circle cx="12" cy="8" r="4"></circle>
|
|
92
|
+
<path d="M5 20c1.6-3.8 4.2-5.7 7-5.7s5.4 1.9 7 5.7"></path>
|
|
93
|
+
</svg>
|
|
94
|
+
</span>
|
|
95
|
+
<span class="tab-labels"><span class="tab-title">Agents</span><span class="tab-copy">Discovered public agents and live directory</span></span>
|
|
96
|
+
</button>
|
|
97
|
+
<button class="tab nav-item" data-tab="network">
|
|
98
|
+
<span class="tab-icon" aria-hidden="true">
|
|
99
|
+
<svg viewBox="0 0 24 24">
|
|
100
|
+
<circle cx="12" cy="12" r="2"></circle>
|
|
101
|
+
<path d="M16.24 7.76a6 6 0 0 1 0 8.49m-8.48-.01a6 6 0 0 1 0-8.49m11.31-2.82a10 10 0 0 1 0 14.14m-14.14 0a10 10 0 0 1 0-14.14"></path>
|
|
102
|
+
</svg>
|
|
103
|
+
</span>
|
|
104
|
+
<span class="tab-labels"><span class="tab-title">Network</span><span class="tab-copy">Broadcast controls, peers, and diagnostics</span></span>
|
|
105
|
+
</button>
|
|
106
|
+
<button class="tab nav-item" data-tab="social">
|
|
107
|
+
<span class="tab-icon" aria-hidden="true">
|
|
108
|
+
<svg viewBox="0 0 24 24">
|
|
109
|
+
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
|
|
110
|
+
</svg>
|
|
111
|
+
</span>
|
|
112
|
+
<span class="tab-labels"><span class="tab-title">Social</span><span class="tab-copy">Runtime mode, bridge status, and social.md</span></span>
|
|
113
|
+
</button>
|
|
104
114
|
</div>
|
|
105
115
|
</section>
|
|
106
116
|
</nav>
|
|
@@ -377,6 +387,13 @@ export const appTemplate = String.raw`<div class="app" id="appShell">
|
|
|
377
387
|
</div>
|
|
378
388
|
</div>
|
|
379
389
|
<div class="logs" id="socialMessageList"></div>
|
|
390
|
+
<div class="agent-list__footer">
|
|
391
|
+
<div class="agent-list__page" id="socialMessagePageMeta">Page 1 / 1</div>
|
|
392
|
+
<div class="agent-list__pager">
|
|
393
|
+
<button class="secondary" type="button" id="socialMessagePrevPageBtn">Prev</button>
|
|
394
|
+
<button class="secondary" type="button" id="socialMessageNextPageBtn">Next</button>
|
|
395
|
+
</div>
|
|
396
|
+
</div>
|
|
380
397
|
</div>
|
|
381
398
|
</div>
|
|
382
399
|
</div>
|
|
@@ -406,6 +423,13 @@ export const appTemplate = String.raw`<div class="app" id="appShell">
|
|
|
406
423
|
</div>
|
|
407
424
|
</div>
|
|
408
425
|
<div class="logs" id="privateConversationList"></div>
|
|
426
|
+
<div class="agent-list__footer">
|
|
427
|
+
<div class="agent-list__page" id="privateConversationPageMeta">Page 1 / 1</div>
|
|
428
|
+
<div class="agent-list__pager">
|
|
429
|
+
<button class="secondary" type="button" id="privateConversationPrevPageBtn">Prev</button>
|
|
430
|
+
<button class="secondary" type="button" id="privateConversationNextPageBtn">Next</button>
|
|
431
|
+
</div>
|
|
432
|
+
</div>
|
|
409
433
|
</div>
|
|
410
434
|
<div class="card stack">
|
|
411
435
|
<div class="overview-panel-header">
|
|
@@ -431,6 +455,13 @@ export const appTemplate = String.raw`<div class="app" id="appShell">
|
|
|
431
455
|
</div>
|
|
432
456
|
<div id="privateFeedback" class="feedback">Ready.</div>
|
|
433
457
|
<div class="logs" id="privateMessageList"></div>
|
|
458
|
+
<div class="agent-list__footer">
|
|
459
|
+
<div class="agent-list__page" id="privateMessagePageMeta">Page 1 / 1</div>
|
|
460
|
+
<div class="agent-list__pager">
|
|
461
|
+
<button class="secondary" type="button" id="privateMessagePrevPageBtn">Prev</button>
|
|
462
|
+
<button class="secondary" type="button" id="privateMessageNextPageBtn">Next</button>
|
|
463
|
+
</div>
|
|
464
|
+
</div>
|
|
434
465
|
</div>
|
|
435
466
|
</div>
|
|
436
467
|
</div>
|
|
@@ -8,6 +8,9 @@ export const TRANSLATIONS = {
|
|
|
8
8
|
},
|
|
9
9
|
common: {
|
|
10
10
|
control: 'Control',
|
|
11
|
+
workspace: 'Workspace',
|
|
12
|
+
messages: 'Messages',
|
|
13
|
+
networkGroup: 'Network',
|
|
11
14
|
version: 'Version',
|
|
12
15
|
localConsole: 'Local Console',
|
|
13
16
|
subtitle: 'Manage identity, discovery, and broadcasts',
|
|
@@ -626,6 +629,9 @@ export const TRANSLATIONS = {
|
|
|
626
629
|
},
|
|
627
630
|
common: {
|
|
628
631
|
control: '控制',
|
|
632
|
+
workspace: '工作区',
|
|
633
|
+
messages: '消息',
|
|
634
|
+
networkGroup: '网络',
|
|
629
635
|
version: '版本',
|
|
630
636
|
localConsole: '本地控制台',
|
|
631
637
|
subtitle: '管理身份、发现与广播',
|
|
@@ -1899,7 +1899,7 @@ export class LocalNodeService {
|
|
|
1899
1899
|
return this.identity;
|
|
1900
1900
|
}
|
|
1901
1901
|
|
|
1902
|
-
getSocialMessages(limit = 50, options?: { agent_id?: string | null }): {
|
|
1902
|
+
getSocialMessages(limit = 50, options?: { agent_id?: string | null; offset?: number | null }): {
|
|
1903
1903
|
total: number;
|
|
1904
1904
|
items: SocialMessageView[];
|
|
1905
1905
|
governance: {
|
|
@@ -1911,6 +1911,7 @@ export class LocalNodeService {
|
|
|
1911
1911
|
};
|
|
1912
1912
|
} {
|
|
1913
1913
|
const resolvedLimit = Math.max(1, Math.min(200, Number(limit) || 50));
|
|
1914
|
+
const resolvedOffset = Math.max(0, Number(options?.offset) || 0);
|
|
1914
1915
|
this.ensureLocalDirectoryBaseline();
|
|
1915
1916
|
this.compactCacheInMemory();
|
|
1916
1917
|
const agentId = String(options?.agent_id || "").trim();
|
|
@@ -1919,7 +1920,7 @@ export class LocalNodeService {
|
|
|
1919
1920
|
: this.socialMessages;
|
|
1920
1921
|
return {
|
|
1921
1922
|
total: filtered.length,
|
|
1922
|
-
items: filtered.slice(
|
|
1923
|
+
items: filtered.slice(resolvedOffset, resolvedOffset + resolvedLimit).map((message) => {
|
|
1923
1924
|
const profile = this.directory.profiles[message.agent_id];
|
|
1924
1925
|
const lastSeenAt = this.directory.presence[message.agent_id] ?? 0;
|
|
1925
1926
|
const observations = this.socialMessageObservations.filter((item) => item.message_id === message.message_id);
|
|
@@ -2000,13 +2001,17 @@ export class LocalNodeService {
|
|
|
2000
2001
|
return Array.from(conversations.values()).sort((a, b) => (b.last_message_at || 0) - (a.last_message_at || 0));
|
|
2001
2002
|
}
|
|
2002
2003
|
|
|
2003
|
-
getPrivateMessages(conversationId: string, limit = PRIVATE_MESSAGE_QUERY_LIMIT):
|
|
2004
|
+
getPrivateMessages(conversationId: string, limit = PRIVATE_MESSAGE_QUERY_LIMIT, offset = 0): {
|
|
2005
|
+
total: number;
|
|
2006
|
+
items: PrivateMessageView[];
|
|
2007
|
+
} {
|
|
2004
2008
|
const normalizedConversationId = String(conversationId || "").trim();
|
|
2005
2009
|
const resolvedLimit = Math.max(1, Math.min(PRIVATE_MESSAGE_QUERY_LIMIT, Number(limit) || PRIVATE_MESSAGE_QUERY_LIMIT));
|
|
2010
|
+
const resolvedOffset = Math.max(0, Number(offset) || 0);
|
|
2006
2011
|
const receiptsByMessageId = new Map(
|
|
2007
2012
|
this.privateMessageReceipts.map((receipt) => [receipt.message_id, receipt.status] as const)
|
|
2008
2013
|
);
|
|
2009
|
-
|
|
2014
|
+
const filtered = this.privateMessages
|
|
2010
2015
|
.filter((message) => {
|
|
2011
2016
|
if (message.from_agent_id === message.to_agent_id) {
|
|
2012
2017
|
return false;
|
|
@@ -2017,9 +2022,12 @@ export class LocalNodeService {
|
|
|
2017
2022
|
}
|
|
2018
2023
|
return !normalizedConversationId || message.conversation_id === normalizedConversationId;
|
|
2019
2024
|
})
|
|
2020
|
-
.sort((a, b) => a.created_at - b.created_at)
|
|
2021
|
-
|
|
2022
|
-
|
|
2025
|
+
.sort((a, b) => a.created_at - b.created_at);
|
|
2026
|
+
const total = filtered.length;
|
|
2027
|
+
const paged = filtered.slice(Math.max(0, total - resolvedOffset - resolvedLimit), Math.max(0, total - resolvedOffset));
|
|
2028
|
+
return {
|
|
2029
|
+
total,
|
|
2030
|
+
items: paged.map((message) => ({
|
|
2023
2031
|
message_id: message.message_id,
|
|
2024
2032
|
conversation_id: message.conversation_id,
|
|
2025
2033
|
from_agent_id: message.from_agent_id,
|
|
@@ -2031,7 +2039,8 @@ export class LocalNodeService {
|
|
|
2031
2039
|
receiptsByMessageId.get(message.message_id) ||
|
|
2032
2040
|
this.privateMessageDeliveryStatusCache.get(message.message_id) ||
|
|
2033
2041
|
(message.from_agent_id === this.identity?.agent_id ? "fallback-sent" : "sent"),
|
|
2034
|
-
}))
|
|
2042
|
+
})),
|
|
2043
|
+
};
|
|
2035
2044
|
}
|
|
2036
2045
|
|
|
2037
2046
|
async sendPrivateMessage(input: {
|
|
@@ -2083,7 +2092,7 @@ export class LocalNodeService {
|
|
|
2083
2092
|
await this.publish(PRIVATE_MESSAGE_TOPIC, message);
|
|
2084
2093
|
}
|
|
2085
2094
|
this.privateMessageDeliveryStatusCache.set(message.message_id, reason as PrivateMessageView["delivery_status"]);
|
|
2086
|
-
const view = this.getPrivateMessages(message.conversation_id).find((item) => item.message_id === message.message_id);
|
|
2095
|
+
const view = this.getPrivateMessages(message.conversation_id, PRIVATE_MESSAGE_QUERY_LIMIT, 0).items.find((item) => item.message_id === message.message_id);
|
|
2087
2096
|
if (view) {
|
|
2088
2097
|
view.delivery_status = reason as PrivateMessageView["delivery_status"];
|
|
2089
2098
|
}
|
|
@@ -4306,7 +4315,7 @@ export async function main() {
|
|
|
4306
4315
|
} catch {
|
|
4307
4316
|
// best effort after response has been sent
|
|
4308
4317
|
}
|
|
4309
|
-
},
|
|
4318
|
+
}, 1200);
|
|
4310
4319
|
})
|
|
4311
4320
|
);
|
|
4312
4321
|
|
|
@@ -4448,8 +4457,9 @@ export async function main() {
|
|
|
4448
4457
|
|
|
4449
4458
|
app.get("/api/messages", (req, res) => {
|
|
4450
4459
|
const limit = Number(req.query.limit ?? 50);
|
|
4460
|
+
const offset = Number(req.query.offset ?? 0);
|
|
4451
4461
|
const agentId = String(req.query.agent_id ?? "").trim();
|
|
4452
|
-
sendOk(res, node.getSocialMessages(limit, { agent_id: agentId || null }));
|
|
4462
|
+
sendOk(res, node.getSocialMessages(limit, { agent_id: agentId || null, offset }));
|
|
4453
4463
|
});
|
|
4454
4464
|
|
|
4455
4465
|
app.get("/api/private/state", (_req, res) => {
|
|
@@ -4463,7 +4473,8 @@ export async function main() {
|
|
|
4463
4473
|
app.get("/api/private/messages", (req, res) => {
|
|
4464
4474
|
const conversationId = String(req.query.conversation_id ?? "").trim();
|
|
4465
4475
|
const limit = Number(req.query.limit ?? PRIVATE_MESSAGE_QUERY_LIMIT);
|
|
4466
|
-
|
|
4476
|
+
const offset = Number(req.query.offset ?? 0);
|
|
4477
|
+
sendOk(res, node.getPrivateMessages(conversationId, limit, offset));
|
|
4467
4478
|
});
|
|
4468
4479
|
|
|
4469
4480
|
app.post(
|
|
@@ -4498,8 +4509,9 @@ export async function main() {
|
|
|
4498
4509
|
|
|
4499
4510
|
app.get("/api/openclaw/bridge/messages", (req, res) => {
|
|
4500
4511
|
const limit = Number(req.query.limit ?? 50);
|
|
4512
|
+
const offset = Number(req.query.offset ?? 0);
|
|
4501
4513
|
const agentId = String(req.query.agent_id ?? "").trim();
|
|
4502
|
-
sendOk(res, node.getSocialMessages(limit, { agent_id: agentId || null }));
|
|
4514
|
+
sendOk(res, node.getSocialMessages(limit, { agent_id: agentId || null, offset }));
|
|
4503
4515
|
});
|
|
4504
4516
|
|
|
4505
4517
|
app.post(
|
|
@@ -4649,10 +4661,17 @@ export async function main() {
|
|
|
4649
4661
|
let html = readFileSync(staticIndexFile, "utf8");
|
|
4650
4662
|
html = html.replace("</body>", `${renderBootstrapScript(payload)}\n</body>`);
|
|
4651
4663
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
4664
|
+
res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
|
|
4652
4665
|
res.send(html);
|
|
4653
4666
|
});
|
|
4654
4667
|
|
|
4655
|
-
app.use(express.static(staticDir
|
|
4668
|
+
app.use(express.static(staticDir, {
|
|
4669
|
+
etag: false,
|
|
4670
|
+
lastModified: false,
|
|
4671
|
+
setHeaders: (res) => {
|
|
4672
|
+
res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
|
|
4673
|
+
},
|
|
4674
|
+
}));
|
|
4656
4675
|
|
|
4657
4676
|
app.use((error: unknown, _req: Request, res: Response, _next: NextFunction) => {
|
|
4658
4677
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
2026.3.20-beta.
|
|
1
|
+
2026.3.20-beta.14
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "silicaclaw-broadcast",
|
|
3
|
-
"version": "2026.3.20-beta.
|
|
3
|
+
"version": "2026.3.20-beta.14",
|
|
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": {
|