@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 +54 -8
- package/dist/index.js +53 -7
- package/dist/lib/vault.d.ts.map +1 -1
- package/dist/mcp/index.js +53 -7
- package/package.json +1 -1
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
|
-
|
|
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 (
|
|
6107
|
-
|
|
6108
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
4551
|
-
|
|
4552
|
-
|
|
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
|
|
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
|
-
|
|
4710
|
+
unlinkSync3(row.encrypted_file_path);
|
|
4665
4711
|
} catch {}
|
|
4666
4712
|
}
|
|
4667
4713
|
_db2.query(`DELETE FROM contact_documents WHERE id = ?`).run(id);
|
package/dist/lib/vault.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vault.d.ts","sourceRoot":"","sources":["../../src/lib/vault.ts"],"names":[],"mappings":"
|
|
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
|
-
|
|
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 (
|
|
4365
|
-
|
|
4366
|
-
|
|
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
|
|
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
|
-
|
|
4510
|
+
unlinkSync3(row.encrypted_file_path);
|
|
4465
4511
|
} catch {}
|
|
4466
4512
|
}
|
|
4467
4513
|
_db2.query(`DELETE FROM contact_documents WHERE id = ?`).run(id);
|