@zahidzorbaz/tudun 1.1.6 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/dist/domain/dto/customer.d.ts +41 -0
  2. package/dist/domain/dto/customer.js +2 -0
  3. package/dist/domain/dto/customer.js.map +1 -0
  4. package/dist/domain/dto/index.d.ts +2 -0
  5. package/dist/domain/dto/index.js +2 -1
  6. package/dist/domain/dto/index.js.map +1 -1
  7. package/dist/domain/dto/secret.d.ts +18 -0
  8. package/dist/domain/dto/secret.js +2 -0
  9. package/dist/domain/dto/secret.js.map +1 -0
  10. package/dist/domain/ports/crm-service.d.ts +10 -0
  11. package/dist/domain/ports/crm-service.js +2 -0
  12. package/dist/domain/ports/crm-service.js.map +1 -0
  13. package/dist/domain/ports/index.d.ts +2 -0
  14. package/dist/domain/ports/vault-service.d.ts +7 -0
  15. package/dist/domain/ports/vault-service.js +2 -0
  16. package/dist/domain/ports/vault-service.js.map +1 -0
  17. package/dist/domain/ports/whatsapp-service.d.ts +1 -1
  18. package/dist/infrastructure/config/app-config.d.ts +3 -0
  19. package/dist/infrastructure/config/app-config.js +3 -0
  20. package/dist/infrastructure/config/app-config.js.map +1 -1
  21. package/dist/infrastructure/crm/crm.service.d.ts +16 -0
  22. package/dist/infrastructure/crm/crm.service.js +143 -0
  23. package/dist/infrastructure/crm/crm.service.js.map +1 -0
  24. package/dist/infrastructure/crm/embedding.d.ts +1 -0
  25. package/dist/infrastructure/crm/embedding.js +20 -0
  26. package/dist/infrastructure/crm/embedding.js.map +1 -0
  27. package/dist/infrastructure/crm/index.d.ts +2 -0
  28. package/dist/infrastructure/crm/index.js +3 -0
  29. package/dist/infrastructure/crm/index.js.map +1 -0
  30. package/dist/infrastructure/db/database.d.ts +6 -0
  31. package/dist/infrastructure/db/database.js +26 -0
  32. package/dist/infrastructure/db/database.js.map +1 -0
  33. package/dist/infrastructure/db/index.d.ts +1 -0
  34. package/dist/infrastructure/db/index.js +2 -0
  35. package/dist/infrastructure/db/index.js.map +1 -0
  36. package/dist/infrastructure/db/migrations/001-customers.d.ts +2 -0
  37. package/dist/infrastructure/db/migrations/001-customers.js +22 -0
  38. package/dist/infrastructure/db/migrations/001-customers.js.map +1 -0
  39. package/dist/infrastructure/db/migrations/002-customer-notes.d.ts +2 -0
  40. package/dist/infrastructure/db/migrations/002-customer-notes.js +22 -0
  41. package/dist/infrastructure/db/migrations/002-customer-notes.js.map +1 -0
  42. package/dist/infrastructure/db/migrations/003-secrets.d.ts +2 -0
  43. package/dist/infrastructure/db/migrations/003-secrets.js +22 -0
  44. package/dist/infrastructure/db/migrations/003-secrets.js.map +1 -0
  45. package/dist/infrastructure/vault/crypto.d.ts +3 -0
  46. package/dist/infrastructure/vault/crypto.js +37 -0
  47. package/dist/infrastructure/vault/crypto.js.map +1 -0
  48. package/dist/infrastructure/vault/index.d.ts +2 -0
  49. package/dist/infrastructure/vault/index.js +3 -0
  50. package/dist/infrastructure/vault/index.js.map +1 -0
  51. package/dist/infrastructure/vault/vault.service.d.ts +12 -0
  52. package/dist/infrastructure/vault/vault.service.js +70 -0
  53. package/dist/infrastructure/vault/vault.service.js.map +1 -0
  54. package/dist/infrastructure/whatsapp/wpbridge.service.d.ts +1 -1
  55. package/dist/infrastructure/whatsapp/wpbridge.service.js +2 -2
  56. package/dist/infrastructure/whatsapp/wpbridge.service.js.map +1 -1
  57. package/dist/interface/mcp/server.js +32 -0
  58. package/dist/interface/mcp/server.js.map +1 -1
  59. package/dist/interface/mcp/tools/crm/add-customer.d.ts +3 -0
  60. package/dist/interface/mcp/tools/crm/add-customer.js +17 -0
  61. package/dist/interface/mcp/tools/crm/add-customer.js.map +1 -0
  62. package/dist/interface/mcp/tools/crm/add-note.d.ts +3 -0
  63. package/dist/interface/mcp/tools/crm/add-note.js +21 -0
  64. package/dist/interface/mcp/tools/crm/add-note.js.map +1 -0
  65. package/dist/interface/mcp/tools/crm/list-customers.d.ts +3 -0
  66. package/dist/interface/mcp/tools/crm/list-customers.js +23 -0
  67. package/dist/interface/mcp/tools/crm/list-customers.js.map +1 -0
  68. package/dist/interface/mcp/tools/crm/search-notes.d.ts +3 -0
  69. package/dist/interface/mcp/tools/crm/search-notes.js +21 -0
  70. package/dist/interface/mcp/tools/crm/search-notes.js.map +1 -0
  71. package/dist/interface/mcp/tools/crm/update-customer.d.ts +3 -0
  72. package/dist/interface/mcp/tools/crm/update-customer.js +25 -0
  73. package/dist/interface/mcp/tools/crm/update-customer.js.map +1 -0
  74. package/dist/interface/mcp/tools/send-whatsapp-message.js +21 -1
  75. package/dist/interface/mcp/tools/send-whatsapp-message.js.map +1 -1
  76. package/dist/interface/mcp/tools/start-whatsapp-dialog.js +7 -0
  77. package/dist/interface/mcp/tools/start-whatsapp-dialog.js.map +1 -1
  78. package/dist/interface/mcp/tools/vault/add-secret.d.ts +3 -0
  79. package/dist/interface/mcp/tools/vault/add-secret.js +18 -0
  80. package/dist/interface/mcp/tools/vault/add-secret.js.map +1 -0
  81. package/dist/interface/mcp/tools/vault/delete-secret.d.ts +3 -0
  82. package/dist/interface/mcp/tools/vault/delete-secret.js +16 -0
  83. package/dist/interface/mcp/tools/vault/delete-secret.js.map +1 -0
  84. package/dist/interface/mcp/tools/vault/get-secret.d.ts +3 -0
  85. package/dist/interface/mcp/tools/vault/get-secret.js +15 -0
  86. package/dist/interface/mcp/tools/vault/get-secret.js.map +1 -0
  87. package/dist/interface/mcp/tools/vault/list-secrets.d.ts +3 -0
  88. package/dist/interface/mcp/tools/vault/list-secrets.js +20 -0
  89. package/dist/interface/mcp/tools/vault/list-secrets.js.map +1 -0
  90. package/dist/interface/mcp/types.d.ts +4 -0
  91. package/dist/shared/errors/index.d.ts +18 -0
  92. package/dist/shared/errors/index.js +30 -0
  93. package/dist/shared/errors/index.js.map +1 -1
  94. package/package.json +47 -43
@@ -0,0 +1,41 @@
1
+ export interface Customer {
2
+ id: string;
3
+ name: string;
4
+ company?: string;
5
+ contactId?: string;
6
+ pipelineStatus: PipelineStatus;
7
+ createdAt: string;
8
+ updatedAt: string;
9
+ }
10
+ export type PipelineStatus = 'lead' | 'teklif' | 'poc' | 'degerlendirme' | 'kazanildi' | 'kaybedildi';
11
+ export interface CustomerNote {
12
+ id: string;
13
+ customerId: string;
14
+ content: string;
15
+ noteType: NoteType;
16
+ interactionDate: string;
17
+ createdAt: string;
18
+ }
19
+ export type NoteType = 'meeting' | 'call' | 'email' | 'task' | 'general';
20
+ export interface CreateCustomerInput {
21
+ name: string;
22
+ company?: string;
23
+ contactId?: string;
24
+ }
25
+ export interface UpdateCustomerInput {
26
+ name?: string;
27
+ company?: string;
28
+ contactId?: string;
29
+ pipelineStatus?: PipelineStatus;
30
+ }
31
+ export interface AddNoteInput {
32
+ customerId: string;
33
+ content: string;
34
+ noteType: NoteType;
35
+ interactionDate?: string;
36
+ }
37
+ export interface NoteSearchResult {
38
+ note: CustomerNote;
39
+ customerName: string;
40
+ similarity: number;
41
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=customer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"customer.js","sourceRoot":"","sources":["../../../src/domain/dto/customer.ts"],"names":[],"mappings":""}
@@ -5,3 +5,5 @@ export type { EmailInput, EmailResult, EmailListItem, EmailDetail, } from './ema
5
5
  export type { WhatsAppStatus, WhatsAppContact, WhatsAppChat, WhatsAppMessage, SendMessageResult, WhatsAppPermission, NumberCheckResult, } from './whatsapp.js';
6
6
  export type { DialogStartInput, DialogStartResult, DialogInfo, } from './dialog.js';
7
7
  export type { StyleProfile } from './style-profile.js';
8
+ export * from './customer.js';
9
+ export * from './secret.js';
@@ -1,2 +1,3 @@
1
- export {};
1
+ export * from './customer.js';
2
+ export * from './secret.js';
2
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/domain/dto/index.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/domain/dto/index.ts"],"names":[],"mappings":"AA4CA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC"}
@@ -0,0 +1,18 @@
1
+ export interface Secret {
2
+ id: string;
3
+ customerId?: string;
4
+ label: string;
5
+ category: SecretCategory;
6
+ createdAt: string;
7
+ updatedAt: string;
8
+ }
9
+ export type SecretCategory = 'vpn' | 'database' | 'api_key' | 'server' | 'credential' | 'other';
10
+ export interface SecretDetail extends Secret {
11
+ data: Record<string, string>;
12
+ }
13
+ export interface CreateSecretInput {
14
+ label: string;
15
+ category: SecretCategory;
16
+ customerId?: string;
17
+ data: Record<string, string>;
18
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=secret.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret.js","sourceRoot":"","sources":["../../../src/domain/dto/secret.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ import type { Customer, CustomerNote, CreateCustomerInput, UpdateCustomerInput, AddNoteInput, NoteSearchResult, PipelineStatus } from '../dto/customer.js';
2
+ export interface ICrmService {
3
+ addCustomer(input: CreateCustomerInput): Promise<Customer>;
4
+ updateCustomer(id: string, input: UpdateCustomerInput): Promise<Customer>;
5
+ getCustomer(id: string): Promise<Customer>;
6
+ listCustomers(pipelineStatus?: PipelineStatus): Promise<Customer[]>;
7
+ deleteCustomer(id: string): Promise<void>;
8
+ addNote(input: AddNoteInput): Promise<CustomerNote>;
9
+ searchNotes(query: string, customerId?: string, limit?: number): Promise<NoteSearchResult[]>;
10
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=crm-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crm-service.js","sourceRoot":"","sources":["../../../src/domain/ports/crm-service.ts"],"names":[],"mappings":""}
@@ -5,3 +5,5 @@ export type { IContactsService } from './contacts-service.js';
5
5
  export type { IGmailService } from './gmail-service.js';
6
6
  export type { IWhatsAppService } from './whatsapp-service.js';
7
7
  export type { IStyleAnalyzer } from './style-analyzer.js';
8
+ export type { ICrmService } from './crm-service.js';
9
+ export type { IVaultService } from './vault-service.js';
@@ -0,0 +1,7 @@
1
+ import type { Secret, SecretDetail, CreateSecretInput, SecretCategory } from '../dto/secret.js';
2
+ export interface IVaultService {
3
+ addSecret(input: CreateSecretInput): Promise<Secret>;
4
+ getSecret(id: string): Promise<SecretDetail>;
5
+ listSecrets(customerId?: string, category?: SecretCategory): Promise<Secret[]>;
6
+ deleteSecret(id: string): Promise<void>;
7
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=vault-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vault-service.js","sourceRoot":"","sources":["../../../src/domain/ports/vault-service.ts"],"names":[],"mappings":""}
@@ -2,7 +2,7 @@ import type { WhatsAppStatus, WhatsAppContact, WhatsAppChat, WhatsAppMessage, Se
2
2
  import type { DialogStartInput, DialogStartResult, DialogInfo } from '../dto/dialog.js';
3
3
  export interface IWhatsAppService {
4
4
  getStatus(): Promise<WhatsAppStatus>;
5
- sendMessage(number: string, message: string): Promise<SendMessageResult>;
5
+ sendMessage(number: string, message: string, typingDelayMs?: number): Promise<SendMessageResult>;
6
6
  grantPermission(number: string, durationHours: number, name?: string): Promise<WhatsAppPermission>;
7
7
  revokePermission(number: string): Promise<void>;
8
8
  listPermissions(): Promise<WhatsAppPermission[]>;
@@ -4,5 +4,8 @@ export interface AppConfig {
4
4
  tokenPath: string;
5
5
  defaultTimeZone: string;
6
6
  wpBridgeUrl: string;
7
+ dbPath: string;
8
+ openaiApiKey: string;
9
+ vaultKeyPath: string;
7
10
  }
8
11
  export declare function loadConfig(): AppConfig;
@@ -10,6 +10,9 @@ export function loadConfig() {
10
10
  tokenPath: process.env.TOKEN_PATH ?? './data/google-token.json',
11
11
  defaultTimeZone: process.env.DEFAULT_TIMEZONE ?? 'Europe/Istanbul',
12
12
  wpBridgeUrl: process.env.WPBRIDGE_URL ?? 'http://localhost:3100',
13
+ dbPath: process.env.DB_PATH ?? './data/tudun.db',
14
+ openaiApiKey: process.env.OPENAI_API_KEY ?? '',
15
+ vaultKeyPath: process.env.VAULT_KEY_PATH ?? './data/vault.key',
13
16
  };
14
17
  }
15
18
  //# sourceMappingURL=app-config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-config.js","sourceRoot":"","sources":["../../../src/infrastructure/config/app-config.ts"],"names":[],"mappings":"AAQA,MAAM,UAAU,UAAU;IACxB,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IACpD,MAAM,kBAAkB,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;IAE5D,IAAI,CAAC,cAAc,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CACb,+EAA+E,CAChF,CAAC;IACJ,CAAC;IAED,OAAO;QACL,cAAc;QACd,kBAAkB;QAClB,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,0BAA0B;QAC/D,eAAe,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,iBAAiB;QAClE,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,uBAAuB;KACjE,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"app-config.js","sourceRoot":"","sources":["../../../src/infrastructure/config/app-config.ts"],"names":[],"mappings":"AAWA,MAAM,UAAU,UAAU;IACxB,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IACpD,MAAM,kBAAkB,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;IAE5D,IAAI,CAAC,cAAc,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CACb,+EAA+E,CAChF,CAAC;IACJ,CAAC;IAED,OAAO;QACL,cAAc;QACd,kBAAkB;QAClB,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,0BAA0B;QAC/D,eAAe,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,iBAAiB;QAClE,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,uBAAuB;QAChE,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,iBAAiB;QAChD,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE;QAC9C,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,kBAAkB;KAC/D,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { Client } from '@libsql/client';
2
+ import type { ICrmService } from '../../domain/ports/crm-service.js';
3
+ import type { Customer, CustomerNote, CreateCustomerInput, UpdateCustomerInput, AddNoteInput, NoteSearchResult, PipelineStatus } from '../../domain/dto/customer.js';
4
+ export declare class CrmService implements ICrmService {
5
+ private readonly client;
6
+ private readonly openaiApiKey;
7
+ constructor(client: Client, openaiApiKey: string);
8
+ addCustomer(input: CreateCustomerInput): Promise<Customer>;
9
+ updateCustomer(id: string, input: UpdateCustomerInput): Promise<Customer>;
10
+ getCustomer(id: string): Promise<Customer>;
11
+ listCustomers(pipelineStatus?: PipelineStatus): Promise<Customer[]>;
12
+ deleteCustomer(id: string): Promise<void>;
13
+ addNote(input: AddNoteInput): Promise<CustomerNote>;
14
+ searchNotes(query: string, customerId?: string, limit?: number): Promise<NoteSearchResult[]>;
15
+ private rowToCustomer;
16
+ }
@@ -0,0 +1,143 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { CustomerNotFoundError, CrmError } from '../../shared/errors/index.js';
3
+ import { createEmbedding } from './embedding.js';
4
+ export class CrmService {
5
+ client;
6
+ openaiApiKey;
7
+ constructor(client, openaiApiKey) {
8
+ this.client = client;
9
+ this.openaiApiKey = openaiApiKey;
10
+ }
11
+ async addCustomer(input) {
12
+ const id = randomUUID();
13
+ const now = new Date().toISOString();
14
+ await this.client.execute({
15
+ sql: `INSERT INTO customers (id, name, company, contact_id, pipeline_status, created_at, updated_at)
16
+ VALUES (?, ?, ?, ?, 'lead', ?, ?)`,
17
+ args: [id, input.name, input.company ?? null, input.contactId ?? null, now, now],
18
+ });
19
+ return this.getCustomer(id);
20
+ }
21
+ async updateCustomer(id, input) {
22
+ const existing = await this.getCustomer(id);
23
+ const fields = [];
24
+ const args = [];
25
+ if (input.name !== undefined) {
26
+ fields.push('name = ?');
27
+ args.push(input.name);
28
+ }
29
+ if (input.company !== undefined) {
30
+ fields.push('company = ?');
31
+ args.push(input.company);
32
+ }
33
+ if (input.contactId !== undefined) {
34
+ fields.push('contact_id = ?');
35
+ args.push(input.contactId);
36
+ }
37
+ if (input.pipelineStatus !== undefined) {
38
+ fields.push('pipeline_status = ?');
39
+ args.push(input.pipelineStatus);
40
+ }
41
+ if (fields.length === 0)
42
+ return existing;
43
+ fields.push('updated_at = ?');
44
+ args.push(new Date().toISOString());
45
+ args.push(id);
46
+ await this.client.execute({
47
+ sql: `UPDATE customers SET ${fields.join(', ')} WHERE id = ?`,
48
+ args,
49
+ });
50
+ return this.getCustomer(id);
51
+ }
52
+ async getCustomer(id) {
53
+ const result = await this.client.execute({ sql: 'SELECT * FROM customers WHERE id = ?', args: [id] });
54
+ if (result.rows.length === 0)
55
+ throw new CustomerNotFoundError(id);
56
+ return this.rowToCustomer(result.rows[0]);
57
+ }
58
+ async listCustomers(pipelineStatus) {
59
+ const sql = pipelineStatus
60
+ ? 'SELECT * FROM customers WHERE pipeline_status = ? ORDER BY updated_at DESC'
61
+ : 'SELECT * FROM customers ORDER BY updated_at DESC';
62
+ const args = pipelineStatus ? [pipelineStatus] : [];
63
+ const result = await this.client.execute({ sql, args });
64
+ return result.rows.map(row => this.rowToCustomer(row));
65
+ }
66
+ async deleteCustomer(id) {
67
+ await this.getCustomer(id);
68
+ await this.client.execute({ sql: 'DELETE FROM customers WHERE id = ?', args: [id] });
69
+ }
70
+ async addNote(input) {
71
+ await this.getCustomer(input.customerId);
72
+ const id = randomUUID();
73
+ const now = new Date().toISOString();
74
+ const interactionDate = input.interactionDate ?? now;
75
+ let embeddingBlob = null;
76
+ try {
77
+ const embedding = await createEmbedding(input.content, this.openaiApiKey);
78
+ embeddingBlob = Buffer.from(new Float32Array(embedding).buffer);
79
+ }
80
+ catch {
81
+ process.stderr.write(`[Tudun] Warning: embedding failed for note ${id}\n`);
82
+ }
83
+ await this.client.execute({
84
+ sql: `INSERT INTO customer_notes (id, customer_id, content, note_type, embedding, interaction_date, created_at)
85
+ VALUES (?, ?, ?, ?, ?, ?, ?)`,
86
+ args: [id, input.customerId, input.content, input.noteType, embeddingBlob, interactionDate, now],
87
+ });
88
+ return { id, customerId: input.customerId, content: input.content, noteType: input.noteType, interactionDate, createdAt: now };
89
+ }
90
+ async searchNotes(query, customerId, limit = 5) {
91
+ let embedding;
92
+ try {
93
+ embedding = await createEmbedding(query, this.openaiApiKey);
94
+ }
95
+ catch (error) {
96
+ throw new CrmError('Failed to create search embedding', error);
97
+ }
98
+ const vectorParam = `[${embedding.join(',')}]`;
99
+ // NOTE: vector is interpolated because @libsql/client may not support vector params.
100
+ // This is safe — the vector comes from OpenAI API, not user input.
101
+ const sql = customerId
102
+ ? `SELECT n.*, c.name as customer_name,
103
+ vector_distance_cos(n.embedding, vector32('${vectorParam}')) as distance
104
+ FROM customer_notes n
105
+ JOIN customers c ON n.customer_id = c.id
106
+ WHERE n.customer_id = ? AND n.embedding IS NOT NULL
107
+ ORDER BY distance ASC
108
+ LIMIT ?`
109
+ : `SELECT n.*, c.name as customer_name,
110
+ vector_distance_cos(n.embedding, vector32('${vectorParam}')) as distance
111
+ FROM customer_notes n
112
+ JOIN customers c ON n.customer_id = c.id
113
+ WHERE n.embedding IS NOT NULL
114
+ ORDER BY distance ASC
115
+ LIMIT ?`;
116
+ const args = customerId ? [customerId, limit] : [limit];
117
+ const result = await this.client.execute({ sql, args });
118
+ return result.rows.map(row => ({
119
+ note: {
120
+ id: row.id,
121
+ customerId: row.customer_id,
122
+ content: row.content,
123
+ noteType: row.note_type,
124
+ interactionDate: row.interaction_date,
125
+ createdAt: row.created_at,
126
+ },
127
+ customerName: row.customer_name,
128
+ similarity: 1 - row.distance,
129
+ }));
130
+ }
131
+ rowToCustomer(row) {
132
+ return {
133
+ id: row.id,
134
+ name: row.name,
135
+ company: row.company ?? undefined,
136
+ contactId: row.contact_id ?? undefined,
137
+ pipelineStatus: row.pipeline_status,
138
+ createdAt: row.created_at,
139
+ updatedAt: row.updated_at,
140
+ };
141
+ }
142
+ }
143
+ //# sourceMappingURL=crm.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crm.service.js","sourceRoot":"","sources":["../../../src/infrastructure/crm/crm.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function createEmbedding(text: string, apiKey: string): Promise<number[]>;
@@ -0,0 +1,20 @@
1
+ import { EmbeddingError } from '../../shared/errors/index.js';
2
+ const OPENAI_EMBEDDING_URL = 'https://api.openai.com/v1/embeddings';
3
+ const MODEL = 'text-embedding-3-small';
4
+ export async function createEmbedding(text, apiKey) {
5
+ const response = await fetch(OPENAI_EMBEDDING_URL, {
6
+ method: 'POST',
7
+ headers: {
8
+ 'Content-Type': 'application/json',
9
+ Authorization: `Bearer ${apiKey}`,
10
+ },
11
+ body: JSON.stringify({ input: text, model: MODEL }),
12
+ });
13
+ if (!response.ok) {
14
+ const body = await response.text();
15
+ throw new EmbeddingError(`Embedding API error (${response.status}): ${body}`);
16
+ }
17
+ const json = await response.json();
18
+ return json.data[0].embedding;
19
+ }
20
+ //# sourceMappingURL=embedding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedding.js","sourceRoot":"","sources":["../../../src/infrastructure/crm/embedding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,MAAM,oBAAoB,GAAG,sCAAsC,CAAC;AACpE,MAAM,KAAK,GAAG,wBAAwB,CAAC;AAEvC,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAY,EAAE,MAAc;IAChE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,oBAAoB,EAAE;QACjD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,MAAM,EAAE;SAClC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;KACpD,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,MAAM,IAAI,cAAc,CAAC,wBAAwB,QAAQ,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC;IAChF,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAA8C,CAAC;IAC/E,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAChC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { CrmService } from './crm.service.js';
2
+ export { createEmbedding } from './embedding.js';
@@ -0,0 +1,3 @@
1
+ export { CrmService } from './crm.service.js';
2
+ export { createEmbedding } from './embedding.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/infrastructure/crm/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { type Client } from '@libsql/client';
2
+ export interface Database {
3
+ client: Client;
4
+ close(): Promise<void>;
5
+ }
6
+ export declare function createDatabase(dbPath: string): Promise<Database>;
@@ -0,0 +1,26 @@
1
+ import { createClient } from '@libsql/client';
2
+ import { DatabaseError } from '../../shared/errors/index.js';
3
+ import { up as migrateCustomers } from './migrations/001-customers.js';
4
+ import { up as migrateNotes } from './migrations/002-customer-notes.js';
5
+ import { up as migrateSecrets } from './migrations/003-secrets.js';
6
+ const migrations = [migrateCustomers, migrateNotes, migrateSecrets];
7
+ export async function createDatabase(dbPath) {
8
+ try {
9
+ const client = createClient({ url: `file:${dbPath}` });
10
+ await client.execute('PRAGMA foreign_keys = ON');
11
+ await client.execute('PRAGMA journal_mode = WAL');
12
+ for (const migration of migrations) {
13
+ await migration(client);
14
+ }
15
+ return {
16
+ client,
17
+ async close() {
18
+ client.close();
19
+ },
20
+ };
21
+ }
22
+ catch (error) {
23
+ throw new DatabaseError('Failed to initialize database', error);
24
+ }
25
+ }
26
+ //# sourceMappingURL=database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../../../src/infrastructure/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAe,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,EAAE,IAAI,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,EAAE,IAAI,YAAY,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,EAAE,EAAE,IAAI,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAOnE,MAAM,UAAU,GAAG,CAAC,gBAAgB,EAAE,YAAY,EAAE,cAAc,CAAC,CAAC;AAEpE,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAc;IACjD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,GAAG,EAAE,QAAQ,MAAM,EAAE,EAAE,CAAC,CAAC;QAEvD,MAAM,MAAM,CAAC,OAAO,CAAC,0BAA0B,CAAC,CAAC;QACjD,MAAM,MAAM,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAC;QAElD,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,MAAM,SAAS,CAAC,MAAM,CAAC,CAAC;QAC1B,CAAC;QAED,OAAO;YACL,MAAM;YACN,KAAK,CAAC,KAAK;gBACT,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,CAAC;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,aAAa,CAAC,+BAA+B,EAAE,KAAK,CAAC,CAAC;IAClE,CAAC;AACH,CAAC"}
@@ -0,0 +1 @@
1
+ export { createDatabase, type Database } from './database.js';
@@ -0,0 +1,2 @@
1
+ export { createDatabase } from './database.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/infrastructure/db/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAiB,MAAM,eAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Client } from '@libsql/client';
2
+ export declare function up(client: Client): Promise<void>;
@@ -0,0 +1,22 @@
1
+ export async function up(client) {
2
+ await client.execute(`
3
+ CREATE TABLE IF NOT EXISTS customers (
4
+ id TEXT PRIMARY KEY,
5
+ name TEXT NOT NULL,
6
+ company TEXT,
7
+ contact_id TEXT,
8
+ pipeline_status TEXT NOT NULL DEFAULT 'lead',
9
+ created_at TEXT NOT NULL,
10
+ updated_at TEXT NOT NULL
11
+ )
12
+ `);
13
+ await client.execute(`
14
+ CREATE INDEX IF NOT EXISTS idx_customers_pipeline
15
+ ON customers(pipeline_status)
16
+ `);
17
+ await client.execute(`
18
+ CREATE INDEX IF NOT EXISTS idx_customers_company
19
+ ON customers(company)
20
+ `);
21
+ }
22
+ //# sourceMappingURL=001-customers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"001-customers.js","sourceRoot":"","sources":["../../../../src/infrastructure/db/migrations/001-customers.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC,MAAc;IACrC,MAAM,MAAM,CAAC,OAAO,CAAC;;;;;;;;;;GAUpB,CAAC,CAAC;IACH,MAAM,MAAM,CAAC,OAAO,CAAC;;;GAGpB,CAAC,CAAC;IACH,MAAM,MAAM,CAAC,OAAO,CAAC;;;GAGpB,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Client } from '@libsql/client';
2
+ export declare function up(client: Client): Promise<void>;
@@ -0,0 +1,22 @@
1
+ export async function up(client) {
2
+ await client.execute(`
3
+ CREATE TABLE IF NOT EXISTS customer_notes (
4
+ id TEXT PRIMARY KEY,
5
+ customer_id TEXT NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
6
+ content TEXT NOT NULL,
7
+ note_type TEXT NOT NULL,
8
+ embedding F32_BLOB(1536),
9
+ interaction_date TEXT,
10
+ created_at TEXT NOT NULL
11
+ )
12
+ `);
13
+ await client.execute(`
14
+ CREATE INDEX IF NOT EXISTS idx_notes_customer
15
+ ON customer_notes(customer_id)
16
+ `);
17
+ await client.execute(`
18
+ CREATE INDEX IF NOT EXISTS idx_notes_type
19
+ ON customer_notes(note_type)
20
+ `);
21
+ }
22
+ //# sourceMappingURL=002-customer-notes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"002-customer-notes.js","sourceRoot":"","sources":["../../../../src/infrastructure/db/migrations/002-customer-notes.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC,MAAc;IACrC,MAAM,MAAM,CAAC,OAAO,CAAC;;;;;;;;;;GAUpB,CAAC,CAAC;IACH,MAAM,MAAM,CAAC,OAAO,CAAC;;;GAGpB,CAAC,CAAC;IACH,MAAM,MAAM,CAAC,OAAO,CAAC;;;GAGpB,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Client } from '@libsql/client';
2
+ export declare function up(client: Client): Promise<void>;
@@ -0,0 +1,22 @@
1
+ export async function up(client) {
2
+ await client.execute(`
3
+ CREATE TABLE IF NOT EXISTS secrets (
4
+ id TEXT PRIMARY KEY,
5
+ customer_id TEXT REFERENCES customers(id) ON DELETE SET NULL,
6
+ label TEXT NOT NULL,
7
+ category TEXT NOT NULL,
8
+ encrypted_data BLOB NOT NULL,
9
+ created_at TEXT NOT NULL,
10
+ updated_at TEXT NOT NULL
11
+ )
12
+ `);
13
+ await client.execute(`
14
+ CREATE INDEX IF NOT EXISTS idx_secrets_customer
15
+ ON secrets(customer_id)
16
+ `);
17
+ await client.execute(`
18
+ CREATE INDEX IF NOT EXISTS idx_secrets_category
19
+ ON secrets(category)
20
+ `);
21
+ }
22
+ //# sourceMappingURL=003-secrets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"003-secrets.js","sourceRoot":"","sources":["../../../../src/infrastructure/db/migrations/003-secrets.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC,MAAc;IACrC,MAAM,MAAM,CAAC,OAAO,CAAC;;;;;;;;;;GAUpB,CAAC,CAAC;IACH,MAAM,MAAM,CAAC,OAAO,CAAC;;;GAGpB,CAAC,CAAC;IACH,MAAM,MAAM,CAAC,OAAO,CAAC;;;GAGpB,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function ensureKey(keyPath: string): Promise<Buffer>;
2
+ export declare function encrypt(plaintext: string, key: Buffer): Buffer;
3
+ export declare function decrypt(data: Buffer, key: Buffer): string;
@@ -0,0 +1,37 @@
1
+ import { randomBytes, createCipheriv, createDecipheriv } from 'node:crypto';
2
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
3
+ import { existsSync } from 'node:fs';
4
+ import { dirname } from 'node:path';
5
+ import { VaultError } from '../../shared/errors/index.js';
6
+ const ALGORITHM = 'aes-256-gcm';
7
+ const IV_LENGTH = 16;
8
+ const AUTH_TAG_LENGTH = 16;
9
+ export async function ensureKey(keyPath) {
10
+ if (existsSync(keyPath)) {
11
+ const hex = await readFile(keyPath, 'utf-8');
12
+ return Buffer.from(hex.trim(), 'hex');
13
+ }
14
+ const key = randomBytes(32);
15
+ await mkdir(dirname(keyPath), { recursive: true });
16
+ await writeFile(keyPath, key.toString('hex'), 'utf-8');
17
+ return key;
18
+ }
19
+ export function encrypt(plaintext, key) {
20
+ const iv = randomBytes(IV_LENGTH);
21
+ const cipher = createCipheriv(ALGORITHM, key, iv);
22
+ const encrypted = Buffer.concat([cipher.update(plaintext, 'utf-8'), cipher.final()]);
23
+ const authTag = cipher.getAuthTag();
24
+ return Buffer.concat([iv, authTag, encrypted]);
25
+ }
26
+ export function decrypt(data, key) {
27
+ if (data.length < IV_LENGTH + AUTH_TAG_LENGTH + 1) {
28
+ throw new VaultError('Invalid encrypted data');
29
+ }
30
+ const iv = data.subarray(0, IV_LENGTH);
31
+ const authTag = data.subarray(IV_LENGTH, IV_LENGTH + AUTH_TAG_LENGTH);
32
+ const ciphertext = data.subarray(IV_LENGTH + AUTH_TAG_LENGTH);
33
+ const decipher = createDecipheriv(ALGORITHM, key, iv);
34
+ decipher.setAuthTag(authTag);
35
+ return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString('utf-8');
36
+ }
37
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../../src/infrastructure/vault/crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAE1D,MAAM,SAAS,GAAG,aAAa,CAAC;AAChC,MAAM,SAAS,GAAG,EAAE,CAAC;AACrB,MAAM,eAAe,GAAG,EAAE,CAAC;AAE3B,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAe;IAC7C,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACxB,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC7C,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC;IACxC,CAAC;IACD,MAAM,GAAG,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAC5B,MAAM,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,MAAM,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC;IACvD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,SAAiB,EAAE,GAAW;IACpD,MAAM,EAAE,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IAClC,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACrF,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IACpC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,GAAW;IAC/C,IAAI,IAAI,CAAC,MAAM,GAAG,SAAS,GAAG,eAAe,GAAG,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,UAAU,CAAC,wBAAwB,CAAC,CAAC;IACjD,CAAC;IACD,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,eAAe,CAAC,CAAC;IACtE,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,GAAG,eAAe,CAAC,CAAC;IAC9D,MAAM,QAAQ,GAAG,gBAAgB,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IACtD,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAC7B,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AAC1F,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { VaultService } from './vault.service.js';
2
+ export { encrypt, decrypt, ensureKey } from './crypto.js';
@@ -0,0 +1,3 @@
1
+ export { VaultService } from './vault.service.js';
2
+ export { encrypt, decrypt, ensureKey } from './crypto.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/infrastructure/vault/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { Client } from '@libsql/client';
2
+ import type { IVaultService } from '../../domain/ports/vault-service.js';
3
+ import type { Secret, SecretDetail, CreateSecretInput, SecretCategory } from '../../domain/dto/secret.js';
4
+ export declare class VaultService implements IVaultService {
5
+ private readonly client;
6
+ private readonly encryptionKey;
7
+ constructor(client: Client, encryptionKey: Buffer);
8
+ addSecret(input: CreateSecretInput): Promise<Secret>;
9
+ getSecret(id: string): Promise<SecretDetail>;
10
+ listSecrets(customerId?: string, category?: SecretCategory): Promise<Secret[]>;
11
+ deleteSecret(id: string): Promise<void>;
12
+ }