@silicaclaw/cli 2026.3.20-2 → 2026.3.20-21

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.
Files changed (145) hide show
  1. package/CHANGELOG.md +108 -0
  2. package/INSTALL.md +13 -7
  3. package/README.md +60 -12
  4. package/VERSION +1 -1
  5. package/apps/local-console/dist/apps/local-console/src/server.d.ts +139 -3
  6. package/apps/local-console/dist/apps/local-console/src/server.js +1029 -92
  7. package/apps/local-console/dist/packages/core/src/index.d.ts +2 -0
  8. package/apps/local-console/dist/packages/core/src/index.js +2 -0
  9. package/apps/local-console/dist/packages/core/src/privateCrypto.d.ts +17 -0
  10. package/apps/local-console/dist/packages/core/src/privateCrypto.js +40 -0
  11. package/apps/local-console/dist/packages/core/src/privateMessage.d.ts +23 -0
  12. package/apps/local-console/dist/packages/core/src/privateMessage.js +74 -0
  13. package/apps/local-console/dist/packages/core/src/profile.js +2 -0
  14. package/apps/local-console/dist/packages/core/src/publicProfileSummary.d.ts +4 -0
  15. package/apps/local-console/dist/packages/core/src/publicProfileSummary.js +3 -0
  16. package/apps/local-console/dist/packages/core/src/types.d.ts +40 -0
  17. package/apps/local-console/dist/packages/network/src/relayPreview.d.ts +12 -0
  18. package/apps/local-console/dist/packages/network/src/relayPreview.js +108 -8
  19. package/apps/local-console/dist/packages/network/src/types.d.ts +4 -0
  20. package/apps/local-console/dist/packages/storage/src/repos.d.ts +27 -1
  21. package/apps/local-console/dist/packages/storage/src/repos.js +35 -1
  22. package/apps/local-console/public/app/app.js +502 -11
  23. package/apps/local-console/public/app/events.js +21 -0
  24. package/apps/local-console/public/app/network.js +144 -32
  25. package/apps/local-console/public/app/overview.js +57 -27
  26. package/apps/local-console/public/app/social.js +342 -105
  27. package/apps/local-console/public/app/styles.css +149 -43
  28. package/apps/local-console/public/app/template.js +196 -100
  29. package/apps/local-console/public/app/translations.js +438 -316
  30. package/apps/local-console/src/server.ts +1177 -90
  31. package/apps/public-explorer/public/app/template.js +2 -2
  32. package/apps/public-explorer/public/app/translations.js +36 -36
  33. package/docs/NEW_USER_OPERATIONS.md +5 -5
  34. package/docs/OPENCLAW_BRIDGE.md +7 -7
  35. package/docs/OPENCLAW_BRIDGE_ZH.md +6 -6
  36. package/node_modules/@silicaclaw/core/dist/packages/core/src/index.d.ts +2 -0
  37. package/node_modules/@silicaclaw/core/dist/packages/core/src/index.js +2 -0
  38. package/node_modules/@silicaclaw/core/dist/packages/core/src/privateCrypto.d.ts +17 -0
  39. package/node_modules/@silicaclaw/core/dist/packages/core/src/privateCrypto.js +40 -0
  40. package/node_modules/@silicaclaw/core/dist/packages/core/src/privateMessage.d.ts +23 -0
  41. package/node_modules/@silicaclaw/core/dist/packages/core/src/privateMessage.js +74 -0
  42. package/node_modules/@silicaclaw/core/dist/packages/core/src/profile.js +2 -0
  43. package/node_modules/@silicaclaw/core/dist/packages/core/src/publicProfileSummary.d.ts +4 -0
  44. package/node_modules/@silicaclaw/core/dist/packages/core/src/publicProfileSummary.js +3 -0
  45. package/node_modules/@silicaclaw/core/dist/packages/core/src/types.d.ts +40 -0
  46. package/node_modules/@silicaclaw/core/package.json +2 -2
  47. package/node_modules/@silicaclaw/core/src/index.ts +2 -0
  48. package/node_modules/@silicaclaw/core/src/privateCrypto.ts +57 -0
  49. package/node_modules/@silicaclaw/core/src/privateMessage.ts +101 -0
  50. package/node_modules/@silicaclaw/core/src/profile.ts +2 -0
  51. package/node_modules/@silicaclaw/core/src/publicProfileSummary.ts +7 -0
  52. package/node_modules/@silicaclaw/core/src/types.ts +44 -0
  53. package/node_modules/@silicaclaw/network/dist/packages/network/src/relayPreview.d.ts +12 -0
  54. package/node_modules/@silicaclaw/network/dist/packages/network/src/relayPreview.js +108 -8
  55. package/node_modules/@silicaclaw/network/dist/packages/network/src/types.d.ts +4 -0
  56. package/node_modules/@silicaclaw/network/src/relayPreview.ts +120 -10
  57. package/node_modules/@silicaclaw/network/src/types.ts +2 -0
  58. package/node_modules/@silicaclaw/storage/dist/packages/core/src/index.d.ts +2 -0
  59. package/node_modules/@silicaclaw/storage/dist/packages/core/src/index.js +2 -0
  60. package/node_modules/@silicaclaw/storage/dist/packages/core/src/privateCrypto.d.ts +17 -0
  61. package/node_modules/@silicaclaw/storage/dist/packages/core/src/privateCrypto.js +40 -0
  62. package/node_modules/@silicaclaw/storage/dist/packages/core/src/privateMessage.d.ts +23 -0
  63. package/node_modules/@silicaclaw/storage/dist/packages/core/src/privateMessage.js +74 -0
  64. package/node_modules/@silicaclaw/storage/dist/packages/core/src/profile.js +2 -0
  65. package/node_modules/@silicaclaw/storage/dist/packages/core/src/publicProfileSummary.d.ts +4 -0
  66. package/node_modules/@silicaclaw/storage/dist/packages/core/src/publicProfileSummary.js +3 -0
  67. package/node_modules/@silicaclaw/storage/dist/packages/core/src/types.d.ts +40 -0
  68. package/node_modules/@silicaclaw/storage/dist/packages/storage/src/repos.d.ts +27 -1
  69. package/node_modules/@silicaclaw/storage/dist/packages/storage/src/repos.js +35 -1
  70. package/node_modules/@silicaclaw/storage/package.json +2 -2
  71. package/node_modules/@silicaclaw/storage/src/repos.ts +59 -1
  72. package/openclaw-skills/silicaclaw-bridge-setup/SKILL.md +18 -0
  73. package/openclaw-skills/silicaclaw-bridge-setup/VERSION +1 -1
  74. package/openclaw-skills/silicaclaw-bridge-setup/manifest.json +2 -2
  75. package/openclaw-skills/silicaclaw-broadcast/SKILL.md +18 -0
  76. package/openclaw-skills/silicaclaw-broadcast/VERSION +1 -1
  77. package/openclaw-skills/silicaclaw-broadcast/manifest.json +2 -2
  78. package/openclaw-skills/silicaclaw-network-config/SKILL.md +158 -0
  79. package/openclaw-skills/silicaclaw-network-config/VERSION +1 -0
  80. package/openclaw-skills/silicaclaw-network-config/agents/openai.yaml +6 -0
  81. package/openclaw-skills/silicaclaw-network-config/manifest.json +27 -0
  82. package/openclaw-skills/silicaclaw-network-config/references/network-modes.md +22 -0
  83. package/openclaw-skills/silicaclaw-network-config/references/owner-dialogue-cheatsheet-zh.md +47 -0
  84. package/openclaw-skills/silicaclaw-network-config/references/public-discovery.md +22 -0
  85. package/openclaw-skills/silicaclaw-owner-push/SKILL.md +18 -0
  86. package/openclaw-skills/silicaclaw-owner-push/VERSION +1 -1
  87. package/openclaw-skills/silicaclaw-owner-push/manifest.json +2 -2
  88. package/openclaw-skills/silicaclaw-owner-push/references/runtime-setup.md +3 -0
  89. package/openclaw-skills/silicaclaw-owner-push/scripts/owner-push-forwarder.mjs +151 -9
  90. package/package.json +1 -1
  91. package/packages/core/dist/packages/core/src/index.d.ts +2 -0
  92. package/packages/core/dist/packages/core/src/index.js +2 -0
  93. package/packages/core/dist/packages/core/src/privateCrypto.d.ts +17 -0
  94. package/packages/core/dist/packages/core/src/privateCrypto.js +40 -0
  95. package/packages/core/dist/packages/core/src/privateMessage.d.ts +23 -0
  96. package/packages/core/dist/packages/core/src/privateMessage.js +74 -0
  97. package/packages/core/dist/packages/core/src/profile.js +2 -0
  98. package/packages/core/dist/packages/core/src/publicProfileSummary.d.ts +4 -0
  99. package/packages/core/dist/packages/core/src/publicProfileSummary.js +3 -0
  100. package/packages/core/dist/packages/core/src/types.d.ts +40 -0
  101. package/packages/core/package.json +2 -2
  102. package/packages/core/src/index.ts +2 -0
  103. package/packages/core/src/privateCrypto.ts +57 -0
  104. package/packages/core/src/privateMessage.ts +101 -0
  105. package/packages/core/src/profile.ts +2 -0
  106. package/packages/core/src/publicProfileSummary.ts +7 -0
  107. package/packages/core/src/types.ts +44 -0
  108. package/packages/network/dist/packages/network/src/relayPreview.d.ts +12 -0
  109. package/packages/network/dist/packages/network/src/relayPreview.js +108 -8
  110. package/packages/network/dist/packages/network/src/types.d.ts +4 -0
  111. package/packages/network/src/relayPreview.ts +120 -10
  112. package/packages/network/src/types.ts +2 -0
  113. package/packages/storage/dist/packages/core/src/index.d.ts +2 -0
  114. package/packages/storage/dist/packages/core/src/index.js +2 -0
  115. package/packages/storage/dist/packages/core/src/privateCrypto.d.ts +17 -0
  116. package/packages/storage/dist/packages/core/src/privateCrypto.js +40 -0
  117. package/packages/storage/dist/packages/core/src/privateMessage.d.ts +23 -0
  118. package/packages/storage/dist/packages/core/src/privateMessage.js +74 -0
  119. package/packages/storage/dist/packages/core/src/profile.js +2 -0
  120. package/packages/storage/dist/packages/core/src/publicProfileSummary.d.ts +4 -0
  121. package/packages/storage/dist/packages/core/src/publicProfileSummary.js +3 -0
  122. package/packages/storage/dist/packages/core/src/types.d.ts +40 -0
  123. package/packages/storage/dist/packages/storage/src/repos.d.ts +27 -1
  124. package/packages/storage/dist/packages/storage/src/repos.js +35 -1
  125. package/packages/storage/package.json +2 -2
  126. package/packages/storage/src/repos.ts +59 -1
  127. package/scripts/silicaclaw-cli.mjs +4 -1
  128. package/scripts/silicaclaw-gateway.mjs +114 -2
  129. package/scripts/validate-openclaw-skill.mjs +19 -0
  130. package/node_modules/@silicaclaw/storage/dist/index.d.ts +0 -3
  131. package/node_modules/@silicaclaw/storage/dist/index.js +0 -19
  132. package/node_modules/@silicaclaw/storage/dist/jsonRepo.d.ts +0 -7
  133. package/node_modules/@silicaclaw/storage/dist/jsonRepo.js +0 -29
  134. package/node_modules/@silicaclaw/storage/dist/repos.d.ts +0 -61
  135. package/node_modules/@silicaclaw/storage/dist/repos.js +0 -67
  136. package/node_modules/@silicaclaw/storage/dist/socialRuntimeRepo.d.ts +0 -5
  137. package/node_modules/@silicaclaw/storage/dist/socialRuntimeRepo.js +0 -57
  138. package/packages/storage/dist/index.d.ts +0 -3
  139. package/packages/storage/dist/index.js +0 -19
  140. package/packages/storage/dist/jsonRepo.d.ts +0 -7
  141. package/packages/storage/dist/jsonRepo.js +0 -29
  142. package/packages/storage/dist/repos.d.ts +0 -61
  143. package/packages/storage/dist/repos.js +0 -67
  144. package/packages/storage/dist/socialRuntimeRepo.d.ts +0 -5
  145. package/packages/storage/dist/socialRuntimeRepo.js +0 -57
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { closeSync, existsSync, mkdirSync, openSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
4
  import { homedir } from "node:os";
5
5
  import { dirname, resolve } from "node:path";
6
6
  import { spawn } from "node:child_process";
@@ -12,8 +12,11 @@ const OWNER_FORWARD_CMD = String(process.env.OPENCLAW_OWNER_FORWARD_CMD || "").t
12
12
  const STATE_PATH = resolve(
13
13
  String(process.env.OPENCLAW_OWNER_FORWARD_STATE_PATH || resolve(homedir(), ".openclaw", "workspace", "state", "silicaclaw-owner-push.json"))
14
14
  );
15
+ const LOCK_PATH = `${STATE_PATH}.lock`;
16
+ const LATEST_ONLY = String(process.env.OPENCLAW_FORWARD_LATEST_ONLY || "true").trim().toLowerCase() !== "false";
15
17
  const ONCE = process.argv.includes("--once");
16
18
  const VERBOSE = process.argv.includes("--verbose");
19
+ let lockFd = null;
17
20
 
18
21
  function parseListEnv(name) {
19
22
  return String(process.env[name] || "")
@@ -61,14 +64,24 @@ function loadState() {
61
64
  return {
62
65
  seen_ids: [],
63
66
  pushed_at: {},
67
+ last_pushed_created_at: 0,
68
+ last_pushed_message_id: "",
64
69
  };
65
70
  }
66
71
  try {
67
- return JSON.parse(readFileSync(STATE_PATH, "utf8"));
72
+ const parsed = JSON.parse(readFileSync(STATE_PATH, "utf8"));
73
+ return {
74
+ seen_ids: Array.isArray(parsed?.seen_ids) ? parsed.seen_ids : [],
75
+ pushed_at: parsed?.pushed_at && typeof parsed.pushed_at === "object" ? parsed.pushed_at : {},
76
+ last_pushed_created_at: Number(parsed?.last_pushed_created_at || 0) || 0,
77
+ last_pushed_message_id: String(parsed?.last_pushed_message_id || ""),
78
+ };
68
79
  } catch {
69
80
  return {
70
81
  seen_ids: [],
71
82
  pushed_at: {},
83
+ last_pushed_created_at: 0,
84
+ last_pushed_message_id: "",
72
85
  };
73
86
  }
74
87
  }
@@ -78,6 +91,85 @@ function saveState(state) {
78
91
  writeFileSync(STATE_PATH, JSON.stringify(state, null, 2), "utf8");
79
92
  }
80
93
 
94
+ function isPidRunning(pid) {
95
+ if (!pid || !Number.isFinite(pid) || pid <= 0) return false;
96
+ try {
97
+ process.kill(pid, 0);
98
+ return true;
99
+ } catch {
100
+ return false;
101
+ }
102
+ }
103
+
104
+ function releaseLock() {
105
+ if (lockFd !== null) {
106
+ try {
107
+ closeSync(lockFd);
108
+ } catch {
109
+ // ignore
110
+ }
111
+ try {
112
+ rmSync(LOCK_PATH, { force: true });
113
+ } catch {
114
+ // ignore
115
+ }
116
+ lockFd = null;
117
+ }
118
+ }
119
+
120
+ function acquireLock() {
121
+ mkdirSync(dirname(LOCK_PATH), { recursive: true });
122
+ try {
123
+ lockFd = openSync(LOCK_PATH, "wx");
124
+ writeFileSync(lockFd, JSON.stringify({
125
+ pid: process.pid,
126
+ started_at: new Date().toISOString(),
127
+ state_path: STATE_PATH,
128
+ }, null, 2), "utf8");
129
+ process.on("exit", releaseLock);
130
+ process.on("SIGINT", () => {
131
+ releaseLock();
132
+ process.exit(130);
133
+ });
134
+ process.on("SIGTERM", () => {
135
+ releaseLock();
136
+ process.exit(143);
137
+ });
138
+ return;
139
+ } catch {
140
+ // fall through
141
+ }
142
+
143
+ try {
144
+ const existing = JSON.parse(readFileSync(LOCK_PATH, "utf8"));
145
+ const existingPid = Number(existing?.pid || 0) || 0;
146
+ if (isPidRunning(existingPid)) {
147
+ throw new Error(`owner push forwarder already running (pid=${existingPid})`);
148
+ }
149
+ } catch (error) {
150
+ if (error instanceof Error && error.message.includes("already running")) {
151
+ throw error;
152
+ }
153
+ }
154
+
155
+ rmSync(LOCK_PATH, { force: true });
156
+ lockFd = openSync(LOCK_PATH, "wx");
157
+ writeFileSync(lockFd, JSON.stringify({
158
+ pid: process.pid,
159
+ started_at: new Date().toISOString(),
160
+ state_path: STATE_PATH,
161
+ }, null, 2), "utf8");
162
+ process.on("exit", releaseLock);
163
+ process.on("SIGINT", () => {
164
+ releaseLock();
165
+ process.exit(130);
166
+ });
167
+ process.on("SIGTERM", () => {
168
+ releaseLock();
169
+ process.exit(143);
170
+ });
171
+ }
172
+
81
173
  function trimState(state) {
82
174
  const recentIds = Array.isArray(state.seen_ids) ? state.seen_ids.slice(-500) : [];
83
175
  const pushedEntries = Object.entries(state.pushed_at || {}).slice(-500);
@@ -85,6 +177,22 @@ function trimState(state) {
85
177
  state.pushed_at = Object.fromEntries(pushedEntries);
86
178
  }
87
179
 
180
+ function messageCreatedAt(item) {
181
+ const createdAt = Number(item?.created_at || 0);
182
+ return Number.isFinite(createdAt) && createdAt > 0 ? createdAt : 0;
183
+ }
184
+
185
+ function isNewerThanCursor(item, state) {
186
+ const createdAt = messageCreatedAt(item);
187
+ const lastCreatedAt = Number(state.last_pushed_created_at || 0) || 0;
188
+ const messageId = String(item?.message_id || "").trim();
189
+ const lastMessageId = String(state.last_pushed_message_id || "").trim();
190
+ if (createdAt > lastCreatedAt) return true;
191
+ if (createdAt < lastCreatedAt) return false;
192
+ if (!createdAt) return !state.seen_ids.includes(messageId);
193
+ return Boolean(messageId) && messageId !== lastMessageId && !state.seen_ids.includes(messageId);
194
+ }
195
+
88
196
  function shouldWatchTopic(message) {
89
197
  if (!TOPIC_FILTERS.length) return true;
90
198
  return TOPIC_FILTERS.includes(String(message?.topic || "global").toLowerCase());
@@ -165,29 +273,60 @@ function dispatchToOwner(route, summary, message) {
165
273
  async function pollOnce(state) {
166
274
  const payload = await request(`/api/openclaw/bridge/messages?limit=${LIMIT}`);
167
275
  const items = Array.isArray(payload?.items) ? payload.items.slice().reverse() : [];
276
+ const candidates = [];
168
277
 
169
278
  for (const item of items) {
170
279
  const messageId = String(item?.message_id || "").trim();
171
280
  if (!messageId) continue;
172
- if (state.seen_ids.includes(messageId)) continue;
173
-
174
- state.seen_ids.push(messageId);
281
+ if (!isNewerThanCursor(item, state)) {
282
+ if (!state.seen_ids.includes(messageId)) {
283
+ state.seen_ids.push(messageId);
284
+ }
285
+ continue;
286
+ }
175
287
 
176
288
  if (!shouldWatchTopic(item)) {
289
+ state.seen_ids.push(messageId);
177
290
  if (VERBOSE) console.log(`skip topic: ${messageId}`);
178
291
  continue;
179
292
  }
180
293
 
181
294
  const route = scoreRoute(item);
182
295
  if (route === "ignore") {
296
+ state.seen_ids.push(messageId);
183
297
  if (VERBOSE) console.log(`ignore low-signal: ${messageId}`);
184
298
  continue;
185
299
  }
186
300
 
187
- const summary = summarizeForOwner(item);
188
- await dispatchToOwner(route, summary, item);
189
- state.pushed_at[messageId] = new Date().toISOString();
190
- if (VERBOSE) console.log(`pushed to owner: ${messageId}`);
301
+ candidates.push({ item, messageId, route, createdAt: messageCreatedAt(item) });
302
+ }
303
+
304
+ const selected = LATEST_ONLY
305
+ ? candidates.sort((left, right) => {
306
+ if (left.createdAt !== right.createdAt) return right.createdAt - left.createdAt;
307
+ return left.messageId.localeCompare(right.messageId);
308
+ })[0] || null
309
+ : null;
310
+
311
+ const toPush = LATEST_ONLY ? (selected ? [selected] : []) : candidates;
312
+
313
+ for (const candidate of toPush) {
314
+ const summary = summarizeForOwner(candidate.item);
315
+ await dispatchToOwner(candidate.route, summary, candidate.item);
316
+ state.pushed_at[candidate.messageId] = new Date().toISOString();
317
+ state.last_pushed_created_at = candidate.createdAt || Date.now();
318
+ state.last_pushed_message_id = candidate.messageId;
319
+ if (VERBOSE) console.log(`pushed to owner: ${candidate.messageId}`);
320
+ }
321
+
322
+ if (LATEST_ONLY && selected) {
323
+ for (const candidate of candidates) {
324
+ state.seen_ids.push(candidate.messageId);
325
+ }
326
+ } else {
327
+ for (const candidate of candidates) {
328
+ state.seen_ids.push(candidate.messageId);
329
+ }
191
330
  }
192
331
 
193
332
  trimState(state);
@@ -195,10 +334,13 @@ async function pollOnce(state) {
195
334
  }
196
335
 
197
336
  async function main() {
337
+ acquireLock();
198
338
  const state = loadState();
199
339
  if (VERBOSE) {
200
340
  console.log(`SilicaClaw owner push watching ${API_BASE}`);
201
341
  console.log(`State file: ${STATE_PATH}`);
342
+ console.log(`Lock file: ${LOCK_PATH}`);
343
+ console.log(`Latest-only mode: ${LATEST_ONLY ? "on" : "off"}`);
202
344
  }
203
345
 
204
346
  do {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@silicaclaw/cli",
3
- "version": "2026.3.20-2",
3
+ "version": "2026.3.20-21",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -4,6 +4,8 @@ export * from "./identity";
4
4
  export * from "./profile";
5
5
  export * from "./presence";
6
6
  export * from "./socialMessage";
7
+ export * from "./privateCrypto";
8
+ export * from "./privateMessage";
7
9
  export * from "./indexing";
8
10
  export * from "./directory";
9
11
  export * from "./publicProfileSummary";
@@ -20,6 +20,8 @@ __exportStar(require("./identity"), exports);
20
20
  __exportStar(require("./profile"), exports);
21
21
  __exportStar(require("./presence"), exports);
22
22
  __exportStar(require("./socialMessage"), exports);
23
+ __exportStar(require("./privateCrypto"), exports);
24
+ __exportStar(require("./privateMessage"), exports);
23
25
  __exportStar(require("./indexing"), exports);
24
26
  __exportStar(require("./directory"), exports);
25
27
  __exportStar(require("./publicProfileSummary"), exports);
@@ -0,0 +1,17 @@
1
+ import { PrivateEncryptionKeyPair } from "./types";
2
+ export declare function createPrivateEncryptionKeyPair(now?: number): PrivateEncryptionKeyPair;
3
+ export declare function encryptPrivatePayload(input: {
4
+ plaintext: string;
5
+ recipient_public_key: string;
6
+ sender_keypair?: PrivateEncryptionKeyPair | null;
7
+ }): {
8
+ ciphertext: string;
9
+ nonce: string;
10
+ sender_encryption_public_key: string;
11
+ };
12
+ export declare function decryptPrivatePayload(input: {
13
+ ciphertext: string;
14
+ nonce: string;
15
+ sender_encryption_public_key: string;
16
+ recipient_private_key: string;
17
+ }): string | null;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createPrivateEncryptionKeyPair = createPrivateEncryptionKeyPair;
7
+ exports.encryptPrivatePayload = encryptPrivatePayload;
8
+ exports.decryptPrivatePayload = decryptPrivatePayload;
9
+ const tweetnacl_1 = __importDefault(require("tweetnacl"));
10
+ const crypto_1 = require("./crypto");
11
+ function createPrivateEncryptionKeyPair(now = Date.now()) {
12
+ const pair = tweetnacl_1.default.box.keyPair();
13
+ return {
14
+ public_key: (0, crypto_1.toBase64)(pair.publicKey),
15
+ private_key: (0, crypto_1.toBase64)(pair.secretKey),
16
+ created_at: now,
17
+ };
18
+ }
19
+ function encryptPrivatePayload(input) {
20
+ const sender = input.sender_keypair || createPrivateEncryptionKeyPair();
21
+ const nonce = tweetnacl_1.default.randomBytes(tweetnacl_1.default.box.nonceLength);
22
+ const message = Buffer.from(String(input.plaintext || ""), "utf8");
23
+ const ciphertext = tweetnacl_1.default.box(new Uint8Array(message), nonce, (0, crypto_1.fromBase64)(input.recipient_public_key), (0, crypto_1.fromBase64)(sender.private_key));
24
+ return {
25
+ ciphertext: (0, crypto_1.toBase64)(ciphertext),
26
+ nonce: (0, crypto_1.toBase64)(nonce),
27
+ sender_encryption_public_key: sender.public_key,
28
+ };
29
+ }
30
+ function decryptPrivatePayload(input) {
31
+ try {
32
+ const opened = tweetnacl_1.default.box.open((0, crypto_1.fromBase64)(input.ciphertext), (0, crypto_1.fromBase64)(input.nonce), (0, crypto_1.fromBase64)(input.sender_encryption_public_key), (0, crypto_1.fromBase64)(input.recipient_private_key));
33
+ if (!opened)
34
+ return null;
35
+ return Buffer.from(opened).toString("utf8");
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ }
@@ -0,0 +1,23 @@
1
+ import { AgentIdentity, PrivateMessageReceiptRecord, PrivateMessageRecord } from "./types";
2
+ export declare function signPrivateMessage(input: {
3
+ identity: AgentIdentity;
4
+ message_id: string;
5
+ conversation_id: string;
6
+ to_agent_id: string;
7
+ sender_encryption_public_key: string;
8
+ recipient_encryption_public_key: string;
9
+ ciphertext: string;
10
+ nonce: string;
11
+ created_at?: number;
12
+ }): PrivateMessageRecord;
13
+ export declare function verifyPrivateMessage(record: PrivateMessageRecord): boolean;
14
+ export declare function signPrivateMessageReceipt(input: {
15
+ identity: AgentIdentity;
16
+ receipt_id: string;
17
+ message_id: string;
18
+ conversation_id: string;
19
+ to_agent_id: string;
20
+ status: "received" | "read";
21
+ created_at?: number;
22
+ }): PrivateMessageReceiptRecord;
23
+ export declare function verifyPrivateMessageReceipt(record: PrivateMessageReceiptRecord): boolean;
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.signPrivateMessage = signPrivateMessage;
4
+ exports.verifyPrivateMessage = verifyPrivateMessage;
5
+ exports.signPrivateMessageReceipt = signPrivateMessageReceipt;
6
+ exports.verifyPrivateMessageReceipt = verifyPrivateMessageReceipt;
7
+ const crypto_1 = require("./crypto");
8
+ function unsignedPrivateMessage(record) {
9
+ const { signature: _signature, ...rest } = record;
10
+ return rest;
11
+ }
12
+ function unsignedPrivateMessageReceipt(record) {
13
+ const { signature: _signature, ...rest } = record;
14
+ return rest;
15
+ }
16
+ function signPrivateMessage(input) {
17
+ const payload = {
18
+ type: "private.message",
19
+ message_id: input.message_id,
20
+ conversation_id: input.conversation_id,
21
+ from_agent_id: input.identity.agent_id,
22
+ to_agent_id: input.to_agent_id,
23
+ sender_public_key: input.identity.public_key,
24
+ sender_encryption_public_key: input.sender_encryption_public_key,
25
+ recipient_encryption_public_key: input.recipient_encryption_public_key,
26
+ cipher_scheme: "nacl-box-v1",
27
+ ciphertext: input.ciphertext,
28
+ nonce: input.nonce,
29
+ created_at: input.created_at ?? Date.now(),
30
+ };
31
+ return {
32
+ ...payload,
33
+ signature: (0, crypto_1.signPayload)(payload, input.identity.private_key),
34
+ };
35
+ }
36
+ function verifyPrivateMessage(record) {
37
+ try {
38
+ if ((0, crypto_1.hashPublicKey)((0, crypto_1.fromBase64)(record.sender_public_key)) !== record.from_agent_id) {
39
+ return false;
40
+ }
41
+ return (0, crypto_1.verifyPayload)(unsignedPrivateMessage(record), record.signature, record.sender_public_key);
42
+ }
43
+ catch {
44
+ return false;
45
+ }
46
+ }
47
+ function signPrivateMessageReceipt(input) {
48
+ const payload = {
49
+ type: "private.message.receipt",
50
+ receipt_id: input.receipt_id,
51
+ message_id: input.message_id,
52
+ conversation_id: input.conversation_id,
53
+ from_agent_id: input.identity.agent_id,
54
+ to_agent_id: input.to_agent_id,
55
+ sender_public_key: input.identity.public_key,
56
+ status: input.status,
57
+ created_at: input.created_at ?? Date.now(),
58
+ };
59
+ return {
60
+ ...payload,
61
+ signature: (0, crypto_1.signPayload)(payload, input.identity.private_key),
62
+ };
63
+ }
64
+ function verifyPrivateMessageReceipt(record) {
65
+ try {
66
+ if ((0, crypto_1.hashPublicKey)((0, crypto_1.fromBase64)(record.sender_public_key)) !== record.from_agent_id) {
67
+ return false;
68
+ }
69
+ return (0, crypto_1.verifyPayload)(unsignedPrivateMessageReceipt(record), record.signature, record.sender_public_key);
70
+ }
71
+ catch {
72
+ return false;
73
+ }
74
+ }
@@ -15,6 +15,7 @@ function signProfile(input, identity) {
15
15
  bio: input.bio,
16
16
  tags: input.tags,
17
17
  avatar_url: input.avatar_url,
18
+ private_encryption_public_key: input.private_encryption_public_key,
18
19
  public_enabled: input.public_enabled,
19
20
  updated_at: Date.now(),
20
21
  };
@@ -34,6 +35,7 @@ function createDefaultProfileInput(agentId) {
34
35
  bio: "",
35
36
  tags: [],
36
37
  avatar_url: "",
38
+ private_encryption_public_key: "",
37
39
  public_enabled: false,
38
40
  };
39
41
  }
@@ -7,9 +7,11 @@ export type ProfileVisibility = {
7
7
  };
8
8
  export type PublicProfileSummary = {
9
9
  agent_id: string;
10
+ is_self: boolean;
10
11
  display_name: string;
11
12
  bio: string;
12
13
  avatar_url?: string;
14
+ private_encryption_public_key?: string;
13
15
  public_enabled: boolean;
14
16
  updated_at: number;
15
17
  online: boolean;
@@ -30,6 +32,7 @@ export type PublicProfileSummary = {
30
32
  display_name: string;
31
33
  bio: string;
32
34
  avatar_url?: string;
35
+ private_encryption_public_key?: string;
33
36
  tags: string[];
34
37
  public_enabled: boolean;
35
38
  profile_version: string;
@@ -57,6 +60,7 @@ export type PublicProfileSummary = {
57
60
  export declare function deriveCapabilitiesSummary(tags: string[]): string[];
58
61
  export declare function buildPublicProfileSummary(args: {
59
62
  profile: PublicProfile;
63
+ is_self?: boolean;
60
64
  online: boolean;
61
65
  last_seen_at: number | null;
62
66
  network_mode?: string;
@@ -53,9 +53,11 @@ function buildPublicProfileSummary(args) {
53
53
  ].filter((field) => Boolean(field));
54
54
  return {
55
55
  agent_id: args.profile.agent_id,
56
+ is_self: Boolean(args.is_self),
56
57
  display_name: args.profile.display_name,
57
58
  bio: args.profile.bio,
58
59
  avatar_url: args.profile.avatar_url,
60
+ private_encryption_public_key: args.profile.private_encryption_public_key,
59
61
  public_enabled: args.profile.public_enabled,
60
62
  updated_at: args.profile.updated_at,
61
63
  online: args.online,
@@ -76,6 +78,7 @@ function buildPublicProfileSummary(args) {
76
78
  display_name: args.profile.display_name,
77
79
  bio: args.profile.bio,
78
80
  avatar_url: args.profile.avatar_url,
81
+ private_encryption_public_key: args.profile.private_encryption_public_key,
79
82
  tags,
80
83
  public_enabled: args.profile.public_enabled,
81
84
  profile_version: args.profile_version ?? "v1",
@@ -4,12 +4,18 @@ export type AgentIdentity = {
4
4
  private_key: string;
5
5
  created_at: number;
6
6
  };
7
+ export type PrivateEncryptionKeyPair = {
8
+ public_key: string;
9
+ private_key: string;
10
+ created_at: number;
11
+ };
7
12
  export type PublicProfile = {
8
13
  agent_id: string;
9
14
  display_name: string;
10
15
  bio: string;
11
16
  tags: string[];
12
17
  avatar_url?: string;
18
+ private_encryption_public_key?: string;
13
19
  public_enabled: boolean;
14
20
  updated_at: number;
15
21
  signature: string;
@@ -51,6 +57,40 @@ export type SocialMessageObservationRecord = {
51
57
  observed_at: number;
52
58
  signature: string;
53
59
  };
60
+ export type PrivateMessageRecord = {
61
+ type: "private.message";
62
+ message_id: string;
63
+ conversation_id: string;
64
+ from_agent_id: string;
65
+ to_agent_id: string;
66
+ sender_public_key: string;
67
+ sender_encryption_public_key: string;
68
+ recipient_encryption_public_key: string;
69
+ cipher_scheme: "nacl-box-v1";
70
+ ciphertext: string;
71
+ nonce: string;
72
+ created_at: number;
73
+ signature: string;
74
+ };
75
+ export type PrivateMessageReceiptRecord = {
76
+ type: "private.message.receipt";
77
+ receipt_id: string;
78
+ message_id: string;
79
+ conversation_id: string;
80
+ from_agent_id: string;
81
+ to_agent_id: string;
82
+ sender_public_key: string;
83
+ status: "received" | "read";
84
+ created_at: number;
85
+ signature: string;
86
+ };
87
+ export type PrivateConversationSummary = {
88
+ conversation_id: string;
89
+ peer_agent_id: string;
90
+ last_message_at: number | null;
91
+ last_message_preview: string;
92
+ unread_count: number;
93
+ };
54
94
  export type DirectoryState = {
55
95
  profiles: Record<string, PublicProfile>;
56
96
  presence: Record<string, number>;
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@silicaclaw/core",
3
3
  "version": "0.1.0",
4
- "main": "dist/index.js",
5
- "types": "dist/index.d.ts",
4
+ "main": "dist/packages/core/src/index.js",
5
+ "types": "dist/packages/core/src/index.d.ts",
6
6
  "scripts": {
7
7
  "build": "tsc -p tsconfig.json",
8
8
  "check": "tsc -p tsconfig.json --noEmit"
@@ -4,6 +4,8 @@ export * from "./identity";
4
4
  export * from "./profile";
5
5
  export * from "./presence";
6
6
  export * from "./socialMessage";
7
+ export * from "./privateCrypto";
8
+ export * from "./privateMessage";
7
9
  export * from "./indexing";
8
10
  export * from "./directory";
9
11
  export * from "./publicProfileSummary";
@@ -0,0 +1,57 @@
1
+ import nacl from "tweetnacl";
2
+ import { fromBase64, toBase64 } from "./crypto";
3
+ import { PrivateEncryptionKeyPair } from "./types";
4
+
5
+ export function createPrivateEncryptionKeyPair(now = Date.now()): PrivateEncryptionKeyPair {
6
+ const pair = nacl.box.keyPair();
7
+ return {
8
+ public_key: toBase64(pair.publicKey),
9
+ private_key: toBase64(pair.secretKey),
10
+ created_at: now,
11
+ };
12
+ }
13
+
14
+ export function encryptPrivatePayload(input: {
15
+ plaintext: string;
16
+ recipient_public_key: string;
17
+ sender_keypair?: PrivateEncryptionKeyPair | null;
18
+ }): {
19
+ ciphertext: string;
20
+ nonce: string;
21
+ sender_encryption_public_key: string;
22
+ } {
23
+ const sender = input.sender_keypair || createPrivateEncryptionKeyPair();
24
+ const nonce = nacl.randomBytes(nacl.box.nonceLength);
25
+ const message = Buffer.from(String(input.plaintext || ""), "utf8");
26
+ const ciphertext = nacl.box(
27
+ new Uint8Array(message),
28
+ nonce,
29
+ fromBase64(input.recipient_public_key),
30
+ fromBase64(sender.private_key),
31
+ );
32
+ return {
33
+ ciphertext: toBase64(ciphertext),
34
+ nonce: toBase64(nonce),
35
+ sender_encryption_public_key: sender.public_key,
36
+ };
37
+ }
38
+
39
+ export function decryptPrivatePayload(input: {
40
+ ciphertext: string;
41
+ nonce: string;
42
+ sender_encryption_public_key: string;
43
+ recipient_private_key: string;
44
+ }): string | null {
45
+ try {
46
+ const opened = nacl.box.open(
47
+ fromBase64(input.ciphertext),
48
+ fromBase64(input.nonce),
49
+ fromBase64(input.sender_encryption_public_key),
50
+ fromBase64(input.recipient_private_key),
51
+ );
52
+ if (!opened) return null;
53
+ return Buffer.from(opened).toString("utf8");
54
+ } catch {
55
+ return null;
56
+ }
57
+ }