@hasna/contacts 0.6.4 → 0.6.6
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 +77 -34
- package/dist/db/database.d.ts +1 -0
- package/dist/db/database.d.ts.map +1 -1
- package/dist/index.js +36 -13
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/images.d.ts.map +1 -1
- package/dist/lib/vault.d.ts.map +1 -1
- package/dist/mcp/index.js +40 -32
- package/dist/server/index.js +25 -24
- package/package.json +3 -1
package/dist/cli/index.js
CHANGED
|
@@ -6,39 +6,60 @@ var __defProp = Object.defineProperty;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
function __accessProp(key) {
|
|
10
|
+
return this[key];
|
|
11
|
+
}
|
|
12
|
+
var __toESMCache_node;
|
|
13
|
+
var __toESMCache_esm;
|
|
9
14
|
var __toESM = (mod, isNodeMode, target) => {
|
|
15
|
+
var canCache = mod != null && typeof mod === "object";
|
|
16
|
+
if (canCache) {
|
|
17
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
18
|
+
var cached = cache.get(mod);
|
|
19
|
+
if (cached)
|
|
20
|
+
return cached;
|
|
21
|
+
}
|
|
10
22
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
11
23
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
12
24
|
for (let key of __getOwnPropNames(mod))
|
|
13
25
|
if (!__hasOwnProp.call(to, key))
|
|
14
26
|
__defProp(to, key, {
|
|
15
|
-
get: (
|
|
27
|
+
get: __accessProp.bind(mod, key),
|
|
16
28
|
enumerable: true
|
|
17
29
|
});
|
|
30
|
+
if (canCache)
|
|
31
|
+
cache.set(mod, to);
|
|
18
32
|
return to;
|
|
19
33
|
};
|
|
20
|
-
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
21
34
|
var __toCommonJS = (from) => {
|
|
22
|
-
var entry = __moduleCache.get(from), desc;
|
|
35
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
23
36
|
if (entry)
|
|
24
37
|
return entry;
|
|
25
38
|
entry = __defProp({}, "__esModule", { value: true });
|
|
26
|
-
if (from && typeof from === "object" || typeof from === "function")
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
40
|
+
for (var key of __getOwnPropNames(from))
|
|
41
|
+
if (!__hasOwnProp.call(entry, key))
|
|
42
|
+
__defProp(entry, key, {
|
|
43
|
+
get: __accessProp.bind(from, key),
|
|
44
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
45
|
+
});
|
|
46
|
+
}
|
|
31
47
|
__moduleCache.set(from, entry);
|
|
32
48
|
return entry;
|
|
33
49
|
};
|
|
50
|
+
var __moduleCache;
|
|
34
51
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
52
|
+
var __returnValue = (v) => v;
|
|
53
|
+
function __exportSetter(name, newValue) {
|
|
54
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
55
|
+
}
|
|
35
56
|
var __export = (target, all) => {
|
|
36
57
|
for (var name in all)
|
|
37
58
|
__defProp(target, name, {
|
|
38
59
|
get: all[name],
|
|
39
60
|
enumerable: true,
|
|
40
61
|
configurable: true,
|
|
41
|
-
set: (
|
|
62
|
+
set: __exportSetter.bind(all, name)
|
|
42
63
|
});
|
|
43
64
|
};
|
|
44
65
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -2114,16 +2135,34 @@ __export(exports_database, {
|
|
|
2114
2135
|
resetDatabase: () => resetDatabase,
|
|
2115
2136
|
now: () => now,
|
|
2116
2137
|
getDbPath: () => getDbPath,
|
|
2117
|
-
getDatabase: () => getDatabase
|
|
2138
|
+
getDatabase: () => getDatabase,
|
|
2139
|
+
getDataDir: () => getDataDir
|
|
2118
2140
|
});
|
|
2119
2141
|
import { Database } from "bun:sqlite";
|
|
2120
|
-
import { existsSync, mkdirSync } from "fs";
|
|
2142
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from "fs";
|
|
2121
2143
|
import { dirname, join, resolve } from "path";
|
|
2144
|
+
function getDataDir() {
|
|
2145
|
+
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
2146
|
+
const newDir = join(home, ".hasna", "contacts");
|
|
2147
|
+
const oldDir = join(home, ".contacts");
|
|
2148
|
+
if (existsSync(oldDir) && !existsSync(newDir)) {
|
|
2149
|
+
mkdirSync(newDir, { recursive: true });
|
|
2150
|
+
for (const file of readdirSync(oldDir)) {
|
|
2151
|
+
const oldPath = join(oldDir, file);
|
|
2152
|
+
if (statSync(oldPath).isFile()) {
|
|
2153
|
+
copyFileSync(oldPath, join(newDir, file));
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
mkdirSync(newDir, { recursive: true });
|
|
2158
|
+
return newDir;
|
|
2159
|
+
}
|
|
2122
2160
|
function getDbPath() {
|
|
2161
|
+
if (process.env["HASNA_CONTACTS_DB_PATH"])
|
|
2162
|
+
return process.env["HASNA_CONTACTS_DB_PATH"];
|
|
2123
2163
|
if (process.env["CONTACTS_DB_PATH"])
|
|
2124
2164
|
return process.env["CONTACTS_DB_PATH"];
|
|
2125
|
-
|
|
2126
|
-
return join(home, ".contacts", "contacts.db");
|
|
2165
|
+
return join(getDataDir(), "contacts.db");
|
|
2127
2166
|
}
|
|
2128
2167
|
function ensureDir(filePath) {
|
|
2129
2168
|
if (filePath === ":memory:")
|
|
@@ -4521,7 +4560,7 @@ __export(exports_images, {
|
|
|
4521
4560
|
getImageAsBase64: () => getImageAsBase64,
|
|
4522
4561
|
deleteImage: () => deleteImage
|
|
4523
4562
|
});
|
|
4524
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync3, copyFileSync, unlinkSync, readdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
4563
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, copyFileSync as copyFileSync2, unlinkSync, readdirSync as readdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
4525
4564
|
import { join as join3, extname, basename } from "path";
|
|
4526
4565
|
function ensureImagesDir() {
|
|
4527
4566
|
if (!existsSync3(IMAGES_DIR))
|
|
@@ -4558,12 +4597,12 @@ function saveImage(entityId, source, options) {
|
|
|
4558
4597
|
throw new Error(`Unsupported image format: ${ext}. Supported: ${validExts.join(", ")}`);
|
|
4559
4598
|
}
|
|
4560
4599
|
const filename = `${entityId}.${ext === "jpeg" ? "jpg" : ext}`;
|
|
4561
|
-
|
|
4600
|
+
copyFileSync2(source, join3(IMAGES_DIR, filename));
|
|
4562
4601
|
return filename;
|
|
4563
4602
|
}
|
|
4564
4603
|
function getImagePath(entityId) {
|
|
4565
4604
|
ensureImagesDir();
|
|
4566
|
-
const files =
|
|
4605
|
+
const files = readdirSync2(IMAGES_DIR);
|
|
4567
4606
|
const match = files.find((f) => f.startsWith(`${entityId}.`));
|
|
4568
4607
|
return match ? join3(IMAGES_DIR, match) : null;
|
|
4569
4608
|
}
|
|
@@ -4578,7 +4617,7 @@ function getImageAsBase64(entityId) {
|
|
|
4578
4617
|
}
|
|
4579
4618
|
function deleteImage(entityId) {
|
|
4580
4619
|
ensureImagesDir();
|
|
4581
|
-
const files =
|
|
4620
|
+
const files = readdirSync2(IMAGES_DIR);
|
|
4582
4621
|
let deleted = false;
|
|
4583
4622
|
for (const f of files) {
|
|
4584
4623
|
if (f.startsWith(`${entityId}.`)) {
|
|
@@ -4590,7 +4629,7 @@ function deleteImage(entityId) {
|
|
|
4590
4629
|
}
|
|
4591
4630
|
function listImages() {
|
|
4592
4631
|
ensureImagesDir();
|
|
4593
|
-
const files =
|
|
4632
|
+
const files = readdirSync2(IMAGES_DIR).filter((f) => !f.startsWith("."));
|
|
4594
4633
|
return files.map((f) => ({
|
|
4595
4634
|
entity_id: basename(f, extname(f)),
|
|
4596
4635
|
filename: f,
|
|
@@ -4599,7 +4638,8 @@ function listImages() {
|
|
|
4599
4638
|
}
|
|
4600
4639
|
var IMAGES_DIR;
|
|
4601
4640
|
var init_images = __esm(() => {
|
|
4602
|
-
|
|
4641
|
+
init_database();
|
|
4642
|
+
IMAGES_DIR = join3(getDataDir(), "images");
|
|
4603
4643
|
});
|
|
4604
4644
|
|
|
4605
4645
|
// src/server/serve.ts
|
|
@@ -6206,8 +6246,8 @@ function storeFile(sourcePath, entityId) {
|
|
|
6206
6246
|
function getDocumentFilePath(entityId) {
|
|
6207
6247
|
if (!existsSync5(DOCUMENTS_DIR))
|
|
6208
6248
|
return null;
|
|
6209
|
-
const { readdirSync:
|
|
6210
|
-
const files =
|
|
6249
|
+
const { readdirSync: readdirSync3 } = __require("fs");
|
|
6250
|
+
const files = readdirSync3(DOCUMENTS_DIR);
|
|
6211
6251
|
const match = files.find((f) => f.startsWith(`${entityId}.`) && !f.endsWith(".enc"));
|
|
6212
6252
|
return match ? join5(DOCUMENTS_DIR, match) : null;
|
|
6213
6253
|
}
|
|
@@ -6228,7 +6268,8 @@ function getDocumentsDir() {
|
|
|
6228
6268
|
}
|
|
6229
6269
|
var VAULT_DIR, VAULT_CONFIG, VAULT_SESSION, DOCUMENTS_DIR, SESSION_TTL_MS, _derivedKey = null;
|
|
6230
6270
|
var init_vault = __esm(() => {
|
|
6231
|
-
|
|
6271
|
+
init_database();
|
|
6272
|
+
VAULT_DIR = getDataDir();
|
|
6232
6273
|
VAULT_CONFIG = join5(VAULT_DIR, "vault.json");
|
|
6233
6274
|
VAULT_SESSION = join5(VAULT_DIR, ".vault-session");
|
|
6234
6275
|
DOCUMENTS_DIR = join5(VAULT_DIR, "documents");
|
|
@@ -6818,9 +6859,10 @@ function findNameDuplicates(db) {
|
|
|
6818
6859
|
}
|
|
6819
6860
|
|
|
6820
6861
|
// src/lib/config.ts
|
|
6862
|
+
init_database();
|
|
6821
6863
|
import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
|
|
6822
6864
|
import { join as join2 } from "path";
|
|
6823
|
-
var CONFIG_DIR =
|
|
6865
|
+
var CONFIG_DIR = getDataDir();
|
|
6824
6866
|
var CONFIG_FILE = join2(CONFIG_DIR, "config.json");
|
|
6825
6867
|
function readConfig() {
|
|
6826
6868
|
if (!existsSync2(CONFIG_FILE))
|
|
@@ -6833,7 +6875,7 @@ function readConfig() {
|
|
|
6833
6875
|
}
|
|
6834
6876
|
|
|
6835
6877
|
// src/cli/index.tsx
|
|
6836
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync8, copyFileSync as
|
|
6878
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync8, copyFileSync as copyFileSync3, statSync as statSync2, mkdirSync as mkdirSync5, readdirSync as readdirSync3 } from "fs";
|
|
6837
6879
|
import { extname as extname3, join as join6 } from "path";
|
|
6838
6880
|
function renderTable(headers, rows) {
|
|
6839
6881
|
const colWidths = headers.map((h) => h.length);
|
|
@@ -7569,7 +7611,8 @@ program.command("init").description("Show setup info, stats, and configuration")
|
|
|
7569
7611
|
console.log();
|
|
7570
7612
|
});
|
|
7571
7613
|
program.command("backup").description("Backup the contacts database").option("--output <path>", "Output path").option("--list", "List existing backups").action((opts) => {
|
|
7572
|
-
const
|
|
7614
|
+
const { getDataDir: getDataDir2 } = (init_database(), __toCommonJS(exports_database));
|
|
7615
|
+
const backupDir = join6(getDataDir2(), "backups");
|
|
7573
7616
|
if (opts.list) {
|
|
7574
7617
|
if (!existsSync8(backupDir)) {
|
|
7575
7618
|
console.log(chalk.gray(`
|
|
@@ -7577,7 +7620,7 @@ No backups found.
|
|
|
7577
7620
|
`));
|
|
7578
7621
|
return;
|
|
7579
7622
|
}
|
|
7580
|
-
const files =
|
|
7623
|
+
const files = readdirSync3(backupDir).filter((f) => f.endsWith(".db")).sort().reverse();
|
|
7581
7624
|
if (files.length === 0) {
|
|
7582
7625
|
console.log(chalk.gray(`
|
|
7583
7626
|
No backups found.
|
|
@@ -7589,8 +7632,8 @@ Existing Backups:
|
|
|
7589
7632
|
`));
|
|
7590
7633
|
for (const f of files) {
|
|
7591
7634
|
const filePath = join6(backupDir, f);
|
|
7592
|
-
const size2 =
|
|
7593
|
-
const mtime =
|
|
7635
|
+
const size2 = statSync2(filePath).size;
|
|
7636
|
+
const mtime = statSync2(filePath).mtime.toISOString().slice(0, 19).replace("T", " ");
|
|
7594
7637
|
console.log(` ${chalk.cyan(f)} ${chalk.gray(`${(size2 / 1024).toFixed(1)} KB ${mtime}`)}`);
|
|
7595
7638
|
}
|
|
7596
7639
|
console.log();
|
|
@@ -7606,8 +7649,8 @@ Database not found: ${src}
|
|
|
7606
7649
|
mkdirSync5(backupDir, { recursive: true });
|
|
7607
7650
|
const ts = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
7608
7651
|
const dest = opts.output || join6(backupDir, `contacts-${ts}.db`);
|
|
7609
|
-
|
|
7610
|
-
const size =
|
|
7652
|
+
copyFileSync3(src, dest);
|
|
7653
|
+
const size = statSync2(dest).size;
|
|
7611
7654
|
console.log(chalk.green(`
|
|
7612
7655
|
\u2713 Backed up to ${dest} (${(size / 1024).toFixed(1)} KB)
|
|
7613
7656
|
`));
|
|
@@ -8866,8 +8909,8 @@ photoCmd.command("set <contact-id> <image-path>").description("Set a contact's p
|
|
|
8866
8909
|
try {
|
|
8867
8910
|
const contact = getContact(contactId);
|
|
8868
8911
|
const filename = saveImage2(contactId, imagePath);
|
|
8869
|
-
updateContact(contactId, { avatar_url: `~/.contacts/images/${filename}` });
|
|
8870
|
-
console.log(chalk.green(`Photo set for ${contact.display_name}: ~/.contacts/images/${filename}`));
|
|
8912
|
+
updateContact(contactId, { avatar_url: `~/.hasna/contacts/images/${filename}` });
|
|
8913
|
+
console.log(chalk.green(`Photo set for ${contact.display_name}: ~/.hasna/contacts/images/${filename}`));
|
|
8871
8914
|
} catch (e) {
|
|
8872
8915
|
console.error(chalk.red(e instanceof Error ? e.message : String(e)));
|
|
8873
8916
|
}
|
|
@@ -8920,8 +8963,8 @@ logoCmd.command("set <company-id> <image-path>").description("Set a company's lo
|
|
|
8920
8963
|
}
|
|
8921
8964
|
const filename = saveImage2(companyId, imagePath);
|
|
8922
8965
|
const { updateCompany: updateCompany2 } = (init_companies(), __toCommonJS(exports_companies));
|
|
8923
|
-
updateCompany2(companyId, { logo_url: `~/.contacts/images/${filename}` });
|
|
8924
|
-
console.log(chalk.green(`Logo set for ${company.name}: ~/.contacts/images/${filename}`));
|
|
8966
|
+
updateCompany2(companyId, { logo_url: `~/.hasna/contacts/images/${filename}` });
|
|
8967
|
+
console.log(chalk.green(`Logo set for ${company.name}: ~/.hasna/contacts/images/${filename}`));
|
|
8925
8968
|
} catch (e) {
|
|
8926
8969
|
console.error(chalk.red(e instanceof Error ? e.message : String(e)));
|
|
8927
8970
|
}
|
package/dist/db/database.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAItC,wBAAgB,SAAS,IAAI,MAAM,CAIlC;AAkhBD,wBAAgB,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,CAUnD;AAED,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,wBAAgB,IAAI,IAAI,MAAM,CAE7B;AAED,wBAAgB,GAAG,IAAI,MAAM,CAE5B"}
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAItC,wBAAgB,UAAU,IAAI,MAAM,CAkBnC;AAED,wBAAgB,SAAS,IAAI,MAAM,CAIlC;AAkhBD,wBAAgB,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,CAUnD;AAED,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,wBAAgB,IAAI,IAAI,MAAM,CAE7B;AAED,wBAAgB,GAAG,IAAI,MAAM,CAE5B"}
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
|
+
var __returnValue = (v) => v;
|
|
4
|
+
function __exportSetter(name, newValue) {
|
|
5
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
+
}
|
|
3
7
|
var __export = (target, all) => {
|
|
4
8
|
for (var name in all)
|
|
5
9
|
__defProp(target, name, {
|
|
6
10
|
get: all[name],
|
|
7
11
|
enumerable: true,
|
|
8
12
|
configurable: true,
|
|
9
|
-
set: (
|
|
13
|
+
set: __exportSetter.bind(all, name)
|
|
10
14
|
});
|
|
11
15
|
};
|
|
12
16
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -14,13 +18,30 @@ var __require = import.meta.require;
|
|
|
14
18
|
|
|
15
19
|
// src/db/database.ts
|
|
16
20
|
import { Database } from "bun:sqlite";
|
|
17
|
-
import { existsSync, mkdirSync } from "fs";
|
|
21
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from "fs";
|
|
18
22
|
import { dirname, join, resolve } from "path";
|
|
23
|
+
function getDataDir() {
|
|
24
|
+
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
25
|
+
const newDir = join(home, ".hasna", "contacts");
|
|
26
|
+
const oldDir = join(home, ".contacts");
|
|
27
|
+
if (existsSync(oldDir) && !existsSync(newDir)) {
|
|
28
|
+
mkdirSync(newDir, { recursive: true });
|
|
29
|
+
for (const file of readdirSync(oldDir)) {
|
|
30
|
+
const oldPath = join(oldDir, file);
|
|
31
|
+
if (statSync(oldPath).isFile()) {
|
|
32
|
+
copyFileSync(oldPath, join(newDir, file));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
mkdirSync(newDir, { recursive: true });
|
|
37
|
+
return newDir;
|
|
38
|
+
}
|
|
19
39
|
function getDbPath() {
|
|
40
|
+
if (process.env["HASNA_CONTACTS_DB_PATH"])
|
|
41
|
+
return process.env["HASNA_CONTACTS_DB_PATH"];
|
|
20
42
|
if (process.env["CONTACTS_DB_PATH"])
|
|
21
43
|
return process.env["CONTACTS_DB_PATH"];
|
|
22
|
-
|
|
23
|
-
return join(home, ".contacts", "contacts.db");
|
|
44
|
+
return join(getDataDir(), "contacts.db");
|
|
24
45
|
}
|
|
25
46
|
function ensureDir(filePath) {
|
|
26
47
|
if (filePath === ":memory:")
|
|
@@ -4426,9 +4447,10 @@ async function ingestMeetingParticipants(event, db) {
|
|
|
4426
4447
|
init_contacts();
|
|
4427
4448
|
|
|
4428
4449
|
// src/lib/images.ts
|
|
4429
|
-
|
|
4450
|
+
init_database();
|
|
4451
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, copyFileSync as copyFileSync2, unlinkSync, readdirSync as readdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
4430
4452
|
import { join as join3, extname, basename } from "path";
|
|
4431
|
-
var IMAGES_DIR = join3(
|
|
4453
|
+
var IMAGES_DIR = join3(getDataDir(), "images");
|
|
4432
4454
|
function ensureImagesDir() {
|
|
4433
4455
|
if (!existsSync3(IMAGES_DIR))
|
|
4434
4456
|
mkdirSync2(IMAGES_DIR, { recursive: true });
|
|
@@ -4464,12 +4486,12 @@ function saveImage(entityId, source, options) {
|
|
|
4464
4486
|
throw new Error(`Unsupported image format: ${ext}. Supported: ${validExts.join(", ")}`);
|
|
4465
4487
|
}
|
|
4466
4488
|
const filename = `${entityId}.${ext === "jpeg" ? "jpg" : ext}`;
|
|
4467
|
-
|
|
4489
|
+
copyFileSync2(source, join3(IMAGES_DIR, filename));
|
|
4468
4490
|
return filename;
|
|
4469
4491
|
}
|
|
4470
4492
|
function getImagePath(entityId) {
|
|
4471
4493
|
ensureImagesDir();
|
|
4472
|
-
const files =
|
|
4494
|
+
const files = readdirSync2(IMAGES_DIR);
|
|
4473
4495
|
const match = files.find((f) => f.startsWith(`${entityId}.`));
|
|
4474
4496
|
return match ? join3(IMAGES_DIR, match) : null;
|
|
4475
4497
|
}
|
|
@@ -4484,7 +4506,7 @@ function getImageAsBase64(entityId) {
|
|
|
4484
4506
|
}
|
|
4485
4507
|
function deleteImage(entityId) {
|
|
4486
4508
|
ensureImagesDir();
|
|
4487
|
-
const files =
|
|
4509
|
+
const files = readdirSync2(IMAGES_DIR);
|
|
4488
4510
|
let deleted = false;
|
|
4489
4511
|
for (const f of files) {
|
|
4490
4512
|
if (f.startsWith(`${entityId}.`)) {
|
|
@@ -4496,7 +4518,7 @@ function deleteImage(entityId) {
|
|
|
4496
4518
|
}
|
|
4497
4519
|
function listImages() {
|
|
4498
4520
|
ensureImagesDir();
|
|
4499
|
-
const files =
|
|
4521
|
+
const files = readdirSync2(IMAGES_DIR).filter((f) => !f.startsWith("."));
|
|
4500
4522
|
return files.map((f) => ({
|
|
4501
4523
|
entity_id: basename(f, extname(f)),
|
|
4502
4524
|
filename: f,
|
|
@@ -4504,10 +4526,11 @@ function listImages() {
|
|
|
4504
4526
|
}));
|
|
4505
4527
|
}
|
|
4506
4528
|
// src/lib/vault.ts
|
|
4529
|
+
init_database();
|
|
4507
4530
|
import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2 } from "fs";
|
|
4508
4531
|
import { join as join4 } from "path";
|
|
4509
4532
|
import { createCipheriv, createDecipheriv, randomBytes, pbkdf2Sync, createHash } from "crypto";
|
|
4510
|
-
var VAULT_DIR =
|
|
4533
|
+
var VAULT_DIR = getDataDir();
|
|
4511
4534
|
var VAULT_CONFIG = join4(VAULT_DIR, "vault.json");
|
|
4512
4535
|
var VAULT_SESSION = join4(VAULT_DIR, ".vault-session");
|
|
4513
4536
|
var DOCUMENTS_DIR = join4(VAULT_DIR, "documents");
|
|
@@ -4630,8 +4653,8 @@ function storeFile(sourcePath, entityId) {
|
|
|
4630
4653
|
function getDocumentFilePath(entityId) {
|
|
4631
4654
|
if (!existsSync4(DOCUMENTS_DIR))
|
|
4632
4655
|
return null;
|
|
4633
|
-
const { readdirSync:
|
|
4634
|
-
const files =
|
|
4656
|
+
const { readdirSync: readdirSync3 } = __require("fs");
|
|
4657
|
+
const files = readdirSync3(DOCUMENTS_DIR);
|
|
4635
4658
|
const match = files.find((f) => f.startsWith(`${entityId}.`) && !f.endsWith(".enc"));
|
|
4636
4659
|
return match ? join4(DOCUMENTS_DIR, match) : null;
|
|
4637
4660
|
}
|
package/dist/lib/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAOA,UAAU,cAAc;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,UAAU,IAAI,cAAc,CAG3C;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAGxD"}
|
package/dist/lib/images.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"images.d.ts","sourceRoot":"","sources":["../../src/lib/images.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"images.d.ts","sourceRoot":"","sources":["../../src/lib/images.ts"],"names":[],"mappings":"AAcA,wBAAgB,YAAY,IAAI,MAAM,CAGrC;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5B,MAAM,CAuCR;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK5D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOhE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAWrD;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,KAAK,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAQzF"}
|
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":"AAiDA,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
|
@@ -1,28 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
-
var __create = Object.create;
|
|
4
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
5
3
|
var __defProp = Object.defineProperty;
|
|
6
|
-
var
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
11
|
-
for (let key of __getOwnPropNames(mod))
|
|
12
|
-
if (!__hasOwnProp.call(to, key))
|
|
13
|
-
__defProp(to, key, {
|
|
14
|
-
get: () => mod[key],
|
|
15
|
-
enumerable: true
|
|
16
|
-
});
|
|
17
|
-
return to;
|
|
18
|
-
};
|
|
4
|
+
var __returnValue = (v) => v;
|
|
5
|
+
function __exportSetter(name, newValue) {
|
|
6
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
7
|
+
}
|
|
19
8
|
var __export = (target, all) => {
|
|
20
9
|
for (var name in all)
|
|
21
10
|
__defProp(target, name, {
|
|
22
11
|
get: all[name],
|
|
23
12
|
enumerable: true,
|
|
24
13
|
configurable: true,
|
|
25
|
-
set: (
|
|
14
|
+
set: __exportSetter.bind(all, name)
|
|
26
15
|
});
|
|
27
16
|
};
|
|
28
17
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -30,13 +19,30 @@ var __require = import.meta.require;
|
|
|
30
19
|
|
|
31
20
|
// src/db/database.ts
|
|
32
21
|
import { Database } from "bun:sqlite";
|
|
33
|
-
import { existsSync, mkdirSync } from "fs";
|
|
22
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from "fs";
|
|
34
23
|
import { dirname, join, resolve } from "path";
|
|
24
|
+
function getDataDir() {
|
|
25
|
+
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
26
|
+
const newDir = join(home, ".hasna", "contacts");
|
|
27
|
+
const oldDir = join(home, ".contacts");
|
|
28
|
+
if (existsSync(oldDir) && !existsSync(newDir)) {
|
|
29
|
+
mkdirSync(newDir, { recursive: true });
|
|
30
|
+
for (const file of readdirSync(oldDir)) {
|
|
31
|
+
const oldPath = join(oldDir, file);
|
|
32
|
+
if (statSync(oldPath).isFile()) {
|
|
33
|
+
copyFileSync(oldPath, join(newDir, file));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
mkdirSync(newDir, { recursive: true });
|
|
38
|
+
return newDir;
|
|
39
|
+
}
|
|
35
40
|
function getDbPath() {
|
|
41
|
+
if (process.env["HASNA_CONTACTS_DB_PATH"])
|
|
42
|
+
return process.env["HASNA_CONTACTS_DB_PATH"];
|
|
36
43
|
if (process.env["CONTACTS_DB_PATH"])
|
|
37
44
|
return process.env["CONTACTS_DB_PATH"];
|
|
38
|
-
|
|
39
|
-
return join(home, ".contacts", "contacts.db");
|
|
45
|
+
return join(getDataDir(), "contacts.db");
|
|
40
46
|
}
|
|
41
47
|
function ensureDir(filePath) {
|
|
42
48
|
if (filePath === ":memory:")
|
|
@@ -4251,9 +4257,10 @@ function getCoverageGaps(companyId, db) {
|
|
|
4251
4257
|
}
|
|
4252
4258
|
|
|
4253
4259
|
// src/lib/images.ts
|
|
4254
|
-
|
|
4260
|
+
init_database();
|
|
4261
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, copyFileSync as copyFileSync2, unlinkSync, readdirSync as readdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
4255
4262
|
import { join as join3, extname, basename } from "path";
|
|
4256
|
-
var IMAGES_DIR = join3(
|
|
4263
|
+
var IMAGES_DIR = join3(getDataDir(), "images");
|
|
4257
4264
|
function ensureImagesDir() {
|
|
4258
4265
|
if (!existsSync3(IMAGES_DIR))
|
|
4259
4266
|
mkdirSync2(IMAGES_DIR, { recursive: true });
|
|
@@ -4285,12 +4292,12 @@ function saveImage(entityId, source, options) {
|
|
|
4285
4292
|
throw new Error(`Unsupported image format: ${ext}. Supported: ${validExts.join(", ")}`);
|
|
4286
4293
|
}
|
|
4287
4294
|
const filename = `${entityId}.${ext === "jpeg" ? "jpg" : ext}`;
|
|
4288
|
-
|
|
4295
|
+
copyFileSync2(source, join3(IMAGES_DIR, filename));
|
|
4289
4296
|
return filename;
|
|
4290
4297
|
}
|
|
4291
4298
|
function getImagePath(entityId) {
|
|
4292
4299
|
ensureImagesDir();
|
|
4293
|
-
const files =
|
|
4300
|
+
const files = readdirSync2(IMAGES_DIR);
|
|
4294
4301
|
const match = files.find((f) => f.startsWith(`${entityId}.`));
|
|
4295
4302
|
return match ? join3(IMAGES_DIR, match) : null;
|
|
4296
4303
|
}
|
|
@@ -4305,7 +4312,7 @@ function getImageAsBase64(entityId) {
|
|
|
4305
4312
|
}
|
|
4306
4313
|
function deleteImage(entityId) {
|
|
4307
4314
|
ensureImagesDir();
|
|
4308
|
-
const files =
|
|
4315
|
+
const files = readdirSync2(IMAGES_DIR);
|
|
4309
4316
|
let deleted = false;
|
|
4310
4317
|
for (const f of files) {
|
|
4311
4318
|
if (f.startsWith(`${entityId}.`)) {
|
|
@@ -4317,10 +4324,11 @@ function deleteImage(entityId) {
|
|
|
4317
4324
|
}
|
|
4318
4325
|
|
|
4319
4326
|
// src/lib/vault.ts
|
|
4327
|
+
init_database();
|
|
4320
4328
|
import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2 } from "fs";
|
|
4321
4329
|
import { join as join4 } from "path";
|
|
4322
4330
|
import { createCipheriv, createDecipheriv, randomBytes, pbkdf2Sync, createHash } from "crypto";
|
|
4323
|
-
var VAULT_DIR =
|
|
4331
|
+
var VAULT_DIR = getDataDir();
|
|
4324
4332
|
var VAULT_CONFIG = join4(VAULT_DIR, "vault.json");
|
|
4325
4333
|
var VAULT_SESSION = join4(VAULT_DIR, ".vault-session");
|
|
4326
4334
|
var DOCUMENTS_DIR = join4(VAULT_DIR, "documents");
|
|
@@ -6177,10 +6185,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
6177
6185
|
{ name: "get_deal_team", description: "Get the full buying committee for a deal with contact names and roles.", inputSchema: { type: "object", properties: { deal_id: { type: "string" } }, required: ["deal_id"] } },
|
|
6178
6186
|
{ name: "get_coverage_gaps", description: "Identify coverage gaps in a company account \u2014 missing economic buyer, technical evaluator, or org chart relationships.", inputSchema: { type: "object", properties: { company_id: { type: "string" } }, required: ["company_id"] } },
|
|
6179
6187
|
{ name: "get_recent_contact_events", description: "Polling fallback for change events \u2014 returns recent activity log entries, optionally filtered by event type or date.", inputSchema: { type: "object", properties: { since: { type: "string", description: "ISO 8601 datetime \u2014 only events after this date" }, event_types: { type: "array", items: { type: "string" } } } } },
|
|
6180
|
-
{ name: "set_contact_photo", description: "Set a contact's profile photo. Provide either a local file path or base64-encoded image data (with or without data URI prefix). Stores image in ~/.contacts/images/ and updates avatar_url. Supported formats: jpg, png, gif, webp, svg, avif.", inputSchema: { type: "object", properties: { contact_id: { type: "string" }, image: { type: "string", description: "File path (e.g. /tmp/photo.jpg) OR base64 data (e.g. data:image/png;base64,...) OR raw base64 string" }, format: { type: "string", description: "Image format hint when using raw base64 (jpg, png, webp). Not needed for file paths or data URIs." } }, required: ["contact_id", "image"] } },
|
|
6188
|
+
{ name: "set_contact_photo", description: "Set a contact's profile photo. Provide either a local file path or base64-encoded image data (with or without data URI prefix). Stores image in ~/.hasna/contacts/images/ and updates avatar_url. Supported formats: jpg, png, gif, webp, svg, avif.", inputSchema: { type: "object", properties: { contact_id: { type: "string" }, image: { type: "string", description: "File path (e.g. /tmp/photo.jpg) OR base64 data (e.g. data:image/png;base64,...) OR raw base64 string" }, format: { type: "string", description: "Image format hint when using raw base64 (jpg, png, webp). Not needed for file paths or data URIs." } }, required: ["contact_id", "image"] } },
|
|
6181
6189
|
{ name: "get_contact_photo", description: "Get a contact's profile photo as base64 data URI. Returns null if no photo is set.", inputSchema: { type: "object", properties: { contact_id: { type: "string" } }, required: ["contact_id"] } },
|
|
6182
6190
|
{ name: "delete_contact_photo", description: "Remove a contact's profile photo.", inputSchema: { type: "object", properties: { contact_id: { type: "string" } }, required: ["contact_id"] } },
|
|
6183
|
-
{ name: "set_company_logo", description: "Set a company's logo image. Provide either a local file path or base64-encoded image data. Stores image in ~/.contacts/images/ and updates logo_url.", inputSchema: { type: "object", properties: { company_id: { type: "string" }, image: { type: "string", description: "File path or base64 data" }, format: { type: "string", description: "Image format hint for raw base64" } }, required: ["company_id", "image"] } },
|
|
6191
|
+
{ name: "set_company_logo", description: "Set a company's logo image. Provide either a local file path or base64-encoded image data. Stores image in ~/.hasna/contacts/images/ and updates logo_url.", inputSchema: { type: "object", properties: { company_id: { type: "string" }, image: { type: "string", description: "File path or base64 data" }, format: { type: "string", description: "Image format hint for raw base64" } }, required: ["company_id", "image"] } },
|
|
6184
6192
|
{ name: "get_company_logo", description: "Get a company's logo as base64 data URI.", inputSchema: { type: "object", properties: { company_id: { type: "string" } }, required: ["company_id"] } },
|
|
6185
6193
|
{ name: "delete_company_logo", description: "Remove a company's logo image.", inputSchema: { type: "object", properties: { company_id: { type: "string" } }, required: ["company_id"] } },
|
|
6186
6194
|
{ name: "set_sensitivity", description: "Set a contact's sensitivity level (normal, confidential, restricted). Restricted contacts are hidden from list/search unless explicitly requested.", inputSchema: { type: "object", properties: { contact_id: { type: "string" }, sensitivity: { type: "string", enum: ["normal", "confidential", "restricted"] } }, required: ["contact_id", "sensitivity"] } },
|
|
@@ -6188,7 +6196,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
6188
6196
|
{ name: "vault_unlock", description: "Unlock the vault for this session with a passphrase.", inputSchema: { type: "object", properties: { passphrase: { type: "string" } }, required: ["passphrase"] } },
|
|
6189
6197
|
{ name: "vault_lock", description: "Lock the vault, clearing the encryption key from memory.", inputSchema: { type: "object", properties: {} } },
|
|
6190
6198
|
{ name: "vault_status", description: "Check vault initialization and lock status.", inputSchema: { type: "object", properties: {} } },
|
|
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"] } },
|
|
6199
|
+
{ 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 ~/.hasna/contacts/documents/ for agent access" }, metadata: { type: "object" }, expires_at: { type: "string" } }, required: ["contact_id", "doc_type", "value"] } },
|
|
6192
6200
|
{ 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
6201
|
{ 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
6202
|
{ 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"] } },
|
|
@@ -7694,8 +7702,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
7694
7702
|
const { contact_id, image, format } = a;
|
|
7695
7703
|
const contact = getContact(contact_id);
|
|
7696
7704
|
const filename = saveImage(contact_id, image, { format });
|
|
7697
|
-
updateContact(contact_id, { avatar_url: `~/.contacts/images/${filename}` });
|
|
7698
|
-
return { content: [{ type: "text", text: JSON.stringify({ ok: true, contact_id, filename, avatar_url: `~/.contacts/images/${filename}` }) }] };
|
|
7705
|
+
updateContact(contact_id, { avatar_url: `~/.hasna/contacts/images/${filename}` });
|
|
7706
|
+
return { content: [{ type: "text", text: JSON.stringify({ ok: true, contact_id, filename, avatar_url: `~/.hasna/contacts/images/${filename}` }) }] };
|
|
7699
7707
|
}
|
|
7700
7708
|
case "get_contact_photo": {
|
|
7701
7709
|
const { contact_id } = a;
|
|
@@ -7715,8 +7723,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
7715
7723
|
const { company_id, image, format } = a;
|
|
7716
7724
|
const co = getCompany(company_id);
|
|
7717
7725
|
const filename = saveImage(company_id, image, { format });
|
|
7718
|
-
updateCompany(company_id, { logo_url: `~/.contacts/images/${filename}` });
|
|
7719
|
-
return { content: [{ type: "text", text: JSON.stringify({ ok: true, company_id, filename, logo_url: `~/.contacts/images/${filename}` }) }] };
|
|
7726
|
+
updateCompany(company_id, { logo_url: `~/.hasna/contacts/images/${filename}` });
|
|
7727
|
+
return { content: [{ type: "text", text: JSON.stringify({ ok: true, company_id, filename, logo_url: `~/.hasna/contacts/images/${filename}` }) }] };
|
|
7720
7728
|
}
|
|
7721
7729
|
case "get_company_logo": {
|
|
7722
7730
|
const { company_id } = a;
|
package/dist/server/index.js
CHANGED
|
@@ -1,21 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
-
var __create = Object.create;
|
|
4
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
5
|
-
var __defProp = Object.defineProperty;
|
|
6
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __toESM = (mod, isNodeMode, target) => {
|
|
9
|
-
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
10
|
-
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
11
|
-
for (let key of __getOwnPropNames(mod))
|
|
12
|
-
if (!__hasOwnProp.call(to, key))
|
|
13
|
-
__defProp(to, key, {
|
|
14
|
-
get: () => mod[key],
|
|
15
|
-
enumerable: true
|
|
16
|
-
});
|
|
17
|
-
return to;
|
|
18
|
-
};
|
|
19
3
|
var __require = import.meta.require;
|
|
20
4
|
|
|
21
5
|
// src/server/serve.ts
|
|
@@ -24,13 +8,30 @@ import { join as join3 } from "path";
|
|
|
24
8
|
|
|
25
9
|
// src/db/database.ts
|
|
26
10
|
import { Database } from "bun:sqlite";
|
|
27
|
-
import { existsSync, mkdirSync } from "fs";
|
|
11
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from "fs";
|
|
28
12
|
import { dirname, join, resolve } from "path";
|
|
13
|
+
function getDataDir() {
|
|
14
|
+
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
15
|
+
const newDir = join(home, ".hasna", "contacts");
|
|
16
|
+
const oldDir = join(home, ".contacts");
|
|
17
|
+
if (existsSync(oldDir) && !existsSync(newDir)) {
|
|
18
|
+
mkdirSync(newDir, { recursive: true });
|
|
19
|
+
for (const file of readdirSync(oldDir)) {
|
|
20
|
+
const oldPath = join(oldDir, file);
|
|
21
|
+
if (statSync(oldPath).isFile()) {
|
|
22
|
+
copyFileSync(oldPath, join(newDir, file));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
mkdirSync(newDir, { recursive: true });
|
|
27
|
+
return newDir;
|
|
28
|
+
}
|
|
29
29
|
function getDbPath() {
|
|
30
|
+
if (process.env["HASNA_CONTACTS_DB_PATH"])
|
|
31
|
+
return process.env["HASNA_CONTACTS_DB_PATH"];
|
|
30
32
|
if (process.env["CONTACTS_DB_PATH"])
|
|
31
33
|
return process.env["CONTACTS_DB_PATH"];
|
|
32
|
-
|
|
33
|
-
return join(home, ".contacts", "contacts.db");
|
|
34
|
+
return join(getDataDir(), "contacts.db");
|
|
34
35
|
}
|
|
35
36
|
function ensureDir(filePath) {
|
|
36
37
|
if (filePath === ":memory:")
|
|
@@ -1769,9 +1770,9 @@ async function exportContacts(format, contacts) {
|
|
|
1769
1770
|
}
|
|
1770
1771
|
|
|
1771
1772
|
// src/lib/images.ts
|
|
1772
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync2, copyFileSync, unlinkSync, readdirSync, readFileSync, writeFileSync } from "fs";
|
|
1773
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, copyFileSync as copyFileSync2, unlinkSync, readdirSync as readdirSync2, readFileSync, writeFileSync } from "fs";
|
|
1773
1774
|
import { join as join2, extname, basename } from "path";
|
|
1774
|
-
var IMAGES_DIR = join2(
|
|
1775
|
+
var IMAGES_DIR = join2(getDataDir(), "images");
|
|
1775
1776
|
function ensureImagesDir() {
|
|
1776
1777
|
if (!existsSync2(IMAGES_DIR))
|
|
1777
1778
|
mkdirSync2(IMAGES_DIR, { recursive: true });
|
|
@@ -1807,18 +1808,18 @@ function saveImage(entityId, source, options) {
|
|
|
1807
1808
|
throw new Error(`Unsupported image format: ${ext}. Supported: ${validExts.join(", ")}`);
|
|
1808
1809
|
}
|
|
1809
1810
|
const filename = `${entityId}.${ext === "jpeg" ? "jpg" : ext}`;
|
|
1810
|
-
|
|
1811
|
+
copyFileSync2(source, join2(IMAGES_DIR, filename));
|
|
1811
1812
|
return filename;
|
|
1812
1813
|
}
|
|
1813
1814
|
function getImagePath(entityId) {
|
|
1814
1815
|
ensureImagesDir();
|
|
1815
|
-
const files =
|
|
1816
|
+
const files = readdirSync2(IMAGES_DIR);
|
|
1816
1817
|
const match = files.find((f) => f.startsWith(`${entityId}.`));
|
|
1817
1818
|
return match ? join2(IMAGES_DIR, match) : null;
|
|
1818
1819
|
}
|
|
1819
1820
|
function deleteImage(entityId) {
|
|
1820
1821
|
ensureImagesDir();
|
|
1821
|
-
const files =
|
|
1822
|
+
const files = readdirSync2(IMAGES_DIR);
|
|
1822
1823
|
let deleted = false;
|
|
1823
1824
|
for (const f of files) {
|
|
1824
1825
|
if (f.startsWith(`${entityId}.`)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/contacts",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.6",
|
|
4
4
|
"description": "Contact management for AI coding agents — CLI + MCP + Web",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"address-book"
|
|
44
44
|
],
|
|
45
45
|
"publishConfig": {
|
|
46
|
+
"registry": "https://registry.npmjs.org",
|
|
46
47
|
"access": "public"
|
|
47
48
|
},
|
|
48
49
|
"repository": {
|
|
@@ -59,6 +60,7 @@
|
|
|
59
60
|
"author": "Andrei Hasna <andrei@hasna.com>",
|
|
60
61
|
"license": "Apache-2.0",
|
|
61
62
|
"dependencies": {
|
|
63
|
+
"@hasna/cloud": "^0.1.0",
|
|
62
64
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
63
65
|
"chalk": "^5.4.1",
|
|
64
66
|
"commander": "^13.1.0",
|