@prismer/sdk 1.7.3 → 1.8.0
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/README.md +539 -8
- package/dist/cli.js +1908 -139
- package/dist/index.d.mts +829 -224
- package/dist/index.d.ts +829 -224
- package/dist/index.js +716 -23
- package/dist/index.mjs +726 -22
- package/package.json +2 -1
package/dist/index.mjs
CHANGED
|
@@ -470,7 +470,11 @@ var WRITE_PATTERNS = [
|
|
|
470
470
|
{ method: "POST", pattern: /\/api\/im\/(messages|direct|groups)\//, opType: "message.send" },
|
|
471
471
|
{ method: "PATCH", pattern: /\/api\/im\/messages\//, opType: "message.edit" },
|
|
472
472
|
{ method: "DELETE", pattern: /\/api\/im\/messages\//, opType: "message.delete" },
|
|
473
|
-
{ method: "POST", pattern: /\/api\/im\/conversations\/[^/]+\/read/, opType: "conversation.read" }
|
|
473
|
+
{ method: "POST", pattern: /\/api\/im\/conversations\/[^/]+\/read/, opType: "conversation.read" },
|
|
474
|
+
// v1.8.0 Community — queued when offline-first IM is enabled
|
|
475
|
+
{ method: "POST", pattern: /\/api\/im\/community\/posts$/, opType: "community_post" },
|
|
476
|
+
{ method: "POST", pattern: /\/api\/im\/community\/posts\/[^/]+\/comments$/, opType: "community_comment" },
|
|
477
|
+
{ method: "POST", pattern: /\/api\/im\/community\/vote$/, opType: "community_vote" }
|
|
474
478
|
];
|
|
475
479
|
function matchWriteOp(method, path) {
|
|
476
480
|
for (const { method: m, pattern, opType } of WRITE_PATTERNS) {
|
|
@@ -1202,6 +1206,293 @@ var AttachmentQueue = class {
|
|
|
1202
1206
|
}
|
|
1203
1207
|
};
|
|
1204
1208
|
|
|
1209
|
+
// src/community-hub.ts
|
|
1210
|
+
var CommunityHub = class {
|
|
1211
|
+
constructor(_r, config) {
|
|
1212
|
+
this._r = _r;
|
|
1213
|
+
this.feedCache = /* @__PURE__ */ new Map();
|
|
1214
|
+
this.statsCache = null;
|
|
1215
|
+
this.notifCountCache = null;
|
|
1216
|
+
this.notifCountTTL = 15e3;
|
|
1217
|
+
this.wsUnsubs = [];
|
|
1218
|
+
this.feedTTL = config?.feedTTLMs ?? 3e5;
|
|
1219
|
+
this.statsTTL = config?.statsTTLMs ?? 6e5;
|
|
1220
|
+
}
|
|
1221
|
+
/** Invalidate cached feeds/stats (e.g. after you posted). */
|
|
1222
|
+
invalidateCache(boardId) {
|
|
1223
|
+
if (boardId) this.feedCache.delete(boardId);
|
|
1224
|
+
else this.feedCache.clear();
|
|
1225
|
+
this.statsCache = null;
|
|
1226
|
+
this.notifCountCache = null;
|
|
1227
|
+
}
|
|
1228
|
+
/**
|
|
1229
|
+
* Subscribe to community.* WebSocket events; updates local notification count hint and invalidates feed.
|
|
1230
|
+
*/
|
|
1231
|
+
attachRealtime(ws) {
|
|
1232
|
+
const onReply = () => {
|
|
1233
|
+
this.notifCountCache = null;
|
|
1234
|
+
this.feedCache.clear();
|
|
1235
|
+
};
|
|
1236
|
+
const types = [
|
|
1237
|
+
"community.reply",
|
|
1238
|
+
"community.vote",
|
|
1239
|
+
"community.answer.accepted",
|
|
1240
|
+
"community.mention"
|
|
1241
|
+
];
|
|
1242
|
+
for (const t of types) {
|
|
1243
|
+
ws.on(t, onReply);
|
|
1244
|
+
this.wsUnsubs.push(() => ws.off(t, onReply));
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
detachRealtime() {
|
|
1248
|
+
for (const u of this.wsUnsubs) u();
|
|
1249
|
+
this.wsUnsubs = [];
|
|
1250
|
+
}
|
|
1251
|
+
// ─── Intent (cached reads) ─────────────────────────────────
|
|
1252
|
+
async feed(opts) {
|
|
1253
|
+
const key = opts?.boardId ?? "__all__";
|
|
1254
|
+
const hit = this.feedCache.get(key);
|
|
1255
|
+
if (hit && Date.now() - hit.at < this.feedTTL) {
|
|
1256
|
+
return { ok: true, data: hit.payload };
|
|
1257
|
+
}
|
|
1258
|
+
const res = await this.listPosts({
|
|
1259
|
+
boardId: opts?.boardId,
|
|
1260
|
+
limit: opts?.limit ?? 20,
|
|
1261
|
+
sort: "hot"
|
|
1262
|
+
});
|
|
1263
|
+
if (res.ok && res.data != null) {
|
|
1264
|
+
this.feedCache.set(key, { at: Date.now(), payload: res.data });
|
|
1265
|
+
}
|
|
1266
|
+
return res;
|
|
1267
|
+
}
|
|
1268
|
+
async aggregatedContext(opts) {
|
|
1269
|
+
const [feed, stats, unreadNotifications] = await Promise.all([
|
|
1270
|
+
this.feed({ boardId: opts?.boardId, limit: opts?.feedLimit ?? 15 }),
|
|
1271
|
+
this.statsCached(),
|
|
1272
|
+
this.unreadCountCached()
|
|
1273
|
+
]);
|
|
1274
|
+
return { feed, stats, unreadNotifications };
|
|
1275
|
+
}
|
|
1276
|
+
async statsCached() {
|
|
1277
|
+
if (this.statsCache && Date.now() - this.statsCache.at < this.statsTTL) {
|
|
1278
|
+
return { ok: true, data: this.statsCache.data };
|
|
1279
|
+
}
|
|
1280
|
+
const res = await this.getStats();
|
|
1281
|
+
if (res.ok && res.data != null) {
|
|
1282
|
+
this.statsCache = { at: Date.now(), data: res.data };
|
|
1283
|
+
}
|
|
1284
|
+
return res;
|
|
1285
|
+
}
|
|
1286
|
+
async unreadCountCached() {
|
|
1287
|
+
if (this.notifCountCache && Date.now() - this.notifCountCache.at < this.notifCountTTL) {
|
|
1288
|
+
return { ok: true, data: { unread: this.notifCountCache.count } };
|
|
1289
|
+
}
|
|
1290
|
+
const res = await this.getNotificationCount();
|
|
1291
|
+
const n = res.data?.unread;
|
|
1292
|
+
if (res.ok && typeof n === "number") {
|
|
1293
|
+
this.notifCountCache = { at: Date.now(), count: n };
|
|
1294
|
+
}
|
|
1295
|
+
return res;
|
|
1296
|
+
}
|
|
1297
|
+
/** Helpdesk question shortcut */
|
|
1298
|
+
async ask(title, content, tags) {
|
|
1299
|
+
const res = await this.createPost({
|
|
1300
|
+
boardId: "helpdesk",
|
|
1301
|
+
title,
|
|
1302
|
+
content,
|
|
1303
|
+
postType: "question",
|
|
1304
|
+
tags
|
|
1305
|
+
});
|
|
1306
|
+
if (res.ok) this.invalidateCache("helpdesk");
|
|
1307
|
+
return res;
|
|
1308
|
+
}
|
|
1309
|
+
/** Showcase battle report shortcut */
|
|
1310
|
+
async reportBattle(input) {
|
|
1311
|
+
const res = await this.createPost({
|
|
1312
|
+
boardId: "showcase",
|
|
1313
|
+
title: input.title,
|
|
1314
|
+
content: input.content,
|
|
1315
|
+
postType: "battleReport",
|
|
1316
|
+
tags: input.tags,
|
|
1317
|
+
linkedGeneIds: input.linkedGeneIds,
|
|
1318
|
+
linkedAgentId: input.linkedAgentId
|
|
1319
|
+
});
|
|
1320
|
+
if (res.ok) this.invalidateCache("showcase");
|
|
1321
|
+
return res;
|
|
1322
|
+
}
|
|
1323
|
+
// ─── Notifications & profile (auth) ────────────────────────
|
|
1324
|
+
async getNotifications(opts) {
|
|
1325
|
+
const q = {};
|
|
1326
|
+
if (opts?.unread) q.unread = "true";
|
|
1327
|
+
if (opts?.limit != null) q.limit = String(opts.limit);
|
|
1328
|
+
if (opts?.offset != null) q.offset = String(opts.offset);
|
|
1329
|
+
return this._r("GET", "/api/im/community/notifications", void 0, q);
|
|
1330
|
+
}
|
|
1331
|
+
async markNotificationsRead(notificationId) {
|
|
1332
|
+
const body = notificationId ? { notificationId } : {};
|
|
1333
|
+
return this._r("POST", "/api/im/community/notifications/read", body);
|
|
1334
|
+
}
|
|
1335
|
+
async getNotificationCount() {
|
|
1336
|
+
return this._r("GET", "/api/im/community/notifications/count");
|
|
1337
|
+
}
|
|
1338
|
+
async listBookmarks(opts) {
|
|
1339
|
+
const q = {};
|
|
1340
|
+
if (opts?.cursor) q.cursor = opts.cursor;
|
|
1341
|
+
if (opts?.limit != null) q.limit = String(opts.limit);
|
|
1342
|
+
return this._r("GET", "/api/im/community/bookmarks", void 0, q);
|
|
1343
|
+
}
|
|
1344
|
+
async followToggle(followingId, followingType) {
|
|
1345
|
+
return this._r("POST", "/api/im/community/follow", { followingId, followingType });
|
|
1346
|
+
}
|
|
1347
|
+
async listFollowing(type) {
|
|
1348
|
+
const q = {};
|
|
1349
|
+
if (type) q.type = type;
|
|
1350
|
+
return this._r("GET", "/api/im/community/following", void 0, q);
|
|
1351
|
+
}
|
|
1352
|
+
async listFollowers(userId) {
|
|
1353
|
+
return this._r("GET", `/api/im/community/followers/${encodeURIComponent(userId)}`);
|
|
1354
|
+
}
|
|
1355
|
+
async getProfile(userId) {
|
|
1356
|
+
return this._r("GET", `/api/im/community/profile/${encodeURIComponent(userId)}`);
|
|
1357
|
+
}
|
|
1358
|
+
// ─── REST (same surface as former CommunityClient) ─────────
|
|
1359
|
+
async createPost(input) {
|
|
1360
|
+
return this._r("POST", "/api/im/community/posts", input);
|
|
1361
|
+
}
|
|
1362
|
+
async listPosts(opts) {
|
|
1363
|
+
const query = {};
|
|
1364
|
+
if (opts?.boardId) query.boardId = opts.boardId;
|
|
1365
|
+
if (opts?.sort) query.sort = opts.sort;
|
|
1366
|
+
if (opts?.period) query.period = opts.period;
|
|
1367
|
+
if (opts?.authorType) query.authorType = opts.authorType;
|
|
1368
|
+
if (opts?.cursor) query.cursor = opts.cursor;
|
|
1369
|
+
if (opts?.limit != null) query.limit = String(opts.limit);
|
|
1370
|
+
return this._r("GET", "/api/im/community/posts", void 0, query);
|
|
1371
|
+
}
|
|
1372
|
+
async getPost(postId) {
|
|
1373
|
+
return this._r("GET", `/api/im/community/posts/${encodeURIComponent(postId)}`);
|
|
1374
|
+
}
|
|
1375
|
+
async updatePost(postId, input) {
|
|
1376
|
+
return this._r("PUT", `/api/im/community/posts/${encodeURIComponent(postId)}`, input);
|
|
1377
|
+
}
|
|
1378
|
+
async deletePost(postId) {
|
|
1379
|
+
return this._r("DELETE", `/api/im/community/posts/${encodeURIComponent(postId)}`);
|
|
1380
|
+
}
|
|
1381
|
+
async createComment(postId, input) {
|
|
1382
|
+
return this._r("POST", `/api/im/community/posts/${encodeURIComponent(postId)}/comments`, input);
|
|
1383
|
+
}
|
|
1384
|
+
async listComments(postId, opts) {
|
|
1385
|
+
const query = {};
|
|
1386
|
+
if (opts?.sort) query.sort = opts.sort;
|
|
1387
|
+
if (opts?.cursor) query.cursor = opts.cursor;
|
|
1388
|
+
if (opts?.limit != null) query.limit = String(opts.limit);
|
|
1389
|
+
return this._r("GET", `/api/im/community/posts/${encodeURIComponent(postId)}/comments`, void 0, query);
|
|
1390
|
+
}
|
|
1391
|
+
async markBestAnswer(commentId) {
|
|
1392
|
+
return this._r("POST", `/api/im/community/comments/${encodeURIComponent(commentId)}/best-answer`);
|
|
1393
|
+
}
|
|
1394
|
+
async vote(targetType, targetId, value) {
|
|
1395
|
+
return this._r("POST", "/api/im/community/vote", { targetType, targetId, value });
|
|
1396
|
+
}
|
|
1397
|
+
async bookmark(postId) {
|
|
1398
|
+
return this._r("POST", "/api/im/community/bookmark", { postId });
|
|
1399
|
+
}
|
|
1400
|
+
async search(query, opts) {
|
|
1401
|
+
const q = { q: query };
|
|
1402
|
+
if (opts?.boardId) q.boardId = opts.boardId;
|
|
1403
|
+
if (opts?.sort) q.sort = opts.sort;
|
|
1404
|
+
if (opts?.limit != null) q.limit = String(opts.limit);
|
|
1405
|
+
return this._r("GET", "/api/im/community/search", void 0, q);
|
|
1406
|
+
}
|
|
1407
|
+
async updateComment(commentId, input) {
|
|
1408
|
+
return this._r("PUT", `/api/im/community/comments/${encodeURIComponent(commentId)}`, input);
|
|
1409
|
+
}
|
|
1410
|
+
async deleteComment(commentId) {
|
|
1411
|
+
return this._r("DELETE", `/api/im/community/comments/${encodeURIComponent(commentId)}`);
|
|
1412
|
+
}
|
|
1413
|
+
async getStats() {
|
|
1414
|
+
return this._r("GET", "/api/im/community/stats");
|
|
1415
|
+
}
|
|
1416
|
+
async getTrendingTags(limit) {
|
|
1417
|
+
const query = {};
|
|
1418
|
+
if (limit != null) query.limit = String(limit);
|
|
1419
|
+
return this._r("GET", "/api/im/community/tags/trending", void 0, query);
|
|
1420
|
+
}
|
|
1421
|
+
async getHotPosts(opts) {
|
|
1422
|
+
const query = {};
|
|
1423
|
+
if (opts?.limit != null) query.limit = String(opts.limit);
|
|
1424
|
+
if (opts?.period) query.period = opts.period;
|
|
1425
|
+
return this._r("GET", "/api/im/community/hot", void 0, query);
|
|
1426
|
+
}
|
|
1427
|
+
async searchSuggest(q) {
|
|
1428
|
+
return this._r("GET", "/api/im/community/search/suggest", void 0, { q });
|
|
1429
|
+
}
|
|
1430
|
+
async autocompleteGenes(q, limit) {
|
|
1431
|
+
const query = { q };
|
|
1432
|
+
if (limit != null) query.limit = String(limit);
|
|
1433
|
+
return this._r("GET", "/api/im/community/autocomplete/genes", void 0, query);
|
|
1434
|
+
}
|
|
1435
|
+
async autocompleteSkills(q, limit) {
|
|
1436
|
+
const query = { q };
|
|
1437
|
+
if (limit != null) query.limit = String(limit);
|
|
1438
|
+
return this._r("GET", "/api/im/community/autocomplete/skills", void 0, query);
|
|
1439
|
+
}
|
|
1440
|
+
async createBattleReport(input) {
|
|
1441
|
+
return this.createPost({
|
|
1442
|
+
boardId: "showcase",
|
|
1443
|
+
title: `Battle Report: ${input.agentId}`,
|
|
1444
|
+
content: input.narrative || "Auto-generated battle report",
|
|
1445
|
+
postType: "battleReport",
|
|
1446
|
+
linkedGeneIds: input.geneIds,
|
|
1447
|
+
linkedAgentId: input.agentId
|
|
1448
|
+
});
|
|
1449
|
+
}
|
|
1450
|
+
async createMilestone(input) {
|
|
1451
|
+
return this.createPost({
|
|
1452
|
+
boardId: "showcase",
|
|
1453
|
+
title: input.title,
|
|
1454
|
+
content: input.content,
|
|
1455
|
+
postType: "milestone",
|
|
1456
|
+
linkedGeneIds: input.geneIds,
|
|
1457
|
+
linkedAgentId: input.agentId,
|
|
1458
|
+
tags: input.tags
|
|
1459
|
+
});
|
|
1460
|
+
}
|
|
1461
|
+
async createGeneRelease(input) {
|
|
1462
|
+
return this.createPost({
|
|
1463
|
+
boardId: "showcase",
|
|
1464
|
+
title: input.title,
|
|
1465
|
+
content: input.content,
|
|
1466
|
+
postType: "geneRelease",
|
|
1467
|
+
linkedGeneIds: [input.geneId],
|
|
1468
|
+
tags: input.tags
|
|
1469
|
+
});
|
|
1470
|
+
}
|
|
1471
|
+
};
|
|
1472
|
+
|
|
1473
|
+
// src/aip.ts
|
|
1474
|
+
import {
|
|
1475
|
+
AIPIdentity
|
|
1476
|
+
} from "@prismer/aip-sdk";
|
|
1477
|
+
import {
|
|
1478
|
+
publicKeyToDIDKey,
|
|
1479
|
+
didKeyToPublicKey,
|
|
1480
|
+
validateDIDKey
|
|
1481
|
+
} from "@prismer/aip-sdk";
|
|
1482
|
+
import {
|
|
1483
|
+
buildCredential,
|
|
1484
|
+
buildPresentation,
|
|
1485
|
+
verifyCredential,
|
|
1486
|
+
verifyPresentation
|
|
1487
|
+
} from "@prismer/aip-sdk";
|
|
1488
|
+
import {
|
|
1489
|
+
buildDelegation,
|
|
1490
|
+
buildEphemeralDelegation,
|
|
1491
|
+
verifyDelegation,
|
|
1492
|
+
verifyEphemeralDelegation
|
|
1493
|
+
} from "@prismer/aip-sdk";
|
|
1494
|
+
import { AIPIdentity as AIPIdentity2 } from "@prismer/aip-sdk";
|
|
1495
|
+
|
|
1205
1496
|
// src/types.ts
|
|
1206
1497
|
var ENVIRONMENTS = {
|
|
1207
1498
|
production: "https://prismer.cloud"
|
|
@@ -2091,20 +2382,27 @@ var PBKDF2_ITERATIONS = 1e5;
|
|
|
2091
2382
|
var SALT_LENGTH = 16;
|
|
2092
2383
|
var IV_LENGTH = 12;
|
|
2093
2384
|
var KEY_LENGTH = 256;
|
|
2094
|
-
var
|
|
2385
|
+
var _E2EEncryption = class _E2EEncryption {
|
|
2095
2386
|
constructor() {
|
|
2096
2387
|
this.masterKey = null;
|
|
2097
2388
|
this.keyPair = null;
|
|
2098
2389
|
this.sessionKeys = /* @__PURE__ */ new Map();
|
|
2099
2390
|
// conversationId → AES key
|
|
2100
2391
|
this.salt = null;
|
|
2392
|
+
// ─── Pipeline Functions ──────────────────────────────────
|
|
2393
|
+
this.messageCount = 0;
|
|
2394
|
+
this.lastRotation = Date.now();
|
|
2101
2395
|
}
|
|
2102
2396
|
/**
|
|
2103
2397
|
* Initialize encryption with user passphrase.
|
|
2104
2398
|
* Derives a master key via PBKDF2 and generates an ECDH key pair.
|
|
2399
|
+
*
|
|
2400
|
+
* @param passphrase - User passphrase for master key derivation
|
|
2401
|
+
* @param salt - Optional Base64-encoded salt. If omitted, a random 16-byte salt is generated.
|
|
2402
|
+
* Store the salt (via exportSalt()) so you can re-derive the same master key later.
|
|
2105
2403
|
*/
|
|
2106
|
-
async init(passphrase) {
|
|
2107
|
-
this.salt = getRandomValues(new Uint8Array(SALT_LENGTH));
|
|
2404
|
+
async init(passphrase, salt) {
|
|
2405
|
+
this.salt = salt ? new Uint8Array(base64ToArrayBuffer(salt)) : getRandomValues(new Uint8Array(SALT_LENGTH));
|
|
2108
2406
|
const passphraseKey = await subtle().importKey(
|
|
2109
2407
|
"raw",
|
|
2110
2408
|
new TextEncoder().encode(passphrase),
|
|
@@ -2115,7 +2413,7 @@ var E2EEncryption = class {
|
|
|
2115
2413
|
this.masterKey = await subtle().deriveKey(
|
|
2116
2414
|
{
|
|
2117
2415
|
name: "PBKDF2",
|
|
2118
|
-
salt: this.salt,
|
|
2416
|
+
salt: new Uint8Array(this.salt.buffer, this.salt.byteOffset, this.salt.byteLength).buffer,
|
|
2119
2417
|
iterations: PBKDF2_ITERATIONS,
|
|
2120
2418
|
hash: "SHA-256"
|
|
2121
2419
|
},
|
|
@@ -2130,6 +2428,14 @@ var E2EEncryption = class {
|
|
|
2130
2428
|
["deriveKey"]
|
|
2131
2429
|
);
|
|
2132
2430
|
}
|
|
2431
|
+
/**
|
|
2432
|
+
* Export the salt as Base64 string for persistent storage.
|
|
2433
|
+
* You must store this and pass it back to init() to re-derive the same master key.
|
|
2434
|
+
*/
|
|
2435
|
+
exportSalt() {
|
|
2436
|
+
if (!this.salt) throw new Error("E2E not initialized. Call init() first.");
|
|
2437
|
+
return arrayBufferToBase64(this.salt.buffer);
|
|
2438
|
+
}
|
|
2133
2439
|
/**
|
|
2134
2440
|
* Export public key for sharing with conversation peers.
|
|
2135
2441
|
*/
|
|
@@ -2242,8 +2548,93 @@ var E2EEncryption = class {
|
|
|
2242
2548
|
this.keyPair = null;
|
|
2243
2549
|
this.sessionKeys.clear();
|
|
2244
2550
|
this.salt = null;
|
|
2551
|
+
this.messageCount = 0;
|
|
2552
|
+
}
|
|
2553
|
+
/**
|
|
2554
|
+
* High-level encrypt-for-send pipeline.
|
|
2555
|
+
* Encrypts content, builds metadata, and handles key rotation.
|
|
2556
|
+
*
|
|
2557
|
+
* Returns { encryptedContent, metadata } ready to send.
|
|
2558
|
+
*/
|
|
2559
|
+
async encryptForSend(conversationId, content) {
|
|
2560
|
+
if (!this.hasSessionKey(conversationId)) {
|
|
2561
|
+
throw new Error("No session key for this conversation. Call deriveSessionKey() first.");
|
|
2562
|
+
}
|
|
2563
|
+
const needsRotation = this.shouldRotateKey();
|
|
2564
|
+
const encryptedContent = await this.encrypt(conversationId, content);
|
|
2565
|
+
this.messageCount++;
|
|
2566
|
+
return {
|
|
2567
|
+
encryptedContent,
|
|
2568
|
+
metadata: {
|
|
2569
|
+
encrypted: true,
|
|
2570
|
+
encryptionVersion: 1,
|
|
2571
|
+
...needsRotation && { keyRotationRequested: true }
|
|
2572
|
+
}
|
|
2573
|
+
};
|
|
2574
|
+
}
|
|
2575
|
+
/**
|
|
2576
|
+
* High-level decrypt-on-receive pipeline.
|
|
2577
|
+
* Decrypts content and validates metadata.
|
|
2578
|
+
*/
|
|
2579
|
+
async decryptOnReceive(conversationId, encryptedContent, metadata) {
|
|
2580
|
+
if (!this.hasSessionKey(conversationId)) {
|
|
2581
|
+
throw new Error("No session key for this conversation. Call deriveSessionKey() first.");
|
|
2582
|
+
}
|
|
2583
|
+
return this.decrypt(conversationId, encryptedContent);
|
|
2584
|
+
}
|
|
2585
|
+
/**
|
|
2586
|
+
* High-level file encryption pipeline.
|
|
2587
|
+
*/
|
|
2588
|
+
async encryptFile(conversationId, fileData) {
|
|
2589
|
+
const base64Data = arrayBufferToBase64(fileData);
|
|
2590
|
+
const encryptedData = await this.encrypt(conversationId, base64Data);
|
|
2591
|
+
return {
|
|
2592
|
+
encryptedData,
|
|
2593
|
+
metadata: {
|
|
2594
|
+
encrypted: true,
|
|
2595
|
+
encryptionVersion: 1,
|
|
2596
|
+
fileEncrypted: true
|
|
2597
|
+
}
|
|
2598
|
+
};
|
|
2599
|
+
}
|
|
2600
|
+
/**
|
|
2601
|
+
* High-level file decryption pipeline.
|
|
2602
|
+
*/
|
|
2603
|
+
async decryptFile(conversationId, encryptedData) {
|
|
2604
|
+
const base64Data = await this.decrypt(conversationId, encryptedData);
|
|
2605
|
+
return base64ToArrayBuffer(base64Data);
|
|
2606
|
+
}
|
|
2607
|
+
/**
|
|
2608
|
+
* Check if key rotation is needed (1000 messages or 24 hours).
|
|
2609
|
+
*/
|
|
2610
|
+
shouldRotateKey() {
|
|
2611
|
+
if (this.messageCount >= _E2EEncryption.KEY_ROTATION_THRESHOLD) {
|
|
2612
|
+
return true;
|
|
2613
|
+
}
|
|
2614
|
+
if (Date.now() - this.lastRotation >= _E2EEncryption.KEY_ROTATION_INTERVAL_MS) {
|
|
2615
|
+
return true;
|
|
2616
|
+
}
|
|
2617
|
+
return false;
|
|
2618
|
+
}
|
|
2619
|
+
/**
|
|
2620
|
+
* Perform key rotation: generate new ECDH keypair and reset counters.
|
|
2621
|
+
* The caller is responsible for re-exchanging keys with peers.
|
|
2622
|
+
*/
|
|
2623
|
+
async rotateKeys() {
|
|
2624
|
+
this.keyPair = await subtle().generateKey(
|
|
2625
|
+
{ name: "ECDH", namedCurve: "P-256" },
|
|
2626
|
+
false,
|
|
2627
|
+
["deriveKey"]
|
|
2628
|
+
);
|
|
2629
|
+
this.messageCount = 0;
|
|
2630
|
+
this.lastRotation = Date.now();
|
|
2631
|
+
this.sessionKeys.clear();
|
|
2632
|
+
return this.exportPublicKey();
|
|
2245
2633
|
}
|
|
2246
2634
|
};
|
|
2635
|
+
_E2EEncryption.KEY_ROTATION_THRESHOLD = 1e3;
|
|
2636
|
+
_E2EEncryption.KEY_ROTATION_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
2637
|
+
var E2EEncryption = _E2EEncryption;
|
|
2247
2638
|
function arrayBufferToBase64(buffer) {
|
|
2248
2639
|
if (typeof btoa !== "undefined") {
|
|
2249
2640
|
const bytes = new Uint8Array(buffer);
|
|
@@ -2811,6 +3202,53 @@ var EvolutionRuntime = class {
|
|
|
2811
3202
|
};
|
|
2812
3203
|
|
|
2813
3204
|
// src/index.ts
|
|
3205
|
+
var _fs = null;
|
|
3206
|
+
var _os = null;
|
|
3207
|
+
var _path = null;
|
|
3208
|
+
try {
|
|
3209
|
+
_fs = __require("fs");
|
|
3210
|
+
_os = __require("os");
|
|
3211
|
+
_path = __require("path");
|
|
3212
|
+
} catch {
|
|
3213
|
+
}
|
|
3214
|
+
function resolveApiKey(explicit) {
|
|
3215
|
+
if (explicit) return explicit;
|
|
3216
|
+
try {
|
|
3217
|
+
if (typeof process !== "undefined" && process.env?.PRISMER_API_KEY) {
|
|
3218
|
+
return process.env.PRISMER_API_KEY;
|
|
3219
|
+
}
|
|
3220
|
+
} catch {
|
|
3221
|
+
}
|
|
3222
|
+
if (_fs && _os && _path) {
|
|
3223
|
+
try {
|
|
3224
|
+
const configPath = _path.join(_os.homedir(), ".prismer", "config.toml");
|
|
3225
|
+
const raw = _fs.readFileSync(configPath, "utf-8");
|
|
3226
|
+
const match = raw.match(/^api_key\s*=\s*'([^']+)'/m) || raw.match(/^api_key\s*=\s*"([^"]+)"/m);
|
|
3227
|
+
if (match?.[1]) return match[1];
|
|
3228
|
+
} catch {
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
3231
|
+
return "";
|
|
3232
|
+
}
|
|
3233
|
+
function resolveBaseUrl(explicit) {
|
|
3234
|
+
if (explicit) return explicit;
|
|
3235
|
+
try {
|
|
3236
|
+
if (typeof process !== "undefined" && process.env?.PRISMER_BASE_URL) {
|
|
3237
|
+
return process.env.PRISMER_BASE_URL;
|
|
3238
|
+
}
|
|
3239
|
+
} catch {
|
|
3240
|
+
}
|
|
3241
|
+
if (_fs && _os && _path) {
|
|
3242
|
+
try {
|
|
3243
|
+
const configPath = _path.join(_os.homedir(), ".prismer", "config.toml");
|
|
3244
|
+
const raw = _fs.readFileSync(configPath, "utf-8");
|
|
3245
|
+
const match = raw.match(/^base_url\s*=\s*'([^']+)'/m) || raw.match(/^base_url\s*=\s*"([^"]+)"/m);
|
|
3246
|
+
if (match?.[1]) return match[1];
|
|
3247
|
+
} catch {
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3250
|
+
return void 0;
|
|
3251
|
+
}
|
|
2814
3252
|
var AccountClient = class {
|
|
2815
3253
|
constructor(_r) {
|
|
2816
3254
|
this._r = _r;
|
|
@@ -2823,6 +3261,10 @@ var AccountClient = class {
|
|
|
2823
3261
|
async me() {
|
|
2824
3262
|
return this._r("GET", "/api/im/me");
|
|
2825
3263
|
}
|
|
3264
|
+
/** Update own profile */
|
|
3265
|
+
async updateProfile(options) {
|
|
3266
|
+
return this._r("PATCH", "/api/im/me", options);
|
|
3267
|
+
}
|
|
2826
3268
|
/** Refresh JWT token */
|
|
2827
3269
|
async refreshToken() {
|
|
2828
3270
|
return this._r("POST", "/api/im/token/refresh");
|
|
@@ -2913,6 +3355,30 @@ var ConversationsClient = class {
|
|
|
2913
3355
|
async markAsRead(conversationId) {
|
|
2914
3356
|
return this._r("POST", `/api/im/conversations/${conversationId}/read`);
|
|
2915
3357
|
}
|
|
3358
|
+
/** Archive a conversation */
|
|
3359
|
+
async archive(conversationId) {
|
|
3360
|
+
return this._r("POST", `/api/im/conversations/${conversationId}/archive`);
|
|
3361
|
+
}
|
|
3362
|
+
/** Unarchive a conversation */
|
|
3363
|
+
async unarchive(conversationId) {
|
|
3364
|
+
return this._r("POST", `/api/im/conversations/${conversationId}/unarchive`);
|
|
3365
|
+
}
|
|
3366
|
+
/** Update conversation metadata */
|
|
3367
|
+
async update(conversationId, options) {
|
|
3368
|
+
return this._r("PATCH", `/api/im/conversations/${conversationId}`, options);
|
|
3369
|
+
}
|
|
3370
|
+
/** Pin or unpin a conversation */
|
|
3371
|
+
async pin(conversationId, pinned) {
|
|
3372
|
+
return this._r("PATCH", `/api/im/conversations/${conversationId}/pin`, { pinned });
|
|
3373
|
+
}
|
|
3374
|
+
/** Mute or unmute a conversation */
|
|
3375
|
+
async mute(conversationId, muted) {
|
|
3376
|
+
return this._r("PATCH", `/api/im/conversations/${conversationId}/mute`, { muted });
|
|
3377
|
+
}
|
|
3378
|
+
/** Delete a conversation */
|
|
3379
|
+
async delete(conversationId) {
|
|
3380
|
+
return this._r("DELETE", `/api/im/conversations/${conversationId}`);
|
|
3381
|
+
}
|
|
2916
3382
|
};
|
|
2917
3383
|
var MessagesClient = class {
|
|
2918
3384
|
constructor(_r) {
|
|
@@ -2942,6 +3408,10 @@ var MessagesClient = class {
|
|
|
2942
3408
|
async delete(conversationId, messageId) {
|
|
2943
3409
|
return this._r("DELETE", `/api/im/messages/${conversationId}/${messageId}`);
|
|
2944
3410
|
}
|
|
3411
|
+
/** Mark messages as delivered */
|
|
3412
|
+
async markDelivered(conversationId, messageIds) {
|
|
3413
|
+
return this._r("POST", "/api/im/messages/delivered", { conversationId, messageIds });
|
|
3414
|
+
}
|
|
2945
3415
|
};
|
|
2946
3416
|
var ContactsClient = class {
|
|
2947
3417
|
constructor(_r) {
|
|
@@ -2951,6 +3421,18 @@ var ContactsClient = class {
|
|
|
2951
3421
|
async list() {
|
|
2952
3422
|
return this._r("GET", "/api/im/contacts");
|
|
2953
3423
|
}
|
|
3424
|
+
/** Search users/agents by query */
|
|
3425
|
+
async search(query, options) {
|
|
3426
|
+
const params = { q: query };
|
|
3427
|
+
if (options?.type && options.type !== "all") params.type = options.type;
|
|
3428
|
+
if (options?.limit) params.limit = String(options.limit);
|
|
3429
|
+
if (options?.offset) params.offset = String(options.offset);
|
|
3430
|
+
return this._r("GET", "/api/im/discover", void 0, params);
|
|
3431
|
+
}
|
|
3432
|
+
/** Get a user's public profile */
|
|
3433
|
+
async getProfile(userId) {
|
|
3434
|
+
return this._r("GET", `/api/im/users/${userId}`);
|
|
3435
|
+
}
|
|
2954
3436
|
/** Discover agents by capability or type */
|
|
2955
3437
|
async discover(options) {
|
|
2956
3438
|
const query = {};
|
|
@@ -2958,6 +3440,67 @@ var ContactsClient = class {
|
|
|
2958
3440
|
if (options?.capability) query.capability = options.capability;
|
|
2959
3441
|
return this._r("GET", "/api/im/discover", void 0, query);
|
|
2960
3442
|
}
|
|
3443
|
+
// ─── Friend System (v1.8.0 P9) ─────────────────────────
|
|
3444
|
+
/** Send a friend request */
|
|
3445
|
+
async request(userId, opts) {
|
|
3446
|
+
return this._r("POST", "/api/im/contacts/request", { userId, ...opts });
|
|
3447
|
+
}
|
|
3448
|
+
/** List pending friend requests received */
|
|
3449
|
+
async pendingReceived(opts) {
|
|
3450
|
+
const params = {};
|
|
3451
|
+
if (opts?.limit) params.limit = String(opts.limit);
|
|
3452
|
+
if (opts?.offset) params.offset = String(opts.offset);
|
|
3453
|
+
return this._r("GET", "/api/im/contacts/requests/received", void 0, params);
|
|
3454
|
+
}
|
|
3455
|
+
/** List pending friend requests sent */
|
|
3456
|
+
async pendingSent(opts) {
|
|
3457
|
+
const params = {};
|
|
3458
|
+
if (opts?.limit) params.limit = String(opts.limit);
|
|
3459
|
+
if (opts?.offset) params.offset = String(opts.offset);
|
|
3460
|
+
return this._r("GET", "/api/im/contacts/requests/sent", void 0, params);
|
|
3461
|
+
}
|
|
3462
|
+
/** Accept a friend request */
|
|
3463
|
+
async accept(requestId) {
|
|
3464
|
+
return this._r("POST", `/api/im/contacts/requests/${requestId}/accept`);
|
|
3465
|
+
}
|
|
3466
|
+
/** Reject a friend request */
|
|
3467
|
+
async reject(requestId) {
|
|
3468
|
+
return this._r("POST", `/api/im/contacts/requests/${requestId}/reject`);
|
|
3469
|
+
}
|
|
3470
|
+
/** List friends */
|
|
3471
|
+
async friends(opts) {
|
|
3472
|
+
const params = {};
|
|
3473
|
+
if (opts?.limit) params.limit = String(opts.limit);
|
|
3474
|
+
if (opts?.offset) params.offset = String(opts.offset);
|
|
3475
|
+
return this._r("GET", "/api/im/contacts/friends", void 0, params);
|
|
3476
|
+
}
|
|
3477
|
+
/** Remove a friend */
|
|
3478
|
+
async remove(userId) {
|
|
3479
|
+
return this._r("DELETE", `/api/im/contacts/${userId}/remove`);
|
|
3480
|
+
}
|
|
3481
|
+
/** Set a remark/alias for a contact */
|
|
3482
|
+
async setRemark(userId, remark) {
|
|
3483
|
+
return this._r("PATCH", `/api/im/contacts/${userId}/remark`, { remark });
|
|
3484
|
+
}
|
|
3485
|
+
/** Block a user */
|
|
3486
|
+
async block(userId) {
|
|
3487
|
+
return this._r("POST", `/api/im/contacts/${userId}/block`, {});
|
|
3488
|
+
}
|
|
3489
|
+
/** Unblock a user */
|
|
3490
|
+
async unblock(userId) {
|
|
3491
|
+
return this._r("DELETE", `/api/im/contacts/${userId}/block`);
|
|
3492
|
+
}
|
|
3493
|
+
/** List blocked users */
|
|
3494
|
+
async blocklist(opts) {
|
|
3495
|
+
const params = {};
|
|
3496
|
+
if (opts?.limit) params.limit = String(opts.limit);
|
|
3497
|
+
if (opts?.offset) params.offset = String(opts.offset);
|
|
3498
|
+
return this._r("GET", "/api/im/contacts/blocked", void 0, params);
|
|
3499
|
+
}
|
|
3500
|
+
/** Get presence status for multiple users */
|
|
3501
|
+
async getPresence(userIds) {
|
|
3502
|
+
return this._r("POST", "/api/im/presence/batch", { userIds });
|
|
3503
|
+
}
|
|
2961
3504
|
};
|
|
2962
3505
|
var BindingsClient = class {
|
|
2963
3506
|
constructor(_r) {
|
|
@@ -3109,6 +3652,23 @@ var MemoryClient = class {
|
|
|
3109
3652
|
if (scope) query.scope = scope;
|
|
3110
3653
|
return this._r("GET", "/api/im/memory/load", void 0, query);
|
|
3111
3654
|
}
|
|
3655
|
+
/** Get memory-gene knowledge links for the authenticated user's memory files (v1.8.0) */
|
|
3656
|
+
async getKnowledgeLinks() {
|
|
3657
|
+
return this._r("GET", "/api/im/memory/links");
|
|
3658
|
+
}
|
|
3659
|
+
};
|
|
3660
|
+
var KnowledgeLinkClient = class {
|
|
3661
|
+
constructor(_r) {
|
|
3662
|
+
this._r = _r;
|
|
3663
|
+
}
|
|
3664
|
+
/**
|
|
3665
|
+
* Get all knowledge links for a given entity.
|
|
3666
|
+
* @param entityType - One of: memory, gene, capsule, signal
|
|
3667
|
+
* @param entityId - The entity ID
|
|
3668
|
+
*/
|
|
3669
|
+
async getLinks(entityType, entityId) {
|
|
3670
|
+
return this._r("GET", "/api/im/knowledge/links", void 0, { entityType, entityId });
|
|
3671
|
+
}
|
|
3112
3672
|
};
|
|
3113
3673
|
var IdentityClient = class {
|
|
3114
3674
|
constructor(_r) {
|
|
@@ -3211,6 +3771,62 @@ var EvolutionClient = class {
|
|
|
3211
3771
|
if (limit != null) query.limit = String(limit);
|
|
3212
3772
|
return this._r("GET", "/api/im/evolution/public/feed", void 0, query);
|
|
3213
3773
|
}
|
|
3774
|
+
// ── Leaderboard V2 (public, no auth required) ──
|
|
3775
|
+
/** Get hero section global stats (total agents, genes, capsules, savings) */
|
|
3776
|
+
async getLeaderboardHero() {
|
|
3777
|
+
return this._r("GET", "/api/im/evolution/leaderboard/hero");
|
|
3778
|
+
}
|
|
3779
|
+
/** Get rising stars leaderboard */
|
|
3780
|
+
async getLeaderboardRising(period, limit) {
|
|
3781
|
+
const query = {};
|
|
3782
|
+
if (period) query.period = period;
|
|
3783
|
+
if (limit != null) query.limit = String(limit);
|
|
3784
|
+
return this._r("GET", "/api/im/evolution/leaderboard/rising", void 0, query);
|
|
3785
|
+
}
|
|
3786
|
+
/** Get leaderboard summary stats (totalAgentsEvolving, totalGenesCreated, etc.) */
|
|
3787
|
+
async getLeaderboardStats() {
|
|
3788
|
+
return this._r("GET", "/api/im/evolution/leaderboard/stats");
|
|
3789
|
+
}
|
|
3790
|
+
/** Get agent improvement board */
|
|
3791
|
+
async getLeaderboardAgents(period, domain) {
|
|
3792
|
+
const query = {};
|
|
3793
|
+
if (period) query.period = period;
|
|
3794
|
+
if (domain) query.domain = domain;
|
|
3795
|
+
return this._r("GET", "/api/im/evolution/leaderboard/agents", void 0, query);
|
|
3796
|
+
}
|
|
3797
|
+
/** Get gene impact board */
|
|
3798
|
+
async getLeaderboardGenes(period, sort) {
|
|
3799
|
+
const query = {};
|
|
3800
|
+
if (period) query.period = period;
|
|
3801
|
+
if (sort) query.sort = sort;
|
|
3802
|
+
return this._r("GET", "/api/im/evolution/leaderboard/genes", void 0, query);
|
|
3803
|
+
}
|
|
3804
|
+
/** Get contributor board */
|
|
3805
|
+
async getLeaderboardContributors(period) {
|
|
3806
|
+
const query = {};
|
|
3807
|
+
if (period) query.period = period;
|
|
3808
|
+
return this._r("GET", "/api/im/evolution/leaderboard/contributors", void 0, query);
|
|
3809
|
+
}
|
|
3810
|
+
/** Get cross-environment comparison data */
|
|
3811
|
+
async getLeaderboardComparison() {
|
|
3812
|
+
return this._r("GET", "/api/im/evolution/leaderboard/comparison");
|
|
3813
|
+
}
|
|
3814
|
+
/** Get public profile page data for an agent or owner */
|
|
3815
|
+
async getPublicProfile(entityId) {
|
|
3816
|
+
return this._r("GET", `/api/im/evolution/profile/${encodeURIComponent(entityId)}`);
|
|
3817
|
+
}
|
|
3818
|
+
/** Render agent/creator card as PNG */
|
|
3819
|
+
async renderCard(input) {
|
|
3820
|
+
return this._r("POST", "/api/im/evolution/card/render", input);
|
|
3821
|
+
}
|
|
3822
|
+
/** Get benchmark data for profile FOMO section */
|
|
3823
|
+
async getBenchmark() {
|
|
3824
|
+
return this._r("GET", "/api/im/evolution/benchmark");
|
|
3825
|
+
}
|
|
3826
|
+
/** Get gene highlight capsules for profile page */
|
|
3827
|
+
async getHighlights(geneId) {
|
|
3828
|
+
return this._r("GET", `/api/im/evolution/highlights/${encodeURIComponent(geneId)}`);
|
|
3829
|
+
}
|
|
3214
3830
|
// ── Authenticated endpoints ──
|
|
3215
3831
|
/** Analyze signals and get gene recommendation */
|
|
3216
3832
|
async analyze(options) {
|
|
@@ -3292,11 +3908,11 @@ var EvolutionClient = class {
|
|
|
3292
3908
|
}
|
|
3293
3909
|
/** Delete a gene */
|
|
3294
3910
|
async deleteGene(geneId) {
|
|
3295
|
-
return this._r("DELETE", `/api/im/evolution/genes/${geneId}`);
|
|
3911
|
+
return this._r("DELETE", `/api/im/evolution/genes/${encodeURIComponent(geneId)}`);
|
|
3296
3912
|
}
|
|
3297
3913
|
/** Publish a gene. Pass skipCanary=true to bypass canary validation (MVP/admin). */
|
|
3298
3914
|
async publishGene(geneId, options) {
|
|
3299
|
-
return this._r("POST", `/api/im/evolution/genes/${geneId}/publish`, options?.skipCanary ? { skipCanary: true } : void 0);
|
|
3915
|
+
return this._r("POST", `/api/im/evolution/genes/${encodeURIComponent(geneId)}/publish`, options?.skipCanary ? { skipCanary: true } : void 0);
|
|
3300
3916
|
}
|
|
3301
3917
|
/** Import a published gene */
|
|
3302
3918
|
async importGene(geneId) {
|
|
@@ -3367,8 +3983,8 @@ var EvolutionClient = class {
|
|
|
3367
3983
|
return this._r("GET", "/api/im/skills/stats");
|
|
3368
3984
|
}
|
|
3369
3985
|
/** Install a skill — creates Gene + returns content + install guide */
|
|
3370
|
-
async installSkill(slugOrId) {
|
|
3371
|
-
return this._r("POST", `/api/im/skills/${encodeURIComponent(slugOrId)}/install
|
|
3986
|
+
async installSkill(slugOrId, scope) {
|
|
3987
|
+
return this._r("POST", `/api/im/skills/${encodeURIComponent(slugOrId)}/install`, scope ? { scope } : void 0);
|
|
3372
3988
|
}
|
|
3373
3989
|
/** Uninstall a skill */
|
|
3374
3990
|
async uninstallSkill(slugOrId) {
|
|
@@ -3382,6 +3998,14 @@ var EvolutionClient = class {
|
|
|
3382
3998
|
async getSkillContent(slugOrId) {
|
|
3383
3999
|
return this._r("GET", `/api/im/skills/${encodeURIComponent(slugOrId)}/content`);
|
|
3384
4000
|
}
|
|
4001
|
+
/** Create/submit a community skill */
|
|
4002
|
+
async createSkill(input) {
|
|
4003
|
+
return this._r("POST", "/api/im/skills", input);
|
|
4004
|
+
}
|
|
4005
|
+
/** Star a skill (increment community rating) */
|
|
4006
|
+
async starSkill(skillId) {
|
|
4007
|
+
return this._r("POST", `/api/im/skills/${encodeURIComponent(skillId)}/star`);
|
|
4008
|
+
}
|
|
3385
4009
|
/**
|
|
3386
4010
|
* Install a skill and write SKILL.md to local filesystem.
|
|
3387
4011
|
* Combines cloud install + local file sync for Claude Code / OpenClaw / OpenCode.
|
|
@@ -3444,8 +4068,8 @@ var EvolutionClient = class {
|
|
|
3444
4068
|
async uninstallSkillLocal(slugOrId) {
|
|
3445
4069
|
const result = await this.uninstallSkill(slugOrId);
|
|
3446
4070
|
const removedPaths = [];
|
|
3447
|
-
const
|
|
3448
|
-
if (!
|
|
4071
|
+
const slug = safeSlug(slugOrId);
|
|
4072
|
+
if (!slug) return { ...result, data: { uninstalled: result.data?.uninstalled ?? false, removedPaths } };
|
|
3449
4073
|
try {
|
|
3450
4074
|
const fs = await import("fs");
|
|
3451
4075
|
const path = await import("path");
|
|
@@ -3453,10 +4077,10 @@ var EvolutionClient = class {
|
|
|
3453
4077
|
const home = os.homedir();
|
|
3454
4078
|
const pluginBase = process.env.PRISMER_PLUGIN_DIR || path.join(home, ".claude", "plugins", "prismer");
|
|
3455
4079
|
const dirs = [
|
|
3456
|
-
path.join(home, ".claude", "skills",
|
|
3457
|
-
path.join(home, ".openclaw", "skills",
|
|
3458
|
-
path.join(home, ".config", "opencode", "skills",
|
|
3459
|
-
path.join(pluginBase, "skills",
|
|
4080
|
+
path.join(home, ".claude", "skills", slug),
|
|
4081
|
+
path.join(home, ".openclaw", "skills", slug),
|
|
4082
|
+
path.join(home, ".config", "opencode", "skills", slug),
|
|
4083
|
+
path.join(pluginBase, "skills", slug)
|
|
3460
4084
|
];
|
|
3461
4085
|
for (const dir of dirs) {
|
|
3462
4086
|
try {
|
|
@@ -3566,6 +4190,9 @@ var EvolutionClient = class {
|
|
|
3566
4190
|
return this._r("POST", "/api/im/evolution/sync", body);
|
|
3567
4191
|
}
|
|
3568
4192
|
};
|
|
4193
|
+
function safeSlug(input) {
|
|
4194
|
+
return input.replace(/[\/\\]/g, "").replace(/\.\./g, "").replace(/\0/g, "");
|
|
4195
|
+
}
|
|
3569
4196
|
function guessMimeType(fileName) {
|
|
3570
4197
|
const ext = fileName.split(".").pop()?.toLowerCase() || "";
|
|
3571
4198
|
const map = {
|
|
@@ -3794,7 +4421,7 @@ var IMRealtimeClient = class {
|
|
|
3794
4421
|
}
|
|
3795
4422
|
};
|
|
3796
4423
|
var IMClient = class {
|
|
3797
|
-
constructor(request, wsBase, fetchFn, getAuthHeaders, offlineManager) {
|
|
4424
|
+
constructor(request, wsBase, fetchFn, getAuthHeaders, offlineManager, communityHubConfig) {
|
|
3798
4425
|
this.account = new AccountClient(request);
|
|
3799
4426
|
this.direct = new DirectClient(request);
|
|
3800
4427
|
this.groups = new GroupsClient(request);
|
|
@@ -3806,9 +4433,11 @@ var IMClient = class {
|
|
|
3806
4433
|
this.workspace = new WorkspaceClient(request);
|
|
3807
4434
|
this.tasks = new TasksClient(request);
|
|
3808
4435
|
this.memory = new MemoryClient(request);
|
|
4436
|
+
this.knowledge = new KnowledgeLinkClient(request);
|
|
3809
4437
|
this.identity = new IdentityClient(request);
|
|
3810
4438
|
this.security = new SecurityClient(request);
|
|
3811
4439
|
this.evolution = new EvolutionClient(request);
|
|
4440
|
+
this.community = new CommunityHub(request, communityHubConfig ?? void 0);
|
|
3812
4441
|
this.files = new FilesClient(request, wsBase, fetchFn, getAuthHeaders);
|
|
3813
4442
|
this.realtime = new IMRealtimeClient(wsBase);
|
|
3814
4443
|
this.offline = offlineManager ?? null;
|
|
@@ -3817,19 +4446,43 @@ var IMClient = class {
|
|
|
3817
4446
|
async health() {
|
|
3818
4447
|
return this.account["_r"]("GET", "/api/im/health");
|
|
3819
4448
|
}
|
|
4449
|
+
/** Get workspace superset view with slot filtering */
|
|
4450
|
+
async getWorkspace(scope, slots, includeContent) {
|
|
4451
|
+
const params = new URLSearchParams();
|
|
4452
|
+
if (scope) params.set("scope", scope);
|
|
4453
|
+
if (slots?.length) params.set("slots", slots.join(","));
|
|
4454
|
+
if (includeContent) params.set("includeContent", "true");
|
|
4455
|
+
return this.workspace["_r"]("GET", `/api/im/workspace/view?${params}`);
|
|
4456
|
+
}
|
|
3820
4457
|
};
|
|
3821
4458
|
var PrismerClient = class {
|
|
3822
4459
|
constructor(config = {}) {
|
|
3823
4460
|
this._offlineManager = null;
|
|
3824
|
-
|
|
4461
|
+
/** AIP identity for auto-signing (v1.8.0 S1) */
|
|
4462
|
+
this._identity = null;
|
|
4463
|
+
this._identityReady = null;
|
|
4464
|
+
const resolvedApiKey = resolveApiKey(config.apiKey);
|
|
4465
|
+
if (resolvedApiKey && !resolvedApiKey.startsWith("sk-prismer-") && !resolvedApiKey.startsWith("eyJ")) {
|
|
3825
4466
|
console.warn('Warning: API key should start with "sk-prismer-" (or "eyJ" for IM JWT)');
|
|
3826
4467
|
}
|
|
3827
|
-
this.apiKey =
|
|
4468
|
+
this.apiKey = resolvedApiKey;
|
|
3828
4469
|
const envUrl = ENVIRONMENTS[config.environment || "production"];
|
|
3829
|
-
this.baseUrl = (config.baseUrl || envUrl).replace(/\/$/, "");
|
|
4470
|
+
this.baseUrl = (resolveBaseUrl(config.baseUrl) || envUrl).replace(/\/$/, "");
|
|
3830
4471
|
this.timeout = config.timeout || 3e4;
|
|
3831
4472
|
this.fetchFn = config.fetch || fetch;
|
|
3832
4473
|
this.imAgent = config.imAgent;
|
|
4474
|
+
if (config.identity) {
|
|
4475
|
+
if (config.identity === "auto" && this.apiKey) {
|
|
4476
|
+
this._identityReady = AIPIdentity.fromApiKey(this.apiKey).then((id) => {
|
|
4477
|
+
this._identity = id;
|
|
4478
|
+
}).catch((err) => console.warn("[PrismerSDK] Identity init failed:", err));
|
|
4479
|
+
} else if (typeof config.identity === "object" && config.identity.privateKey) {
|
|
4480
|
+
const keyBytes = typeof Buffer !== "undefined" ? new Uint8Array(Buffer.from(config.identity.privateKey, "base64")) : new Uint8Array(atob(config.identity.privateKey).split("").map((c) => c.charCodeAt(0)));
|
|
4481
|
+
this._identityReady = AIPIdentity.fromPrivateKey(keyBytes).then((id) => {
|
|
4482
|
+
this._identity = id;
|
|
4483
|
+
}).catch((err) => console.warn("[PrismerSDK] Identity init failed:", err));
|
|
4484
|
+
}
|
|
4485
|
+
}
|
|
3833
4486
|
if (config.offline) {
|
|
3834
4487
|
this._offlineManager = new OfflineManager(
|
|
3835
4488
|
config.offline.storage,
|
|
@@ -3840,14 +4493,60 @@ var PrismerClient = class {
|
|
|
3840
4493
|
(err) => console.warn("[PrismerSDK] Offline storage init failed:", err)
|
|
3841
4494
|
);
|
|
3842
4495
|
}
|
|
3843
|
-
|
|
4496
|
+
let imRequest = this._offlineManager ? (m, p, b, q) => this._offlineManager.dispatch(m, p, b, q) : (m, p, b, q) => this._request(m, p, b, q);
|
|
4497
|
+
if (config.identity) {
|
|
4498
|
+
const baseRequest = imRequest;
|
|
4499
|
+
imRequest = (method, path, body, query) => {
|
|
4500
|
+
if (method === "POST" && path.includes("/messages") && body) {
|
|
4501
|
+
const b = body;
|
|
4502
|
+
if (!b.signature && !b.skipSigning) {
|
|
4503
|
+
const ready = this._identityReady || Promise.resolve();
|
|
4504
|
+
return ready.then(() => {
|
|
4505
|
+
if (this._identity) {
|
|
4506
|
+
return this._signAndSend(baseRequest, method, path, b, query);
|
|
4507
|
+
}
|
|
4508
|
+
return baseRequest(method, path, body, query);
|
|
4509
|
+
});
|
|
4510
|
+
}
|
|
4511
|
+
}
|
|
4512
|
+
return baseRequest(method, path, body, query);
|
|
4513
|
+
};
|
|
4514
|
+
}
|
|
3844
4515
|
this.im = new IMClient(
|
|
3845
4516
|
imRequest,
|
|
3846
4517
|
this.baseUrl,
|
|
3847
4518
|
this.fetchFn,
|
|
3848
4519
|
() => this._getAuthHeaders(),
|
|
3849
|
-
this._offlineManager
|
|
4520
|
+
this._offlineManager,
|
|
4521
|
+
config.community ?? null
|
|
4522
|
+
);
|
|
4523
|
+
}
|
|
4524
|
+
/** Wait for identity to be ready (useful for tests or explicit await) */
|
|
4525
|
+
async ensureIdentity() {
|
|
4526
|
+
if (this._identityReady) await this._identityReady;
|
|
4527
|
+
return this._identity;
|
|
4528
|
+
}
|
|
4529
|
+
/** Auto-sign a message body and send (v1.8.0 S1) */
|
|
4530
|
+
async _signAndSend(baseRequest, method, path, body, query) {
|
|
4531
|
+
if (this._identityReady) await this._identityReady;
|
|
4532
|
+
if (!this._identity) return baseRequest(method, path, body, query);
|
|
4533
|
+
const content = body.content || "";
|
|
4534
|
+
const contentHashBytes = new Uint8Array(
|
|
4535
|
+
await crypto.subtle.digest("SHA-256", new TextEncoder().encode(content))
|
|
3850
4536
|
);
|
|
4537
|
+
const contentHash = Array.from(contentHashBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
4538
|
+
const timestamp = Date.now();
|
|
4539
|
+
const payload = `1|${this._identity.did}|${body.type || "text"}|${timestamp}|${contentHash}`;
|
|
4540
|
+
const payloadBytes = new TextEncoder().encode(payload);
|
|
4541
|
+
const signature = await this._identity.sign(payloadBytes);
|
|
4542
|
+
return baseRequest(method, path, {
|
|
4543
|
+
...body,
|
|
4544
|
+
secVersion: 1,
|
|
4545
|
+
senderDid: this._identity.did,
|
|
4546
|
+
contentHash,
|
|
4547
|
+
signature,
|
|
4548
|
+
signedAt: timestamp
|
|
4549
|
+
}, query);
|
|
3851
4550
|
}
|
|
3852
4551
|
/** Build auth headers for raw HTTP requests (used by file upload) */
|
|
3853
4552
|
_getAuthHeaders() {
|
|
@@ -3982,9 +4681,11 @@ function createClient(config) {
|
|
|
3982
4681
|
return new PrismerClient(config);
|
|
3983
4682
|
}
|
|
3984
4683
|
export {
|
|
4684
|
+
AIPIdentity,
|
|
3985
4685
|
AccountClient,
|
|
3986
4686
|
AttachmentQueue,
|
|
3987
4687
|
BindingsClient,
|
|
4688
|
+
CommunityHub,
|
|
3988
4689
|
ContactsClient,
|
|
3989
4690
|
ConversationsClient,
|
|
3990
4691
|
CreditsClient,
|
|
@@ -4000,6 +4701,7 @@ export {
|
|
|
4000
4701
|
IMRealtimeClient,
|
|
4001
4702
|
IdentityClient,
|
|
4002
4703
|
IndexedDBStorage,
|
|
4704
|
+
KnowledgeLinkClient,
|
|
4003
4705
|
MemoryClient,
|
|
4004
4706
|
MemoryStorage,
|
|
4005
4707
|
MessagesClient,
|
|
@@ -4022,5 +4724,7 @@ export {
|
|
|
4022
4724
|
encryptContext,
|
|
4023
4725
|
encryptFile,
|
|
4024
4726
|
encryptForSend,
|
|
4025
|
-
extractSignals
|
|
4727
|
+
extractSignals,
|
|
4728
|
+
guessMimeType,
|
|
4729
|
+
safeSlug
|
|
4026
4730
|
};
|