@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.js
CHANGED
|
@@ -30,9 +30,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
AIPIdentity: () => import_aip_sdk.AIPIdentity,
|
|
33
34
|
AccountClient: () => AccountClient,
|
|
34
35
|
AttachmentQueue: () => AttachmentQueue,
|
|
35
36
|
BindingsClient: () => BindingsClient,
|
|
37
|
+
CommunityHub: () => CommunityHub,
|
|
36
38
|
ContactsClient: () => ContactsClient,
|
|
37
39
|
ConversationsClient: () => ConversationsClient,
|
|
38
40
|
CreditsClient: () => CreditsClient,
|
|
@@ -48,6 +50,7 @@ __export(index_exports, {
|
|
|
48
50
|
IMRealtimeClient: () => IMRealtimeClient,
|
|
49
51
|
IdentityClient: () => IdentityClient,
|
|
50
52
|
IndexedDBStorage: () => IndexedDBStorage,
|
|
53
|
+
KnowledgeLinkClient: () => KnowledgeLinkClient,
|
|
51
54
|
MemoryClient: () => MemoryClient,
|
|
52
55
|
MemoryStorage: () => MemoryStorage,
|
|
53
56
|
MessagesClient: () => MessagesClient,
|
|
@@ -70,7 +73,9 @@ __export(index_exports, {
|
|
|
70
73
|
encryptContext: () => encryptContext,
|
|
71
74
|
encryptFile: () => encryptFile,
|
|
72
75
|
encryptForSend: () => encryptForSend,
|
|
73
|
-
extractSignals: () => extractSignals
|
|
76
|
+
extractSignals: () => extractSignals,
|
|
77
|
+
guessMimeType: () => guessMimeType,
|
|
78
|
+
safeSlug: () => safeSlug
|
|
74
79
|
});
|
|
75
80
|
module.exports = __toCommonJS(index_exports);
|
|
76
81
|
|
|
@@ -542,7 +547,11 @@ var WRITE_PATTERNS = [
|
|
|
542
547
|
{ method: "POST", pattern: /\/api\/im\/(messages|direct|groups)\//, opType: "message.send" },
|
|
543
548
|
{ method: "PATCH", pattern: /\/api\/im\/messages\//, opType: "message.edit" },
|
|
544
549
|
{ method: "DELETE", pattern: /\/api\/im\/messages\//, opType: "message.delete" },
|
|
545
|
-
{ method: "POST", pattern: /\/api\/im\/conversations\/[^/]+\/read/, opType: "conversation.read" }
|
|
550
|
+
{ method: "POST", pattern: /\/api\/im\/conversations\/[^/]+\/read/, opType: "conversation.read" },
|
|
551
|
+
// v1.8.0 Community — queued when offline-first IM is enabled
|
|
552
|
+
{ method: "POST", pattern: /\/api\/im\/community\/posts$/, opType: "community_post" },
|
|
553
|
+
{ method: "POST", pattern: /\/api\/im\/community\/posts\/[^/]+\/comments$/, opType: "community_comment" },
|
|
554
|
+
{ method: "POST", pattern: /\/api\/im\/community\/vote$/, opType: "community_vote" }
|
|
546
555
|
];
|
|
547
556
|
function matchWriteOp(method, path) {
|
|
548
557
|
for (const { method: m, pattern, opType } of WRITE_PATTERNS) {
|
|
@@ -1274,6 +1283,277 @@ var AttachmentQueue = class {
|
|
|
1274
1283
|
}
|
|
1275
1284
|
};
|
|
1276
1285
|
|
|
1286
|
+
// src/community-hub.ts
|
|
1287
|
+
var CommunityHub = class {
|
|
1288
|
+
constructor(_r, config) {
|
|
1289
|
+
this._r = _r;
|
|
1290
|
+
this.feedCache = /* @__PURE__ */ new Map();
|
|
1291
|
+
this.statsCache = null;
|
|
1292
|
+
this.notifCountCache = null;
|
|
1293
|
+
this.notifCountTTL = 15e3;
|
|
1294
|
+
this.wsUnsubs = [];
|
|
1295
|
+
this.feedTTL = config?.feedTTLMs ?? 3e5;
|
|
1296
|
+
this.statsTTL = config?.statsTTLMs ?? 6e5;
|
|
1297
|
+
}
|
|
1298
|
+
/** Invalidate cached feeds/stats (e.g. after you posted). */
|
|
1299
|
+
invalidateCache(boardId) {
|
|
1300
|
+
if (boardId) this.feedCache.delete(boardId);
|
|
1301
|
+
else this.feedCache.clear();
|
|
1302
|
+
this.statsCache = null;
|
|
1303
|
+
this.notifCountCache = null;
|
|
1304
|
+
}
|
|
1305
|
+
/**
|
|
1306
|
+
* Subscribe to community.* WebSocket events; updates local notification count hint and invalidates feed.
|
|
1307
|
+
*/
|
|
1308
|
+
attachRealtime(ws) {
|
|
1309
|
+
const onReply = () => {
|
|
1310
|
+
this.notifCountCache = null;
|
|
1311
|
+
this.feedCache.clear();
|
|
1312
|
+
};
|
|
1313
|
+
const types = [
|
|
1314
|
+
"community.reply",
|
|
1315
|
+
"community.vote",
|
|
1316
|
+
"community.answer.accepted",
|
|
1317
|
+
"community.mention"
|
|
1318
|
+
];
|
|
1319
|
+
for (const t of types) {
|
|
1320
|
+
ws.on(t, onReply);
|
|
1321
|
+
this.wsUnsubs.push(() => ws.off(t, onReply));
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
detachRealtime() {
|
|
1325
|
+
for (const u of this.wsUnsubs) u();
|
|
1326
|
+
this.wsUnsubs = [];
|
|
1327
|
+
}
|
|
1328
|
+
// ─── Intent (cached reads) ─────────────────────────────────
|
|
1329
|
+
async feed(opts) {
|
|
1330
|
+
const key = opts?.boardId ?? "__all__";
|
|
1331
|
+
const hit = this.feedCache.get(key);
|
|
1332
|
+
if (hit && Date.now() - hit.at < this.feedTTL) {
|
|
1333
|
+
return { ok: true, data: hit.payload };
|
|
1334
|
+
}
|
|
1335
|
+
const res = await this.listPosts({
|
|
1336
|
+
boardId: opts?.boardId,
|
|
1337
|
+
limit: opts?.limit ?? 20,
|
|
1338
|
+
sort: "hot"
|
|
1339
|
+
});
|
|
1340
|
+
if (res.ok && res.data != null) {
|
|
1341
|
+
this.feedCache.set(key, { at: Date.now(), payload: res.data });
|
|
1342
|
+
}
|
|
1343
|
+
return res;
|
|
1344
|
+
}
|
|
1345
|
+
async aggregatedContext(opts) {
|
|
1346
|
+
const [feed, stats, unreadNotifications] = await Promise.all([
|
|
1347
|
+
this.feed({ boardId: opts?.boardId, limit: opts?.feedLimit ?? 15 }),
|
|
1348
|
+
this.statsCached(),
|
|
1349
|
+
this.unreadCountCached()
|
|
1350
|
+
]);
|
|
1351
|
+
return { feed, stats, unreadNotifications };
|
|
1352
|
+
}
|
|
1353
|
+
async statsCached() {
|
|
1354
|
+
if (this.statsCache && Date.now() - this.statsCache.at < this.statsTTL) {
|
|
1355
|
+
return { ok: true, data: this.statsCache.data };
|
|
1356
|
+
}
|
|
1357
|
+
const res = await this.getStats();
|
|
1358
|
+
if (res.ok && res.data != null) {
|
|
1359
|
+
this.statsCache = { at: Date.now(), data: res.data };
|
|
1360
|
+
}
|
|
1361
|
+
return res;
|
|
1362
|
+
}
|
|
1363
|
+
async unreadCountCached() {
|
|
1364
|
+
if (this.notifCountCache && Date.now() - this.notifCountCache.at < this.notifCountTTL) {
|
|
1365
|
+
return { ok: true, data: { unread: this.notifCountCache.count } };
|
|
1366
|
+
}
|
|
1367
|
+
const res = await this.getNotificationCount();
|
|
1368
|
+
const n = res.data?.unread;
|
|
1369
|
+
if (res.ok && typeof n === "number") {
|
|
1370
|
+
this.notifCountCache = { at: Date.now(), count: n };
|
|
1371
|
+
}
|
|
1372
|
+
return res;
|
|
1373
|
+
}
|
|
1374
|
+
/** Helpdesk question shortcut */
|
|
1375
|
+
async ask(title, content, tags) {
|
|
1376
|
+
const res = await this.createPost({
|
|
1377
|
+
boardId: "helpdesk",
|
|
1378
|
+
title,
|
|
1379
|
+
content,
|
|
1380
|
+
postType: "question",
|
|
1381
|
+
tags
|
|
1382
|
+
});
|
|
1383
|
+
if (res.ok) this.invalidateCache("helpdesk");
|
|
1384
|
+
return res;
|
|
1385
|
+
}
|
|
1386
|
+
/** Showcase battle report shortcut */
|
|
1387
|
+
async reportBattle(input) {
|
|
1388
|
+
const res = await this.createPost({
|
|
1389
|
+
boardId: "showcase",
|
|
1390
|
+
title: input.title,
|
|
1391
|
+
content: input.content,
|
|
1392
|
+
postType: "battleReport",
|
|
1393
|
+
tags: input.tags,
|
|
1394
|
+
linkedGeneIds: input.linkedGeneIds,
|
|
1395
|
+
linkedAgentId: input.linkedAgentId
|
|
1396
|
+
});
|
|
1397
|
+
if (res.ok) this.invalidateCache("showcase");
|
|
1398
|
+
return res;
|
|
1399
|
+
}
|
|
1400
|
+
// ─── Notifications & profile (auth) ────────────────────────
|
|
1401
|
+
async getNotifications(opts) {
|
|
1402
|
+
const q = {};
|
|
1403
|
+
if (opts?.unread) q.unread = "true";
|
|
1404
|
+
if (opts?.limit != null) q.limit = String(opts.limit);
|
|
1405
|
+
if (opts?.offset != null) q.offset = String(opts.offset);
|
|
1406
|
+
return this._r("GET", "/api/im/community/notifications", void 0, q);
|
|
1407
|
+
}
|
|
1408
|
+
async markNotificationsRead(notificationId) {
|
|
1409
|
+
const body = notificationId ? { notificationId } : {};
|
|
1410
|
+
return this._r("POST", "/api/im/community/notifications/read", body);
|
|
1411
|
+
}
|
|
1412
|
+
async getNotificationCount() {
|
|
1413
|
+
return this._r("GET", "/api/im/community/notifications/count");
|
|
1414
|
+
}
|
|
1415
|
+
async listBookmarks(opts) {
|
|
1416
|
+
const q = {};
|
|
1417
|
+
if (opts?.cursor) q.cursor = opts.cursor;
|
|
1418
|
+
if (opts?.limit != null) q.limit = String(opts.limit);
|
|
1419
|
+
return this._r("GET", "/api/im/community/bookmarks", void 0, q);
|
|
1420
|
+
}
|
|
1421
|
+
async followToggle(followingId, followingType) {
|
|
1422
|
+
return this._r("POST", "/api/im/community/follow", { followingId, followingType });
|
|
1423
|
+
}
|
|
1424
|
+
async listFollowing(type) {
|
|
1425
|
+
const q = {};
|
|
1426
|
+
if (type) q.type = type;
|
|
1427
|
+
return this._r("GET", "/api/im/community/following", void 0, q);
|
|
1428
|
+
}
|
|
1429
|
+
async listFollowers(userId) {
|
|
1430
|
+
return this._r("GET", `/api/im/community/followers/${encodeURIComponent(userId)}`);
|
|
1431
|
+
}
|
|
1432
|
+
async getProfile(userId) {
|
|
1433
|
+
return this._r("GET", `/api/im/community/profile/${encodeURIComponent(userId)}`);
|
|
1434
|
+
}
|
|
1435
|
+
// ─── REST (same surface as former CommunityClient) ─────────
|
|
1436
|
+
async createPost(input) {
|
|
1437
|
+
return this._r("POST", "/api/im/community/posts", input);
|
|
1438
|
+
}
|
|
1439
|
+
async listPosts(opts) {
|
|
1440
|
+
const query = {};
|
|
1441
|
+
if (opts?.boardId) query.boardId = opts.boardId;
|
|
1442
|
+
if (opts?.sort) query.sort = opts.sort;
|
|
1443
|
+
if (opts?.period) query.period = opts.period;
|
|
1444
|
+
if (opts?.authorType) query.authorType = opts.authorType;
|
|
1445
|
+
if (opts?.cursor) query.cursor = opts.cursor;
|
|
1446
|
+
if (opts?.limit != null) query.limit = String(opts.limit);
|
|
1447
|
+
return this._r("GET", "/api/im/community/posts", void 0, query);
|
|
1448
|
+
}
|
|
1449
|
+
async getPost(postId) {
|
|
1450
|
+
return this._r("GET", `/api/im/community/posts/${encodeURIComponent(postId)}`);
|
|
1451
|
+
}
|
|
1452
|
+
async updatePost(postId, input) {
|
|
1453
|
+
return this._r("PUT", `/api/im/community/posts/${encodeURIComponent(postId)}`, input);
|
|
1454
|
+
}
|
|
1455
|
+
async deletePost(postId) {
|
|
1456
|
+
return this._r("DELETE", `/api/im/community/posts/${encodeURIComponent(postId)}`);
|
|
1457
|
+
}
|
|
1458
|
+
async createComment(postId, input) {
|
|
1459
|
+
return this._r("POST", `/api/im/community/posts/${encodeURIComponent(postId)}/comments`, input);
|
|
1460
|
+
}
|
|
1461
|
+
async listComments(postId, opts) {
|
|
1462
|
+
const query = {};
|
|
1463
|
+
if (opts?.sort) query.sort = opts.sort;
|
|
1464
|
+
if (opts?.cursor) query.cursor = opts.cursor;
|
|
1465
|
+
if (opts?.limit != null) query.limit = String(opts.limit);
|
|
1466
|
+
return this._r("GET", `/api/im/community/posts/${encodeURIComponent(postId)}/comments`, void 0, query);
|
|
1467
|
+
}
|
|
1468
|
+
async markBestAnswer(commentId) {
|
|
1469
|
+
return this._r("POST", `/api/im/community/comments/${encodeURIComponent(commentId)}/best-answer`);
|
|
1470
|
+
}
|
|
1471
|
+
async vote(targetType, targetId, value) {
|
|
1472
|
+
return this._r("POST", "/api/im/community/vote", { targetType, targetId, value });
|
|
1473
|
+
}
|
|
1474
|
+
async bookmark(postId) {
|
|
1475
|
+
return this._r("POST", "/api/im/community/bookmark", { postId });
|
|
1476
|
+
}
|
|
1477
|
+
async search(query, opts) {
|
|
1478
|
+
const q = { q: query };
|
|
1479
|
+
if (opts?.boardId) q.boardId = opts.boardId;
|
|
1480
|
+
if (opts?.sort) q.sort = opts.sort;
|
|
1481
|
+
if (opts?.limit != null) q.limit = String(opts.limit);
|
|
1482
|
+
return this._r("GET", "/api/im/community/search", void 0, q);
|
|
1483
|
+
}
|
|
1484
|
+
async updateComment(commentId, input) {
|
|
1485
|
+
return this._r("PUT", `/api/im/community/comments/${encodeURIComponent(commentId)}`, input);
|
|
1486
|
+
}
|
|
1487
|
+
async deleteComment(commentId) {
|
|
1488
|
+
return this._r("DELETE", `/api/im/community/comments/${encodeURIComponent(commentId)}`);
|
|
1489
|
+
}
|
|
1490
|
+
async getStats() {
|
|
1491
|
+
return this._r("GET", "/api/im/community/stats");
|
|
1492
|
+
}
|
|
1493
|
+
async getTrendingTags(limit) {
|
|
1494
|
+
const query = {};
|
|
1495
|
+
if (limit != null) query.limit = String(limit);
|
|
1496
|
+
return this._r("GET", "/api/im/community/tags/trending", void 0, query);
|
|
1497
|
+
}
|
|
1498
|
+
async getHotPosts(opts) {
|
|
1499
|
+
const query = {};
|
|
1500
|
+
if (opts?.limit != null) query.limit = String(opts.limit);
|
|
1501
|
+
if (opts?.period) query.period = opts.period;
|
|
1502
|
+
return this._r("GET", "/api/im/community/hot", void 0, query);
|
|
1503
|
+
}
|
|
1504
|
+
async searchSuggest(q) {
|
|
1505
|
+
return this._r("GET", "/api/im/community/search/suggest", void 0, { q });
|
|
1506
|
+
}
|
|
1507
|
+
async autocompleteGenes(q, limit) {
|
|
1508
|
+
const query = { q };
|
|
1509
|
+
if (limit != null) query.limit = String(limit);
|
|
1510
|
+
return this._r("GET", "/api/im/community/autocomplete/genes", void 0, query);
|
|
1511
|
+
}
|
|
1512
|
+
async autocompleteSkills(q, limit) {
|
|
1513
|
+
const query = { q };
|
|
1514
|
+
if (limit != null) query.limit = String(limit);
|
|
1515
|
+
return this._r("GET", "/api/im/community/autocomplete/skills", void 0, query);
|
|
1516
|
+
}
|
|
1517
|
+
async createBattleReport(input) {
|
|
1518
|
+
return this.createPost({
|
|
1519
|
+
boardId: "showcase",
|
|
1520
|
+
title: `Battle Report: ${input.agentId}`,
|
|
1521
|
+
content: input.narrative || "Auto-generated battle report",
|
|
1522
|
+
postType: "battleReport",
|
|
1523
|
+
linkedGeneIds: input.geneIds,
|
|
1524
|
+
linkedAgentId: input.agentId
|
|
1525
|
+
});
|
|
1526
|
+
}
|
|
1527
|
+
async createMilestone(input) {
|
|
1528
|
+
return this.createPost({
|
|
1529
|
+
boardId: "showcase",
|
|
1530
|
+
title: input.title,
|
|
1531
|
+
content: input.content,
|
|
1532
|
+
postType: "milestone",
|
|
1533
|
+
linkedGeneIds: input.geneIds,
|
|
1534
|
+
linkedAgentId: input.agentId,
|
|
1535
|
+
tags: input.tags
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
async createGeneRelease(input) {
|
|
1539
|
+
return this.createPost({
|
|
1540
|
+
boardId: "showcase",
|
|
1541
|
+
title: input.title,
|
|
1542
|
+
content: input.content,
|
|
1543
|
+
postType: "geneRelease",
|
|
1544
|
+
linkedGeneIds: [input.geneId],
|
|
1545
|
+
tags: input.tags
|
|
1546
|
+
});
|
|
1547
|
+
}
|
|
1548
|
+
};
|
|
1549
|
+
|
|
1550
|
+
// src/aip.ts
|
|
1551
|
+
var import_aip_sdk = require("@prismer/aip-sdk");
|
|
1552
|
+
var import_aip_sdk2 = require("@prismer/aip-sdk");
|
|
1553
|
+
var import_aip_sdk3 = require("@prismer/aip-sdk");
|
|
1554
|
+
var import_aip_sdk4 = require("@prismer/aip-sdk");
|
|
1555
|
+
var import_aip_sdk5 = require("@prismer/aip-sdk");
|
|
1556
|
+
|
|
1277
1557
|
// src/types.ts
|
|
1278
1558
|
var ENVIRONMENTS = {
|
|
1279
1559
|
production: "https://prismer.cloud"
|
|
@@ -2163,20 +2443,27 @@ var PBKDF2_ITERATIONS = 1e5;
|
|
|
2163
2443
|
var SALT_LENGTH = 16;
|
|
2164
2444
|
var IV_LENGTH = 12;
|
|
2165
2445
|
var KEY_LENGTH = 256;
|
|
2166
|
-
var
|
|
2446
|
+
var _E2EEncryption = class _E2EEncryption {
|
|
2167
2447
|
constructor() {
|
|
2168
2448
|
this.masterKey = null;
|
|
2169
2449
|
this.keyPair = null;
|
|
2170
2450
|
this.sessionKeys = /* @__PURE__ */ new Map();
|
|
2171
2451
|
// conversationId → AES key
|
|
2172
2452
|
this.salt = null;
|
|
2453
|
+
// ─── Pipeline Functions ──────────────────────────────────
|
|
2454
|
+
this.messageCount = 0;
|
|
2455
|
+
this.lastRotation = Date.now();
|
|
2173
2456
|
}
|
|
2174
2457
|
/**
|
|
2175
2458
|
* Initialize encryption with user passphrase.
|
|
2176
2459
|
* Derives a master key via PBKDF2 and generates an ECDH key pair.
|
|
2460
|
+
*
|
|
2461
|
+
* @param passphrase - User passphrase for master key derivation
|
|
2462
|
+
* @param salt - Optional Base64-encoded salt. If omitted, a random 16-byte salt is generated.
|
|
2463
|
+
* Store the salt (via exportSalt()) so you can re-derive the same master key later.
|
|
2177
2464
|
*/
|
|
2178
|
-
async init(passphrase) {
|
|
2179
|
-
this.salt = getRandomValues(new Uint8Array(SALT_LENGTH));
|
|
2465
|
+
async init(passphrase, salt) {
|
|
2466
|
+
this.salt = salt ? new Uint8Array(base64ToArrayBuffer(salt)) : getRandomValues(new Uint8Array(SALT_LENGTH));
|
|
2180
2467
|
const passphraseKey = await subtle().importKey(
|
|
2181
2468
|
"raw",
|
|
2182
2469
|
new TextEncoder().encode(passphrase),
|
|
@@ -2187,7 +2474,7 @@ var E2EEncryption = class {
|
|
|
2187
2474
|
this.masterKey = await subtle().deriveKey(
|
|
2188
2475
|
{
|
|
2189
2476
|
name: "PBKDF2",
|
|
2190
|
-
salt: this.salt,
|
|
2477
|
+
salt: new Uint8Array(this.salt.buffer, this.salt.byteOffset, this.salt.byteLength).buffer,
|
|
2191
2478
|
iterations: PBKDF2_ITERATIONS,
|
|
2192
2479
|
hash: "SHA-256"
|
|
2193
2480
|
},
|
|
@@ -2202,6 +2489,14 @@ var E2EEncryption = class {
|
|
|
2202
2489
|
["deriveKey"]
|
|
2203
2490
|
);
|
|
2204
2491
|
}
|
|
2492
|
+
/**
|
|
2493
|
+
* Export the salt as Base64 string for persistent storage.
|
|
2494
|
+
* You must store this and pass it back to init() to re-derive the same master key.
|
|
2495
|
+
*/
|
|
2496
|
+
exportSalt() {
|
|
2497
|
+
if (!this.salt) throw new Error("E2E not initialized. Call init() first.");
|
|
2498
|
+
return arrayBufferToBase64(this.salt.buffer);
|
|
2499
|
+
}
|
|
2205
2500
|
/**
|
|
2206
2501
|
* Export public key for sharing with conversation peers.
|
|
2207
2502
|
*/
|
|
@@ -2314,8 +2609,93 @@ var E2EEncryption = class {
|
|
|
2314
2609
|
this.keyPair = null;
|
|
2315
2610
|
this.sessionKeys.clear();
|
|
2316
2611
|
this.salt = null;
|
|
2612
|
+
this.messageCount = 0;
|
|
2613
|
+
}
|
|
2614
|
+
/**
|
|
2615
|
+
* High-level encrypt-for-send pipeline.
|
|
2616
|
+
* Encrypts content, builds metadata, and handles key rotation.
|
|
2617
|
+
*
|
|
2618
|
+
* Returns { encryptedContent, metadata } ready to send.
|
|
2619
|
+
*/
|
|
2620
|
+
async encryptForSend(conversationId, content) {
|
|
2621
|
+
if (!this.hasSessionKey(conversationId)) {
|
|
2622
|
+
throw new Error("No session key for this conversation. Call deriveSessionKey() first.");
|
|
2623
|
+
}
|
|
2624
|
+
const needsRotation = this.shouldRotateKey();
|
|
2625
|
+
const encryptedContent = await this.encrypt(conversationId, content);
|
|
2626
|
+
this.messageCount++;
|
|
2627
|
+
return {
|
|
2628
|
+
encryptedContent,
|
|
2629
|
+
metadata: {
|
|
2630
|
+
encrypted: true,
|
|
2631
|
+
encryptionVersion: 1,
|
|
2632
|
+
...needsRotation && { keyRotationRequested: true }
|
|
2633
|
+
}
|
|
2634
|
+
};
|
|
2635
|
+
}
|
|
2636
|
+
/**
|
|
2637
|
+
* High-level decrypt-on-receive pipeline.
|
|
2638
|
+
* Decrypts content and validates metadata.
|
|
2639
|
+
*/
|
|
2640
|
+
async decryptOnReceive(conversationId, encryptedContent, metadata) {
|
|
2641
|
+
if (!this.hasSessionKey(conversationId)) {
|
|
2642
|
+
throw new Error("No session key for this conversation. Call deriveSessionKey() first.");
|
|
2643
|
+
}
|
|
2644
|
+
return this.decrypt(conversationId, encryptedContent);
|
|
2645
|
+
}
|
|
2646
|
+
/**
|
|
2647
|
+
* High-level file encryption pipeline.
|
|
2648
|
+
*/
|
|
2649
|
+
async encryptFile(conversationId, fileData) {
|
|
2650
|
+
const base64Data = arrayBufferToBase64(fileData);
|
|
2651
|
+
const encryptedData = await this.encrypt(conversationId, base64Data);
|
|
2652
|
+
return {
|
|
2653
|
+
encryptedData,
|
|
2654
|
+
metadata: {
|
|
2655
|
+
encrypted: true,
|
|
2656
|
+
encryptionVersion: 1,
|
|
2657
|
+
fileEncrypted: true
|
|
2658
|
+
}
|
|
2659
|
+
};
|
|
2660
|
+
}
|
|
2661
|
+
/**
|
|
2662
|
+
* High-level file decryption pipeline.
|
|
2663
|
+
*/
|
|
2664
|
+
async decryptFile(conversationId, encryptedData) {
|
|
2665
|
+
const base64Data = await this.decrypt(conversationId, encryptedData);
|
|
2666
|
+
return base64ToArrayBuffer(base64Data);
|
|
2667
|
+
}
|
|
2668
|
+
/**
|
|
2669
|
+
* Check if key rotation is needed (1000 messages or 24 hours).
|
|
2670
|
+
*/
|
|
2671
|
+
shouldRotateKey() {
|
|
2672
|
+
if (this.messageCount >= _E2EEncryption.KEY_ROTATION_THRESHOLD) {
|
|
2673
|
+
return true;
|
|
2674
|
+
}
|
|
2675
|
+
if (Date.now() - this.lastRotation >= _E2EEncryption.KEY_ROTATION_INTERVAL_MS) {
|
|
2676
|
+
return true;
|
|
2677
|
+
}
|
|
2678
|
+
return false;
|
|
2679
|
+
}
|
|
2680
|
+
/**
|
|
2681
|
+
* Perform key rotation: generate new ECDH keypair and reset counters.
|
|
2682
|
+
* The caller is responsible for re-exchanging keys with peers.
|
|
2683
|
+
*/
|
|
2684
|
+
async rotateKeys() {
|
|
2685
|
+
this.keyPair = await subtle().generateKey(
|
|
2686
|
+
{ name: "ECDH", namedCurve: "P-256" },
|
|
2687
|
+
false,
|
|
2688
|
+
["deriveKey"]
|
|
2689
|
+
);
|
|
2690
|
+
this.messageCount = 0;
|
|
2691
|
+
this.lastRotation = Date.now();
|
|
2692
|
+
this.sessionKeys.clear();
|
|
2693
|
+
return this.exportPublicKey();
|
|
2317
2694
|
}
|
|
2318
2695
|
};
|
|
2696
|
+
_E2EEncryption.KEY_ROTATION_THRESHOLD = 1e3;
|
|
2697
|
+
_E2EEncryption.KEY_ROTATION_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
2698
|
+
var E2EEncryption = _E2EEncryption;
|
|
2319
2699
|
function arrayBufferToBase64(buffer) {
|
|
2320
2700
|
if (typeof btoa !== "undefined") {
|
|
2321
2701
|
const bytes = new Uint8Array(buffer);
|
|
@@ -2883,6 +3263,53 @@ var EvolutionRuntime = class {
|
|
|
2883
3263
|
};
|
|
2884
3264
|
|
|
2885
3265
|
// src/index.ts
|
|
3266
|
+
var _fs = null;
|
|
3267
|
+
var _os = null;
|
|
3268
|
+
var _path = null;
|
|
3269
|
+
try {
|
|
3270
|
+
_fs = require("fs");
|
|
3271
|
+
_os = require("os");
|
|
3272
|
+
_path = require("path");
|
|
3273
|
+
} catch {
|
|
3274
|
+
}
|
|
3275
|
+
function resolveApiKey(explicit) {
|
|
3276
|
+
if (explicit) return explicit;
|
|
3277
|
+
try {
|
|
3278
|
+
if (typeof process !== "undefined" && process.env?.PRISMER_API_KEY) {
|
|
3279
|
+
return process.env.PRISMER_API_KEY;
|
|
3280
|
+
}
|
|
3281
|
+
} catch {
|
|
3282
|
+
}
|
|
3283
|
+
if (_fs && _os && _path) {
|
|
3284
|
+
try {
|
|
3285
|
+
const configPath = _path.join(_os.homedir(), ".prismer", "config.toml");
|
|
3286
|
+
const raw = _fs.readFileSync(configPath, "utf-8");
|
|
3287
|
+
const match = raw.match(/^api_key\s*=\s*'([^']+)'/m) || raw.match(/^api_key\s*=\s*"([^"]+)"/m);
|
|
3288
|
+
if (match?.[1]) return match[1];
|
|
3289
|
+
} catch {
|
|
3290
|
+
}
|
|
3291
|
+
}
|
|
3292
|
+
return "";
|
|
3293
|
+
}
|
|
3294
|
+
function resolveBaseUrl(explicit) {
|
|
3295
|
+
if (explicit) return explicit;
|
|
3296
|
+
try {
|
|
3297
|
+
if (typeof process !== "undefined" && process.env?.PRISMER_BASE_URL) {
|
|
3298
|
+
return process.env.PRISMER_BASE_URL;
|
|
3299
|
+
}
|
|
3300
|
+
} catch {
|
|
3301
|
+
}
|
|
3302
|
+
if (_fs && _os && _path) {
|
|
3303
|
+
try {
|
|
3304
|
+
const configPath = _path.join(_os.homedir(), ".prismer", "config.toml");
|
|
3305
|
+
const raw = _fs.readFileSync(configPath, "utf-8");
|
|
3306
|
+
const match = raw.match(/^base_url\s*=\s*'([^']+)'/m) || raw.match(/^base_url\s*=\s*"([^"]+)"/m);
|
|
3307
|
+
if (match?.[1]) return match[1];
|
|
3308
|
+
} catch {
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
return void 0;
|
|
3312
|
+
}
|
|
2886
3313
|
var AccountClient = class {
|
|
2887
3314
|
constructor(_r) {
|
|
2888
3315
|
this._r = _r;
|
|
@@ -2895,6 +3322,10 @@ var AccountClient = class {
|
|
|
2895
3322
|
async me() {
|
|
2896
3323
|
return this._r("GET", "/api/im/me");
|
|
2897
3324
|
}
|
|
3325
|
+
/** Update own profile */
|
|
3326
|
+
async updateProfile(options) {
|
|
3327
|
+
return this._r("PATCH", "/api/im/me", options);
|
|
3328
|
+
}
|
|
2898
3329
|
/** Refresh JWT token */
|
|
2899
3330
|
async refreshToken() {
|
|
2900
3331
|
return this._r("POST", "/api/im/token/refresh");
|
|
@@ -2985,6 +3416,30 @@ var ConversationsClient = class {
|
|
|
2985
3416
|
async markAsRead(conversationId) {
|
|
2986
3417
|
return this._r("POST", `/api/im/conversations/${conversationId}/read`);
|
|
2987
3418
|
}
|
|
3419
|
+
/** Archive a conversation */
|
|
3420
|
+
async archive(conversationId) {
|
|
3421
|
+
return this._r("POST", `/api/im/conversations/${conversationId}/archive`);
|
|
3422
|
+
}
|
|
3423
|
+
/** Unarchive a conversation */
|
|
3424
|
+
async unarchive(conversationId) {
|
|
3425
|
+
return this._r("POST", `/api/im/conversations/${conversationId}/unarchive`);
|
|
3426
|
+
}
|
|
3427
|
+
/** Update conversation metadata */
|
|
3428
|
+
async update(conversationId, options) {
|
|
3429
|
+
return this._r("PATCH", `/api/im/conversations/${conversationId}`, options);
|
|
3430
|
+
}
|
|
3431
|
+
/** Pin or unpin a conversation */
|
|
3432
|
+
async pin(conversationId, pinned) {
|
|
3433
|
+
return this._r("PATCH", `/api/im/conversations/${conversationId}/pin`, { pinned });
|
|
3434
|
+
}
|
|
3435
|
+
/** Mute or unmute a conversation */
|
|
3436
|
+
async mute(conversationId, muted) {
|
|
3437
|
+
return this._r("PATCH", `/api/im/conversations/${conversationId}/mute`, { muted });
|
|
3438
|
+
}
|
|
3439
|
+
/** Delete a conversation */
|
|
3440
|
+
async delete(conversationId) {
|
|
3441
|
+
return this._r("DELETE", `/api/im/conversations/${conversationId}`);
|
|
3442
|
+
}
|
|
2988
3443
|
};
|
|
2989
3444
|
var MessagesClient = class {
|
|
2990
3445
|
constructor(_r) {
|
|
@@ -3014,6 +3469,10 @@ var MessagesClient = class {
|
|
|
3014
3469
|
async delete(conversationId, messageId) {
|
|
3015
3470
|
return this._r("DELETE", `/api/im/messages/${conversationId}/${messageId}`);
|
|
3016
3471
|
}
|
|
3472
|
+
/** Mark messages as delivered */
|
|
3473
|
+
async markDelivered(conversationId, messageIds) {
|
|
3474
|
+
return this._r("POST", "/api/im/messages/delivered", { conversationId, messageIds });
|
|
3475
|
+
}
|
|
3017
3476
|
};
|
|
3018
3477
|
var ContactsClient = class {
|
|
3019
3478
|
constructor(_r) {
|
|
@@ -3023,6 +3482,18 @@ var ContactsClient = class {
|
|
|
3023
3482
|
async list() {
|
|
3024
3483
|
return this._r("GET", "/api/im/contacts");
|
|
3025
3484
|
}
|
|
3485
|
+
/** Search users/agents by query */
|
|
3486
|
+
async search(query, options) {
|
|
3487
|
+
const params = { q: query };
|
|
3488
|
+
if (options?.type && options.type !== "all") params.type = options.type;
|
|
3489
|
+
if (options?.limit) params.limit = String(options.limit);
|
|
3490
|
+
if (options?.offset) params.offset = String(options.offset);
|
|
3491
|
+
return this._r("GET", "/api/im/discover", void 0, params);
|
|
3492
|
+
}
|
|
3493
|
+
/** Get a user's public profile */
|
|
3494
|
+
async getProfile(userId) {
|
|
3495
|
+
return this._r("GET", `/api/im/users/${userId}`);
|
|
3496
|
+
}
|
|
3026
3497
|
/** Discover agents by capability or type */
|
|
3027
3498
|
async discover(options) {
|
|
3028
3499
|
const query = {};
|
|
@@ -3030,6 +3501,67 @@ var ContactsClient = class {
|
|
|
3030
3501
|
if (options?.capability) query.capability = options.capability;
|
|
3031
3502
|
return this._r("GET", "/api/im/discover", void 0, query);
|
|
3032
3503
|
}
|
|
3504
|
+
// ─── Friend System (v1.8.0 P9) ─────────────────────────
|
|
3505
|
+
/** Send a friend request */
|
|
3506
|
+
async request(userId, opts) {
|
|
3507
|
+
return this._r("POST", "/api/im/contacts/request", { userId, ...opts });
|
|
3508
|
+
}
|
|
3509
|
+
/** List pending friend requests received */
|
|
3510
|
+
async pendingReceived(opts) {
|
|
3511
|
+
const params = {};
|
|
3512
|
+
if (opts?.limit) params.limit = String(opts.limit);
|
|
3513
|
+
if (opts?.offset) params.offset = String(opts.offset);
|
|
3514
|
+
return this._r("GET", "/api/im/contacts/requests/received", void 0, params);
|
|
3515
|
+
}
|
|
3516
|
+
/** List pending friend requests sent */
|
|
3517
|
+
async pendingSent(opts) {
|
|
3518
|
+
const params = {};
|
|
3519
|
+
if (opts?.limit) params.limit = String(opts.limit);
|
|
3520
|
+
if (opts?.offset) params.offset = String(opts.offset);
|
|
3521
|
+
return this._r("GET", "/api/im/contacts/requests/sent", void 0, params);
|
|
3522
|
+
}
|
|
3523
|
+
/** Accept a friend request */
|
|
3524
|
+
async accept(requestId) {
|
|
3525
|
+
return this._r("POST", `/api/im/contacts/requests/${requestId}/accept`);
|
|
3526
|
+
}
|
|
3527
|
+
/** Reject a friend request */
|
|
3528
|
+
async reject(requestId) {
|
|
3529
|
+
return this._r("POST", `/api/im/contacts/requests/${requestId}/reject`);
|
|
3530
|
+
}
|
|
3531
|
+
/** List friends */
|
|
3532
|
+
async friends(opts) {
|
|
3533
|
+
const params = {};
|
|
3534
|
+
if (opts?.limit) params.limit = String(opts.limit);
|
|
3535
|
+
if (opts?.offset) params.offset = String(opts.offset);
|
|
3536
|
+
return this._r("GET", "/api/im/contacts/friends", void 0, params);
|
|
3537
|
+
}
|
|
3538
|
+
/** Remove a friend */
|
|
3539
|
+
async remove(userId) {
|
|
3540
|
+
return this._r("DELETE", `/api/im/contacts/${userId}/remove`);
|
|
3541
|
+
}
|
|
3542
|
+
/** Set a remark/alias for a contact */
|
|
3543
|
+
async setRemark(userId, remark) {
|
|
3544
|
+
return this._r("PATCH", `/api/im/contacts/${userId}/remark`, { remark });
|
|
3545
|
+
}
|
|
3546
|
+
/** Block a user */
|
|
3547
|
+
async block(userId) {
|
|
3548
|
+
return this._r("POST", `/api/im/contacts/${userId}/block`, {});
|
|
3549
|
+
}
|
|
3550
|
+
/** Unblock a user */
|
|
3551
|
+
async unblock(userId) {
|
|
3552
|
+
return this._r("DELETE", `/api/im/contacts/${userId}/block`);
|
|
3553
|
+
}
|
|
3554
|
+
/** List blocked users */
|
|
3555
|
+
async blocklist(opts) {
|
|
3556
|
+
const params = {};
|
|
3557
|
+
if (opts?.limit) params.limit = String(opts.limit);
|
|
3558
|
+
if (opts?.offset) params.offset = String(opts.offset);
|
|
3559
|
+
return this._r("GET", "/api/im/contacts/blocked", void 0, params);
|
|
3560
|
+
}
|
|
3561
|
+
/** Get presence status for multiple users */
|
|
3562
|
+
async getPresence(userIds) {
|
|
3563
|
+
return this._r("POST", "/api/im/presence/batch", { userIds });
|
|
3564
|
+
}
|
|
3033
3565
|
};
|
|
3034
3566
|
var BindingsClient = class {
|
|
3035
3567
|
constructor(_r) {
|
|
@@ -3181,6 +3713,23 @@ var MemoryClient = class {
|
|
|
3181
3713
|
if (scope) query.scope = scope;
|
|
3182
3714
|
return this._r("GET", "/api/im/memory/load", void 0, query);
|
|
3183
3715
|
}
|
|
3716
|
+
/** Get memory-gene knowledge links for the authenticated user's memory files (v1.8.0) */
|
|
3717
|
+
async getKnowledgeLinks() {
|
|
3718
|
+
return this._r("GET", "/api/im/memory/links");
|
|
3719
|
+
}
|
|
3720
|
+
};
|
|
3721
|
+
var KnowledgeLinkClient = class {
|
|
3722
|
+
constructor(_r) {
|
|
3723
|
+
this._r = _r;
|
|
3724
|
+
}
|
|
3725
|
+
/**
|
|
3726
|
+
* Get all knowledge links for a given entity.
|
|
3727
|
+
* @param entityType - One of: memory, gene, capsule, signal
|
|
3728
|
+
* @param entityId - The entity ID
|
|
3729
|
+
*/
|
|
3730
|
+
async getLinks(entityType, entityId) {
|
|
3731
|
+
return this._r("GET", "/api/im/knowledge/links", void 0, { entityType, entityId });
|
|
3732
|
+
}
|
|
3184
3733
|
};
|
|
3185
3734
|
var IdentityClient = class {
|
|
3186
3735
|
constructor(_r) {
|
|
@@ -3283,6 +3832,62 @@ var EvolutionClient = class {
|
|
|
3283
3832
|
if (limit != null) query.limit = String(limit);
|
|
3284
3833
|
return this._r("GET", "/api/im/evolution/public/feed", void 0, query);
|
|
3285
3834
|
}
|
|
3835
|
+
// ── Leaderboard V2 (public, no auth required) ──
|
|
3836
|
+
/** Get hero section global stats (total agents, genes, capsules, savings) */
|
|
3837
|
+
async getLeaderboardHero() {
|
|
3838
|
+
return this._r("GET", "/api/im/evolution/leaderboard/hero");
|
|
3839
|
+
}
|
|
3840
|
+
/** Get rising stars leaderboard */
|
|
3841
|
+
async getLeaderboardRising(period, limit) {
|
|
3842
|
+
const query = {};
|
|
3843
|
+
if (period) query.period = period;
|
|
3844
|
+
if (limit != null) query.limit = String(limit);
|
|
3845
|
+
return this._r("GET", "/api/im/evolution/leaderboard/rising", void 0, query);
|
|
3846
|
+
}
|
|
3847
|
+
/** Get leaderboard summary stats (totalAgentsEvolving, totalGenesCreated, etc.) */
|
|
3848
|
+
async getLeaderboardStats() {
|
|
3849
|
+
return this._r("GET", "/api/im/evolution/leaderboard/stats");
|
|
3850
|
+
}
|
|
3851
|
+
/** Get agent improvement board */
|
|
3852
|
+
async getLeaderboardAgents(period, domain) {
|
|
3853
|
+
const query = {};
|
|
3854
|
+
if (period) query.period = period;
|
|
3855
|
+
if (domain) query.domain = domain;
|
|
3856
|
+
return this._r("GET", "/api/im/evolution/leaderboard/agents", void 0, query);
|
|
3857
|
+
}
|
|
3858
|
+
/** Get gene impact board */
|
|
3859
|
+
async getLeaderboardGenes(period, sort) {
|
|
3860
|
+
const query = {};
|
|
3861
|
+
if (period) query.period = period;
|
|
3862
|
+
if (sort) query.sort = sort;
|
|
3863
|
+
return this._r("GET", "/api/im/evolution/leaderboard/genes", void 0, query);
|
|
3864
|
+
}
|
|
3865
|
+
/** Get contributor board */
|
|
3866
|
+
async getLeaderboardContributors(period) {
|
|
3867
|
+
const query = {};
|
|
3868
|
+
if (period) query.period = period;
|
|
3869
|
+
return this._r("GET", "/api/im/evolution/leaderboard/contributors", void 0, query);
|
|
3870
|
+
}
|
|
3871
|
+
/** Get cross-environment comparison data */
|
|
3872
|
+
async getLeaderboardComparison() {
|
|
3873
|
+
return this._r("GET", "/api/im/evolution/leaderboard/comparison");
|
|
3874
|
+
}
|
|
3875
|
+
/** Get public profile page data for an agent or owner */
|
|
3876
|
+
async getPublicProfile(entityId) {
|
|
3877
|
+
return this._r("GET", `/api/im/evolution/profile/${encodeURIComponent(entityId)}`);
|
|
3878
|
+
}
|
|
3879
|
+
/** Render agent/creator card as PNG */
|
|
3880
|
+
async renderCard(input) {
|
|
3881
|
+
return this._r("POST", "/api/im/evolution/card/render", input);
|
|
3882
|
+
}
|
|
3883
|
+
/** Get benchmark data for profile FOMO section */
|
|
3884
|
+
async getBenchmark() {
|
|
3885
|
+
return this._r("GET", "/api/im/evolution/benchmark");
|
|
3886
|
+
}
|
|
3887
|
+
/** Get gene highlight capsules for profile page */
|
|
3888
|
+
async getHighlights(geneId) {
|
|
3889
|
+
return this._r("GET", `/api/im/evolution/highlights/${encodeURIComponent(geneId)}`);
|
|
3890
|
+
}
|
|
3286
3891
|
// ── Authenticated endpoints ──
|
|
3287
3892
|
/** Analyze signals and get gene recommendation */
|
|
3288
3893
|
async analyze(options) {
|
|
@@ -3364,11 +3969,11 @@ var EvolutionClient = class {
|
|
|
3364
3969
|
}
|
|
3365
3970
|
/** Delete a gene */
|
|
3366
3971
|
async deleteGene(geneId) {
|
|
3367
|
-
return this._r("DELETE", `/api/im/evolution/genes/${geneId}`);
|
|
3972
|
+
return this._r("DELETE", `/api/im/evolution/genes/${encodeURIComponent(geneId)}`);
|
|
3368
3973
|
}
|
|
3369
3974
|
/** Publish a gene. Pass skipCanary=true to bypass canary validation (MVP/admin). */
|
|
3370
3975
|
async publishGene(geneId, options) {
|
|
3371
|
-
return this._r("POST", `/api/im/evolution/genes/${geneId}/publish`, options?.skipCanary ? { skipCanary: true } : void 0);
|
|
3976
|
+
return this._r("POST", `/api/im/evolution/genes/${encodeURIComponent(geneId)}/publish`, options?.skipCanary ? { skipCanary: true } : void 0);
|
|
3372
3977
|
}
|
|
3373
3978
|
/** Import a published gene */
|
|
3374
3979
|
async importGene(geneId) {
|
|
@@ -3439,8 +4044,8 @@ var EvolutionClient = class {
|
|
|
3439
4044
|
return this._r("GET", "/api/im/skills/stats");
|
|
3440
4045
|
}
|
|
3441
4046
|
/** Install a skill — creates Gene + returns content + install guide */
|
|
3442
|
-
async installSkill(slugOrId) {
|
|
3443
|
-
return this._r("POST", `/api/im/skills/${encodeURIComponent(slugOrId)}/install
|
|
4047
|
+
async installSkill(slugOrId, scope) {
|
|
4048
|
+
return this._r("POST", `/api/im/skills/${encodeURIComponent(slugOrId)}/install`, scope ? { scope } : void 0);
|
|
3444
4049
|
}
|
|
3445
4050
|
/** Uninstall a skill */
|
|
3446
4051
|
async uninstallSkill(slugOrId) {
|
|
@@ -3454,6 +4059,14 @@ var EvolutionClient = class {
|
|
|
3454
4059
|
async getSkillContent(slugOrId) {
|
|
3455
4060
|
return this._r("GET", `/api/im/skills/${encodeURIComponent(slugOrId)}/content`);
|
|
3456
4061
|
}
|
|
4062
|
+
/** Create/submit a community skill */
|
|
4063
|
+
async createSkill(input) {
|
|
4064
|
+
return this._r("POST", "/api/im/skills", input);
|
|
4065
|
+
}
|
|
4066
|
+
/** Star a skill (increment community rating) */
|
|
4067
|
+
async starSkill(skillId) {
|
|
4068
|
+
return this._r("POST", `/api/im/skills/${encodeURIComponent(skillId)}/star`);
|
|
4069
|
+
}
|
|
3457
4070
|
/**
|
|
3458
4071
|
* Install a skill and write SKILL.md to local filesystem.
|
|
3459
4072
|
* Combines cloud install + local file sync for Claude Code / OpenClaw / OpenCode.
|
|
@@ -3516,8 +4129,8 @@ var EvolutionClient = class {
|
|
|
3516
4129
|
async uninstallSkillLocal(slugOrId) {
|
|
3517
4130
|
const result = await this.uninstallSkill(slugOrId);
|
|
3518
4131
|
const removedPaths = [];
|
|
3519
|
-
const
|
|
3520
|
-
if (!
|
|
4132
|
+
const slug = safeSlug(slugOrId);
|
|
4133
|
+
if (!slug) return { ...result, data: { uninstalled: result.data?.uninstalled ?? false, removedPaths } };
|
|
3521
4134
|
try {
|
|
3522
4135
|
const fs = await import("fs");
|
|
3523
4136
|
const path = await import("path");
|
|
@@ -3525,10 +4138,10 @@ var EvolutionClient = class {
|
|
|
3525
4138
|
const home = os.homedir();
|
|
3526
4139
|
const pluginBase = process.env.PRISMER_PLUGIN_DIR || path.join(home, ".claude", "plugins", "prismer");
|
|
3527
4140
|
const dirs = [
|
|
3528
|
-
path.join(home, ".claude", "skills",
|
|
3529
|
-
path.join(home, ".openclaw", "skills",
|
|
3530
|
-
path.join(home, ".config", "opencode", "skills",
|
|
3531
|
-
path.join(pluginBase, "skills",
|
|
4141
|
+
path.join(home, ".claude", "skills", slug),
|
|
4142
|
+
path.join(home, ".openclaw", "skills", slug),
|
|
4143
|
+
path.join(home, ".config", "opencode", "skills", slug),
|
|
4144
|
+
path.join(pluginBase, "skills", slug)
|
|
3532
4145
|
];
|
|
3533
4146
|
for (const dir of dirs) {
|
|
3534
4147
|
try {
|
|
@@ -3638,6 +4251,9 @@ var EvolutionClient = class {
|
|
|
3638
4251
|
return this._r("POST", "/api/im/evolution/sync", body);
|
|
3639
4252
|
}
|
|
3640
4253
|
};
|
|
4254
|
+
function safeSlug(input) {
|
|
4255
|
+
return input.replace(/[\/\\]/g, "").replace(/\.\./g, "").replace(/\0/g, "");
|
|
4256
|
+
}
|
|
3641
4257
|
function guessMimeType(fileName) {
|
|
3642
4258
|
const ext = fileName.split(".").pop()?.toLowerCase() || "";
|
|
3643
4259
|
const map = {
|
|
@@ -3866,7 +4482,7 @@ var IMRealtimeClient = class {
|
|
|
3866
4482
|
}
|
|
3867
4483
|
};
|
|
3868
4484
|
var IMClient = class {
|
|
3869
|
-
constructor(request, wsBase, fetchFn, getAuthHeaders, offlineManager) {
|
|
4485
|
+
constructor(request, wsBase, fetchFn, getAuthHeaders, offlineManager, communityHubConfig) {
|
|
3870
4486
|
this.account = new AccountClient(request);
|
|
3871
4487
|
this.direct = new DirectClient(request);
|
|
3872
4488
|
this.groups = new GroupsClient(request);
|
|
@@ -3878,9 +4494,11 @@ var IMClient = class {
|
|
|
3878
4494
|
this.workspace = new WorkspaceClient(request);
|
|
3879
4495
|
this.tasks = new TasksClient(request);
|
|
3880
4496
|
this.memory = new MemoryClient(request);
|
|
4497
|
+
this.knowledge = new KnowledgeLinkClient(request);
|
|
3881
4498
|
this.identity = new IdentityClient(request);
|
|
3882
4499
|
this.security = new SecurityClient(request);
|
|
3883
4500
|
this.evolution = new EvolutionClient(request);
|
|
4501
|
+
this.community = new CommunityHub(request, communityHubConfig ?? void 0);
|
|
3884
4502
|
this.files = new FilesClient(request, wsBase, fetchFn, getAuthHeaders);
|
|
3885
4503
|
this.realtime = new IMRealtimeClient(wsBase);
|
|
3886
4504
|
this.offline = offlineManager ?? null;
|
|
@@ -3889,19 +4507,43 @@ var IMClient = class {
|
|
|
3889
4507
|
async health() {
|
|
3890
4508
|
return this.account["_r"]("GET", "/api/im/health");
|
|
3891
4509
|
}
|
|
4510
|
+
/** Get workspace superset view with slot filtering */
|
|
4511
|
+
async getWorkspace(scope, slots, includeContent) {
|
|
4512
|
+
const params = new URLSearchParams();
|
|
4513
|
+
if (scope) params.set("scope", scope);
|
|
4514
|
+
if (slots?.length) params.set("slots", slots.join(","));
|
|
4515
|
+
if (includeContent) params.set("includeContent", "true");
|
|
4516
|
+
return this.workspace["_r"]("GET", `/api/im/workspace/view?${params}`);
|
|
4517
|
+
}
|
|
3892
4518
|
};
|
|
3893
4519
|
var PrismerClient = class {
|
|
3894
4520
|
constructor(config = {}) {
|
|
3895
4521
|
this._offlineManager = null;
|
|
3896
|
-
|
|
4522
|
+
/** AIP identity for auto-signing (v1.8.0 S1) */
|
|
4523
|
+
this._identity = null;
|
|
4524
|
+
this._identityReady = null;
|
|
4525
|
+
const resolvedApiKey = resolveApiKey(config.apiKey);
|
|
4526
|
+
if (resolvedApiKey && !resolvedApiKey.startsWith("sk-prismer-") && !resolvedApiKey.startsWith("eyJ")) {
|
|
3897
4527
|
console.warn('Warning: API key should start with "sk-prismer-" (or "eyJ" for IM JWT)');
|
|
3898
4528
|
}
|
|
3899
|
-
this.apiKey =
|
|
4529
|
+
this.apiKey = resolvedApiKey;
|
|
3900
4530
|
const envUrl = ENVIRONMENTS[config.environment || "production"];
|
|
3901
|
-
this.baseUrl = (config.baseUrl || envUrl).replace(/\/$/, "");
|
|
4531
|
+
this.baseUrl = (resolveBaseUrl(config.baseUrl) || envUrl).replace(/\/$/, "");
|
|
3902
4532
|
this.timeout = config.timeout || 3e4;
|
|
3903
4533
|
this.fetchFn = config.fetch || fetch;
|
|
3904
4534
|
this.imAgent = config.imAgent;
|
|
4535
|
+
if (config.identity) {
|
|
4536
|
+
if (config.identity === "auto" && this.apiKey) {
|
|
4537
|
+
this._identityReady = import_aip_sdk.AIPIdentity.fromApiKey(this.apiKey).then((id) => {
|
|
4538
|
+
this._identity = id;
|
|
4539
|
+
}).catch((err) => console.warn("[PrismerSDK] Identity init failed:", err));
|
|
4540
|
+
} else if (typeof config.identity === "object" && config.identity.privateKey) {
|
|
4541
|
+
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)));
|
|
4542
|
+
this._identityReady = import_aip_sdk.AIPIdentity.fromPrivateKey(keyBytes).then((id) => {
|
|
4543
|
+
this._identity = id;
|
|
4544
|
+
}).catch((err) => console.warn("[PrismerSDK] Identity init failed:", err));
|
|
4545
|
+
}
|
|
4546
|
+
}
|
|
3905
4547
|
if (config.offline) {
|
|
3906
4548
|
this._offlineManager = new OfflineManager(
|
|
3907
4549
|
config.offline.storage,
|
|
@@ -3912,14 +4554,60 @@ var PrismerClient = class {
|
|
|
3912
4554
|
(err) => console.warn("[PrismerSDK] Offline storage init failed:", err)
|
|
3913
4555
|
);
|
|
3914
4556
|
}
|
|
3915
|
-
|
|
4557
|
+
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);
|
|
4558
|
+
if (config.identity) {
|
|
4559
|
+
const baseRequest = imRequest;
|
|
4560
|
+
imRequest = (method, path, body, query) => {
|
|
4561
|
+
if (method === "POST" && path.includes("/messages") && body) {
|
|
4562
|
+
const b = body;
|
|
4563
|
+
if (!b.signature && !b.skipSigning) {
|
|
4564
|
+
const ready = this._identityReady || Promise.resolve();
|
|
4565
|
+
return ready.then(() => {
|
|
4566
|
+
if (this._identity) {
|
|
4567
|
+
return this._signAndSend(baseRequest, method, path, b, query);
|
|
4568
|
+
}
|
|
4569
|
+
return baseRequest(method, path, body, query);
|
|
4570
|
+
});
|
|
4571
|
+
}
|
|
4572
|
+
}
|
|
4573
|
+
return baseRequest(method, path, body, query);
|
|
4574
|
+
};
|
|
4575
|
+
}
|
|
3916
4576
|
this.im = new IMClient(
|
|
3917
4577
|
imRequest,
|
|
3918
4578
|
this.baseUrl,
|
|
3919
4579
|
this.fetchFn,
|
|
3920
4580
|
() => this._getAuthHeaders(),
|
|
3921
|
-
this._offlineManager
|
|
4581
|
+
this._offlineManager,
|
|
4582
|
+
config.community ?? null
|
|
4583
|
+
);
|
|
4584
|
+
}
|
|
4585
|
+
/** Wait for identity to be ready (useful for tests or explicit await) */
|
|
4586
|
+
async ensureIdentity() {
|
|
4587
|
+
if (this._identityReady) await this._identityReady;
|
|
4588
|
+
return this._identity;
|
|
4589
|
+
}
|
|
4590
|
+
/** Auto-sign a message body and send (v1.8.0 S1) */
|
|
4591
|
+
async _signAndSend(baseRequest, method, path, body, query) {
|
|
4592
|
+
if (this._identityReady) await this._identityReady;
|
|
4593
|
+
if (!this._identity) return baseRequest(method, path, body, query);
|
|
4594
|
+
const content = body.content || "";
|
|
4595
|
+
const contentHashBytes = new Uint8Array(
|
|
4596
|
+
await crypto.subtle.digest("SHA-256", new TextEncoder().encode(content))
|
|
3922
4597
|
);
|
|
4598
|
+
const contentHash = Array.from(contentHashBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
4599
|
+
const timestamp = Date.now();
|
|
4600
|
+
const payload = `1|${this._identity.did}|${body.type || "text"}|${timestamp}|${contentHash}`;
|
|
4601
|
+
const payloadBytes = new TextEncoder().encode(payload);
|
|
4602
|
+
const signature = await this._identity.sign(payloadBytes);
|
|
4603
|
+
return baseRequest(method, path, {
|
|
4604
|
+
...body,
|
|
4605
|
+
secVersion: 1,
|
|
4606
|
+
senderDid: this._identity.did,
|
|
4607
|
+
contentHash,
|
|
4608
|
+
signature,
|
|
4609
|
+
signedAt: timestamp
|
|
4610
|
+
}, query);
|
|
3923
4611
|
}
|
|
3924
4612
|
/** Build auth headers for raw HTTP requests (used by file upload) */
|
|
3925
4613
|
_getAuthHeaders() {
|
|
@@ -4055,9 +4743,11 @@ function createClient(config) {
|
|
|
4055
4743
|
}
|
|
4056
4744
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4057
4745
|
0 && (module.exports = {
|
|
4746
|
+
AIPIdentity,
|
|
4058
4747
|
AccountClient,
|
|
4059
4748
|
AttachmentQueue,
|
|
4060
4749
|
BindingsClient,
|
|
4750
|
+
CommunityHub,
|
|
4061
4751
|
ContactsClient,
|
|
4062
4752
|
ConversationsClient,
|
|
4063
4753
|
CreditsClient,
|
|
@@ -4073,6 +4763,7 @@ function createClient(config) {
|
|
|
4073
4763
|
IMRealtimeClient,
|
|
4074
4764
|
IdentityClient,
|
|
4075
4765
|
IndexedDBStorage,
|
|
4766
|
+
KnowledgeLinkClient,
|
|
4076
4767
|
MemoryClient,
|
|
4077
4768
|
MemoryStorage,
|
|
4078
4769
|
MessagesClient,
|
|
@@ -4094,5 +4785,7 @@ function createClient(config) {
|
|
|
4094
4785
|
encryptContext,
|
|
4095
4786
|
encryptFile,
|
|
4096
4787
|
encryptForSend,
|
|
4097
|
-
extractSignals
|
|
4788
|
+
extractSignals,
|
|
4789
|
+
guessMimeType,
|
|
4790
|
+
safeSlug
|
|
4098
4791
|
});
|