indelible-mcp 4.8.1 → 4.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +155 -21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "indelible-mcp",
3
- "version": "4.8.1",
3
+ "version": "4.8.2",
4
4
  "description": "Blockchain-backed memory and code storage for Claude Code. Save AI conversations and source code permanently on BSV.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/index.js CHANGED
@@ -1590,6 +1590,14 @@ function detectSaveFormat(payload) {
1590
1590
  if (payload && typeof payload.wrap_owner === "string" && payload.wrap_owner.length > 0) return "v2-wrapped";
1591
1591
  return "legacy";
1592
1592
  }
1593
+ function detectVaultFormat(payload) {
1594
+ if (!payload) return "legacy";
1595
+ return detectSaveFormat({
1596
+ wrap_owner: payload.wrap_owner,
1597
+ wrap_self: payload.wrap_self,
1598
+ wrap_version: payload.wrap_version
1599
+ });
1600
+ }
1593
1601
  function generateContentKey() {
1594
1602
  return randomBytes3(32);
1595
1603
  }
@@ -5244,12 +5252,41 @@ ${formatSaveReceipt(receipt)}`
5244
5252
  // mcp-server/tools/load_file.js
5245
5253
  init_config_customer();
5246
5254
  init_crypto();
5247
- init_spv();
5248
5255
  import { gunzipSync as gunzipSync2 } from "zlib";
5249
5256
  import { writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
5250
5257
  import { existsSync as existsSync11 } from "fs";
5251
5258
  import { dirname as dirname2 } from "path";
5252
- import { Transaction as Transaction6 } from "@bsv/sdk";
5259
+ import { Transaction as Transaction6, PrivateKey as PrivateKey8, CompletedProtoWallet as CompletedProtoWallet3 } from "@bsv/sdk";
5260
+ init_spv();
5261
+ async function unwrapVaultV3(payload, wif) {
5262
+ let w = null;
5263
+ try {
5264
+ w = new CompletedProtoWallet3(PrivateKey8.fromWif(wif));
5265
+ } catch {
5266
+ w = null;
5267
+ }
5268
+ if (!w) return { ok: false, code: "WALLET_UNAVAILABLE" };
5269
+ if (payload.owner_pubkey) {
5270
+ let idPub = null;
5271
+ try {
5272
+ idPub = (await w.getPublicKey({ identityKey: true }))?.publicKey || null;
5273
+ } catch {
5274
+ }
5275
+ if (idPub && idPub !== payload.owner_pubkey) {
5276
+ if (payload.wrap_recovery) {
5277
+ const rr = await unwrapRecoveryV3(payload.wrap_recovery, w, payload.key_id, payload.owner_pubkey);
5278
+ if (rr.ok) return { ok: true, contentKey: rr.contentKey };
5279
+ }
5280
+ return { ok: false, code: "WALLET_UNAVAILABLE" };
5281
+ }
5282
+ }
5283
+ let r = await unwrapForSelfV3(payload.wrap_self, w, payload.key_id);
5284
+ if (!r.ok && r.reason === "corrupt" && payload.wrap_recovery && payload.owner_pubkey) {
5285
+ r = await unwrapRecoveryV3(payload.wrap_recovery, w, payload.key_id, payload.owner_pubkey);
5286
+ }
5287
+ if (!r.ok) return { ok: false, code: r.reason === "wallet_unavailable" ? "WALLET_UNAVAILABLE" : "V3_CORRUPT" };
5288
+ return { ok: true, contentKey: r.contentKey };
5289
+ }
5253
5290
  async function fetchJsonPayload(txId) {
5254
5291
  const cleanTxId = txId.trim();
5255
5292
  try {
@@ -5299,6 +5336,47 @@ async function loadFile(txId, options = {}) {
5299
5336
  if (payload.protocol !== "indelible.file") {
5300
5337
  return { success: false, error: `Not an indelible.file tx (got: ${payload.protocol || "unknown"})` };
5301
5338
  }
5339
+ const vfmt = detectVaultFormat(payload);
5340
+ if (vfmt === "v3-wallet") {
5341
+ const uv = await unwrapVaultV3(payload, wif);
5342
+ if (!uv.ok) {
5343
+ return { success: false, code: uv.code, error: uv.code === "WALLET_UNAVAILABLE" ? "wallet-native file: not readable from this box \u2014 its key lives in an external wallet (open it in the web app), or this WIF is not the owner" : "wallet-native file: unwrap failed (corrupt or wrong wallet)" };
5344
+ }
5345
+ let outBuf2;
5346
+ try {
5347
+ const dec = decryptWithKey(payload.encrypted, Buffer.from(uv.contentKey));
5348
+ outBuf2 = dec.startsWith("gz:") ? gunzipSync2(Buffer.from(dec.slice(3), "base64")) : Buffer.from(dec, "utf8");
5349
+ } catch (err) {
5350
+ return { success: false, code: "V3_CORRUPT", error: `wallet-native decrypt failed: ${err.message}` };
5351
+ }
5352
+ const contentHash2 = sha256(outBuf2);
5353
+ let meta = {};
5354
+ try {
5355
+ meta = JSON.parse(decryptWithKey(payload.meta_enc, Buffer.from(uv.contentKey)));
5356
+ } catch {
5357
+ }
5358
+ const verified2 = meta.sha256 ? contentHash2 === meta.sha256 : payload.content_hash ? contentHash2 === payload.content_hash.replace("sha256:", "") : null;
5359
+ if (options.outputPath) {
5360
+ const dir = dirname2(options.outputPath);
5361
+ if (!existsSync11(dir)) await mkdir4(dir, { recursive: true });
5362
+ await writeFile4(options.outputPath, outBuf2);
5363
+ }
5364
+ const isBinary2 = payload.enc === "b64";
5365
+ return {
5366
+ success: true,
5367
+ txId,
5368
+ wallet_native: true,
5369
+ filename: meta.filename,
5370
+ path: meta.path,
5371
+ size: payload.size,
5372
+ content_hash: payload.content_hash,
5373
+ verified: verified2,
5374
+ content: isBinary2 ? `[binary file: ${outBuf2.length} bytes${options.outputPath ? ", written to disk" : ""}]` : outBuf2.toString("utf8")
5375
+ };
5376
+ }
5377
+ if (vfmt === "v3-malformed") {
5378
+ return { success: false, code: "V3_CORRUPT", error: "file carries malformed v3 wrap fields \u2014 failing closed" };
5379
+ }
5302
5380
  let encrypted;
5303
5381
  if (payload._chunks) {
5304
5382
  let encryptedParts = "";
@@ -5363,11 +5441,12 @@ async function loadFile(txId, options = {}) {
5363
5441
  // mcp-server/tools/load_project.js
5364
5442
  init_config_customer();
5365
5443
  init_crypto();
5366
- init_spv();
5444
+ import { gunzipSync as gunzipSync3 } from "zlib";
5367
5445
  import { mkdir as mkdir5 } from "fs/promises";
5368
5446
  import { existsSync as existsSync12 } from "fs";
5369
5447
  import { join as join11, dirname as dirname3 } from "path";
5370
5448
  import { Transaction as Transaction7 } from "@bsv/sdk";
5449
+ init_spv();
5371
5450
  async function fetchJsonPayload2(txId) {
5372
5451
  const cleanTxId = txId.trim();
5373
5452
  try {
@@ -5420,6 +5499,61 @@ async function loadProject(txId, options = {}) {
5420
5499
  if (!manifest.files || !Array.isArray(manifest.files)) {
5421
5500
  return { success: false, error: "Manifest has no files array" };
5422
5501
  }
5502
+ if (detectVaultFormat(manifest) === "v3-wallet") {
5503
+ const uv = await unwrapVaultV3(manifest, wif);
5504
+ if (!uv.ok) {
5505
+ return { success: false, code: uv.code, error: uv.code === "WALLET_UNAVAILABLE" ? "wallet-native project: not readable from this box \u2014 open it in the web app with the wallet that saved it" : "wallet-native project: manifest unwrap failed (corrupt or wrong wallet)" };
5506
+ }
5507
+ const ck = Buffer.from(uv.contentKey);
5508
+ const dk = (s) => decryptWithKey(s, ck);
5509
+ let sealed = {};
5510
+ try {
5511
+ sealed = JSON.parse(dk(manifest.name_enc));
5512
+ } catch {
5513
+ }
5514
+ const v3Files = [];
5515
+ const v3Errors = [];
5516
+ for (const fileRef of manifest.files) {
5517
+ try {
5518
+ const meta = JSON.parse(dk(fileRef.meta_enc));
5519
+ const dec = dk(fileRef.encrypted);
5520
+ const outBuf = dec.startsWith("gz:") ? gunzipSync3(Buffer.from(dec.slice(3), "base64")) : Buffer.from(dec, "utf8");
5521
+ const verified = meta.sha256 ? sha256(outBuf) === meta.sha256 : null;
5522
+ if (verified === false) {
5523
+ v3Errors.push({ path: "(tampered)", error: "content hash mismatch vs sealed meta" });
5524
+ continue;
5525
+ }
5526
+ if (options.outputDir) {
5527
+ const outputPath = join11(options.outputDir, sealed.name || "project", meta.path);
5528
+ const dir = dirname3(outputPath);
5529
+ if (!existsSync12(dir)) await mkdir5(dir, { recursive: true });
5530
+ const { writeFile: writeFile5 } = await import("fs/promises");
5531
+ await writeFile5(outputPath, outBuf);
5532
+ }
5533
+ const isBinary = fileRef.enc === "b64";
5534
+ v3Files.push({ path: meta.path, size: fileRef.size, verified, content: options.outputDir ? void 0 : isBinary ? `[binary: ${outBuf.length} bytes]` : outBuf.toString("utf8") });
5535
+ } catch (err) {
5536
+ v3Errors.push({ path: "(locked)", error: err.message });
5537
+ }
5538
+ }
5539
+ const setOk = sealed.file_count == null || sealed.file_count === manifest.files.length;
5540
+ return {
5541
+ success: true,
5542
+ txId,
5543
+ wallet_native: true,
5544
+ projectName: sealed.name,
5545
+ fileCount: sealed.file_count ?? manifest.files.length,
5546
+ set_integrity: setOk ? "ok" : "FAILED",
5547
+ totalSize: manifest.total_size,
5548
+ loadedFiles: v3Files.length,
5549
+ files: v3Files,
5550
+ errors: v3Errors.length ? v3Errors : void 0,
5551
+ message: `Project "${sealed.name || ""}" loaded (wallet-native): ${v3Files.length}/${sealed.file_count ?? manifest.files.length} files${setOk ? "" : " \u2014 \u26A0\uFE0F SET-INTEGRITY MISMATCH"}`
5552
+ };
5553
+ }
5554
+ if (detectVaultFormat(manifest) === "v3-malformed") {
5555
+ return { success: false, code: "V3_CORRUPT", error: "project manifest carries malformed v3 wrap fields \u2014 failing closed" };
5556
+ }
5423
5557
  const isBundle = manifest.protocol === "indelible.project-bundle";
5424
5558
  const projectName = manifest.name_enc ? decrypt(manifest.name_enc, wif) : manifest.name;
5425
5559
  const loadedFiles = [];
@@ -5496,20 +5630,20 @@ async function updateVaultIndex() {
5496
5630
  if (!_gate.ok) return { success: false, gated: true, plan: _gate.plan, upsell: _gate.upsell, error: _gate.error };
5497
5631
  const files = config.file_txids || [];
5498
5632
  const projects = config.project_txids || [];
5499
- const encFiles = files.map((f) => ({
5633
+ const encFiles = files.map((f) => f.filename ? {
5500
5634
  txid: f.txId,
5501
- filename_enc: encrypt(f.filename || "", wif),
5502
- path_enc: encrypt(f.path || "", wif),
5635
+ filename_enc: encrypt(f.filename, wif),
5636
+ path_enc: encrypt(f.path || f.filename, wif),
5503
5637
  size: f.size || 0,
5504
5638
  timestamp: f.timestamp || null
5505
- }));
5506
- const encProjects = projects.map((p) => ({
5639
+ } : { txid: f.txId, size: f.size || 0, timestamp: f.timestamp || null, wrap: "v3" });
5640
+ const encProjects = projects.map((p) => p.name ? {
5507
5641
  txid: p.txId,
5508
- name_enc: encrypt(p.name || "", wif),
5642
+ name_enc: encrypt(p.name, wif),
5509
5643
  fileCount: p.fileCount || 0,
5510
5644
  totalSize: p.totalSize || 0,
5511
5645
  timestamp: p.timestamp || null
5512
- }));
5646
+ } : { txid: p.txId, fileCount: p.fileCount || 0, totalSize: p.totalSize || 0, timestamp: p.timestamp || null, wrap: "v3" });
5513
5647
  const payload = {
5514
5648
  protocol: "indelible.vault-index",
5515
5649
  version: 1,
@@ -6168,7 +6302,7 @@ ${context}` });
6168
6302
  init_config_customer();
6169
6303
  init_spv();
6170
6304
  init_utxo_cache();
6171
- import { PrivateKey as PrivateKey8 } from "@bsv/sdk";
6305
+ import { PrivateKey as PrivateKey9 } from "@bsv/sdk";
6172
6306
  var DEFAULT_MAX_SATS = 1e4;
6173
6307
  async function x402Fetch({ url, method = "GET", headers = {}, body, maxSats }) {
6174
6308
  const cap = typeof maxSats === "number" && maxSats > 0 ? maxSats : DEFAULT_MAX_SATS;
@@ -6229,7 +6363,7 @@ async function x402Fetch({ url, method = "GET", headers = {}, body, maxSats }) {
6229
6363
  pricing
6230
6364
  };
6231
6365
  }
6232
- const privateKey = PrivateKey8.fromWif(wif);
6366
+ const privateKey = PrivateKey9.fromWif(wif);
6233
6367
  const address = privateKey.toPublicKey().toAddress();
6234
6368
  let utxos;
6235
6369
  try {
@@ -6314,10 +6448,10 @@ init_utxo_cache();
6314
6448
  init_config_customer();
6315
6449
  init_api_client();
6316
6450
  import crypto from "crypto";
6317
- import { PrivateKey as PrivateKey9 } from "@bsv/sdk";
6451
+ import { PrivateKey as PrivateKey10 } from "@bsv/sdk";
6318
6452
  var GOALS_PATH2 = goalsPath("");
6319
6453
  function deriveAddress(wif) {
6320
- return PrivateKey9.fromWif(wif).toAddress().toString();
6454
+ return PrivateKey10.fromWif(wif).toAddress().toString();
6321
6455
  }
6322
6456
  function encryptGoals(goalsData, wif) {
6323
6457
  const key = crypto.createHash("sha256").update(wif).digest();
@@ -6584,12 +6718,12 @@ init_spv();
6584
6718
  init_api_client();
6585
6719
  import {
6586
6720
  Transaction as Transaction8,
6587
- PrivateKey as PrivateKey10,
6721
+ PrivateKey as PrivateKey11,
6588
6722
  P2PKH as P2PKH3,
6589
6723
  SatoshisPerKilobyte as SatoshisPerKilobyte3,
6590
6724
  LockingScript as LockingScript3,
6591
6725
  OP as OP3,
6592
- CompletedProtoWallet as CompletedProtoWallet3,
6726
+ CompletedProtoWallet as CompletedProtoWallet4,
6593
6727
  AuthFetch as AuthFetch2
6594
6728
  } from "@bsv/sdk";
6595
6729
  var SHARE_DUST_SATS = 546;
@@ -6664,14 +6798,14 @@ async function shareSession({ session_txid, recipient_addr, expires_at = null })
6664
6798
  if (!wif) return { success: false, error: "wallet not configured \u2014 run setup_wallet first" };
6665
6799
  const _gate = await requireProForWrite(config.address);
6666
6800
  if (!_gate.ok) return { success: false, gated: true, plan: _gate.plan, upsell: _gate.upsell, error: _gate.error };
6667
- const ownerPriv = PrivateKey10.fromWif(wif);
6801
+ const ownerPriv = PrivateKey11.fromWif(wif);
6668
6802
  const ownerAddr = ownerPriv.toPublicKey().toAddress();
6669
6803
  const payload = await fetchScopePayload(session_txid);
6670
6804
  if (!payload) return { success: false, error: `could not fetch scope tx ${session_txid}` };
6671
6805
  const fmt = detectSaveFormat({ wrap_owner: payload.wrap_owner, wrap_self: payload.wrap_self, wrap_version: payload.wrap_version });
6672
6806
  let contentKey;
6673
6807
  if (fmt === "v3-wallet") {
6674
- const rv3 = await unwrapForSelfV3(payload.wrap_self, new CompletedProtoWallet3(ownerPriv), payload.key_id);
6808
+ const rv3 = await unwrapForSelfV3(payload.wrap_self, new CompletedProtoWallet4(ownerPriv), payload.key_id);
6675
6809
  if (!rv3.ok) {
6676
6810
  return { success: false, error: rv3.reason === "wallet_unavailable" ? "failed to unwrap own v3 content_key: no wallet available" : `failed to unwrap own v3 content_key: ${rv3.reason} (wallet-native saves made by an external wallet can only be shared from the web app)` };
6677
6811
  }
@@ -6764,7 +6898,7 @@ async function indexShareOnBridge(txId, envelope, ownerPriv) {
6764
6898
  // the envelope stamps ISO; the bridge index demands ms-epoch (putShare validation)
6765
6899
  issued_at: typeof envelope.issued_at === "number" ? envelope.issued_at : Date.parse(envelope.issued_at)
6766
6900
  };
6767
- const af = new AuthFetch2(new CompletedProtoWallet3(ownerPriv));
6901
+ const af = new AuthFetch2(new CompletedProtoWallet4(ownerPriv));
6768
6902
  let lastErr = null;
6769
6903
  for (const b of bridges.slice(0, 3)) {
6770
6904
  try {
@@ -7083,7 +7217,7 @@ Commands:
7083
7217
  }
7084
7218
  function printHelp() {
7085
7219
  console.log(`
7086
- Indelible MCP \u2014 Blockchain memory for Claude Code (v4.8.1)
7220
+ Indelible MCP \u2014 Blockchain memory for Claude Code (v4.8.2)
7087
7221
 
7088
7222
  Setup:
7089
7223
  indelible-mcp setup --wif=KEY --pin=PIN Import and encrypt your private key
@@ -7302,7 +7436,7 @@ function readStdin() {
7302
7436
  }
7303
7437
  var SERVER_INFO = {
7304
7438
  name: "indelible",
7305
- version: "4.8.1",
7439
+ version: "4.8.2",
7306
7440
  description: "Blockchain-backed memory and code storage for Claude Code"
7307
7441
  };
7308
7442
  var TOOLS = [