@elevasis/core 0.12.0 → 0.13.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.
- package/dist/test-utils/index.d.ts +17 -12
- package/dist/test-utils/index.js +19 -0
- package/package.json +1 -1
- package/src/auth/multi-tenancy/credentials/__tests__/encryption.test.ts +217 -216
- package/src/auth/multi-tenancy/credentials/server/encryption.ts +5 -19
- package/src/auth/multi-tenancy/credentials/server/kek-loader.ts +3 -13
- package/src/auth/multi-tenancy/permissions.ts +12 -5
- package/src/business/acquisition/activity-events.ts +142 -0
- package/src/business/acquisition/api-schemas.ts +694 -689
- package/src/business/acquisition/derive-actions.ts +90 -0
- package/src/business/acquisition/index.ts +111 -109
- package/src/execution/engine/index.ts +434 -434
- package/src/execution/engine/tools/integration/server/adapters/apify/__tests__/apify-run-actor.integration.test.ts +1 -2
- package/src/execution/engine/tools/integration/server/adapters/attio/__tests__/attio-crud.integration.test.ts +0 -1
- package/src/execution/engine/tools/lead-service-types.ts +882 -879
- package/src/execution/engine/tools/registry.ts +699 -700
- package/src/execution/engine/tools/tool-maps.ts +777 -780
- package/src/organization-model/organization-graph.mdx +2 -2
- package/src/platform/constants/versions.ts +1 -1
- package/src/scaffold-registry/index.ts +10 -9
- package/src/scaffold-registry/schema.ts +68 -62
- package/src/supabase/database.types.ts +9 -7
- package/src/test-utils/rls/RLSTestContext.ts +585 -553
|
@@ -1,700 +1,699 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
* Tool Services Registry
|
|
4
|
-
* Global singleton registry for tool services (command queue, task scheduler, integrations)
|
|
5
|
-
*
|
|
6
|
-
* Pattern: Global registry with initialization safety
|
|
7
|
-
* - Initialized once at server startup (apps/api/src/main.ts)
|
|
8
|
-
* - Tools import getToolServices() directly (no dependency injection)
|
|
9
|
-
* - Fail-fast behavior if not initialized
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import type { CreateTaskParams, Task } from '../../../commands/queue/types'
|
|
13
|
-
import type { CreateScheduleInput, TaskSchedule } from '../../scheduler/types'
|
|
14
|
-
import type { CreateNotificationParams, Notification } from '../../../operations/notifications/types'
|
|
15
|
-
import type { ILeadService } from './lead-service-types'
|
|
16
|
-
import type { ExecutionContext } from '../base/types'
|
|
17
|
-
import type {
|
|
18
|
-
ProjectRow,
|
|
19
|
-
ProjectWithCounts,
|
|
20
|
-
ProjectDetail,
|
|
21
|
-
MilestoneRow,
|
|
22
|
-
TaskRow,
|
|
23
|
-
NoteRow
|
|
24
|
-
} from '../../../business/projects'
|
|
25
|
-
import type { DealListItem, DealDetail,
|
|
26
|
-
import type { RecentActivityEntry } from '../../../business/sales/api-schemas'
|
|
27
|
-
import type { Database } from '../../../supabase/database.types'
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Service interfaces for platform tools
|
|
31
|
-
*
|
|
32
|
-
* IMPORTANT: These interfaces define the contract that services must implement
|
|
33
|
-
* Actual service implementations live in apps/api/
|
|
34
|
-
* TypeScript structural typing ensures compatibility without circular dependencies
|
|
35
|
-
*/
|
|
36
|
-
export interface ICommandQueueService {
|
|
37
|
-
/**
|
|
38
|
-
* Create approval/rejection task
|
|
39
|
-
* @see CommandQueueService.createTask (apps/api/src/command-queue/service.ts:23)
|
|
40
|
-
*/
|
|
41
|
-
createTask(params: CreateTaskParams): Promise<Task>
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Delete pending HITL tasks matching metadata filter
|
|
45
|
-
* Uses JSONB containment (.contains()) for metadata-based batch deletion
|
|
46
|
-
* @see CommandQueueService.deleteByMetadata (apps/api/src/commands/queue/service.ts)
|
|
47
|
-
*/
|
|
48
|
-
deleteByMetadata(
|
|
49
|
-
organizationId: string,
|
|
50
|
-
metadata: Record<string, unknown>,
|
|
51
|
-
status?: string
|
|
52
|
-
): Promise<{ deleted: number }>
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface ITaskSchedulerService {
|
|
56
|
-
/**
|
|
57
|
-
* Create a unified schedule
|
|
58
|
-
* @see TaskScheduleService.createSchedule (apps/api/src/task-scheduler/service.ts:190)
|
|
59
|
-
*/
|
|
60
|
-
createSchedule(input: CreateScheduleInput): Promise<TaskSchedule>
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Update anchor time for a relative schedule (used for rescheduling)
|
|
64
|
-
* @see TaskScheduleService.updateAnchor (apps/api/src/task-scheduler/service.ts)
|
|
65
|
-
*/
|
|
66
|
-
updateAnchor(id: string, organizationId: string, anchorAt: string): Promise<TaskSchedule>
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Delete (cancel) a schedule
|
|
70
|
-
* @see TaskScheduleService.deleteSchedule (apps/api/src/task-scheduler/service.ts)
|
|
71
|
-
*/
|
|
72
|
-
deleteSchedule(id: string, organizationId: string): Promise<void>
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Find schedule by idempotency key
|
|
76
|
-
* @see TaskScheduleService.findByIdempotencyKey (apps/api/src/task-scheduler/service.ts)
|
|
77
|
-
*/
|
|
78
|
-
findByIdempotencyKey(organizationId: string, idempotencyKey: string): Promise<TaskSchedule | null>
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Delete (cancel) schedule by idempotency key
|
|
82
|
-
* Finds schedule by key and cancels it. No-op if schedule not found.
|
|
83
|
-
* @see TaskScheduleService.deleteScheduleByIdempotencyKey (apps/api/src/task-scheduler/service.ts)
|
|
84
|
-
*/
|
|
85
|
-
deleteScheduleByIdempotencyKey(idempotencyKey: string, organizationId: string): Promise<void>
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Cancel a schedule by idempotency key (soft delete)
|
|
89
|
-
* Finds schedule by key and sets status to 'cancelled'. No-op if not found.
|
|
90
|
-
* @see TaskScheduleService.cancelScheduleByIdempotencyKey (apps/api/src/execution/task-scheduler/service.ts)
|
|
91
|
-
*/
|
|
92
|
-
cancelScheduleByIdempotencyKey(idempotencyKey: string, organizationId: string): Promise<{ cancelled: boolean }>
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* List schedules with optional metadata filtering
|
|
96
|
-
* Supports JSONB containment for metadata-based queries
|
|
97
|
-
* @see TaskScheduleService.listSchedules (apps/api/src/execution/task-scheduler/service.ts)
|
|
98
|
-
*/
|
|
99
|
-
listSchedules(params: {
|
|
100
|
-
organizationId: string
|
|
101
|
-
targetResourceId?: string
|
|
102
|
-
status?: string
|
|
103
|
-
metadata?: Record<string, unknown>
|
|
104
|
-
limit?: number
|
|
105
|
-
offset?: number
|
|
106
|
-
}): Promise<{ schedules: TaskSchedule[]; total: number }>
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Get a single schedule by ID
|
|
110
|
-
* @see TaskScheduleService.getSchedule (apps/api/src/execution/task-scheduler/service.ts)
|
|
111
|
-
*/
|
|
112
|
-
getSchedule(id: string, organizationId: string): Promise<TaskSchedule | null>
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Cancel a schedule (soft delete - sets status to 'cancelled')
|
|
116
|
-
* @see TaskScheduleService.cancelSchedule (apps/api/src/execution/task-scheduler/service.ts)
|
|
117
|
-
*/
|
|
118
|
-
cancelSchedule(id: string, organizationId: string): Promise<TaskSchedule>
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Cancel all schedules matching metadata filter (batch soft delete)
|
|
122
|
-
* Uses JSONB containment (.contains()) for metadata-based filtering
|
|
123
|
-
* @see TaskScheduleService.cancelSchedulesByMetadata (apps/api/src/execution/task-scheduler/service.ts)
|
|
124
|
-
*/
|
|
125
|
-
cancelSchedulesByMetadata(
|
|
126
|
-
organizationId: string,
|
|
127
|
-
metadata: Record<string, unknown>,
|
|
128
|
-
status?: string
|
|
129
|
-
): Promise<{ cancelled: number }>
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export interface INotificationsService {
|
|
133
|
-
/**
|
|
134
|
-
* Create platform notification for user
|
|
135
|
-
* @see NotificationService.create (apps/api/src/notifications/service.ts:17)
|
|
136
|
-
*/
|
|
137
|
-
create(params: CreateNotificationParams): Promise<Notification>
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export interface ICrmService {
|
|
141
|
-
getRecentActivity(organizationId: string, limit: number): Promise<RecentActivityEntry[]>
|
|
142
|
-
listDeals(organizationId: string, filters?: Partial<ListDealsQuery>): Promise<DealListItem[]>
|
|
143
|
-
getDeal(dealId: string, organizationId: string): Promise<DealDetail | null>
|
|
144
|
-
getDealByEmail(email: string, organizationId: string): Promise<DealDetail | null>
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
type
|
|
169
|
-
type
|
|
170
|
-
type
|
|
171
|
-
type
|
|
172
|
-
type
|
|
173
|
-
type
|
|
174
|
-
type
|
|
175
|
-
type
|
|
176
|
-
type
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
): Promise<
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
): Promise<
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
): Promise<
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
): Promise<TaskRow
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
): Promise<
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
//
|
|
233
|
-
//
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
*
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
//
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
*
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
* -
|
|
288
|
-
* -
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
* @
|
|
292
|
-
* @
|
|
293
|
-
* @
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
*
|
|
312
|
-
* @param
|
|
313
|
-
* @param
|
|
314
|
-
* @
|
|
315
|
-
* @
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
//
|
|
326
|
-
//
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
*
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
*
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
*
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
*
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
*
|
|
422
|
-
*
|
|
423
|
-
*
|
|
424
|
-
*
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
*
|
|
432
|
-
*
|
|
433
|
-
*
|
|
434
|
-
*
|
|
435
|
-
* @param
|
|
436
|
-
* @param
|
|
437
|
-
* @param
|
|
438
|
-
* @param
|
|
439
|
-
* @
|
|
440
|
-
* @
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
//
|
|
453
|
-
//
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
*
|
|
458
|
-
*
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
*
|
|
490
|
-
*
|
|
491
|
-
*
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
*
|
|
497
|
-
*
|
|
498
|
-
*
|
|
499
|
-
*
|
|
500
|
-
*
|
|
501
|
-
*
|
|
502
|
-
*
|
|
503
|
-
*
|
|
504
|
-
*
|
|
505
|
-
* @
|
|
506
|
-
* @
|
|
507
|
-
* @
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
*
|
|
514
|
-
*
|
|
515
|
-
*
|
|
516
|
-
*
|
|
517
|
-
* @
|
|
518
|
-
* @
|
|
519
|
-
* @
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
//
|
|
526
|
-
//
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
type
|
|
530
|
-
type
|
|
531
|
-
type
|
|
532
|
-
type
|
|
533
|
-
type
|
|
534
|
-
type
|
|
535
|
-
type
|
|
536
|
-
type
|
|
537
|
-
type
|
|
538
|
-
type
|
|
539
|
-
type
|
|
540
|
-
type
|
|
541
|
-
type
|
|
542
|
-
type
|
|
543
|
-
type
|
|
544
|
-
type
|
|
545
|
-
type
|
|
546
|
-
type
|
|
547
|
-
type
|
|
548
|
-
type
|
|
549
|
-
type
|
|
550
|
-
type
|
|
551
|
-
type
|
|
552
|
-
type
|
|
553
|
-
type
|
|
554
|
-
type
|
|
555
|
-
type
|
|
556
|
-
type
|
|
557
|
-
type
|
|
558
|
-
type
|
|
559
|
-
type
|
|
560
|
-
type
|
|
561
|
-
type
|
|
562
|
-
type
|
|
563
|
-
type
|
|
564
|
-
type
|
|
565
|
-
type
|
|
566
|
-
type
|
|
567
|
-
type
|
|
568
|
-
type
|
|
569
|
-
type
|
|
570
|
-
type
|
|
571
|
-
type
|
|
572
|
-
type
|
|
573
|
-
type
|
|
574
|
-
type
|
|
575
|
-
type
|
|
576
|
-
type
|
|
577
|
-
type
|
|
578
|
-
type
|
|
579
|
-
type
|
|
580
|
-
type
|
|
581
|
-
type
|
|
582
|
-
type
|
|
583
|
-
type
|
|
584
|
-
type
|
|
585
|
-
type
|
|
586
|
-
type
|
|
587
|
-
type
|
|
588
|
-
type
|
|
589
|
-
type
|
|
590
|
-
type
|
|
591
|
-
type
|
|
592
|
-
type
|
|
593
|
-
type
|
|
594
|
-
type
|
|
595
|
-
type
|
|
596
|
-
type
|
|
597
|
-
type
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
*
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
*
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
*
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
this.
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
*
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
*
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
this.
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
*
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
*
|
|
693
|
-
*
|
|
694
|
-
*
|
|
695
|
-
*
|
|
696
|
-
*
|
|
697
|
-
*
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
export const getToolServices = (): ToolServices => toolServicesRegistry.get()
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Tool Services Registry
|
|
4
|
+
* Global singleton registry for tool services (command queue, task scheduler, integrations)
|
|
5
|
+
*
|
|
6
|
+
* Pattern: Global registry with initialization safety
|
|
7
|
+
* - Initialized once at server startup (apps/api/src/main.ts)
|
|
8
|
+
* - Tools import getToolServices() directly (no dependency injection)
|
|
9
|
+
* - Fail-fast behavior if not initialized
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { CreateTaskParams, Task } from '../../../commands/queue/types'
|
|
13
|
+
import type { CreateScheduleInput, TaskSchedule } from '../../scheduler/types'
|
|
14
|
+
import type { CreateNotificationParams, Notification } from '../../../operations/notifications/types'
|
|
15
|
+
import type { ILeadService } from './lead-service-types'
|
|
16
|
+
import type { ExecutionContext } from '../base/types'
|
|
17
|
+
import type {
|
|
18
|
+
ProjectRow,
|
|
19
|
+
ProjectWithCounts,
|
|
20
|
+
ProjectDetail,
|
|
21
|
+
MilestoneRow,
|
|
22
|
+
TaskRow,
|
|
23
|
+
NoteRow
|
|
24
|
+
} from '../../../business/projects'
|
|
25
|
+
import type { DealListItem, DealDetail, ListDealsQuery } from '../../../business/acquisition'
|
|
26
|
+
import type { RecentActivityEntry } from '../../../business/sales/api-schemas'
|
|
27
|
+
import type { Database } from '../../../supabase/database.types'
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Service interfaces for platform tools
|
|
31
|
+
*
|
|
32
|
+
* IMPORTANT: These interfaces define the contract that services must implement
|
|
33
|
+
* Actual service implementations live in apps/api/
|
|
34
|
+
* TypeScript structural typing ensures compatibility without circular dependencies
|
|
35
|
+
*/
|
|
36
|
+
export interface ICommandQueueService {
|
|
37
|
+
/**
|
|
38
|
+
* Create approval/rejection task
|
|
39
|
+
* @see CommandQueueService.createTask (apps/api/src/command-queue/service.ts:23)
|
|
40
|
+
*/
|
|
41
|
+
createTask(params: CreateTaskParams): Promise<Task>
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Delete pending HITL tasks matching metadata filter
|
|
45
|
+
* Uses JSONB containment (.contains()) for metadata-based batch deletion
|
|
46
|
+
* @see CommandQueueService.deleteByMetadata (apps/api/src/commands/queue/service.ts)
|
|
47
|
+
*/
|
|
48
|
+
deleteByMetadata(
|
|
49
|
+
organizationId: string,
|
|
50
|
+
metadata: Record<string, unknown>,
|
|
51
|
+
status?: string
|
|
52
|
+
): Promise<{ deleted: number }>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface ITaskSchedulerService {
|
|
56
|
+
/**
|
|
57
|
+
* Create a unified schedule
|
|
58
|
+
* @see TaskScheduleService.createSchedule (apps/api/src/task-scheduler/service.ts:190)
|
|
59
|
+
*/
|
|
60
|
+
createSchedule(input: CreateScheduleInput): Promise<TaskSchedule>
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Update anchor time for a relative schedule (used for rescheduling)
|
|
64
|
+
* @see TaskScheduleService.updateAnchor (apps/api/src/task-scheduler/service.ts)
|
|
65
|
+
*/
|
|
66
|
+
updateAnchor(id: string, organizationId: string, anchorAt: string): Promise<TaskSchedule>
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Delete (cancel) a schedule
|
|
70
|
+
* @see TaskScheduleService.deleteSchedule (apps/api/src/task-scheduler/service.ts)
|
|
71
|
+
*/
|
|
72
|
+
deleteSchedule(id: string, organizationId: string): Promise<void>
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Find schedule by idempotency key
|
|
76
|
+
* @see TaskScheduleService.findByIdempotencyKey (apps/api/src/task-scheduler/service.ts)
|
|
77
|
+
*/
|
|
78
|
+
findByIdempotencyKey(organizationId: string, idempotencyKey: string): Promise<TaskSchedule | null>
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Delete (cancel) schedule by idempotency key
|
|
82
|
+
* Finds schedule by key and cancels it. No-op if schedule not found.
|
|
83
|
+
* @see TaskScheduleService.deleteScheduleByIdempotencyKey (apps/api/src/task-scheduler/service.ts)
|
|
84
|
+
*/
|
|
85
|
+
deleteScheduleByIdempotencyKey(idempotencyKey: string, organizationId: string): Promise<void>
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Cancel a schedule by idempotency key (soft delete)
|
|
89
|
+
* Finds schedule by key and sets status to 'cancelled'. No-op if not found.
|
|
90
|
+
* @see TaskScheduleService.cancelScheduleByIdempotencyKey (apps/api/src/execution/task-scheduler/service.ts)
|
|
91
|
+
*/
|
|
92
|
+
cancelScheduleByIdempotencyKey(idempotencyKey: string, organizationId: string): Promise<{ cancelled: boolean }>
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* List schedules with optional metadata filtering
|
|
96
|
+
* Supports JSONB containment for metadata-based queries
|
|
97
|
+
* @see TaskScheduleService.listSchedules (apps/api/src/execution/task-scheduler/service.ts)
|
|
98
|
+
*/
|
|
99
|
+
listSchedules(params: {
|
|
100
|
+
organizationId: string
|
|
101
|
+
targetResourceId?: string
|
|
102
|
+
status?: string
|
|
103
|
+
metadata?: Record<string, unknown>
|
|
104
|
+
limit?: number
|
|
105
|
+
offset?: number
|
|
106
|
+
}): Promise<{ schedules: TaskSchedule[]; total: number }>
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get a single schedule by ID
|
|
110
|
+
* @see TaskScheduleService.getSchedule (apps/api/src/execution/task-scheduler/service.ts)
|
|
111
|
+
*/
|
|
112
|
+
getSchedule(id: string, organizationId: string): Promise<TaskSchedule | null>
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Cancel a schedule (soft delete - sets status to 'cancelled')
|
|
116
|
+
* @see TaskScheduleService.cancelSchedule (apps/api/src/execution/task-scheduler/service.ts)
|
|
117
|
+
*/
|
|
118
|
+
cancelSchedule(id: string, organizationId: string): Promise<TaskSchedule>
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Cancel all schedules matching metadata filter (batch soft delete)
|
|
122
|
+
* Uses JSONB containment (.contains()) for metadata-based filtering
|
|
123
|
+
* @see TaskScheduleService.cancelSchedulesByMetadata (apps/api/src/execution/task-scheduler/service.ts)
|
|
124
|
+
*/
|
|
125
|
+
cancelSchedulesByMetadata(
|
|
126
|
+
organizationId: string,
|
|
127
|
+
metadata: Record<string, unknown>,
|
|
128
|
+
status?: string
|
|
129
|
+
): Promise<{ cancelled: number }>
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface INotificationsService {
|
|
133
|
+
/**
|
|
134
|
+
* Create platform notification for user
|
|
135
|
+
* @see NotificationService.create (apps/api/src/notifications/service.ts:17)
|
|
136
|
+
*/
|
|
137
|
+
create(params: CreateNotificationParams): Promise<Notification>
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export interface ICrmService {
|
|
141
|
+
getRecentActivity(organizationId: string, limit: number): Promise<RecentActivityEntry[]>
|
|
142
|
+
listDeals(organizationId: string, filters?: Partial<ListDealsQuery>): Promise<DealListItem[]>
|
|
143
|
+
getDeal(dealId: string, organizationId: string): Promise<DealDetail | null>
|
|
144
|
+
getDealByEmail(email: string, organizationId: string): Promise<DealDetail | null>
|
|
145
|
+
createDealNote(
|
|
146
|
+
params: import('./lead-service-types').CreateDealNoteParams
|
|
147
|
+
): Promise<import('./lead-service-types').AcqDealNote>
|
|
148
|
+
listDealNotes(
|
|
149
|
+
params: import('./lead-service-types').ListDealNotesParams
|
|
150
|
+
): Promise<import('./lead-service-types').AcqDealNote[]>
|
|
151
|
+
createDealTask(
|
|
152
|
+
params: import('./lead-service-types').CreateDealTaskParams
|
|
153
|
+
): Promise<import('./lead-service-types').AcqDealTask>
|
|
154
|
+
listDealTasks(
|
|
155
|
+
params: import('./lead-service-types').ListDealTasksParams
|
|
156
|
+
): Promise<import('./lead-service-types').AcqDealTask[]>
|
|
157
|
+
listDealTasksDue(
|
|
158
|
+
params: import('./lead-service-types').ListDealTasksDueParams
|
|
159
|
+
): Promise<import('./lead-service-types').AcqDealTask[]>
|
|
160
|
+
completeDealTask(
|
|
161
|
+
params: import('./lead-service-types').CompleteDealTaskParams
|
|
162
|
+
): Promise<import('./lead-service-types').AcqDealTask>
|
|
163
|
+
recordActivity(params: import('./lead-service-types').RecordDealActivityParams): Promise<void>
|
|
164
|
+
deleteDeal(params: import('./lead-service-types').DeleteDealParams): Promise<void>
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
type ProjectInsert = Database['public']['Tables']['prj_projects']['Insert']
|
|
168
|
+
type ProjectUpdate = Database['public']['Tables']['prj_projects']['Update']
|
|
169
|
+
type MilestoneInsert = Database['public']['Tables']['prj_milestones']['Insert']
|
|
170
|
+
type MilestoneUpdate = Database['public']['Tables']['prj_milestones']['Update']
|
|
171
|
+
type TaskInsert = Database['public']['Tables']['prj_tasks']['Insert']
|
|
172
|
+
type TaskUpdate = Database['public']['Tables']['prj_tasks']['Update']
|
|
173
|
+
type NoteInsert = Database['public']['Tables']['prj_notes']['Insert']
|
|
174
|
+
type NoteUpdate = Database['public']['Tables']['prj_notes']['Update']
|
|
175
|
+
type TaskResumeContextRow = Pick<TaskRow, 'id' | 'resume_context' | 'updated_at'>
|
|
176
|
+
type DeleteEntityResult = { id: string; project_id: string }
|
|
177
|
+
|
|
178
|
+
export interface IProjectsService {
|
|
179
|
+
listProjects(
|
|
180
|
+
organizationId: string,
|
|
181
|
+
filters?: {
|
|
182
|
+
kind?: string
|
|
183
|
+
status?: string
|
|
184
|
+
}
|
|
185
|
+
): Promise<ProjectWithCounts[]>
|
|
186
|
+
getProject(id: string, organizationId: string): Promise<ProjectDetail | null>
|
|
187
|
+
createProject(input: Omit<ProjectInsert, 'organization_id'> & { organizationId: string }): Promise<ProjectRow>
|
|
188
|
+
updateProject(
|
|
189
|
+
id: string,
|
|
190
|
+
organizationId: string,
|
|
191
|
+
input: Omit<ProjectUpdate, 'organization_id'>
|
|
192
|
+
): Promise<ProjectRow | null>
|
|
193
|
+
deleteProject(id: string, organizationId: string): Promise<boolean>
|
|
194
|
+
listMilestones(projectId: string, organizationId: string): Promise<MilestoneRow[]>
|
|
195
|
+
createMilestone(
|
|
196
|
+
input: Omit<MilestoneInsert, 'organization_id' | 'project_id'> & {
|
|
197
|
+
organizationId: string
|
|
198
|
+
projectId: string
|
|
199
|
+
}
|
|
200
|
+
): Promise<MilestoneRow>
|
|
201
|
+
updateMilestone(
|
|
202
|
+
id: string,
|
|
203
|
+
organizationId: string,
|
|
204
|
+
input: Omit<MilestoneUpdate, 'organization_id'>
|
|
205
|
+
): Promise<MilestoneRow | null>
|
|
206
|
+
deleteMilestone(id: string, organizationId: string): Promise<DeleteEntityResult | null>
|
|
207
|
+
listTasks(
|
|
208
|
+
projectId: string,
|
|
209
|
+
organizationId: string,
|
|
210
|
+
filters?: {
|
|
211
|
+
status?: string
|
|
212
|
+
milestone_id?: string
|
|
213
|
+
parent_task_id?: string
|
|
214
|
+
}
|
|
215
|
+
): Promise<TaskRow[]>
|
|
216
|
+
getTask(id: string, organizationId: string): Promise<TaskRow | null>
|
|
217
|
+
createTask(input: Omit<TaskInsert, 'organization_id'> & { organizationId: string }): Promise<TaskRow>
|
|
218
|
+
updateTask(id: string, organizationId: string, input: Omit<TaskUpdate, 'organization_id'>): Promise<TaskRow | null>
|
|
219
|
+
deleteTask(id: string, organizationId: string): Promise<DeleteEntityResult | null>
|
|
220
|
+
mergeTaskResumeContext(
|
|
221
|
+
id: string,
|
|
222
|
+
organizationId: string,
|
|
223
|
+
incoming: Record<string, unknown>
|
|
224
|
+
): Promise<TaskResumeContextRow | null>
|
|
225
|
+
listNotes(projectId: string, organizationId: string): Promise<NoteRow[]>
|
|
226
|
+
createNote(input: Omit<NoteInsert, 'organization_id'> & { organizationId: string }): Promise<NoteRow>
|
|
227
|
+
updateNote(id: string, organizationId: string, input: Omit<NoteUpdate, 'organization_id'>): Promise<NoteRow | null>
|
|
228
|
+
deleteNote(id: string, organizationId: string): Promise<DeleteEntityResult | null>
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// =============================================================================
|
|
232
|
+
// Platform Email Service Types
|
|
233
|
+
// =============================================================================
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Parameters for sending platform emails
|
|
237
|
+
* Supports three targeting modes: explicit userIds, role-based, or broadcast
|
|
238
|
+
*/
|
|
239
|
+
export interface SendPlatformEmailParams {
|
|
240
|
+
organizationId: string
|
|
241
|
+
|
|
242
|
+
// Targeting (exactly one required)
|
|
243
|
+
userIds?: string[] // Explicit: specific users
|
|
244
|
+
targetRole?: string // Role-based: users with role
|
|
245
|
+
targetAll?: boolean // Broadcast: all org members
|
|
246
|
+
|
|
247
|
+
// Content
|
|
248
|
+
subject: string
|
|
249
|
+
html?: string
|
|
250
|
+
text?: string
|
|
251
|
+
|
|
252
|
+
// Optional
|
|
253
|
+
replyTo?: string
|
|
254
|
+
tags?: Array<{ name: string; value: string }>
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Result of platform email send operation
|
|
259
|
+
* Follows partial success pattern (similar to BatchProcessResult)
|
|
260
|
+
*/
|
|
261
|
+
export interface EmailResult {
|
|
262
|
+
sent: number // Successfully sent
|
|
263
|
+
failed: number // Failed to send
|
|
264
|
+
errors?: Array<{
|
|
265
|
+
// Only if failed > 0
|
|
266
|
+
userId: string
|
|
267
|
+
email: string
|
|
268
|
+
error: string
|
|
269
|
+
}>
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Platform email service interface
|
|
274
|
+
* Sends transactional emails from notifications@elevasis.io using platform Resend credentials
|
|
275
|
+
*
|
|
276
|
+
* Implementation: apps/api/src/email/service.ts (EmailService class)
|
|
277
|
+
*
|
|
278
|
+
* Multi-tenancy: All operations require organizationId for tenant isolation.
|
|
279
|
+
* User targeting queries filtered by organizationId via org_memberships join.
|
|
280
|
+
*/
|
|
281
|
+
export interface IEmailService {
|
|
282
|
+
/**
|
|
283
|
+
* Send platform email to targeted users within an organization
|
|
284
|
+
*
|
|
285
|
+
* Targeting modes (exactly one required):
|
|
286
|
+
* - userIds: Send to specific users (verified against org membership)
|
|
287
|
+
* - targetRole: Send to users with specific role
|
|
288
|
+
* - targetAll: Send to all org members
|
|
289
|
+
*
|
|
290
|
+
* @param params - Email parameters including targeting and content
|
|
291
|
+
* @returns Result with sent/failed counts and optional errors array
|
|
292
|
+
* @throws Error if no targeting specified or all userIds invalid
|
|
293
|
+
* @see EmailService.send (apps/api/src/email/service.ts)
|
|
294
|
+
*/
|
|
295
|
+
send(params: SendPlatformEmailParams): Promise<EmailResult>
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Credentials service interface
|
|
300
|
+
* Provides access to organization credentials for integration tools
|
|
301
|
+
*
|
|
302
|
+
* Implementation: apps/api/src/multi-tenancy/credentials/service.ts (CredentialsService class)
|
|
303
|
+
*
|
|
304
|
+
* Note: Integration tools only need getCredential() method.
|
|
305
|
+
* Other methods (createCredential, listCredentials, deleteCredential) exist in the implementation
|
|
306
|
+
* but are only used by API handlers.
|
|
307
|
+
*/
|
|
308
|
+
export interface ICredentialsService {
|
|
309
|
+
/**
|
|
310
|
+
* Get decrypted credential for organization and integration
|
|
311
|
+
* @param organizationId - Organization ID from ExecutionContext
|
|
312
|
+
* @param credentialName - Integration name (e.g., 'gmail', 'slack')
|
|
313
|
+
* @param environment - Optional environment filter ('development' | 'production')
|
|
314
|
+
* @returns Decrypted credential object or null if not found
|
|
315
|
+
* @see CredentialsService.getCredential (apps/api/src/multi-tenancy/credentials/service.ts:41-57)
|
|
316
|
+
*/
|
|
317
|
+
getCredential(
|
|
318
|
+
organizationId: string,
|
|
319
|
+
credentialName: string,
|
|
320
|
+
environment?: 'development' | 'production'
|
|
321
|
+
): Promise<Record<string, unknown> | null>
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// =============================================================================
|
|
325
|
+
// Platform Storage Service Types
|
|
326
|
+
// =============================================================================
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Parameters for storage upload
|
|
330
|
+
*/
|
|
331
|
+
export interface StorageUploadParams {
|
|
332
|
+
organizationId: string
|
|
333
|
+
bucket: string
|
|
334
|
+
path: string
|
|
335
|
+
file: Buffer | Blob
|
|
336
|
+
contentType: string
|
|
337
|
+
upsert?: boolean
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Parameters for storage download/delete
|
|
342
|
+
*/
|
|
343
|
+
export interface StorageDownloadParams {
|
|
344
|
+
organizationId: string
|
|
345
|
+
bucket: string
|
|
346
|
+
path: string
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Parameters for creating signed URL
|
|
351
|
+
*/
|
|
352
|
+
export interface StorageSignedUrlParams {
|
|
353
|
+
organizationId: string
|
|
354
|
+
bucket: string
|
|
355
|
+
path: string
|
|
356
|
+
expiresIn: number // seconds
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Result of storage upload operation
|
|
361
|
+
*/
|
|
362
|
+
export interface StorageUploadResult {
|
|
363
|
+
path: string
|
|
364
|
+
fullPath: string
|
|
365
|
+
size: number
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Result of signed URL creation
|
|
370
|
+
*/
|
|
371
|
+
export interface StorageSignedUrlResult {
|
|
372
|
+
signedUrl: string
|
|
373
|
+
path: string
|
|
374
|
+
expiresAt: Date
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Storage service interface for platform storage tools
|
|
379
|
+
* Provides org-scoped file storage using Supabase Storage
|
|
380
|
+
*
|
|
381
|
+
* Implementation: apps/api/src/storage/service.ts (StorageService class)
|
|
382
|
+
*
|
|
383
|
+
* Multi-tenancy: All paths prefixed with organizationId for tenant isolation.
|
|
384
|
+
* RLS policies enforce access at storage.objects level.
|
|
385
|
+
*/
|
|
386
|
+
export interface IStorageService {
|
|
387
|
+
/**
|
|
388
|
+
* Upload file to org-scoped path
|
|
389
|
+
* Path format: {organizationId}/{path}
|
|
390
|
+
* @see StorageService.upload (apps/api/src/storage/service.ts)
|
|
391
|
+
*/
|
|
392
|
+
upload(params: StorageUploadParams): Promise<StorageUploadResult>
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Download file from org-scoped path
|
|
396
|
+
* @see StorageService.download (apps/api/src/storage/service.ts)
|
|
397
|
+
*/
|
|
398
|
+
download(params: StorageDownloadParams): Promise<Blob>
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Create signed URL for temporary access
|
|
402
|
+
* @see StorageService.createSignedUrl (apps/api/src/storage/service.ts)
|
|
403
|
+
*/
|
|
404
|
+
createSignedUrl(params: StorageSignedUrlParams): Promise<StorageSignedUrlResult>
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Delete file from org-scoped path
|
|
408
|
+
* @see StorageService.delete (apps/api/src/storage/service.ts)
|
|
409
|
+
*/
|
|
410
|
+
delete(params: StorageDownloadParams): Promise<void>
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* List files in org-scoped path
|
|
414
|
+
* @see StorageService.list (apps/api/src/storage/service.ts)
|
|
415
|
+
*/
|
|
416
|
+
list(params: { organizationId: string; bucket: string; prefix?: string }): Promise<string[]>
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Integration service interface
|
|
421
|
+
* Provides access to external API integrations (Gmail, Slack, etc.)
|
|
422
|
+
*
|
|
423
|
+
* Implementation: packages/core/src/execution-engine/tools/integration/service.ts
|
|
424
|
+
*
|
|
425
|
+
* Interface Segregation Principle: Only exposes call() method used by consumers.
|
|
426
|
+
* Internal state (adapters, rateLimiter, retryPolicy) hidden from interface.
|
|
427
|
+
*/
|
|
428
|
+
export interface IIntegrationService {
|
|
429
|
+
/**
|
|
430
|
+
* Call integration method with full orchestration
|
|
431
|
+
*
|
|
432
|
+
* Orchestrates: rate limiting, credential retrieval, validation, retry
|
|
433
|
+
*
|
|
434
|
+
* @param integration - Integration adapter type (e.g., 'gmail', 'slack')
|
|
435
|
+
* @param method - Method name (e.g., 'sendEmail', 'postMessage')
|
|
436
|
+
* @param params - Method parameters
|
|
437
|
+
* @param credentialName - Credential name from credentials table
|
|
438
|
+
* @param context - Execution context (required for multi-tenant isolation)
|
|
439
|
+
* @returns Method result
|
|
440
|
+
* @throws ToolingError if call fails
|
|
441
|
+
*/
|
|
442
|
+
call(
|
|
443
|
+
integration: string,
|
|
444
|
+
method: string,
|
|
445
|
+
params: unknown,
|
|
446
|
+
credentialName: string,
|
|
447
|
+
context?: ExecutionContext
|
|
448
|
+
): Promise<unknown>
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// =============================================================================
|
|
452
|
+
// PDF Service Types
|
|
453
|
+
// =============================================================================
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* PDF document structure (schema-driven)
|
|
457
|
+
* Full type definition lives in packages/core/src/pdf/types.ts
|
|
458
|
+
* Using Record<string, unknown> here to avoid circular dependencies
|
|
459
|
+
*/
|
|
460
|
+
export interface PdfRenderParams {
|
|
461
|
+
organizationId: string
|
|
462
|
+
/** Document structure with pages and content blocks */
|
|
463
|
+
document: Record<string, unknown>
|
|
464
|
+
/** Optional theme override (partial merge with default theme) */
|
|
465
|
+
theme?: Record<string, unknown>
|
|
466
|
+
/** Storage destination for rendered PDF */
|
|
467
|
+
storage: {
|
|
468
|
+
bucket: string
|
|
469
|
+
path: string
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Result of PDF render operation
|
|
475
|
+
*/
|
|
476
|
+
export interface PdfRenderResult {
|
|
477
|
+
success: boolean
|
|
478
|
+
/** Signed URL for accessing the rendered PDF */
|
|
479
|
+
pdfUrl: string
|
|
480
|
+
/** PDF file size in bytes */
|
|
481
|
+
size: number
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* PDF service interface for rendering documents
|
|
486
|
+
* Provides pdfmake-based document rendering with optional storage upload
|
|
487
|
+
*
|
|
488
|
+
* Implementation: packages/core/src/pdf/server/pdfmake-service.ts (PdfMakeService class)
|
|
489
|
+
*
|
|
490
|
+
* Multi-tenancy: organizationId used for storage path prefixing.
|
|
491
|
+
* All PDFs stored under org-scoped paths via StorageService.
|
|
492
|
+
*/
|
|
493
|
+
export interface IPdfService {
|
|
494
|
+
/**
|
|
495
|
+
* Render PDF document and upload to storage
|
|
496
|
+
*
|
|
497
|
+
* Flow:
|
|
498
|
+
* 1. Parse document structure
|
|
499
|
+
* 2. Merge theme with defaults
|
|
500
|
+
* 3. Render via pdfmake
|
|
501
|
+
* 4. Upload to storage via StorageService
|
|
502
|
+
* 5. Return signed URL
|
|
503
|
+
*
|
|
504
|
+
* @param params - Render parameters including document, theme, and storage destination
|
|
505
|
+
* @returns Result with signed URL and file size
|
|
506
|
+
* @throws Error if rendering fails or storage upload fails
|
|
507
|
+
* @see PdfMakeService.render (packages/core/src/pdf/server/pdfmake-service.ts)
|
|
508
|
+
*/
|
|
509
|
+
render(params: PdfRenderParams): Promise<PdfRenderResult>
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Render PDF document to buffer (no storage upload)
|
|
513
|
+
*
|
|
514
|
+
* Use this for testing or when you need to handle storage upload separately.
|
|
515
|
+
*
|
|
516
|
+
* @param params - Render parameters (organizationId, document, optional theme)
|
|
517
|
+
* @returns Buffer containing the PDF
|
|
518
|
+
* @throws Error if rendering fails
|
|
519
|
+
* @see PdfMakeService.renderToBuffer (packages/core/src/pdf/server/pdfmake-service.ts)
|
|
520
|
+
*/
|
|
521
|
+
renderToBuffer(params: Omit<PdfRenderParams, 'storage'>): Promise<Buffer>
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// =============================================================================
|
|
525
|
+
// Lead Service Types (extracted to lead-service-types.ts)
|
|
526
|
+
// =============================================================================
|
|
527
|
+
export {
|
|
528
|
+
type AcqList,
|
|
529
|
+
type AcqCompany,
|
|
530
|
+
type AcqContact,
|
|
531
|
+
type PaginationParams,
|
|
532
|
+
type PaginatedResult,
|
|
533
|
+
type CreateListParams,
|
|
534
|
+
type UpdateListParams,
|
|
535
|
+
type CreateCompanyParams,
|
|
536
|
+
type UpdateCompanyParams,
|
|
537
|
+
type UpsertCompanyParams,
|
|
538
|
+
type CompanyFilters,
|
|
539
|
+
type CreateContactParams,
|
|
540
|
+
type UpdateContactParams,
|
|
541
|
+
type UpsertContactParams,
|
|
542
|
+
type ContactFilters,
|
|
543
|
+
type UpsertDealParams,
|
|
544
|
+
type AcqDeal,
|
|
545
|
+
type DealActivityEntry,
|
|
546
|
+
type AddContactsToListParams,
|
|
547
|
+
type AddContactsToListResult,
|
|
548
|
+
type BulkImportParams,
|
|
549
|
+
type BulkImportResult,
|
|
550
|
+
type BulkImportCompanyEntry,
|
|
551
|
+
type BulkImportCompaniesParams,
|
|
552
|
+
type BulkImportCompaniesResult,
|
|
553
|
+
type DeactivateContactsByCompanyParams,
|
|
554
|
+
type DeactivateContactsByCompanyResult,
|
|
555
|
+
type ILeadService,
|
|
556
|
+
type UpdateDiscoveryDataParams,
|
|
557
|
+
type UpdateProposalDataParams,
|
|
558
|
+
type MarkProposalSentParams,
|
|
559
|
+
type MarkProposalReviewedParams,
|
|
560
|
+
type UpdateCloseLostReasonParams,
|
|
561
|
+
type UpdateFeesParams,
|
|
562
|
+
type TransitionItemParams,
|
|
563
|
+
type SetContactNurtureParams,
|
|
564
|
+
type CancelSchedulesAndHitlByEmailParams,
|
|
565
|
+
type CancelHitlByDealIdParams,
|
|
566
|
+
type ClearDealFieldsParams,
|
|
567
|
+
type DeleteDealParams,
|
|
568
|
+
type DealFilters,
|
|
569
|
+
type DealStageSummary,
|
|
570
|
+
type StaleDeal,
|
|
571
|
+
type DealPipelineAnalytics,
|
|
572
|
+
type DealAnalyticsParams,
|
|
573
|
+
type UpsertSocialPostParams,
|
|
574
|
+
type UpsertSocialPostsParams,
|
|
575
|
+
type UpsertSocialPostsResult,
|
|
576
|
+
type AcqDealNote,
|
|
577
|
+
type CreateDealNoteParams,
|
|
578
|
+
type ListDealNotesParams,
|
|
579
|
+
type AcqDealTask,
|
|
580
|
+
type AcqDealTaskKind,
|
|
581
|
+
type CreateDealTaskParams,
|
|
582
|
+
type ListDealTasksParams,
|
|
583
|
+
type ListDealTasksDueParams,
|
|
584
|
+
type CompleteDealTaskParams,
|
|
585
|
+
type RecordDealActivityParams,
|
|
586
|
+
type GetDealByIdParams,
|
|
587
|
+
type GetContactByIdParams,
|
|
588
|
+
type GetCompanyByIdParams,
|
|
589
|
+
type UpdateListConfigParams,
|
|
590
|
+
type UpdateCompanyStageParams,
|
|
591
|
+
type UpdateContactStageParams,
|
|
592
|
+
type AddCompaniesToListParams,
|
|
593
|
+
type AddCompaniesToListResult,
|
|
594
|
+
type RemoveCompaniesFromListParams,
|
|
595
|
+
type RemoveCompaniesFromListResult,
|
|
596
|
+
type RecordListExecutionParams,
|
|
597
|
+
type ListExecutionSummary
|
|
598
|
+
} from './lead-service-types'
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Tool services registry
|
|
602
|
+
* Contains all services available to tools
|
|
603
|
+
*/
|
|
604
|
+
export interface ToolServices {
|
|
605
|
+
commandQueueService: ICommandQueueService | null
|
|
606
|
+
taskSchedulerService: ITaskSchedulerService | null
|
|
607
|
+
notificationsService: INotificationsService | null
|
|
608
|
+
projectsService?: IProjectsService | null
|
|
609
|
+
crmService?: ICrmService | null
|
|
610
|
+
emailService: IEmailService | null
|
|
611
|
+
credentialsService: ICredentialsService | null
|
|
612
|
+
integrationService: IIntegrationService | null
|
|
613
|
+
leadService: ILeadService | null
|
|
614
|
+
storageService: IStorageService | null
|
|
615
|
+
/** PDF rendering service - optional for gradual adoption */
|
|
616
|
+
pdfService?: IPdfService | null
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Global registry for tool services
|
|
621
|
+
* Ensures services are initialized before use
|
|
622
|
+
*/
|
|
623
|
+
class ToolServicesRegistry {
|
|
624
|
+
private services: ToolServices = {
|
|
625
|
+
commandQueueService: null,
|
|
626
|
+
taskSchedulerService: null,
|
|
627
|
+
notificationsService: null,
|
|
628
|
+
projectsService: null,
|
|
629
|
+
crmService: null,
|
|
630
|
+
emailService: null,
|
|
631
|
+
credentialsService: null,
|
|
632
|
+
integrationService: null,
|
|
633
|
+
leadService: null,
|
|
634
|
+
storageService: null,
|
|
635
|
+
pdfService: null
|
|
636
|
+
}
|
|
637
|
+
private initialized = false
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Initialize registry with services
|
|
641
|
+
* @throws Error if already initialized (prevents double initialization)
|
|
642
|
+
*/
|
|
643
|
+
initialize(services: ToolServices): void {
|
|
644
|
+
if (this.initialized) {
|
|
645
|
+
throw new Error('ToolServices already initialized')
|
|
646
|
+
}
|
|
647
|
+
this.services = services
|
|
648
|
+
this.initialized = true
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Get registered services
|
|
653
|
+
* @throws Error if not initialized (fail-fast)
|
|
654
|
+
*/
|
|
655
|
+
get(): ToolServices {
|
|
656
|
+
if (!this.initialized) {
|
|
657
|
+
throw new Error('ToolServices not initialized. Call toolServicesRegistry.initialize() from apps/api/src/main.ts')
|
|
658
|
+
}
|
|
659
|
+
return this.services
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Reset registry (testing only)
|
|
664
|
+
* Allows test isolation by resetting global state
|
|
665
|
+
*/
|
|
666
|
+
reset(): void {
|
|
667
|
+
this.initialized = false
|
|
668
|
+
this.services = {
|
|
669
|
+
commandQueueService: null,
|
|
670
|
+
taskSchedulerService: null,
|
|
671
|
+
notificationsService: null,
|
|
672
|
+
projectsService: null,
|
|
673
|
+
crmService: null,
|
|
674
|
+
emailService: null,
|
|
675
|
+
credentialsService: null,
|
|
676
|
+
integrationService: null,
|
|
677
|
+
leadService: null,
|
|
678
|
+
storageService: null,
|
|
679
|
+
pdfService: null
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Global singleton registry
|
|
686
|
+
* Initialized once at server startup
|
|
687
|
+
*/
|
|
688
|
+
export const toolServicesRegistry = new ToolServicesRegistry()
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Get tool services
|
|
692
|
+
* Convenience function for tools to access services
|
|
693
|
+
*
|
|
694
|
+
* @example
|
|
695
|
+
* const { commandQueue } = getToolServices()
|
|
696
|
+
* if (!commandQueue) throw new Error('CommandQueueService not initialized')
|
|
697
|
+
* await commandQueue.createTask(params)
|
|
698
|
+
*/
|
|
699
|
+
export const getToolServices = (): ToolServices => toolServicesRegistry.get()
|