@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.
- package/CHANGELOG.md +108 -0
- package/INSTALL.md +13 -7
- package/README.md +60 -12
- package/VERSION +1 -1
- package/apps/local-console/dist/apps/local-console/src/server.d.ts +139 -3
- package/apps/local-console/dist/apps/local-console/src/server.js +1029 -92
- package/apps/local-console/dist/packages/core/src/index.d.ts +2 -0
- package/apps/local-console/dist/packages/core/src/index.js +2 -0
- package/apps/local-console/dist/packages/core/src/privateCrypto.d.ts +17 -0
- package/apps/local-console/dist/packages/core/src/privateCrypto.js +40 -0
- package/apps/local-console/dist/packages/core/src/privateMessage.d.ts +23 -0
- package/apps/local-console/dist/packages/core/src/privateMessage.js +74 -0
- package/apps/local-console/dist/packages/core/src/profile.js +2 -0
- package/apps/local-console/dist/packages/core/src/publicProfileSummary.d.ts +4 -0
- package/apps/local-console/dist/packages/core/src/publicProfileSummary.js +3 -0
- package/apps/local-console/dist/packages/core/src/types.d.ts +40 -0
- package/apps/local-console/dist/packages/network/src/relayPreview.d.ts +12 -0
- package/apps/local-console/dist/packages/network/src/relayPreview.js +108 -8
- package/apps/local-console/dist/packages/network/src/types.d.ts +4 -0
- package/apps/local-console/dist/packages/storage/src/repos.d.ts +27 -1
- package/apps/local-console/dist/packages/storage/src/repos.js +35 -1
- package/apps/local-console/public/app/app.js +502 -11
- package/apps/local-console/public/app/events.js +21 -0
- package/apps/local-console/public/app/network.js +144 -32
- package/apps/local-console/public/app/overview.js +57 -27
- package/apps/local-console/public/app/social.js +342 -105
- package/apps/local-console/public/app/styles.css +149 -43
- package/apps/local-console/public/app/template.js +196 -100
- package/apps/local-console/public/app/translations.js +438 -316
- package/apps/local-console/src/server.ts +1177 -90
- package/apps/public-explorer/public/app/template.js +2 -2
- package/apps/public-explorer/public/app/translations.js +36 -36
- package/docs/NEW_USER_OPERATIONS.md +5 -5
- package/docs/OPENCLAW_BRIDGE.md +7 -7
- package/docs/OPENCLAW_BRIDGE_ZH.md +6 -6
- package/node_modules/@silicaclaw/core/dist/packages/core/src/index.d.ts +2 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/index.js +2 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/privateCrypto.d.ts +17 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/privateCrypto.js +40 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/privateMessage.d.ts +23 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/privateMessage.js +74 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/profile.js +2 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/publicProfileSummary.d.ts +4 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/publicProfileSummary.js +3 -0
- package/node_modules/@silicaclaw/core/dist/packages/core/src/types.d.ts +40 -0
- package/node_modules/@silicaclaw/core/package.json +2 -2
- package/node_modules/@silicaclaw/core/src/index.ts +2 -0
- package/node_modules/@silicaclaw/core/src/privateCrypto.ts +57 -0
- package/node_modules/@silicaclaw/core/src/privateMessage.ts +101 -0
- package/node_modules/@silicaclaw/core/src/profile.ts +2 -0
- package/node_modules/@silicaclaw/core/src/publicProfileSummary.ts +7 -0
- package/node_modules/@silicaclaw/core/src/types.ts +44 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/relayPreview.d.ts +12 -0
- package/node_modules/@silicaclaw/network/dist/packages/network/src/relayPreview.js +108 -8
- package/node_modules/@silicaclaw/network/dist/packages/network/src/types.d.ts +4 -0
- package/node_modules/@silicaclaw/network/src/relayPreview.ts +120 -10
- package/node_modules/@silicaclaw/network/src/types.ts +2 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/index.d.ts +2 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/index.js +2 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/privateCrypto.d.ts +17 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/privateCrypto.js +40 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/privateMessage.d.ts +23 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/privateMessage.js +74 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/profile.js +2 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/publicProfileSummary.d.ts +4 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/publicProfileSummary.js +3 -0
- package/node_modules/@silicaclaw/storage/dist/packages/core/src/types.d.ts +40 -0
- package/node_modules/@silicaclaw/storage/dist/packages/storage/src/repos.d.ts +27 -1
- package/node_modules/@silicaclaw/storage/dist/packages/storage/src/repos.js +35 -1
- package/node_modules/@silicaclaw/storage/package.json +2 -2
- package/node_modules/@silicaclaw/storage/src/repos.ts +59 -1
- package/openclaw-skills/silicaclaw-bridge-setup/SKILL.md +18 -0
- package/openclaw-skills/silicaclaw-bridge-setup/VERSION +1 -1
- package/openclaw-skills/silicaclaw-bridge-setup/manifest.json +2 -2
- package/openclaw-skills/silicaclaw-broadcast/SKILL.md +18 -0
- package/openclaw-skills/silicaclaw-broadcast/VERSION +1 -1
- package/openclaw-skills/silicaclaw-broadcast/manifest.json +2 -2
- package/openclaw-skills/silicaclaw-network-config/SKILL.md +158 -0
- package/openclaw-skills/silicaclaw-network-config/VERSION +1 -0
- package/openclaw-skills/silicaclaw-network-config/agents/openai.yaml +6 -0
- package/openclaw-skills/silicaclaw-network-config/manifest.json +27 -0
- package/openclaw-skills/silicaclaw-network-config/references/network-modes.md +22 -0
- package/openclaw-skills/silicaclaw-network-config/references/owner-dialogue-cheatsheet-zh.md +47 -0
- package/openclaw-skills/silicaclaw-network-config/references/public-discovery.md +22 -0
- package/openclaw-skills/silicaclaw-owner-push/SKILL.md +18 -0
- package/openclaw-skills/silicaclaw-owner-push/VERSION +1 -1
- package/openclaw-skills/silicaclaw-owner-push/manifest.json +2 -2
- package/openclaw-skills/silicaclaw-owner-push/references/runtime-setup.md +3 -0
- package/openclaw-skills/silicaclaw-owner-push/scripts/owner-push-forwarder.mjs +151 -9
- package/package.json +1 -1
- package/packages/core/dist/packages/core/src/index.d.ts +2 -0
- package/packages/core/dist/packages/core/src/index.js +2 -0
- package/packages/core/dist/packages/core/src/privateCrypto.d.ts +17 -0
- package/packages/core/dist/packages/core/src/privateCrypto.js +40 -0
- package/packages/core/dist/packages/core/src/privateMessage.d.ts +23 -0
- package/packages/core/dist/packages/core/src/privateMessage.js +74 -0
- package/packages/core/dist/packages/core/src/profile.js +2 -0
- package/packages/core/dist/packages/core/src/publicProfileSummary.d.ts +4 -0
- package/packages/core/dist/packages/core/src/publicProfileSummary.js +3 -0
- package/packages/core/dist/packages/core/src/types.d.ts +40 -0
- package/packages/core/package.json +2 -2
- package/packages/core/src/index.ts +2 -0
- package/packages/core/src/privateCrypto.ts +57 -0
- package/packages/core/src/privateMessage.ts +101 -0
- package/packages/core/src/profile.ts +2 -0
- package/packages/core/src/publicProfileSummary.ts +7 -0
- package/packages/core/src/types.ts +44 -0
- package/packages/network/dist/packages/network/src/relayPreview.d.ts +12 -0
- package/packages/network/dist/packages/network/src/relayPreview.js +108 -8
- package/packages/network/dist/packages/network/src/types.d.ts +4 -0
- package/packages/network/src/relayPreview.ts +120 -10
- package/packages/network/src/types.ts +2 -0
- package/packages/storage/dist/packages/core/src/index.d.ts +2 -0
- package/packages/storage/dist/packages/core/src/index.js +2 -0
- package/packages/storage/dist/packages/core/src/privateCrypto.d.ts +17 -0
- package/packages/storage/dist/packages/core/src/privateCrypto.js +40 -0
- package/packages/storage/dist/packages/core/src/privateMessage.d.ts +23 -0
- package/packages/storage/dist/packages/core/src/privateMessage.js +74 -0
- package/packages/storage/dist/packages/core/src/profile.js +2 -0
- package/packages/storage/dist/packages/core/src/publicProfileSummary.d.ts +4 -0
- package/packages/storage/dist/packages/core/src/publicProfileSummary.js +3 -0
- package/packages/storage/dist/packages/core/src/types.d.ts +40 -0
- package/packages/storage/dist/packages/storage/src/repos.d.ts +27 -1
- package/packages/storage/dist/packages/storage/src/repos.js +35 -1
- package/packages/storage/package.json +2 -2
- package/packages/storage/src/repos.ts +59 -1
- package/scripts/silicaclaw-cli.mjs +4 -1
- package/scripts/silicaclaw-gateway.mjs +114 -2
- package/scripts/validate-openclaw-skill.mjs +19 -0
- package/node_modules/@silicaclaw/storage/dist/index.d.ts +0 -3
- package/node_modules/@silicaclaw/storage/dist/index.js +0 -19
- package/node_modules/@silicaclaw/storage/dist/jsonRepo.d.ts +0 -7
- package/node_modules/@silicaclaw/storage/dist/jsonRepo.js +0 -29
- package/node_modules/@silicaclaw/storage/dist/repos.d.ts +0 -61
- package/node_modules/@silicaclaw/storage/dist/repos.js +0 -67
- package/node_modules/@silicaclaw/storage/dist/socialRuntimeRepo.d.ts +0 -5
- package/node_modules/@silicaclaw/storage/dist/socialRuntimeRepo.js +0 -57
- package/packages/storage/dist/index.d.ts +0 -3
- package/packages/storage/dist/index.js +0 -19
- package/packages/storage/dist/jsonRepo.d.ts +0 -7
- package/packages/storage/dist/jsonRepo.js +0 -29
- package/packages/storage/dist/repos.d.ts +0 -61
- package/packages/storage/dist/repos.js +0 -67
- package/packages/storage/dist/socialRuntimeRepo.d.ts +0 -5
- 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
|
-
|
|
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
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
@@ -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
|
+
}
|