@prismer/sdk 1.7.3 → 1.7.4

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/dist/index.mjs CHANGED
@@ -1207,6 +1207,28 @@ var ENVIRONMENTS = {
1207
1207
  production: "https://prismer.cloud"
1208
1208
  };
1209
1209
 
1210
+ // src/aip.ts
1211
+ import {
1212
+ AIPIdentity
1213
+ } from "@prismer/aip-sdk";
1214
+ import {
1215
+ publicKeyToDIDKey,
1216
+ didKeyToPublicKey,
1217
+ validateDIDKey
1218
+ } from "@prismer/aip-sdk";
1219
+ import {
1220
+ buildCredential,
1221
+ buildPresentation,
1222
+ verifyCredential,
1223
+ verifyPresentation
1224
+ } from "@prismer/aip-sdk";
1225
+ import {
1226
+ buildDelegation,
1227
+ buildEphemeralDelegation,
1228
+ verifyDelegation,
1229
+ verifyEphemeralDelegation
1230
+ } from "@prismer/aip-sdk";
1231
+
1210
1232
  // src/storage.ts
1211
1233
  var MemoryStorage = class {
1212
1234
  constructor() {
@@ -2091,20 +2113,27 @@ var PBKDF2_ITERATIONS = 1e5;
2091
2113
  var SALT_LENGTH = 16;
2092
2114
  var IV_LENGTH = 12;
2093
2115
  var KEY_LENGTH = 256;
2094
- var E2EEncryption = class {
2116
+ var _E2EEncryption = class _E2EEncryption {
2095
2117
  constructor() {
2096
2118
  this.masterKey = null;
2097
2119
  this.keyPair = null;
2098
2120
  this.sessionKeys = /* @__PURE__ */ new Map();
2099
2121
  // conversationId → AES key
2100
2122
  this.salt = null;
2123
+ // ─── Pipeline Functions ──────────────────────────────────
2124
+ this.messageCount = 0;
2125
+ this.lastRotation = Date.now();
2101
2126
  }
2102
2127
  /**
2103
2128
  * Initialize encryption with user passphrase.
2104
2129
  * Derives a master key via PBKDF2 and generates an ECDH key pair.
2130
+ *
2131
+ * @param passphrase - User passphrase for master key derivation
2132
+ * @param salt - Optional Base64-encoded salt. If omitted, a random 16-byte salt is generated.
2133
+ * Store the salt (via exportSalt()) so you can re-derive the same master key later.
2105
2134
  */
2106
- async init(passphrase) {
2107
- this.salt = getRandomValues(new Uint8Array(SALT_LENGTH));
2135
+ async init(passphrase, salt) {
2136
+ this.salt = salt ? new Uint8Array(base64ToArrayBuffer(salt)) : getRandomValues(new Uint8Array(SALT_LENGTH));
2108
2137
  const passphraseKey = await subtle().importKey(
2109
2138
  "raw",
2110
2139
  new TextEncoder().encode(passphrase),
@@ -2115,7 +2144,7 @@ var E2EEncryption = class {
2115
2144
  this.masterKey = await subtle().deriveKey(
2116
2145
  {
2117
2146
  name: "PBKDF2",
2118
- salt: this.salt,
2147
+ salt: new Uint8Array(this.salt.buffer, this.salt.byteOffset, this.salt.byteLength).buffer,
2119
2148
  iterations: PBKDF2_ITERATIONS,
2120
2149
  hash: "SHA-256"
2121
2150
  },
@@ -2130,6 +2159,14 @@ var E2EEncryption = class {
2130
2159
  ["deriveKey"]
2131
2160
  );
2132
2161
  }
2162
+ /**
2163
+ * Export the salt as Base64 string for persistent storage.
2164
+ * You must store this and pass it back to init() to re-derive the same master key.
2165
+ */
2166
+ exportSalt() {
2167
+ if (!this.salt) throw new Error("E2E not initialized. Call init() first.");
2168
+ return arrayBufferToBase64(this.salt.buffer);
2169
+ }
2133
2170
  /**
2134
2171
  * Export public key for sharing with conversation peers.
2135
2172
  */
@@ -2242,8 +2279,93 @@ var E2EEncryption = class {
2242
2279
  this.keyPair = null;
2243
2280
  this.sessionKeys.clear();
2244
2281
  this.salt = null;
2282
+ this.messageCount = 0;
2283
+ }
2284
+ /**
2285
+ * High-level encrypt-for-send pipeline.
2286
+ * Encrypts content, builds metadata, and handles key rotation.
2287
+ *
2288
+ * Returns { encryptedContent, metadata } ready to send.
2289
+ */
2290
+ async encryptForSend(conversationId, content) {
2291
+ if (!this.hasSessionKey(conversationId)) {
2292
+ throw new Error("No session key for this conversation. Call deriveSessionKey() first.");
2293
+ }
2294
+ const needsRotation = this.shouldRotateKey();
2295
+ const encryptedContent = await this.encrypt(conversationId, content);
2296
+ this.messageCount++;
2297
+ return {
2298
+ encryptedContent,
2299
+ metadata: {
2300
+ encrypted: true,
2301
+ encryptionVersion: 1,
2302
+ ...needsRotation && { keyRotationRequested: true }
2303
+ }
2304
+ };
2305
+ }
2306
+ /**
2307
+ * High-level decrypt-on-receive pipeline.
2308
+ * Decrypts content and validates metadata.
2309
+ */
2310
+ async decryptOnReceive(conversationId, encryptedContent, metadata) {
2311
+ if (!this.hasSessionKey(conversationId)) {
2312
+ throw new Error("No session key for this conversation. Call deriveSessionKey() first.");
2313
+ }
2314
+ return this.decrypt(conversationId, encryptedContent);
2315
+ }
2316
+ /**
2317
+ * High-level file encryption pipeline.
2318
+ */
2319
+ async encryptFile(conversationId, fileData) {
2320
+ const base64Data = arrayBufferToBase64(fileData);
2321
+ const encryptedData = await this.encrypt(conversationId, base64Data);
2322
+ return {
2323
+ encryptedData,
2324
+ metadata: {
2325
+ encrypted: true,
2326
+ encryptionVersion: 1,
2327
+ fileEncrypted: true
2328
+ }
2329
+ };
2330
+ }
2331
+ /**
2332
+ * High-level file decryption pipeline.
2333
+ */
2334
+ async decryptFile(conversationId, encryptedData) {
2335
+ const base64Data = await this.decrypt(conversationId, encryptedData);
2336
+ return base64ToArrayBuffer(base64Data);
2337
+ }
2338
+ /**
2339
+ * Check if key rotation is needed (1000 messages or 24 hours).
2340
+ */
2341
+ shouldRotateKey() {
2342
+ if (this.messageCount >= _E2EEncryption.KEY_ROTATION_THRESHOLD) {
2343
+ return true;
2344
+ }
2345
+ if (Date.now() - this.lastRotation >= _E2EEncryption.KEY_ROTATION_INTERVAL_MS) {
2346
+ return true;
2347
+ }
2348
+ return false;
2349
+ }
2350
+ /**
2351
+ * Perform key rotation: generate new ECDH keypair and reset counters.
2352
+ * The caller is responsible for re-exchanging keys with peers.
2353
+ */
2354
+ async rotateKeys() {
2355
+ this.keyPair = await subtle().generateKey(
2356
+ { name: "ECDH", namedCurve: "P-256" },
2357
+ false,
2358
+ ["deriveKey"]
2359
+ );
2360
+ this.messageCount = 0;
2361
+ this.lastRotation = Date.now();
2362
+ this.sessionKeys.clear();
2363
+ return this.exportPublicKey();
2245
2364
  }
2246
2365
  };
2366
+ _E2EEncryption.KEY_ROTATION_THRESHOLD = 1e3;
2367
+ _E2EEncryption.KEY_ROTATION_INTERVAL_MS = 24 * 60 * 60 * 1e3;
2368
+ var E2EEncryption = _E2EEncryption;
2247
2369
  function arrayBufferToBase64(buffer) {
2248
2370
  if (typeof btoa !== "undefined") {
2249
2371
  const bytes = new Uint8Array(buffer);
@@ -3292,11 +3414,11 @@ var EvolutionClient = class {
3292
3414
  }
3293
3415
  /** Delete a gene */
3294
3416
  async deleteGene(geneId) {
3295
- return this._r("DELETE", `/api/im/evolution/genes/${geneId}`);
3417
+ return this._r("DELETE", `/api/im/evolution/genes/${encodeURIComponent(geneId)}`);
3296
3418
  }
3297
3419
  /** Publish a gene. Pass skipCanary=true to bypass canary validation (MVP/admin). */
3298
3420
  async publishGene(geneId, options) {
3299
- return this._r("POST", `/api/im/evolution/genes/${geneId}/publish`, options?.skipCanary ? { skipCanary: true } : void 0);
3421
+ return this._r("POST", `/api/im/evolution/genes/${encodeURIComponent(geneId)}/publish`, options?.skipCanary ? { skipCanary: true } : void 0);
3300
3422
  }
3301
3423
  /** Import a published gene */
3302
3424
  async importGene(geneId) {
@@ -3382,6 +3504,14 @@ var EvolutionClient = class {
3382
3504
  async getSkillContent(slugOrId) {
3383
3505
  return this._r("GET", `/api/im/skills/${encodeURIComponent(slugOrId)}/content`);
3384
3506
  }
3507
+ /** Create/submit a community skill */
3508
+ async createSkill(input) {
3509
+ return this._r("POST", "/api/im/skills", input);
3510
+ }
3511
+ /** Star a skill (increment community rating) */
3512
+ async starSkill(skillId) {
3513
+ return this._r("POST", `/api/im/skills/${encodeURIComponent(skillId)}/star`);
3514
+ }
3385
3515
  /**
3386
3516
  * Install a skill and write SKILL.md to local filesystem.
3387
3517
  * Combines cloud install + local file sync for Claude Code / OpenClaw / OpenCode.
@@ -3444,8 +3574,8 @@ var EvolutionClient = class {
3444
3574
  async uninstallSkillLocal(slugOrId) {
3445
3575
  const result = await this.uninstallSkill(slugOrId);
3446
3576
  const removedPaths = [];
3447
- const safeSlug = slugOrId.replace(/[\/\\]/g, "").replace(/\.\./g, "");
3448
- if (!safeSlug) return { ...result, data: { uninstalled: result.data?.uninstalled ?? false, removedPaths } };
3577
+ const slug = safeSlug(slugOrId);
3578
+ if (!slug) return { ...result, data: { uninstalled: result.data?.uninstalled ?? false, removedPaths } };
3449
3579
  try {
3450
3580
  const fs = await import("fs");
3451
3581
  const path = await import("path");
@@ -3453,10 +3583,10 @@ var EvolutionClient = class {
3453
3583
  const home = os.homedir();
3454
3584
  const pluginBase = process.env.PRISMER_PLUGIN_DIR || path.join(home, ".claude", "plugins", "prismer");
3455
3585
  const dirs = [
3456
- path.join(home, ".claude", "skills", safeSlug),
3457
- path.join(home, ".openclaw", "skills", safeSlug),
3458
- path.join(home, ".config", "opencode", "skills", safeSlug),
3459
- path.join(pluginBase, "skills", safeSlug)
3586
+ path.join(home, ".claude", "skills", slug),
3587
+ path.join(home, ".openclaw", "skills", slug),
3588
+ path.join(home, ".config", "opencode", "skills", slug),
3589
+ path.join(pluginBase, "skills", slug)
3460
3590
  ];
3461
3591
  for (const dir of dirs) {
3462
3592
  try {
@@ -3566,6 +3696,9 @@ var EvolutionClient = class {
3566
3696
  return this._r("POST", "/api/im/evolution/sync", body);
3567
3697
  }
3568
3698
  };
3699
+ function safeSlug(input) {
3700
+ return input.replace(/[\/\\]/g, "").replace(/\.\./g, "").replace(/\0/g, "");
3701
+ }
3569
3702
  function guessMimeType(fileName) {
3570
3703
  const ext = fileName.split(".").pop()?.toLowerCase() || "";
3571
3704
  const map = {
@@ -3982,6 +4115,7 @@ function createClient(config) {
3982
4115
  return new PrismerClient(config);
3983
4116
  }
3984
4117
  export {
4118
+ AIPIdentity,
3985
4119
  AccountClient,
3986
4120
  AttachmentQueue,
3987
4121
  BindingsClient,
@@ -4022,5 +4156,7 @@ export {
4022
4156
  encryptContext,
4023
4157
  encryptFile,
4024
4158
  encryptForSend,
4025
- extractSignals
4159
+ extractSignals,
4160
+ guessMimeType,
4161
+ safeSlug
4026
4162
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prismer/sdk",
3
- "version": "1.7.3",
3
+ "version": "1.7.4",
4
4
  "description": "Official TypeScript SDK for Prismer Cloud API",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -34,6 +34,7 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@iarna/toml": "^2.2.5",
37
+ "@prismer/aip-sdk": "file:../../aip/typescript",
37
38
  "commander": "^12.1.0"
38
39
  },
39
40
  "devDependencies": {