@hasna/contacts 0.6.2 → 0.6.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/cli/index.js CHANGED
@@ -4813,6 +4813,25 @@ async function handleExport(req) {
4813
4813
  }
4814
4814
  });
4815
4815
  }
4816
+ function handleDocumentFiles(req, segments) {
4817
+ const docId = segments[2];
4818
+ const sub = segments[3];
4819
+ if (!docId)
4820
+ return apiError("Document ID required");
4821
+ if (req.method !== "GET")
4822
+ return apiError("Method not allowed", 405);
4823
+ if (sub === "file") {
4824
+ const db = getDatabase();
4825
+ const row = db.query(`SELECT encrypted_file_path FROM contact_documents WHERE id = ?`).get(docId);
4826
+ if (!row?.encrypted_file_path || !existsSync4(row.encrypted_file_path)) {
4827
+ return new Response("No file attachment", { status: 404 });
4828
+ }
4829
+ return new Response(Bun.file(row.encrypted_file_path), {
4830
+ headers: { "Cache-Control": "public, max-age=3600" }
4831
+ });
4832
+ }
4833
+ return apiError("Use /api/documents/:id/file to get the attachment", 400);
4834
+ }
4816
4835
  async function handleImages(req, _url, segments) {
4817
4836
  const entityId = segments[2];
4818
4837
  if (!entityId)
@@ -4909,6 +4928,9 @@ function startServer(port) {
4909
4928
  case "images":
4910
4929
  response = await handleImages(req, url, segments);
4911
4930
  break;
4931
+ case "documents":
4932
+ response = handleDocumentFiles(req, segments);
4933
+ break;
4912
4934
  default:
4913
4935
  response = apiError("Not found", 404);
4914
4936
  }
@@ -6052,23 +6074,53 @@ var init_org_chart = __esm(() => {
6052
6074
  var exports_vault = {};
6053
6075
  __export(exports_vault, {
6054
6076
  unlockVault: () => unlockVault,
6077
+ storeFile: () => storeFile,
6055
6078
  requireVault: () => requireVault,
6056
6079
  lockVault: () => lockVault,
6057
6080
  isVaultUnlocked: () => isVaultUnlocked,
6058
6081
  isVaultInitialized: () => isVaultInitialized,
6059
6082
  initVault: () => initVault,
6060
6083
  getDocumentsDir: () => getDocumentsDir,
6061
- encryptFile: () => encryptFile,
6084
+ getDocumentFilePath: () => getDocumentFilePath,
6062
6085
  encrypt: () => encrypt,
6063
6086
  decryptFile: () => decryptFile,
6064
6087
  decrypt: () => decrypt
6065
6088
  });
6066
- import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4 } from "fs";
6089
+ import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, unlinkSync as unlinkSync2 } from "fs";
6067
6090
  import { join as join5 } from "path";
6068
6091
  import { createCipheriv, createDecipheriv, randomBytes, pbkdf2Sync, createHash } from "crypto";
6069
6092
  function deriveKey(passphrase, salt) {
6070
6093
  return pbkdf2Sync(passphrase, salt, 1e5, 32, "sha512");
6071
6094
  }
6095
+ function saveSession(key) {
6096
+ const session = {
6097
+ key: key.toString("hex"),
6098
+ expires_at: new Date(Date.now() + SESSION_TTL_MS).toISOString()
6099
+ };
6100
+ writeFileSync3(VAULT_SESSION, JSON.stringify(session), { mode: 384 });
6101
+ }
6102
+ function loadSession() {
6103
+ if (!existsSync5(VAULT_SESSION))
6104
+ return null;
6105
+ try {
6106
+ const session = JSON.parse(readFileSync3(VAULT_SESSION, "utf-8"));
6107
+ if (new Date(session.expires_at).getTime() < Date.now()) {
6108
+ try {
6109
+ unlinkSync2(VAULT_SESSION);
6110
+ } catch {}
6111
+ return null;
6112
+ }
6113
+ return Buffer.from(session.key, "hex");
6114
+ } catch {
6115
+ return null;
6116
+ }
6117
+ }
6118
+ function clearSession() {
6119
+ try {
6120
+ if (existsSync5(VAULT_SESSION))
6121
+ unlinkSync2(VAULT_SESSION);
6122
+ } catch {}
6123
+ }
6072
6124
  function initVault(passphrase) {
6073
6125
  if (!existsSync5(VAULT_DIR))
6074
6126
  mkdirSync4(VAULT_DIR, { recursive: true });
@@ -6080,6 +6132,7 @@ function initVault(passphrase) {
6080
6132
  const config = { salt: salt.toString("hex"), key_hash: keyHash, created_at: new Date().toISOString() };
6081
6133
  writeFileSync3(VAULT_CONFIG, JSON.stringify(config, null, 2));
6082
6134
  _derivedKey = key;
6135
+ saveSession(key);
6083
6136
  }
6084
6137
  function isVaultInitialized() {
6085
6138
  return existsSync5(VAULT_CONFIG);
@@ -6094,18 +6147,32 @@ function unlockVault(passphrase) {
6094
6147
  if (keyHash !== config.key_hash)
6095
6148
  return false;
6096
6149
  _derivedKey = key;
6150
+ saveSession(key);
6097
6151
  return true;
6098
6152
  }
6099
6153
  function lockVault() {
6100
6154
  _derivedKey = null;
6155
+ clearSession();
6101
6156
  }
6102
6157
  function isVaultUnlocked() {
6103
- return _derivedKey !== null;
6158
+ if (_derivedKey)
6159
+ return true;
6160
+ const sessionKey = loadSession();
6161
+ if (sessionKey) {
6162
+ _derivedKey = sessionKey;
6163
+ return true;
6164
+ }
6165
+ return false;
6104
6166
  }
6105
6167
  function requireVault() {
6106
- if (!_derivedKey)
6107
- throw new Error("Vault is locked. Unlock with 'contacts vault unlock' or vault_unlock MCP tool first.");
6108
- return _derivedKey;
6168
+ if (_derivedKey)
6169
+ return _derivedKey;
6170
+ const sessionKey = loadSession();
6171
+ if (sessionKey) {
6172
+ _derivedKey = sessionKey;
6173
+ return _derivedKey;
6174
+ }
6175
+ throw new Error("Vault is locked. Unlock with 'contacts vault unlock --passphrase <pass>' or vault_unlock MCP tool first.");
6109
6176
  }
6110
6177
  function encrypt(plaintext) {
6111
6178
  const key = requireVault();
@@ -6127,20 +6194,23 @@ function decrypt(ciphertext, iv) {
6127
6194
  decrypted += decipher.final("utf8");
6128
6195
  return decrypted;
6129
6196
  }
6130
- function encryptFile(sourcePath, entityId) {
6131
- const key = requireVault();
6197
+ function storeFile(sourcePath, entityId) {
6132
6198
  if (!existsSync5(DOCUMENTS_DIR))
6133
6199
  mkdirSync4(DOCUMENTS_DIR, { recursive: true });
6200
+ const ext = sourcePath.split(".").pop() || "bin";
6201
+ const destPath = join5(DOCUMENTS_DIR, `${entityId}.${ext}`);
6134
6202
  const data = readFileSync3(sourcePath);
6135
- const iv = randomBytes(16);
6136
- const cipher = createCipheriv("aes-256-gcm", key, iv);
6137
- const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
6138
- const authTag = cipher.getAuthTag();
6139
- const destPath = join5(DOCUMENTS_DIR, `${entityId}.enc`);
6140
- const output = Buffer.concat([iv, authTag, encrypted]);
6141
- writeFileSync3(destPath, output);
6203
+ writeFileSync3(destPath, data);
6142
6204
  return destPath;
6143
6205
  }
6206
+ function getDocumentFilePath(entityId) {
6207
+ if (!existsSync5(DOCUMENTS_DIR))
6208
+ return null;
6209
+ const { readdirSync: readdirSync2 } = __require("fs");
6210
+ const files = readdirSync2(DOCUMENTS_DIR);
6211
+ const match = files.find((f) => f.startsWith(`${entityId}.`) && !f.endsWith(".enc"));
6212
+ return match ? join5(DOCUMENTS_DIR, match) : null;
6213
+ }
6144
6214
  function decryptFile(encPath) {
6145
6215
  const key = requireVault();
6146
6216
  const data = readFileSync3(encPath);
@@ -6156,11 +6226,13 @@ function getDocumentsDir() {
6156
6226
  mkdirSync4(DOCUMENTS_DIR, { recursive: true });
6157
6227
  return DOCUMENTS_DIR;
6158
6228
  }
6159
- var VAULT_DIR, VAULT_CONFIG, DOCUMENTS_DIR, _derivedKey = null;
6229
+ var VAULT_DIR, VAULT_CONFIG, VAULT_SESSION, DOCUMENTS_DIR, SESSION_TTL_MS, _derivedKey = null;
6160
6230
  var init_vault = __esm(() => {
6161
6231
  VAULT_DIR = join5(process.env["HOME"] || "~", ".contacts");
6162
6232
  VAULT_CONFIG = join5(VAULT_DIR, "vault.json");
6233
+ VAULT_SESSION = join5(VAULT_DIR, ".vault-session");
6163
6234
  DOCUMENTS_DIR = join5(VAULT_DIR, "documents");
6235
+ SESSION_TTL_MS = 30 * 60 * 1000;
6164
6236
  });
6165
6237
 
6166
6238
  // src/db/documents.ts
@@ -6172,17 +6244,17 @@ __export(exports_documents, {
6172
6244
  addDocument: () => addDocument,
6173
6245
  DOCUMENT_TYPES: () => DOCUMENT_TYPES
6174
6246
  });
6175
- import { existsSync as existsSync6, unlinkSync as unlinkSync2 } from "fs";
6247
+ import { existsSync as existsSync6, unlinkSync as unlinkSync3 } from "fs";
6176
6248
  function addDocument(input, db) {
6177
6249
  requireVault();
6178
6250
  const _db2 = db || getDatabase();
6179
6251
  const id = uuid();
6180
6252
  const { ciphertext, iv } = encrypt(input.value);
6181
- let encFilePath = null;
6253
+ let filePath = null;
6182
6254
  if (input.file_path) {
6183
- encFilePath = encryptFile(input.file_path, id);
6255
+ filePath = storeFile(input.file_path, id);
6184
6256
  }
6185
- _db2.query(`INSERT INTO contact_documents (id, contact_id, doc_type, label, encrypted_value, iv, encrypted_file_path, metadata, expires_at, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?)`).run(id, input.contact_id, input.doc_type, input.label ?? null, ciphertext, iv, encFilePath, JSON.stringify(input.metadata || {}), input.expires_at ?? null, now(), now());
6257
+ _db2.query(`INSERT INTO contact_documents (id, contact_id, doc_type, label, encrypted_value, iv, encrypted_file_path, metadata, expires_at, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?)`).run(id, input.contact_id, input.doc_type, input.label ?? null, ciphertext, iv, filePath, JSON.stringify(input.metadata || {}), input.expires_at ?? null, now(), now());
6186
6258
  return getDocument(id, _db2);
6187
6259
  }
6188
6260
  function getDocument(id, db) {
@@ -6210,7 +6282,7 @@ function deleteDocument(id, db) {
6210
6282
  const row = _db2.query(`SELECT encrypted_file_path FROM contact_documents WHERE id = ?`).get(id);
6211
6283
  if (row?.encrypted_file_path && existsSync6(row.encrypted_file_path)) {
6212
6284
  try {
6213
- unlinkSync2(row.encrypted_file_path);
6285
+ unlinkSync3(row.encrypted_file_path);
6214
6286
  } catch {}
6215
6287
  }
6216
6288
  _db2.query(`DELETE FROM contact_documents WHERE id = ?`).run(id);
@@ -6223,6 +6295,7 @@ function rowToDoc(row) {
6223
6295
  label: row.label,
6224
6296
  value: decrypt(row.encrypted_value, row.iv),
6225
6297
  has_file: !!row.encrypted_file_path,
6298
+ file_path: row.encrypted_file_path,
6226
6299
  metadata: JSON.parse(row.metadata || "{}"),
6227
6300
  expires_at: row.expires_at,
6228
6301
  created_at: row.created_at,
@@ -8,6 +8,7 @@ export interface ContactDocument {
8
8
  label: string | null;
9
9
  value: string;
10
10
  has_file: boolean;
11
+ file_path: string | null;
11
12
  metadata: Record<string, unknown>;
12
13
  expires_at: string | null;
13
14
  created_at: string;
@@ -1 +1 @@
1
- {"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../src/db/documents.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,eAAO,MAAM,cAAc,wSAMjB,CAAC;AACX,MAAM,MAAM,YAAY,GAAG,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC;AAEzD,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,mBAAmB,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,eAAe,CAYtF;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,eAAe,CAMtE;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,sBAAsB,EAAE,CASxF;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,CAO9D"}
1
+ {"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../src/db/documents.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,eAAO,MAAM,cAAc,wSAMjB,CAAC;AACX,MAAM,MAAM,YAAY,GAAG,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC;AAEzD,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,mBAAmB,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,eAAe,CAYtF;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,eAAe,CAMtE;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,sBAAsB,EAAE,CASxF;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,CAO9D"}
package/dist/index.d.ts CHANGED
@@ -62,7 +62,7 @@ export type { ParsedSignature } from "./lib/signature-parser.js";
62
62
  export { ingestMeetingParticipants } from "./lib/meeting-capture.js";
63
63
  export { findOrCreateContact } from "./db/contacts.js";
64
64
  export { saveImage, getImagePath, getImageAsBase64, deleteImage, listImages, getImagesDir } from "./lib/images.js";
65
- export { initVault, unlockVault, lockVault, isVaultUnlocked, isVaultInitialized, encrypt, decrypt, encryptFile, decryptFile, requireVault, getDocumentsDir } from "./lib/vault.js";
65
+ export { initVault, unlockVault, lockVault, isVaultUnlocked, isVaultInitialized, encrypt, decrypt, storeFile, getDocumentFilePath, decryptFile, requireVault, getDocumentsDir } from "./lib/vault.js";
66
66
  export { addDocument, getDocument, listDocuments, deleteDocument, DOCUMENT_TYPES } from "./db/documents.js";
67
67
  export type { DocumentType, ContactDocument, ContactDocumentSummary, CreateDocumentInput } from "./db/documents.js";
68
68
  export { setHealthData, getHealthData, deleteHealthData } from "./db/health.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ var __export = (target, all) => {
10
10
  });
11
11
  };
12
12
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
13
+ var __require = import.meta.require;
13
14
 
14
15
  // src/db/database.ts
15
16
  import { Database } from "bun:sqlite";
@@ -4503,16 +4504,47 @@ function listImages() {
4503
4504
  }));
4504
4505
  }
4505
4506
  // src/lib/vault.ts
4506
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3 } from "fs";
4507
+ import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2 } from "fs";
4507
4508
  import { join as join4 } from "path";
4508
4509
  import { createCipheriv, createDecipheriv, randomBytes, pbkdf2Sync, createHash } from "crypto";
4509
4510
  var VAULT_DIR = join4(process.env["HOME"] || "~", ".contacts");
4510
4511
  var VAULT_CONFIG = join4(VAULT_DIR, "vault.json");
4512
+ var VAULT_SESSION = join4(VAULT_DIR, ".vault-session");
4511
4513
  var DOCUMENTS_DIR = join4(VAULT_DIR, "documents");
4514
+ var SESSION_TTL_MS = 30 * 60 * 1000;
4512
4515
  var _derivedKey = null;
4513
4516
  function deriveKey(passphrase, salt) {
4514
4517
  return pbkdf2Sync(passphrase, salt, 1e5, 32, "sha512");
4515
4518
  }
4519
+ function saveSession(key) {
4520
+ const session = {
4521
+ key: key.toString("hex"),
4522
+ expires_at: new Date(Date.now() + SESSION_TTL_MS).toISOString()
4523
+ };
4524
+ writeFileSync2(VAULT_SESSION, JSON.stringify(session), { mode: 384 });
4525
+ }
4526
+ function loadSession() {
4527
+ if (!existsSync4(VAULT_SESSION))
4528
+ return null;
4529
+ try {
4530
+ const session = JSON.parse(readFileSync3(VAULT_SESSION, "utf-8"));
4531
+ if (new Date(session.expires_at).getTime() < Date.now()) {
4532
+ try {
4533
+ unlinkSync2(VAULT_SESSION);
4534
+ } catch {}
4535
+ return null;
4536
+ }
4537
+ return Buffer.from(session.key, "hex");
4538
+ } catch {
4539
+ return null;
4540
+ }
4541
+ }
4542
+ function clearSession() {
4543
+ try {
4544
+ if (existsSync4(VAULT_SESSION))
4545
+ unlinkSync2(VAULT_SESSION);
4546
+ } catch {}
4547
+ }
4516
4548
  function initVault(passphrase) {
4517
4549
  if (!existsSync4(VAULT_DIR))
4518
4550
  mkdirSync3(VAULT_DIR, { recursive: true });
@@ -4524,6 +4556,7 @@ function initVault(passphrase) {
4524
4556
  const config = { salt: salt.toString("hex"), key_hash: keyHash, created_at: new Date().toISOString() };
4525
4557
  writeFileSync2(VAULT_CONFIG, JSON.stringify(config, null, 2));
4526
4558
  _derivedKey = key;
4559
+ saveSession(key);
4527
4560
  }
4528
4561
  function isVaultInitialized() {
4529
4562
  return existsSync4(VAULT_CONFIG);
@@ -4538,18 +4571,32 @@ function unlockVault(passphrase) {
4538
4571
  if (keyHash !== config.key_hash)
4539
4572
  return false;
4540
4573
  _derivedKey = key;
4574
+ saveSession(key);
4541
4575
  return true;
4542
4576
  }
4543
4577
  function lockVault() {
4544
4578
  _derivedKey = null;
4579
+ clearSession();
4545
4580
  }
4546
4581
  function isVaultUnlocked() {
4547
- return _derivedKey !== null;
4582
+ if (_derivedKey)
4583
+ return true;
4584
+ const sessionKey = loadSession();
4585
+ if (sessionKey) {
4586
+ _derivedKey = sessionKey;
4587
+ return true;
4588
+ }
4589
+ return false;
4548
4590
  }
4549
4591
  function requireVault() {
4550
- if (!_derivedKey)
4551
- throw new Error("Vault is locked. Unlock with 'contacts vault unlock' or vault_unlock MCP tool first.");
4552
- return _derivedKey;
4592
+ if (_derivedKey)
4593
+ return _derivedKey;
4594
+ const sessionKey = loadSession();
4595
+ if (sessionKey) {
4596
+ _derivedKey = sessionKey;
4597
+ return _derivedKey;
4598
+ }
4599
+ throw new Error("Vault is locked. Unlock with 'contacts vault unlock --passphrase <pass>' or vault_unlock MCP tool first.");
4553
4600
  }
4554
4601
  function encrypt(plaintext) {
4555
4602
  const key = requireVault();
@@ -4571,20 +4618,23 @@ function decrypt(ciphertext, iv) {
4571
4618
  decrypted += decipher.final("utf8");
4572
4619
  return decrypted;
4573
4620
  }
4574
- function encryptFile(sourcePath, entityId) {
4575
- const key = requireVault();
4621
+ function storeFile(sourcePath, entityId) {
4576
4622
  if (!existsSync4(DOCUMENTS_DIR))
4577
4623
  mkdirSync3(DOCUMENTS_DIR, { recursive: true });
4624
+ const ext = sourcePath.split(".").pop() || "bin";
4625
+ const destPath = join4(DOCUMENTS_DIR, `${entityId}.${ext}`);
4578
4626
  const data = readFileSync3(sourcePath);
4579
- const iv = randomBytes(16);
4580
- const cipher = createCipheriv("aes-256-gcm", key, iv);
4581
- const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
4582
- const authTag = cipher.getAuthTag();
4583
- const destPath = join4(DOCUMENTS_DIR, `${entityId}.enc`);
4584
- const output = Buffer.concat([iv, authTag, encrypted]);
4585
- writeFileSync2(destPath, output);
4627
+ writeFileSync2(destPath, data);
4586
4628
  return destPath;
4587
4629
  }
4630
+ function getDocumentFilePath(entityId) {
4631
+ if (!existsSync4(DOCUMENTS_DIR))
4632
+ return null;
4633
+ const { readdirSync: readdirSync2 } = __require("fs");
4634
+ const files = readdirSync2(DOCUMENTS_DIR);
4635
+ const match = files.find((f) => f.startsWith(`${entityId}.`) && !f.endsWith(".enc"));
4636
+ return match ? join4(DOCUMENTS_DIR, match) : null;
4637
+ }
4588
4638
  function decryptFile(encPath) {
4589
4639
  const key = requireVault();
4590
4640
  const data = readFileSync3(encPath);
@@ -4602,7 +4652,7 @@ function getDocumentsDir() {
4602
4652
  }
4603
4653
  // src/db/documents.ts
4604
4654
  init_database();
4605
- import { existsSync as existsSync5, unlinkSync as unlinkSync2 } from "fs";
4655
+ import { existsSync as existsSync5, unlinkSync as unlinkSync3 } from "fs";
4606
4656
  var DOCUMENT_TYPES = [
4607
4657
  "passport",
4608
4658
  "national_id",
@@ -4629,11 +4679,11 @@ function addDocument(input, db) {
4629
4679
  const _db2 = db || getDatabase();
4630
4680
  const id = uuid();
4631
4681
  const { ciphertext, iv } = encrypt(input.value);
4632
- let encFilePath = null;
4682
+ let filePath = null;
4633
4683
  if (input.file_path) {
4634
- encFilePath = encryptFile(input.file_path, id);
4684
+ filePath = storeFile(input.file_path, id);
4635
4685
  }
4636
- _db2.query(`INSERT INTO contact_documents (id, contact_id, doc_type, label, encrypted_value, iv, encrypted_file_path, metadata, expires_at, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?)`).run(id, input.contact_id, input.doc_type, input.label ?? null, ciphertext, iv, encFilePath, JSON.stringify(input.metadata || {}), input.expires_at ?? null, now(), now());
4686
+ _db2.query(`INSERT INTO contact_documents (id, contact_id, doc_type, label, encrypted_value, iv, encrypted_file_path, metadata, expires_at, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?)`).run(id, input.contact_id, input.doc_type, input.label ?? null, ciphertext, iv, filePath, JSON.stringify(input.metadata || {}), input.expires_at ?? null, now(), now());
4637
4687
  return getDocument(id, _db2);
4638
4688
  }
4639
4689
  function getDocument(id, db) {
@@ -4661,7 +4711,7 @@ function deleteDocument(id, db) {
4661
4711
  const row = _db2.query(`SELECT encrypted_file_path FROM contact_documents WHERE id = ?`).get(id);
4662
4712
  if (row?.encrypted_file_path && existsSync5(row.encrypted_file_path)) {
4663
4713
  try {
4664
- unlinkSync2(row.encrypted_file_path);
4714
+ unlinkSync3(row.encrypted_file_path);
4665
4715
  } catch {}
4666
4716
  }
4667
4717
  _db2.query(`DELETE FROM contact_documents WHERE id = ?`).run(id);
@@ -4674,6 +4724,7 @@ function rowToDoc(row) {
4674
4724
  label: row.label,
4675
4725
  value: decrypt(row.encrypted_value, row.iv),
4676
4726
  has_file: !!row.encrypted_file_path,
4727
+ file_path: row.encrypted_file_path,
4677
4728
  metadata: JSON.parse(row.metadata || "{}"),
4678
4729
  expires_at: row.expires_at,
4679
4730
  created_at: row.created_at,
@@ -4881,6 +4932,7 @@ export {
4881
4932
  unlockVault,
4882
4933
  unarchiveContact,
4883
4934
  unarchiveCompany,
4935
+ storeFile,
4884
4936
  setHealthData,
4885
4937
  setDealContactRole,
4886
4938
  semanticSearch,
@@ -4987,6 +5039,7 @@ export {
4987
5039
  getEvent,
4988
5040
  getEntityTeam,
4989
5041
  getDocumentsDir,
5042
+ getDocumentFilePath,
4990
5043
  getDocument,
4991
5044
  getDealsByStage,
4992
5045
  getDealTeam,
@@ -5014,7 +5067,6 @@ export {
5014
5067
  extractContactsFromEmailThread,
5015
5068
  exportFromApple,
5016
5069
  exportContacts,
5017
- encryptFile,
5018
5070
  encrypt,
5019
5071
  embedContact,
5020
5072
  embedAllContacts,
@@ -9,7 +9,16 @@ export declare function encrypt(plaintext: string): {
9
9
  iv: string;
10
10
  };
11
11
  export declare function decrypt(ciphertext: string, iv: string): string;
12
- export declare function encryptFile(sourcePath: string, entityId: string): string;
12
+ /**
13
+ * Store a file attachment (PLAIN, not encrypted) so agents can access it directly.
14
+ * Text values in the DB are encrypted; file attachments are plain for agent access.
15
+ * Returns the destination path.
16
+ */
17
+ export declare function storeFile(sourcePath: string, entityId: string): string;
18
+ /**
19
+ * Get the file path for a document attachment. Returns null if no file exists.
20
+ */
21
+ export declare function getDocumentFilePath(entityId: string): string | null;
13
22
  export declare function decryptFile(encPath: string): Buffer;
14
23
  export declare function getDocumentsDir(): string;
15
24
  //# sourceMappingURL=vault.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vault.d.ts","sourceRoot":"","sources":["../../src/lib/vault.ts"],"names":[],"mappings":"AAoBA,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CASlD;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CASvD;AAED,wBAAgB,SAAS,IAAI,IAAI,CAEhC;AAED,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED,wBAAgB,YAAY,IAAI,MAAM,CAGrC;AAED,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAQ7E;AAED,wBAAgB,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAS9D;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAaxE;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CASnD;AAED,wBAAgB,eAAe,IAAI,MAAM,CAGxC"}
1
+ {"version":3,"file":"vault.d.ts","sourceRoot":"","sources":["../../src/lib/vault.ts"],"names":[],"mappings":"AAgDA,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAUlD;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAUvD;AAED,wBAAgB,SAAS,IAAI,IAAI,CAGhC;AAED,wBAAgB,eAAe,IAAI,OAAO,CASzC;AAED,wBAAgB,YAAY,IAAI,MAAM,CASrC;AAED,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAQ7E;AAED,wBAAgB,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAS9D;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOtE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMnE;AAGD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CASnD;AAED,wBAAgB,eAAe,IAAI,MAAM,CAGxC"}
package/dist/mcp/index.js CHANGED
@@ -4317,16 +4317,47 @@ function deleteImage(entityId) {
4317
4317
  }
4318
4318
 
4319
4319
  // src/lib/vault.ts
4320
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3 } from "fs";
4320
+ import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2 } from "fs";
4321
4321
  import { join as join4 } from "path";
4322
4322
  import { createCipheriv, createDecipheriv, randomBytes, pbkdf2Sync, createHash } from "crypto";
4323
4323
  var VAULT_DIR = join4(process.env["HOME"] || "~", ".contacts");
4324
4324
  var VAULT_CONFIG = join4(VAULT_DIR, "vault.json");
4325
+ var VAULT_SESSION = join4(VAULT_DIR, ".vault-session");
4325
4326
  var DOCUMENTS_DIR = join4(VAULT_DIR, "documents");
4327
+ var SESSION_TTL_MS = 30 * 60 * 1000;
4326
4328
  var _derivedKey = null;
4327
4329
  function deriveKey(passphrase, salt) {
4328
4330
  return pbkdf2Sync(passphrase, salt, 1e5, 32, "sha512");
4329
4331
  }
4332
+ function saveSession(key) {
4333
+ const session = {
4334
+ key: key.toString("hex"),
4335
+ expires_at: new Date(Date.now() + SESSION_TTL_MS).toISOString()
4336
+ };
4337
+ writeFileSync2(VAULT_SESSION, JSON.stringify(session), { mode: 384 });
4338
+ }
4339
+ function loadSession() {
4340
+ if (!existsSync4(VAULT_SESSION))
4341
+ return null;
4342
+ try {
4343
+ const session = JSON.parse(readFileSync3(VAULT_SESSION, "utf-8"));
4344
+ if (new Date(session.expires_at).getTime() < Date.now()) {
4345
+ try {
4346
+ unlinkSync2(VAULT_SESSION);
4347
+ } catch {}
4348
+ return null;
4349
+ }
4350
+ return Buffer.from(session.key, "hex");
4351
+ } catch {
4352
+ return null;
4353
+ }
4354
+ }
4355
+ function clearSession() {
4356
+ try {
4357
+ if (existsSync4(VAULT_SESSION))
4358
+ unlinkSync2(VAULT_SESSION);
4359
+ } catch {}
4360
+ }
4330
4361
  function initVault(passphrase) {
4331
4362
  if (!existsSync4(VAULT_DIR))
4332
4363
  mkdirSync3(VAULT_DIR, { recursive: true });
@@ -4338,6 +4369,7 @@ function initVault(passphrase) {
4338
4369
  const config = { salt: salt.toString("hex"), key_hash: keyHash, created_at: new Date().toISOString() };
4339
4370
  writeFileSync2(VAULT_CONFIG, JSON.stringify(config, null, 2));
4340
4371
  _derivedKey = key;
4372
+ saveSession(key);
4341
4373
  }
4342
4374
  function isVaultInitialized() {
4343
4375
  return existsSync4(VAULT_CONFIG);
@@ -4352,18 +4384,32 @@ function unlockVault(passphrase) {
4352
4384
  if (keyHash !== config.key_hash)
4353
4385
  return false;
4354
4386
  _derivedKey = key;
4387
+ saveSession(key);
4355
4388
  return true;
4356
4389
  }
4357
4390
  function lockVault() {
4358
4391
  _derivedKey = null;
4392
+ clearSession();
4359
4393
  }
4360
4394
  function isVaultUnlocked() {
4361
- return _derivedKey !== null;
4395
+ if (_derivedKey)
4396
+ return true;
4397
+ const sessionKey = loadSession();
4398
+ if (sessionKey) {
4399
+ _derivedKey = sessionKey;
4400
+ return true;
4401
+ }
4402
+ return false;
4362
4403
  }
4363
4404
  function requireVault() {
4364
- if (!_derivedKey)
4365
- throw new Error("Vault is locked. Unlock with 'contacts vault unlock' or vault_unlock MCP tool first.");
4366
- return _derivedKey;
4405
+ if (_derivedKey)
4406
+ return _derivedKey;
4407
+ const sessionKey = loadSession();
4408
+ if (sessionKey) {
4409
+ _derivedKey = sessionKey;
4410
+ return _derivedKey;
4411
+ }
4412
+ throw new Error("Vault is locked. Unlock with 'contacts vault unlock --passphrase <pass>' or vault_unlock MCP tool first.");
4367
4413
  }
4368
4414
  function encrypt(plaintext) {
4369
4415
  const key = requireVault();
@@ -4385,24 +4431,19 @@ function decrypt(ciphertext, iv) {
4385
4431
  decrypted += decipher.final("utf8");
4386
4432
  return decrypted;
4387
4433
  }
4388
- function encryptFile(sourcePath, entityId) {
4389
- const key = requireVault();
4434
+ function storeFile(sourcePath, entityId) {
4390
4435
  if (!existsSync4(DOCUMENTS_DIR))
4391
4436
  mkdirSync3(DOCUMENTS_DIR, { recursive: true });
4437
+ const ext = sourcePath.split(".").pop() || "bin";
4438
+ const destPath = join4(DOCUMENTS_DIR, `${entityId}.${ext}`);
4392
4439
  const data = readFileSync3(sourcePath);
4393
- const iv = randomBytes(16);
4394
- const cipher = createCipheriv("aes-256-gcm", key, iv);
4395
- const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
4396
- const authTag = cipher.getAuthTag();
4397
- const destPath = join4(DOCUMENTS_DIR, `${entityId}.enc`);
4398
- const output = Buffer.concat([iv, authTag, encrypted]);
4399
- writeFileSync2(destPath, output);
4440
+ writeFileSync2(destPath, data);
4400
4441
  return destPath;
4401
4442
  }
4402
4443
 
4403
4444
  // src/db/documents.ts
4404
4445
  init_database();
4405
- import { existsSync as existsSync5, unlinkSync as unlinkSync2 } from "fs";
4446
+ import { existsSync as existsSync5, unlinkSync as unlinkSync3 } from "fs";
4406
4447
  var DOCUMENT_TYPES = [
4407
4448
  "passport",
4408
4449
  "national_id",
@@ -4429,11 +4470,11 @@ function addDocument(input, db) {
4429
4470
  const _db2 = db || getDatabase();
4430
4471
  const id = uuid();
4431
4472
  const { ciphertext, iv } = encrypt(input.value);
4432
- let encFilePath = null;
4473
+ let filePath = null;
4433
4474
  if (input.file_path) {
4434
- encFilePath = encryptFile(input.file_path, id);
4475
+ filePath = storeFile(input.file_path, id);
4435
4476
  }
4436
- _db2.query(`INSERT INTO contact_documents (id, contact_id, doc_type, label, encrypted_value, iv, encrypted_file_path, metadata, expires_at, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?)`).run(id, input.contact_id, input.doc_type, input.label ?? null, ciphertext, iv, encFilePath, JSON.stringify(input.metadata || {}), input.expires_at ?? null, now(), now());
4477
+ _db2.query(`INSERT INTO contact_documents (id, contact_id, doc_type, label, encrypted_value, iv, encrypted_file_path, metadata, expires_at, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?)`).run(id, input.contact_id, input.doc_type, input.label ?? null, ciphertext, iv, filePath, JSON.stringify(input.metadata || {}), input.expires_at ?? null, now(), now());
4437
4478
  return getDocument(id, _db2);
4438
4479
  }
4439
4480
  function getDocument(id, db) {
@@ -4461,7 +4502,7 @@ function deleteDocument(id, db) {
4461
4502
  const row = _db2.query(`SELECT encrypted_file_path FROM contact_documents WHERE id = ?`).get(id);
4462
4503
  if (row?.encrypted_file_path && existsSync5(row.encrypted_file_path)) {
4463
4504
  try {
4464
- unlinkSync2(row.encrypted_file_path);
4505
+ unlinkSync3(row.encrypted_file_path);
4465
4506
  } catch {}
4466
4507
  }
4467
4508
  _db2.query(`DELETE FROM contact_documents WHERE id = ?`).run(id);
@@ -4474,6 +4515,7 @@ function rowToDoc(row) {
4474
4515
  label: row.label,
4475
4516
  value: decrypt(row.encrypted_value, row.iv),
4476
4517
  has_file: !!row.encrypted_file_path,
4518
+ file_path: row.encrypted_file_path,
4477
4519
  metadata: JSON.parse(row.metadata || "{}"),
4478
4520
  expires_at: row.expires_at,
4479
4521
  created_at: row.created_at,
@@ -6146,10 +6188,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
6146
6188
  { name: "vault_unlock", description: "Unlock the vault for this session with a passphrase.", inputSchema: { type: "object", properties: { passphrase: { type: "string" } }, required: ["passphrase"] } },
6147
6189
  { name: "vault_lock", description: "Lock the vault, clearing the encryption key from memory.", inputSchema: { type: "object", properties: {} } },
6148
6190
  { name: "vault_status", description: "Check vault initialization and lock status.", inputSchema: { type: "object", properties: {} } },
6149
- { name: "add_document", description: "Store an encrypted document for a contact (passport, tax_id, medical_record, etc.). Vault must be unlocked.", inputSchema: { type: "object", properties: { contact_id: { type: "string" }, doc_type: { type: "string", enum: [...DOCUMENT_TYPES] }, label: { type: "string" }, value: { type: "string", description: "Plaintext value (will be encrypted)" }, file_path: { type: "string", description: "Optional file to encrypt and attach" }, metadata: { type: "object" }, expires_at: { type: "string" } }, required: ["contact_id", "doc_type", "value"] } },
6150
- { name: "list_documents", description: "List documents for a contact (metadata only \u2014 no decryption needed).", inputSchema: { type: "object", properties: { contact_id: { type: "string" } }, required: ["contact_id"] } },
6151
- { name: "get_document", description: "Get a document with decrypted value. Vault must be unlocked.", inputSchema: { type: "object", properties: { document_id: { type: "string" } }, required: ["document_id"] } },
6152
- { name: "delete_document", description: "Delete a document and its encrypted file.", inputSchema: { type: "object", properties: { document_id: { type: "string" } }, required: ["document_id"] } },
6191
+ { name: "add_document", description: "Store a document for a contact (passport, tax_id, medical_record, etc.). Text values are encrypted; file attachments are stored plain so agents can read them. Vault must be unlocked.", inputSchema: { type: "object", properties: { contact_id: { type: "string" }, doc_type: { type: "string", enum: [...DOCUMENT_TYPES] }, label: { type: "string" }, value: { type: "string", description: "Plaintext value (will be encrypted in DB)" }, file_path: { type: "string", description: "File to attach \u2014 stored PLAIN in ~/.contacts/documents/ for agent access" }, metadata: { type: "object" }, expires_at: { type: "string" } }, required: ["contact_id", "doc_type", "value"] } },
6192
+ { name: "list_documents", description: "List documents for a contact (metadata only \u2014 no decryption needed). Returns file_path for attachments so agents can read them directly.", inputSchema: { type: "object", properties: { contact_id: { type: "string" } }, required: ["contact_id"] } },
6193
+ { name: "get_document", description: "Get a document with decrypted value and file_path. Vault must be unlocked for the text value; file is always accessible.", inputSchema: { type: "object", properties: { document_id: { type: "string" } }, required: ["document_id"] } },
6194
+ { name: "get_document_file", description: "Get the plain file path for a document attachment. Agents can read this file directly \u2014 it is NOT encrypted. Returns null if no file attached.", inputSchema: { type: "object", properties: { document_id: { type: "string" } }, required: ["document_id"] } },
6195
+ { name: "delete_document", description: "Delete a document and its file attachment.", inputSchema: { type: "object", properties: { document_id: { type: "string" } }, required: ["document_id"] } },
6153
6196
  { name: "scan_document", description: "Scan a document image using AI vision (OpenAI GPT-4o) to extract structured data. Optionally auto-save to vault.", inputSchema: { type: "object", properties: { image: { type: "string", description: "File path or base64 image data" }, doc_type: { type: "string", description: "Hint: passport, national_id, drivers_license, etc." }, contact_id: { type: "string", description: "Contact to associate scanned document with" }, auto_save: { type: "boolean", description: "Automatically save extracted data as a vault document" } }, required: ["image"] } },
6154
6197
  { name: "set_health_data", description: "Set or update health data for a contact. Vault must be unlocked.", inputSchema: { type: "object", properties: { contact_id: { type: "string" }, blood_type: { type: "string" }, allergies: { type: "array", items: { type: "string" } }, medical_conditions: { type: "array", items: { type: "string" } }, medications: { type: "array", items: { type: "string" } }, emergency_contacts: { type: "array", items: { type: "object", properties: { name: { type: "string" }, phone: { type: "string" }, relationship: { type: "string" } }, required: ["name", "phone", "relationship"] } }, health_insurance_provider: { type: "string" }, health_insurance_id: { type: "string" }, primary_physician: { type: "string" }, primary_physician_phone: { type: "string" }, organ_donor: { type: "boolean" }, notes: { type: "string" } }, required: ["contact_id"] } },
6155
6198
  { name: "get_health_data", description: "Get health data for a contact. Vault must be unlocked.", inputSchema: { type: "object", properties: { contact_id: { type: "string" } }, required: ["contact_id"] } },
@@ -7737,6 +7780,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
7737
7780
  const doc = getDocument(a.document_id);
7738
7781
  return { content: [{ type: "text", text: JSON.stringify(doc, null, 2) }] };
7739
7782
  }
7783
+ case "get_document_file": {
7784
+ const db = getDatabase();
7785
+ const row = db.query(`SELECT encrypted_file_path FROM contact_documents WHERE id = ?`).get(a.document_id);
7786
+ if (!row)
7787
+ return { content: [{ type: "text", text: JSON.stringify({ error: "Document not found" }) }], isError: true };
7788
+ const filePath = row.encrypted_file_path;
7789
+ return { content: [{ type: "text", text: JSON.stringify({ document_id: a.document_id, file_path: filePath, has_file: !!filePath }) }] };
7790
+ }
7740
7791
  case "delete_document": {
7741
7792
  deleteDocument(a.document_id);
7742
7793
  return { content: [{ type: "text", text: JSON.stringify({ deleted: true }) }] };
@@ -2035,6 +2035,25 @@ async function handleExport(req) {
2035
2035
  }
2036
2036
  });
2037
2037
  }
2038
+ function handleDocumentFiles(req, segments) {
2039
+ const docId = segments[2];
2040
+ const sub = segments[3];
2041
+ if (!docId)
2042
+ return apiError("Document ID required");
2043
+ if (req.method !== "GET")
2044
+ return apiError("Method not allowed", 405);
2045
+ if (sub === "file") {
2046
+ const db = getDatabase();
2047
+ const row = db.query(`SELECT encrypted_file_path FROM contact_documents WHERE id = ?`).get(docId);
2048
+ if (!row?.encrypted_file_path || !existsSync3(row.encrypted_file_path)) {
2049
+ return new Response("No file attachment", { status: 404 });
2050
+ }
2051
+ return new Response(Bun.file(row.encrypted_file_path), {
2052
+ headers: { "Cache-Control": "public, max-age=3600" }
2053
+ });
2054
+ }
2055
+ return apiError("Use /api/documents/:id/file to get the attachment", 400);
2056
+ }
2038
2057
  async function handleImages(req, _url, segments) {
2039
2058
  const entityId = segments[2];
2040
2059
  if (!entityId)
@@ -2131,6 +2150,9 @@ function startServer(port) {
2131
2150
  case "images":
2132
2151
  response = await handleImages(req, url, segments);
2133
2152
  break;
2153
+ case "documents":
2154
+ response = handleDocumentFiles(req, segments);
2155
+ break;
2134
2156
  default:
2135
2157
  response = apiError("Not found", 404);
2136
2158
  }
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/server/serve.ts"],"names":[],"mappings":"AAoVA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAyE9C"}
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/server/serve.ts"],"names":[],"mappings":"AA2WA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CA4E9C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/contacts",
3
- "version": "0.6.2",
3
+ "version": "0.6.4",
4
4
  "description": "Contact management for AI coding agents — CLI + MCP + Web",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",