@ebowwa/crm 0.1.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 (187) hide show
  1. package/README.md +174 -0
  2. package/dist/cli/commands/activities.d.ts +11 -0
  3. package/dist/cli/commands/activities.d.ts.map +1 -0
  4. package/dist/cli/commands/activities.js +427 -0
  5. package/dist/cli/commands/activities.js.map +1 -0
  6. package/dist/cli/commands/contacts.d.ts +11 -0
  7. package/dist/cli/commands/contacts.d.ts.map +1 -0
  8. package/dist/cli/commands/contacts.js +458 -0
  9. package/dist/cli/commands/contacts.js.map +1 -0
  10. package/dist/cli/commands/deals.d.ts +11 -0
  11. package/dist/cli/commands/deals.d.ts.map +1 -0
  12. package/dist/cli/commands/deals.js +498 -0
  13. package/dist/cli/commands/deals.js.map +1 -0
  14. package/dist/cli/commands/media.d.ts +11 -0
  15. package/dist/cli/commands/media.d.ts.map +1 -0
  16. package/dist/cli/commands/media.js +417 -0
  17. package/dist/cli/commands/media.js.map +1 -0
  18. package/dist/cli/commands/search.d.ts +11 -0
  19. package/dist/cli/commands/search.d.ts.map +1 -0
  20. package/dist/cli/commands/search.js +346 -0
  21. package/dist/cli/commands/search.js.map +1 -0
  22. package/dist/cli/index.d.ts +13 -0
  23. package/dist/cli/index.d.ts.map +1 -0
  24. package/dist/cli/index.js +173 -0
  25. package/dist/cli/index.js.map +1 -0
  26. package/dist/cli/repl.d.ts +15 -0
  27. package/dist/cli/repl.d.ts.map +1 -0
  28. package/dist/cli/repl.js +318 -0
  29. package/dist/cli/repl.js.map +1 -0
  30. package/dist/cli/utils/config.d.ts +91 -0
  31. package/dist/cli/utils/config.d.ts.map +1 -0
  32. package/dist/cli/utils/config.js +212 -0
  33. package/dist/cli/utils/config.js.map +1 -0
  34. package/dist/cli/utils/output.d.ts +136 -0
  35. package/dist/cli/utils/output.d.ts.map +1 -0
  36. package/dist/cli/utils/output.js +323 -0
  37. package/dist/cli/utils/output.js.map +1 -0
  38. package/dist/cli/utils/prompt.d.ts +81 -0
  39. package/dist/cli/utils/prompt.d.ts.map +1 -0
  40. package/dist/cli/utils/prompt.js +341 -0
  41. package/dist/cli/utils/prompt.js.map +1 -0
  42. package/dist/cli.d.ts +3 -0
  43. package/dist/cli.d.ts.map +1 -0
  44. package/dist/cli.js +8 -0
  45. package/dist/cli.js.map +1 -0
  46. package/dist/core/index.d.ts +6 -0
  47. package/dist/core/index.d.ts.map +1 -0
  48. package/dist/core/index.js +32 -0
  49. package/dist/core/index.js.map +1 -0
  50. package/dist/core/schemas.d.ts +3050 -0
  51. package/dist/core/schemas.d.ts.map +1 -0
  52. package/dist/core/schemas.js +667 -0
  53. package/dist/core/schemas.js.map +1 -0
  54. package/dist/core/types.d.ts +597 -0
  55. package/dist/core/types.d.ts.map +1 -0
  56. package/dist/core/types.js +8 -0
  57. package/dist/core/types.js.map +1 -0
  58. package/dist/index.d.ts +7 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +8 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/mcp/index.d.ts +14 -0
  63. package/dist/mcp/index.d.ts.map +1 -0
  64. package/dist/mcp/index.js +11 -0
  65. package/dist/mcp/index.js.map +1 -0
  66. package/dist/mcp/server.d.ts +13 -0
  67. package/dist/mcp/server.d.ts.map +1 -0
  68. package/dist/mcp/server.js +18 -0
  69. package/dist/mcp/server.js.map +1 -0
  70. package/dist/mcp/storage/client.d.ts +109 -0
  71. package/dist/mcp/storage/client.d.ts.map +1 -0
  72. package/dist/mcp/storage/client.js +355 -0
  73. package/dist/mcp/storage/client.js.map +1 -0
  74. package/dist/mcp/storage/index.d.ts +7 -0
  75. package/dist/mcp/storage/index.d.ts.map +1 -0
  76. package/dist/mcp/storage/index.js +6 -0
  77. package/dist/mcp/storage/index.js.map +1 -0
  78. package/dist/mcp/storage/types.d.ts +44 -0
  79. package/dist/mcp/storage/types.d.ts.map +1 -0
  80. package/dist/mcp/storage/types.js +35 -0
  81. package/dist/mcp/storage/types.js.map +1 -0
  82. package/dist/mcp/tools/definitions.d.ts +16 -0
  83. package/dist/mcp/tools/definitions.d.ts.map +1 -0
  84. package/dist/mcp/tools/definitions.js +914 -0
  85. package/dist/mcp/tools/definitions.js.map +1 -0
  86. package/dist/mcp/tools/handlers.d.ts +50 -0
  87. package/dist/mcp/tools/handlers.d.ts.map +1 -0
  88. package/dist/mcp/tools/handlers.js +760 -0
  89. package/dist/mcp/tools/handlers.js.map +1 -0
  90. package/dist/mcp/tools/index.d.ts +7 -0
  91. package/dist/mcp/tools/index.d.ts.map +1 -0
  92. package/dist/mcp/tools/index.js +6 -0
  93. package/dist/mcp/tools/index.js.map +1 -0
  94. package/dist/mcp/tools/types.d.ts +314 -0
  95. package/dist/mcp/tools/types.d.ts.map +1 -0
  96. package/dist/mcp/tools/types.js +5 -0
  97. package/dist/mcp/tools/types.js.map +1 -0
  98. package/dist/mcp/transports/stdio.d.ts +27 -0
  99. package/dist/mcp/transports/stdio.d.ts.map +1 -0
  100. package/dist/mcp/transports/stdio.js +237 -0
  101. package/dist/mcp/transports/stdio.js.map +1 -0
  102. package/dist/telemetry/index.d.ts +58 -0
  103. package/dist/telemetry/index.d.ts.map +1 -0
  104. package/dist/telemetry/index.js +109 -0
  105. package/dist/telemetry/index.js.map +1 -0
  106. package/dist/telemetry/logger.d.ts +116 -0
  107. package/dist/telemetry/logger.d.ts.map +1 -0
  108. package/dist/telemetry/logger.js +256 -0
  109. package/dist/telemetry/logger.js.map +1 -0
  110. package/dist/telemetry/metrics.d.ts +115 -0
  111. package/dist/telemetry/metrics.d.ts.map +1 -0
  112. package/dist/telemetry/metrics.js +292 -0
  113. package/dist/telemetry/metrics.js.map +1 -0
  114. package/dist/telemetry/tracer.d.ts +227 -0
  115. package/dist/telemetry/tracer.d.ts.map +1 -0
  116. package/dist/telemetry/tracer.js +355 -0
  117. package/dist/telemetry/tracer.js.map +1 -0
  118. package/dist/web/app.d.ts +2 -0
  119. package/dist/web/app.d.ts.map +1 -0
  120. package/dist/web/app.js +115 -0
  121. package/dist/web/app.js.map +1 -0
  122. package/dist/web/components/ContactList.d.ts +3 -0
  123. package/dist/web/components/ContactList.d.ts.map +1 -0
  124. package/dist/web/components/ContactList.js +262 -0
  125. package/dist/web/components/ContactList.js.map +1 -0
  126. package/dist/web/components/Dashboard.d.ts +3 -0
  127. package/dist/web/components/Dashboard.d.ts.map +1 -0
  128. package/dist/web/components/Dashboard.js +158 -0
  129. package/dist/web/components/Dashboard.js.map +1 -0
  130. package/dist/web/components/DealPipeline.d.ts +3 -0
  131. package/dist/web/components/DealPipeline.d.ts.map +1 -0
  132. package/dist/web/components/DealPipeline.js +306 -0
  133. package/dist/web/components/DealPipeline.js.map +1 -0
  134. package/dist/web/index.d.ts +2 -0
  135. package/dist/web/index.d.ts.map +1 -0
  136. package/dist/web/index.js +269 -0
  137. package/dist/web/index.js.map +1 -0
  138. package/dist/web/types.d.ts +75 -0
  139. package/dist/web/types.d.ts.map +1 -0
  140. package/dist/web/types.js +3 -0
  141. package/dist/web/types.js.map +1 -0
  142. package/native/index.d.ts +571 -0
  143. package/native/index.js +687 -0
  144. package/package.json +105 -0
  145. package/src/cli/commands/activities.ts +543 -0
  146. package/src/cli/commands/contacts.ts +563 -0
  147. package/src/cli/commands/deals.ts +637 -0
  148. package/src/cli/commands/media.ts +521 -0
  149. package/src/cli/commands/search.ts +426 -0
  150. package/src/cli/index.ts +203 -0
  151. package/src/cli/repl.ts +379 -0
  152. package/src/cli/utils/config.ts +299 -0
  153. package/src/cli/utils/output.ts +386 -0
  154. package/src/cli/utils/prompt.ts +444 -0
  155. package/src/cli.ts +11 -0
  156. package/src/core/index.ts +184 -0
  157. package/src/core/schemas.ts +770 -0
  158. package/src/core/types.ts +969 -0
  159. package/src/index.ts +8 -0
  160. package/src/mcp/index.ts +17 -0
  161. package/src/mcp/server.ts +26 -0
  162. package/src/mcp/storage/client.ts +408 -0
  163. package/src/mcp/storage/index.ts +7 -0
  164. package/src/mcp/storage/types.ts +72 -0
  165. package/src/mcp/tools/definitions.ts +961 -0
  166. package/src/mcp/tools/handlers.ts +805 -0
  167. package/src/mcp/tools/index.ts +7 -0
  168. package/src/mcp/tools/types.ts +390 -0
  169. package/src/mcp/transports/stdio.ts +225 -0
  170. package/src/telemetry/index.ts +131 -0
  171. package/src/telemetry/logger.ts +318 -0
  172. package/src/telemetry/metrics.ts +393 -0
  173. package/src/telemetry/tracer.ts +487 -0
  174. package/src/web/api/activities.ts +41 -0
  175. package/src/web/api/contacts.ts +114 -0
  176. package/src/web/api/deals.ts +108 -0
  177. package/src/web/api/media.ts +98 -0
  178. package/src/web/app.tsx +143 -0
  179. package/src/web/components/ActivityFeed.tsx +195 -0
  180. package/src/web/components/ContactList.tsx +340 -0
  181. package/src/web/components/Dashboard.tsx +214 -0
  182. package/src/web/components/DealPipeline.tsx +405 -0
  183. package/src/web/components/MediaGallery.tsx +334 -0
  184. package/src/web/index.html +14 -0
  185. package/src/web/index.ts +326 -0
  186. package/src/web/styles/main.css +180 -0
  187. package/src/web/types.ts +311 -0
@@ -0,0 +1,393 @@
1
+ /**
2
+ * Metrics module for CRM system
3
+ *
4
+ * Provides a simple metrics collection system with counters, gauges, and histograms.
5
+ * Supports exporting metrics in Prometheus format.
6
+ */
7
+
8
+ export type MetricType = 'counter' | 'gauge' | 'histogram';
9
+
10
+ export interface MetricLabel {
11
+ [key: string]: string | number;
12
+ }
13
+
14
+ export interface MetricValue {
15
+ value: number;
16
+ timestamp: number;
17
+ labels: MetricLabel;
18
+ }
19
+
20
+ export interface MetricDefinition {
21
+ name: string;
22
+ type: MetricType;
23
+ description: string;
24
+ unit?: string;
25
+ }
26
+
27
+ export interface HistogramOptions {
28
+ buckets?: number[];
29
+ }
30
+
31
+ interface CounterMetric extends MetricDefinition {
32
+ type: 'counter';
33
+ values: MetricValue[];
34
+ }
35
+
36
+ interface GaugeMetric extends MetricDefinition {
37
+ type: 'gauge';
38
+ values: MetricValue[];
39
+ }
40
+
41
+ interface HistogramMetric extends MetricDefinition {
42
+ type: 'histogram';
43
+ values: {
44
+ sum: number;
45
+ count: number;
46
+ buckets: { boundary: number; count: number }[];
47
+ timestamp: number;
48
+ labels: MetricLabel;
49
+ }[];
50
+ options: HistogramOptions;
51
+ }
52
+
53
+ type Metric = CounterMetric | GaugeMetric | HistogramMetric;
54
+
55
+ /**
56
+ * Default histogram buckets (in seconds for latency)
57
+ */
58
+ const DEFAULT_BUCKETS = [0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10];
59
+
60
+ /**
61
+ * Metrics registry for collecting and exporting metrics
62
+ */
63
+ export class MetricsRegistry {
64
+ private readonly metrics: Map<string, Metric> = new Map();
65
+ private readonly prefix: string;
66
+
67
+ constructor(prefix: string = 'crm_') {
68
+ this.prefix = prefix;
69
+ }
70
+
71
+ /**
72
+ * Register a counter metric
73
+ */
74
+ registerCounter(name: string, description: string, unit?: string): void {
75
+ const metricName = this.prefix + name;
76
+ if (this.metrics.has(metricName)) {
77
+ throw new Error(`Metric ${metricName} already registered`);
78
+ }
79
+
80
+ this.metrics.set(metricName, {
81
+ name: metricName,
82
+ type: 'counter',
83
+ description,
84
+ unit,
85
+ values: [],
86
+ });
87
+ }
88
+
89
+ /**
90
+ * Register a gauge metric
91
+ */
92
+ registerGauge(name: string, description: string, unit?: string): void {
93
+ const metricName = this.prefix + name;
94
+ if (this.metrics.has(metricName)) {
95
+ throw new Error(`Metric ${metricName} already registered`);
96
+ }
97
+
98
+ this.metrics.set(metricName, {
99
+ name: metricName,
100
+ type: 'gauge',
101
+ description,
102
+ unit,
103
+ values: [],
104
+ });
105
+ }
106
+
107
+ /**
108
+ * Register a histogram metric
109
+ */
110
+ registerHistogram(
111
+ name: string,
112
+ description: string,
113
+ unit?: string,
114
+ options?: HistogramOptions
115
+ ): void {
116
+ const metricName = this.prefix + name;
117
+ if (this.metrics.has(metricName)) {
118
+ throw new Error(`Metric ${metricName} already registered`);
119
+ }
120
+
121
+ this.metrics.set(metricName, {
122
+ name: metricName,
123
+ type: 'histogram',
124
+ description,
125
+ unit,
126
+ options: { buckets: options?.buckets ?? DEFAULT_BUCKETS },
127
+ values: [],
128
+ });
129
+ }
130
+
131
+ /**
132
+ * Increment a counter
133
+ */
134
+ increment(name: string, value: number = 1, labels: MetricLabel = {}): void {
135
+ const metricName = this.prefix + name;
136
+ const metric = this.metrics.get(metricName);
137
+
138
+ if (!metric) {
139
+ throw new Error(`Metric ${metricName} not found`);
140
+ }
141
+
142
+ if (metric.type !== 'counter') {
143
+ throw new Error(`Metric ${metricName} is not a counter`);
144
+ }
145
+
146
+ const existingIndex = metric.values.findIndex(v =>
147
+ this.labelsMatch(v.labels, labels)
148
+ );
149
+
150
+ if (existingIndex >= 0) {
151
+ metric.values[existingIndex].value += value;
152
+ metric.values[existingIndex].timestamp = Date.now();
153
+ } else {
154
+ metric.values.push({
155
+ value,
156
+ timestamp: Date.now(),
157
+ labels,
158
+ });
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Decrement a counter (useful for error tracking)
164
+ */
165
+ decrement(name: string, value: number = 1, labels: MetricLabel = {}): void {
166
+ this.increment(name, -value, labels);
167
+ }
168
+
169
+ /**
170
+ * Set a gauge value
171
+ */
172
+ gauge(name: string, value: number, labels: MetricLabel = {}): void {
173
+ const metricName = this.prefix + name;
174
+ const metric = this.metrics.get(metricName);
175
+
176
+ if (!metric) {
177
+ throw new Error(`Metric ${metricName} not found`);
178
+ }
179
+
180
+ if (metric.type !== 'gauge') {
181
+ throw new Error(`Metric ${metricName} is not a gauge`);
182
+ }
183
+
184
+ const existingIndex = metric.values.findIndex(v =>
185
+ this.labelsMatch(v.labels, labels)
186
+ );
187
+
188
+ if (existingIndex >= 0) {
189
+ metric.values[existingIndex].value = value;
190
+ metric.values[existingIndex].timestamp = Date.now();
191
+ } else {
192
+ metric.values.push({
193
+ value,
194
+ timestamp: Date.now(),
195
+ labels,
196
+ });
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Observe a value for histogram
202
+ */
203
+ observe(name: string, value: number, labels: MetricLabel = {}): void {
204
+ const metricName = this.prefix + name;
205
+ const metric = this.metrics.get(metricName);
206
+
207
+ if (!metric) {
208
+ throw new Error(`Metric ${metricName} not found`);
209
+ }
210
+
211
+ if (metric.type !== 'histogram') {
212
+ throw new Error(`Metric ${metricName} is not a histogram`);
213
+ }
214
+
215
+ const existingIndex = metric.values.findIndex(v =>
216
+ this.labelsMatch(v.labels, labels)
217
+ );
218
+
219
+ if (existingIndex >= 0) {
220
+ const existing = metric.values[existingIndex];
221
+ existing.sum += value;
222
+ existing.count += 1;
223
+
224
+ for (const bucket of existing.buckets) {
225
+ if (value <= bucket.boundary) {
226
+ bucket.count += 1;
227
+ }
228
+ }
229
+ existing.timestamp = Date.now();
230
+ } else {
231
+ const buckets = metric.options.buckets ?? DEFAULT_BUCKETS;
232
+ metric.values.push({
233
+ sum: value,
234
+ count: 1,
235
+ buckets: buckets.map(boundary => ({
236
+ boundary,
237
+ count: value <= boundary ? 1 : 0,
238
+ })),
239
+ timestamp: Date.now(),
240
+ labels,
241
+ });
242
+ }
243
+ }
244
+
245
+ /**
246
+ * Create a timer that records duration in a histogram
247
+ */
248
+ startTimer(name: string, labels: MetricLabel = {}): () => number {
249
+ const start = process.hrtime.bigint();
250
+
251
+ return () => {
252
+ const end = process.hrtime.bigint();
253
+ const durationSeconds = Number(end - start) / 1e9;
254
+ this.observe(name, durationSeconds, labels);
255
+ return durationSeconds;
256
+ };
257
+ }
258
+
259
+ /**
260
+ * Get all metrics
261
+ */
262
+ getMetrics(): Metric[] {
263
+ return Array.from(this.metrics.values());
264
+ }
265
+
266
+ /**
267
+ * Export metrics in Prometheus text format
268
+ */
269
+ exportPrometheus(): string {
270
+ const lines: string[] = [];
271
+
272
+ for (const metric of this.metrics.values()) {
273
+ // Add help and type comments
274
+ lines.push(`# HELP ${metric.name} ${metric.description}`);
275
+ lines.push(`# TYPE ${metric.name} ${metric.type}`);
276
+
277
+ if (metric.type === 'counter' || metric.type === 'gauge') {
278
+ for (const value of metric.values) {
279
+ const labelStr = this.formatLabels(value.labels);
280
+ lines.push(`${metric.name}${labelStr} ${value.value}`);
281
+ }
282
+ } else if (metric.type === 'histogram') {
283
+ for (const value of metric.values) {
284
+ const labelStr = this.formatLabels(value.labels);
285
+
286
+ // Bucket counts
287
+ for (const bucket of value.buckets) {
288
+ const bucketLabels = { ...value.labels, le: bucket.boundary.toString() };
289
+ const bucketLabelStr = this.formatLabels(bucketLabels);
290
+ lines.push(`${metric.name}_bucket${bucketLabelStr} ${bucket.count}`);
291
+ }
292
+
293
+ // +Inf bucket (same as count)
294
+ const infLabels = { ...value.labels, le: '+Inf' };
295
+ const infLabelStr = this.formatLabels(infLabels);
296
+ lines.push(`${metric.name}_bucket${infLabelStr} ${value.count}`);
297
+
298
+ // Sum and count
299
+ lines.push(`${metric.name}_sum${labelStr} ${value.sum}`);
300
+ lines.push(`${metric.name}_count${labelStr} ${value.count}`);
301
+ }
302
+ }
303
+
304
+ lines.push(''); // Empty line between metrics
305
+ }
306
+
307
+ return lines.join('\n');
308
+ }
309
+
310
+ /**
311
+ * Reset all metrics
312
+ */
313
+ reset(): void {
314
+ for (const metric of this.metrics.values()) {
315
+ if (metric.type === 'histogram') {
316
+ metric.values = [];
317
+ } else {
318
+ metric.values = [];
319
+ }
320
+ }
321
+ }
322
+
323
+ /**
324
+ * Get metric by name
325
+ */
326
+ getMetric(name: string): Metric | undefined {
327
+ return this.metrics.get(this.prefix + name);
328
+ }
329
+
330
+ private labelsMatch(a: MetricLabel, b: MetricLabel): boolean {
331
+ const keysA = Object.keys(a).sort();
332
+ const keysB = Object.keys(b).sort();
333
+
334
+ if (keysA.length !== keysB.length) return false;
335
+
336
+ return keysA.every((key, i) => {
337
+ if (key !== keysB[i]) return false;
338
+ return a[key] === b[key];
339
+ });
340
+ }
341
+
342
+ private formatLabels(labels: MetricLabel): string {
343
+ const entries = Object.entries(labels);
344
+ if (entries.length === 0) return '';
345
+
346
+ const formatted = entries.map(([key, value]) => {
347
+ const escapedValue = String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"');
348
+ return `${key}="${escapedValue}"`;
349
+ });
350
+
351
+ return `{${formatted.join(',')}}`;
352
+ }
353
+ }
354
+
355
+ /**
356
+ * Default metrics registry
357
+ */
358
+ export const metrics = new MetricsRegistry('crm_');
359
+
360
+ /**
361
+ * Pre-registered CRM metrics
362
+ */
363
+ export function initializeCrmMetrics(registry: MetricsRegistry = metrics): void {
364
+ // Contact metrics
365
+ registry.registerCounter('contacts_created_total', 'Total number of contacts created');
366
+ registry.registerCounter('contacts_updated_total', 'Total number of contacts updated');
367
+ registry.registerCounter('contacts_deleted_total', 'Total number of contacts deleted');
368
+ registry.registerGauge('contacts_active_count', 'Current number of active contacts');
369
+
370
+ // Deal metrics
371
+ registry.registerCounter('deals_created_total', 'Total number of deals created');
372
+ registry.registerCounter('deals_won_total', 'Total number of deals won');
373
+ registry.registerCounter('deals_lost_total', 'Total number of deals lost');
374
+ registry.registerGauge('deals_active_count', 'Current number of active deals');
375
+ registry.registerGauge('deals_pipeline_value', 'Total value of deals in pipeline', 'USD');
376
+
377
+ // Activity metrics
378
+ registry.registerCounter('activities_created_total', 'Total number of activities created');
379
+ registry.registerCounter('activities_completed_total', 'Total number of activities completed');
380
+ registry.registerGauge('activities_pending_count', 'Current number of pending activities');
381
+
382
+ // API metrics
383
+ registry.registerHistogram('api_request_duration_seconds', 'API request duration in seconds');
384
+ registry.registerCounter('api_requests_total', 'Total number of API requests');
385
+ registry.registerCounter('api_errors_total', 'Total number of API errors');
386
+
387
+ // Search metrics
388
+ registry.registerHistogram('search_duration_seconds', 'Search query duration in seconds');
389
+ registry.registerCounter('search_queries_total', 'Total number of search queries');
390
+ }
391
+
392
+ // Initialize default metrics
393
+ initializeCrmMetrics();