@hasna/contacts 0.6.2 → 0.6.3

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
@@ -6063,12 +6063,41 @@ __export(exports_vault, {
6063
6063
  decryptFile: () => decryptFile,
6064
6064
  decrypt: () => decrypt
6065
6065
  });
6066
- import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4 } from "fs";
6066
+ import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, unlinkSync as unlinkSync2 } from "fs";
6067
6067
  import { join as join5 } from "path";
6068
6068
  import { createCipheriv, createDecipheriv, randomBytes, pbkdf2Sync, createHash } from "crypto";
6069
6069
  function deriveKey(passphrase, salt) {
6070
6070
  return pbkdf2Sync(passphrase, salt, 1e5, 32, "sha512");
6071
6071
  }
6072
+ function saveSession(key) {
6073
+ const session = {
6074
+ key: key.toString("hex"),
6075
+ expires_at: new Date(Date.now() + SESSION_TTL_MS).toISOString()
6076
+ };
6077
+ writeFileSync3(VAULT_SESSION, JSON.stringify(session), { mode: 384 });
6078
+ }
6079
+ function loadSession() {
6080
+ if (!existsSync5(VAULT_SESSION))
6081
+ return null;
6082
+ try {
6083
+ const session = JSON.parse(readFileSync3(VAULT_SESSION, "utf-8"));
6084
+ if (new Date(session.expires_at).getTime() < Date.now()) {
6085
+ try {
6086
+ unlinkSync2(VAULT_SESSION);
6087
+ } catch {}
6088
+ return null;
6089
+ }
6090
+ return Buffer.from(session.key, "hex");
6091
+ } catch {
6092
+ return null;
6093
+ }
6094
+ }
6095
+ function clearSession() {
6096
+ try {
6097
+ if (existsSync5(VAULT_SESSION))
6098
+ unlinkSync2(VAULT_SESSION);
6099
+ } catch {}
6100
+ }
6072
6101
  function initVault(passphrase) {
6073
6102
  if (!existsSync5(VAULT_DIR))
6074
6103
  mkdirSync4(VAULT_DIR, { recursive: true });
@@ -6080,6 +6109,7 @@ function initVault(passphrase) {
6080
6109
  const config = { salt: salt.toString("hex"), key_hash: keyHash, created_at: new Date().toISOString() };
6081
6110
  writeFileSync3(VAULT_CONFIG, JSON.stringify(config, null, 2));
6082
6111
  _derivedKey = key;
6112
+ saveSession(key);
6083
6113
  }
6084
6114
  function isVaultInitialized() {
6085
6115
  return existsSync5(VAULT_CONFIG);
@@ -6094,18 +6124,32 @@ function unlockVault(passphrase) {
6094
6124
  if (keyHash !== config.key_hash)
6095
6125
  return false;
6096
6126
  _derivedKey = key;
6127
+ saveSession(key);
6097
6128
  return true;
6098
6129
  }
6099
6130
  function lockVault() {
6100
6131
  _derivedKey = null;
6132
+ clearSession();
6101
6133
  }
6102
6134
  function isVaultUnlocked() {
6103
- return _derivedKey !== null;
6135
+ if (_derivedKey)
6136
+ return true;
6137
+ const sessionKey = loadSession();
6138
+ if (sessionKey) {
6139
+ _derivedKey = sessionKey;
6140
+ return true;
6141
+ }
6142
+ return false;
6104
6143
  }
6105
6144
  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;
6145
+ if (_derivedKey)
6146
+ return _derivedKey;
6147
+ const sessionKey = loadSession();
6148
+ if (sessionKey) {
6149
+ _derivedKey = sessionKey;
6150
+ return _derivedKey;
6151
+ }
6152
+ throw new Error("Vault is locked. Unlock with 'contacts vault unlock --passphrase <pass>' or vault_unlock MCP tool first.");
6109
6153
  }
6110
6154
  function encrypt(plaintext) {
6111
6155
  const key = requireVault();
@@ -6156,11 +6200,13 @@ function getDocumentsDir() {
6156
6200
  mkdirSync4(DOCUMENTS_DIR, { recursive: true });
6157
6201
  return DOCUMENTS_DIR;
6158
6202
  }
6159
- var VAULT_DIR, VAULT_CONFIG, DOCUMENTS_DIR, _derivedKey = null;
6203
+ var VAULT_DIR, VAULT_CONFIG, VAULT_SESSION, DOCUMENTS_DIR, SESSION_TTL_MS, _derivedKey = null;
6160
6204
  var init_vault = __esm(() => {
6161
6205
  VAULT_DIR = join5(process.env["HOME"] || "~", ".contacts");
6162
6206
  VAULT_CONFIG = join5(VAULT_DIR, "vault.json");
6207
+ VAULT_SESSION = join5(VAULT_DIR, ".vault-session");
6163
6208
  DOCUMENTS_DIR = join5(VAULT_DIR, "documents");
6209
+ SESSION_TTL_MS = 30 * 60 * 1000;
6164
6210
  });
6165
6211
 
6166
6212
  // src/db/documents.ts
@@ -6172,7 +6218,7 @@ __export(exports_documents, {
6172
6218
  addDocument: () => addDocument,
6173
6219
  DOCUMENT_TYPES: () => DOCUMENT_TYPES
6174
6220
  });
6175
- import { existsSync as existsSync6, unlinkSync as unlinkSync2 } from "fs";
6221
+ import { existsSync as existsSync6, unlinkSync as unlinkSync3 } from "fs";
6176
6222
  function addDocument(input, db) {
6177
6223
  requireVault();
6178
6224
  const _db2 = db || getDatabase();
@@ -6210,7 +6256,7 @@ function deleteDocument(id, db) {
6210
6256
  const row = _db2.query(`SELECT encrypted_file_path FROM contact_documents WHERE id = ?`).get(id);
6211
6257
  if (row?.encrypted_file_path && existsSync6(row.encrypted_file_path)) {
6212
6258
  try {
6213
- unlinkSync2(row.encrypted_file_path);
6259
+ unlinkSync3(row.encrypted_file_path);
6214
6260
  } catch {}
6215
6261
  }
6216
6262
  _db2.query(`DELETE FROM contact_documents WHERE id = ?`).run(id);
package/dist/index.js CHANGED
@@ -4503,16 +4503,47 @@ function listImages() {
4503
4503
  }));
4504
4504
  }
4505
4505
  // src/lib/vault.ts
4506
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3 } from "fs";
4506
+ import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2 } from "fs";
4507
4507
  import { join as join4 } from "path";
4508
4508
  import { createCipheriv, createDecipheriv, randomBytes, pbkdf2Sync, createHash } from "crypto";
4509
4509
  var VAULT_DIR = join4(process.env["HOME"] || "~", ".contacts");
4510
4510
  var VAULT_CONFIG = join4(VAULT_DIR, "vault.json");
4511
+ var VAULT_SESSION = join4(VAULT_DIR, ".vault-session");
4511
4512
  var DOCUMENTS_DIR = join4(VAULT_DIR, "documents");
4513
+ var SESSION_TTL_MS = 30 * 60 * 1000;
4512
4514
  var _derivedKey = null;
4513
4515
  function deriveKey(passphrase, salt) {
4514
4516
  return pbkdf2Sync(passphrase, salt, 1e5, 32, "sha512");
4515
4517
  }
4518
+ function saveSession(key) {
4519
+ const session = {
4520
+ key: key.toString("hex"),
4521
+ expires_at: new Date(Date.now() + SESSION_TTL_MS).toISOString()
4522
+ };
4523
+ writeFileSync2(VAULT_SESSION, JSON.stringify(session), { mode: 384 });
4524
+ }
4525
+ function loadSession() {
4526
+ if (!existsSync4(VAULT_SESSION))
4527
+ return null;
4528
+ try {
4529
+ const session = JSON.parse(readFileSync3(VAULT_SESSION, "utf-8"));
4530
+ if (new Date(session.expires_at).getTime() < Date.now()) {
4531
+ try {
4532
+ unlinkSync2(VAULT_SESSION);
4533
+ } catch {}
4534
+ return null;
4535
+ }
4536
+ return Buffer.from(session.key, "hex");
4537
+ } catch {
4538
+ return null;
4539
+ }
4540
+ }
4541
+ function clearSession() {
4542
+ try {
4543
+ if (existsSync4(VAULT_SESSION))
4544
+ unlinkSync2(VAULT_SESSION);
4545
+ } catch {}
4546
+ }
4516
4547
  function initVault(passphrase) {
4517
4548
  if (!existsSync4(VAULT_DIR))
4518
4549
  mkdirSync3(VAULT_DIR, { recursive: true });
@@ -4524,6 +4555,7 @@ function initVault(passphrase) {
4524
4555
  const config = { salt: salt.toString("hex"), key_hash: keyHash, created_at: new Date().toISOString() };
4525
4556
  writeFileSync2(VAULT_CONFIG, JSON.stringify(config, null, 2));
4526
4557
  _derivedKey = key;
4558
+ saveSession(key);
4527
4559
  }
4528
4560
  function isVaultInitialized() {
4529
4561
  return existsSync4(VAULT_CONFIG);
@@ -4538,18 +4570,32 @@ function unlockVault(passphrase) {
4538
4570
  if (keyHash !== config.key_hash)
4539
4571
  return false;
4540
4572
  _derivedKey = key;
4573
+ saveSession(key);
4541
4574
  return true;
4542
4575
  }
4543
4576
  function lockVault() {
4544
4577
  _derivedKey = null;
4578
+ clearSession();
4545
4579
  }
4546
4580
  function isVaultUnlocked() {
4547
- return _derivedKey !== null;
4581
+ if (_derivedKey)
4582
+ return true;
4583
+ const sessionKey = loadSession();
4584
+ if (sessionKey) {
4585
+ _derivedKey = sessionKey;
4586
+ return true;
4587
+ }
4588
+ return false;
4548
4589
  }
4549
4590
  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;
4591
+ if (_derivedKey)
4592
+ return _derivedKey;
4593
+ const sessionKey = loadSession();
4594
+ if (sessionKey) {
4595
+ _derivedKey = sessionKey;
4596
+ return _derivedKey;
4597
+ }
4598
+ throw new Error("Vault is locked. Unlock with 'contacts vault unlock --passphrase <pass>' or vault_unlock MCP tool first.");
4553
4599
  }
4554
4600
  function encrypt(plaintext) {
4555
4601
  const key = requireVault();
@@ -4602,7 +4648,7 @@ function getDocumentsDir() {
4602
4648
  }
4603
4649
  // src/db/documents.ts
4604
4650
  init_database();
4605
- import { existsSync as existsSync5, unlinkSync as unlinkSync2 } from "fs";
4651
+ import { existsSync as existsSync5, unlinkSync as unlinkSync3 } from "fs";
4606
4652
  var DOCUMENT_TYPES = [
4607
4653
  "passport",
4608
4654
  "national_id",
@@ -4661,7 +4707,7 @@ function deleteDocument(id, db) {
4661
4707
  const row = _db2.query(`SELECT encrypted_file_path FROM contact_documents WHERE id = ?`).get(id);
4662
4708
  if (row?.encrypted_file_path && existsSync5(row.encrypted_file_path)) {
4663
4709
  try {
4664
- unlinkSync2(row.encrypted_file_path);
4710
+ unlinkSync3(row.encrypted_file_path);
4665
4711
  } catch {}
4666
4712
  }
4667
4713
  _db2.query(`DELETE FROM contact_documents WHERE id = ?`).run(id);
@@ -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,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"}
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();
@@ -4402,7 +4448,7 @@ function encryptFile(sourcePath, entityId) {
4402
4448
 
4403
4449
  // src/db/documents.ts
4404
4450
  init_database();
4405
- import { existsSync as existsSync5, unlinkSync as unlinkSync2 } from "fs";
4451
+ import { existsSync as existsSync5, unlinkSync as unlinkSync3 } from "fs";
4406
4452
  var DOCUMENT_TYPES = [
4407
4453
  "passport",
4408
4454
  "national_id",
@@ -4461,7 +4507,7 @@ function deleteDocument(id, db) {
4461
4507
  const row = _db2.query(`SELECT encrypted_file_path FROM contact_documents WHERE id = ?`).get(id);
4462
4508
  if (row?.encrypted_file_path && existsSync5(row.encrypted_file_path)) {
4463
4509
  try {
4464
- unlinkSync2(row.encrypted_file_path);
4510
+ unlinkSync3(row.encrypted_file_path);
4465
4511
  } catch {}
4466
4512
  }
4467
4513
  _db2.query(`DELETE FROM contact_documents WHERE id = ?`).run(id);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/contacts",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "Contact management for AI coding agents — CLI + MCP + Web",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",