@automate.ax/integration-contracts 0.148.0 → 0.150.1
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/calcom/events.d.ts +1067 -11
- package/dist/calcom/events.js +66 -32
- package/dist/kit/schemas.js +57 -0
- package/dist/kit/types.d.ts +7 -1
- package/dist/krisp/api.d.ts +61 -0
- package/dist/krisp/api.js +170 -0
- package/dist/krisp/index.d.ts +2 -0
- package/dist/krisp/index.js +2 -0
- package/dist/krisp/schemas.d.ts +145 -0
- package/dist/krisp/schemas.js +97 -0
- package/package.json +8 -2
- package/src/calcom/events.ts +149 -30
- package/src/kit/schemas.ts +64 -0
- package/src/kit/types.ts +15 -1
- package/src/krisp/api.ts +215 -0
- package/src/krisp/index.ts +2 -0
- package/src/krisp/schemas.ts +122 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
const DATE_TIME_SCHEMA = z.iso.datetime({ offset: true });
|
|
3
|
+
/** Krisp profile represented by a personal API key. */
|
|
4
|
+
export const KRISP_PROFILE_SCHEMA = z.object({
|
|
5
|
+
avatar: z.url().nullable().prefault(null),
|
|
6
|
+
email: z.email(),
|
|
7
|
+
firstName: z.string().nullable().prefault(null),
|
|
8
|
+
id: z.number().int().positive(),
|
|
9
|
+
lastName: z.string().nullable().prefault(null),
|
|
10
|
+
teamId: z.number().int().positive(),
|
|
11
|
+
workspaceId: z.number().int().positive(),
|
|
12
|
+
});
|
|
13
|
+
/** Calendar attendee or diarized speaker attached to a Krisp meeting. */
|
|
14
|
+
export const KRISP_PARTICIPANT_SCHEMA = z.object({
|
|
15
|
+
email: z.string().nullable().prefault(null),
|
|
16
|
+
firstName: z.string().nullable().prefault(null),
|
|
17
|
+
lastName: z.string().nullable().prefault(null),
|
|
18
|
+
photo: z.url().nullable().prefault(null),
|
|
19
|
+
status: z.string().nullable().prefault(null),
|
|
20
|
+
});
|
|
21
|
+
/** Meeting metadata returned by Krisp list and detail endpoints. */
|
|
22
|
+
export const KRISP_MEETING_SCHEMA = z.object({
|
|
23
|
+
duration: z.number().int().nonnegative().nullable(),
|
|
24
|
+
id: z.string().min(1),
|
|
25
|
+
ownership: z.enum(["owned", "shared"]),
|
|
26
|
+
participants: KRISP_PARTICIPANT_SCHEMA.array().optional(),
|
|
27
|
+
source: z.string().nullable(),
|
|
28
|
+
startedAt: DATE_TIME_SCHEMA.nullable(),
|
|
29
|
+
status: z.string(),
|
|
30
|
+
tags: z.string().array(),
|
|
31
|
+
title: z.string(),
|
|
32
|
+
});
|
|
33
|
+
/** One diarized transcript segment. Times are seconds from recording start. */
|
|
34
|
+
export const KRISP_TRANSCRIPT_SEGMENT_SCHEMA = z.object({
|
|
35
|
+
end: z.number().nonnegative(),
|
|
36
|
+
speaker: z.number().int().nonnegative(),
|
|
37
|
+
start: z.number().nonnegative(),
|
|
38
|
+
text: z.string(),
|
|
39
|
+
});
|
|
40
|
+
const KRISP_NOTE_ASSIGNEE_SCHEMA = z.record(z.string(), z.json());
|
|
41
|
+
/** Recursive block returned by Krisp's fixed and custom meeting-note sections. */
|
|
42
|
+
export const KRISP_NOTE_BLOCK_SCHEMA = z.lazy(() => z.object({
|
|
43
|
+
assignee: KRISP_NOTE_ASSIGNEE_SCHEMA.nullable().optional(),
|
|
44
|
+
children: KRISP_NOTE_BLOCK_SCHEMA.array().optional(),
|
|
45
|
+
completed: z.boolean().nullable().optional(),
|
|
46
|
+
dueDate: z.string().nullable().optional(),
|
|
47
|
+
text: z.string().optional(),
|
|
48
|
+
type: z.string(),
|
|
49
|
+
}));
|
|
50
|
+
/** Complete Krisp meeting detail with optional heavy transcript and note data. */
|
|
51
|
+
export const KRISP_MEETING_DETAIL_SCHEMA = KRISP_MEETING_SCHEMA.extend({
|
|
52
|
+
language: z.string().nullable().prefault(null),
|
|
53
|
+
meetingType: z.string().nullable().prefault(null),
|
|
54
|
+
notes: z
|
|
55
|
+
.object({ blocks: KRISP_NOTE_BLOCK_SCHEMA.array() })
|
|
56
|
+
.nullable()
|
|
57
|
+
.optional(),
|
|
58
|
+
transcript: z
|
|
59
|
+
.object({
|
|
60
|
+
language: z.string().nullable().prefault(null),
|
|
61
|
+
segments: KRISP_TRANSCRIPT_SEGMENT_SCHEMA.array().prefault([]),
|
|
62
|
+
speakers: z.record(z.string(), KRISP_PARTICIPANT_SCHEMA).prefault({}),
|
|
63
|
+
})
|
|
64
|
+
.nullable()
|
|
65
|
+
.optional(),
|
|
66
|
+
});
|
|
67
|
+
/** One action item extracted from a Krisp meeting. */
|
|
68
|
+
export const KRISP_ACTION_ITEM_SCHEMA = z.object({
|
|
69
|
+
assignee: KRISP_PARTICIPANT_SCHEMA.nullable().prefault(null),
|
|
70
|
+
completed: z.boolean().nullable().prefault(null),
|
|
71
|
+
dueDate: z.string().nullable().prefault(null),
|
|
72
|
+
id: z.string().min(1),
|
|
73
|
+
meetingId: z.string().min(1),
|
|
74
|
+
meetingStartedAt: DATE_TIME_SCHEMA.nullable().prefault(null),
|
|
75
|
+
meetingTitle: z.string(),
|
|
76
|
+
title: z.string(),
|
|
77
|
+
});
|
|
78
|
+
/** Tag used to organize Krisp meetings. */
|
|
79
|
+
export const KRISP_TAG_SCHEMA = z.object({
|
|
80
|
+
color: z.string().nullable(),
|
|
81
|
+
createdAt: DATE_TIME_SCHEMA,
|
|
82
|
+
id: z.string().min(1),
|
|
83
|
+
name: z.string(),
|
|
84
|
+
});
|
|
85
|
+
/** Recording-import identity returned after Krisp issues an upload URL. */
|
|
86
|
+
export const KRISP_IMPORT_SCHEMA = z.object({
|
|
87
|
+
expiresAt: DATE_TIME_SCHEMA,
|
|
88
|
+
importId: z.string().min(1),
|
|
89
|
+
url: z.url(),
|
|
90
|
+
});
|
|
91
|
+
/** Current processing state for a Krisp recording import. */
|
|
92
|
+
export const KRISP_IMPORT_STATUS_SCHEMA = z.object({
|
|
93
|
+
error: z.string().nullable().prefault(null),
|
|
94
|
+
importId: z.string().min(1),
|
|
95
|
+
meetingId: z.string().min(1).nullable().prefault(null),
|
|
96
|
+
status: z.enum(["uploading", "processing", "ready", "failed"]),
|
|
97
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automate.ax/integration-contracts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.150.1",
|
|
4
4
|
"description": "Shared integration payload contracts and provider primitives for Automate.ax.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"./highlevel": "./src/highlevel/index.ts",
|
|
48
48
|
"./jobnimbus": "./src/jobnimbus/index.ts",
|
|
49
49
|
"./linear": "./src/linear/index.ts",
|
|
50
|
+
"./krisp": "./src/krisp/index.ts",
|
|
50
51
|
"./millionverifier": "./src/millionverifier/index.ts",
|
|
51
52
|
"./notion": "./src/notion/index.ts",
|
|
52
53
|
"./outlook": "./src/outlook/index.ts",
|
|
@@ -70,7 +71,7 @@
|
|
|
70
71
|
},
|
|
71
72
|
"dependencies": {
|
|
72
73
|
"@anthropic-ai/sdk": "0.123.0",
|
|
73
|
-
"@automate.ax/codec": "0.
|
|
74
|
+
"@automate.ax/codec": "0.150.1",
|
|
74
75
|
"@cfworker/json-schema": "^4.1.1",
|
|
75
76
|
"@googleapis/calendar": "^16.0.0",
|
|
76
77
|
"@googleapis/forms": "^6.0.1",
|
|
@@ -272,6 +273,11 @@
|
|
|
272
273
|
"types": "./dist/linear/index.d.ts",
|
|
273
274
|
"default": "./dist/linear/index.js"
|
|
274
275
|
},
|
|
276
|
+
"./krisp": {
|
|
277
|
+
"bun": "./src/krisp/index.ts",
|
|
278
|
+
"types": "./dist/krisp/index.d.ts",
|
|
279
|
+
"default": "./dist/krisp/index.js"
|
|
280
|
+
},
|
|
275
281
|
"./millionverifier": {
|
|
276
282
|
"bun": "./src/millionverifier/index.ts",
|
|
277
283
|
"types": "./dist/millionverifier/index.d.ts",
|
package/src/calcom/events.ts
CHANGED
|
@@ -330,7 +330,42 @@ export type CalcomEvent<
|
|
|
330
330
|
}
|
|
331
331
|
: never
|
|
332
332
|
|
|
333
|
-
export const
|
|
333
|
+
export const CALCOM_USER_WEBHOOK_SCOPE_SCHEMA = z.object({
|
|
334
|
+
type: z.literal("user"),
|
|
335
|
+
})
|
|
336
|
+
export const CALCOM_TEAM_WEBHOOK_SCOPE_SCHEMA = z.object({
|
|
337
|
+
eventTypeId: z.number().int().positive(),
|
|
338
|
+
teamId: z.number().int().positive(),
|
|
339
|
+
type: z.literal("team"),
|
|
340
|
+
})
|
|
341
|
+
export const CALCOM_ORGANIZATION_WEBHOOK_SCOPE_SCHEMA = z.object({
|
|
342
|
+
organizationId: z.number().int().positive(),
|
|
343
|
+
type: z.literal("organization"),
|
|
344
|
+
})
|
|
345
|
+
export const CALCOM_WEBHOOK_SCOPE_SCHEMA = z.discriminatedUnion("type", [
|
|
346
|
+
CALCOM_USER_WEBHOOK_SCOPE_SCHEMA,
|
|
347
|
+
CALCOM_TEAM_WEBHOOK_SCOPE_SCHEMA,
|
|
348
|
+
CALCOM_ORGANIZATION_WEBHOOK_SCOPE_SCHEMA,
|
|
349
|
+
])
|
|
350
|
+
export type CalcomWebhookScope = z.output<typeof CALCOM_WEBHOOK_SCOPE_SCHEMA>
|
|
351
|
+
|
|
352
|
+
export const CALCOM_USER_TRIGGER_CONFIG_SCHEMA = z.object({
|
|
353
|
+
scope: CALCOM_USER_WEBHOOK_SCOPE_SCHEMA.optional(),
|
|
354
|
+
})
|
|
355
|
+
export const CALCOM_TEAM_TRIGGER_CONFIG_SCHEMA = z.object({
|
|
356
|
+
scope: z
|
|
357
|
+
.discriminatedUnion("type", [
|
|
358
|
+
CALCOM_USER_WEBHOOK_SCOPE_SCHEMA,
|
|
359
|
+
CALCOM_TEAM_WEBHOOK_SCOPE_SCHEMA,
|
|
360
|
+
])
|
|
361
|
+
.optional(),
|
|
362
|
+
})
|
|
363
|
+
export const CALCOM_ORGANIZATION_TRIGGER_CONFIG_SCHEMA = z.object({
|
|
364
|
+
scope: CALCOM_ORGANIZATION_WEBHOOK_SCOPE_SCHEMA,
|
|
365
|
+
})
|
|
366
|
+
export const CALCOM_BROAD_TRIGGER_CONFIG_SCHEMA = z.object({
|
|
367
|
+
scope: CALCOM_WEBHOOK_SCOPE_SCHEMA.optional(),
|
|
368
|
+
})
|
|
334
369
|
|
|
335
370
|
export const CALCOM_EVENT_SCHEMA: z.ZodType<CalcomEvent> =
|
|
336
371
|
z.custom<CalcomEvent>((value) => {
|
|
@@ -398,8 +433,14 @@ function isCalcomWrappedTriggerType(
|
|
|
398
433
|
return Object.hasOwn(CALCOM_PAYLOAD_SCHEMAS, type)
|
|
399
434
|
}
|
|
400
435
|
|
|
436
|
+
type CalcomTriggerConfigSchema =
|
|
437
|
+
| typeof CALCOM_BROAD_TRIGGER_CONFIG_SCHEMA
|
|
438
|
+
| typeof CALCOM_ORGANIZATION_TRIGGER_CONFIG_SCHEMA
|
|
439
|
+
| typeof CALCOM_TEAM_TRIGGER_CONFIG_SCHEMA
|
|
440
|
+
| typeof CALCOM_USER_TRIGGER_CONFIG_SCHEMA
|
|
441
|
+
|
|
401
442
|
type CalcomTriggerContract<TEvent> = {
|
|
402
|
-
readonly configSchema:
|
|
443
|
+
readonly configSchema: CalcomTriggerConfigSchema
|
|
403
444
|
readonly eventSchema: z.ZodType<TEvent>
|
|
404
445
|
}
|
|
405
446
|
|
|
@@ -444,59 +485,137 @@ type CalcomSemanticContracts = {
|
|
|
444
485
|
>
|
|
445
486
|
} & { readonly "calcom.event": CalcomTriggerContract<CalcomEvent> }
|
|
446
487
|
|
|
447
|
-
export const calcomTriggerContracts
|
|
488
|
+
export const calcomTriggerContracts = {
|
|
448
489
|
"calcom.afterGuestsCalVideoNoShow": contract(
|
|
449
490
|
"AFTER_GUESTS_CAL_VIDEO_NO_SHOW",
|
|
491
|
+
CALCOM_USER_TRIGGER_CONFIG_SCHEMA,
|
|
492
|
+
),
|
|
493
|
+
"calcom.afterHostsCalVideoNoShow": contract(
|
|
494
|
+
"AFTER_HOSTS_CAL_VIDEO_NO_SHOW",
|
|
495
|
+
CALCOM_USER_TRIGGER_CONFIG_SCHEMA,
|
|
496
|
+
),
|
|
497
|
+
"calcom.booking.cancelled": contract(
|
|
498
|
+
"BOOKING_CANCELLED",
|
|
499
|
+
CALCOM_TEAM_TRIGGER_CONFIG_SCHEMA,
|
|
500
|
+
),
|
|
501
|
+
"calcom.booking.created": contract(
|
|
502
|
+
"BOOKING_CREATED",
|
|
503
|
+
CALCOM_TEAM_TRIGGER_CONFIG_SCHEMA,
|
|
504
|
+
),
|
|
505
|
+
"calcom.booking.locationUpdated": contract(
|
|
506
|
+
"BOOKING_LOCATION_UPDATED",
|
|
507
|
+
CALCOM_TEAM_TRIGGER_CONFIG_SCHEMA,
|
|
508
|
+
),
|
|
509
|
+
"calcom.booking.noShowUpdated": contract(
|
|
510
|
+
"BOOKING_NO_SHOW_UPDATED",
|
|
511
|
+
CALCOM_TEAM_TRIGGER_CONFIG_SCHEMA,
|
|
512
|
+
),
|
|
513
|
+
"calcom.booking.paid": contract(
|
|
514
|
+
"BOOKING_PAID",
|
|
515
|
+
CALCOM_TEAM_TRIGGER_CONFIG_SCHEMA,
|
|
516
|
+
),
|
|
517
|
+
"calcom.booking.paymentInitiated": contract(
|
|
518
|
+
"BOOKING_PAYMENT_INITIATED",
|
|
519
|
+
CALCOM_TEAM_TRIGGER_CONFIG_SCHEMA,
|
|
520
|
+
),
|
|
521
|
+
"calcom.booking.reassigned": contract(
|
|
522
|
+
"BOOKING_REASSIGNED",
|
|
523
|
+
CALCOM_TEAM_TRIGGER_CONFIG_SCHEMA,
|
|
524
|
+
),
|
|
525
|
+
"calcom.booking.rejected": contract(
|
|
526
|
+
"BOOKING_REJECTED",
|
|
527
|
+
CALCOM_TEAM_TRIGGER_CONFIG_SCHEMA,
|
|
528
|
+
),
|
|
529
|
+
"calcom.booking.requested": contract(
|
|
530
|
+
"BOOKING_REQUESTED",
|
|
531
|
+
CALCOM_TEAM_TRIGGER_CONFIG_SCHEMA,
|
|
532
|
+
),
|
|
533
|
+
"calcom.booking.rescheduled": contract(
|
|
534
|
+
"BOOKING_RESCHEDULED",
|
|
535
|
+
CALCOM_TEAM_TRIGGER_CONFIG_SCHEMA,
|
|
536
|
+
),
|
|
537
|
+
"calcom.calendarEntryRejected": contract(
|
|
538
|
+
"CALENDAR_ENTRY_REJECTED",
|
|
539
|
+
CALCOM_USER_TRIGGER_CONFIG_SCHEMA,
|
|
540
|
+
),
|
|
541
|
+
"calcom.delegationCredential.error": contract(
|
|
542
|
+
"DELEGATION_CREDENTIAL_ERROR",
|
|
543
|
+
CALCOM_ORGANIZATION_TRIGGER_CONFIG_SCHEMA,
|
|
450
544
|
),
|
|
451
|
-
"calcom.afterHostsCalVideoNoShow": contract("AFTER_HOSTS_CAL_VIDEO_NO_SHOW"),
|
|
452
|
-
"calcom.booking.cancelled": contract("BOOKING_CANCELLED"),
|
|
453
|
-
"calcom.booking.created": contract("BOOKING_CREATED"),
|
|
454
|
-
"calcom.booking.locationUpdated": contract("BOOKING_LOCATION_UPDATED"),
|
|
455
|
-
"calcom.booking.noShowUpdated": contract("BOOKING_NO_SHOW_UPDATED"),
|
|
456
|
-
"calcom.booking.paid": contract("BOOKING_PAID"),
|
|
457
|
-
"calcom.booking.paymentInitiated": contract("BOOKING_PAYMENT_INITIATED"),
|
|
458
|
-
"calcom.booking.reassigned": contract("BOOKING_REASSIGNED"),
|
|
459
|
-
"calcom.booking.rejected": contract("BOOKING_REJECTED"),
|
|
460
|
-
"calcom.booking.requested": contract("BOOKING_REQUESTED"),
|
|
461
|
-
"calcom.booking.rescheduled": contract("BOOKING_RESCHEDULED"),
|
|
462
|
-
"calcom.calendarEntryRejected": contract("CALENDAR_ENTRY_REJECTED"),
|
|
463
|
-
"calcom.delegationCredential.error": contract("DELEGATION_CREDENTIAL_ERROR"),
|
|
464
545
|
"calcom.delegationCredential.rotationRequired": contract(
|
|
465
546
|
"DELEGATION_CREDENTIAL_ROTATION_REQUIRED",
|
|
547
|
+
CALCOM_ORGANIZATION_TRIGGER_CONFIG_SCHEMA,
|
|
466
548
|
),
|
|
467
549
|
"calcom.delegationCredential.secretRotated": contract(
|
|
468
550
|
"DELEGATION_CREDENTIAL_SECRET_ROTATED",
|
|
551
|
+
CALCOM_ORGANIZATION_TRIGGER_CONFIG_SCHEMA,
|
|
469
552
|
),
|
|
470
553
|
"calcom.delegationCredential.secretRotationFailed": contract(
|
|
471
554
|
"DELEGATION_CREDENTIAL_SECRET_ROTATION_FAILED",
|
|
555
|
+
CALCOM_ORGANIZATION_TRIGGER_CONFIG_SCHEMA,
|
|
472
556
|
),
|
|
473
557
|
"calcom.event": {
|
|
474
|
-
configSchema:
|
|
558
|
+
configSchema: CALCOM_BROAD_TRIGGER_CONFIG_SCHEMA,
|
|
475
559
|
eventSchema: CALCOM_EVENT_SCHEMA,
|
|
476
560
|
},
|
|
477
|
-
"calcom.form.submitted": contract(
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
"calcom.
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
561
|
+
"calcom.form.submitted": contract(
|
|
562
|
+
"FORM_SUBMITTED",
|
|
563
|
+
CALCOM_USER_TRIGGER_CONFIG_SCHEMA,
|
|
564
|
+
),
|
|
565
|
+
"calcom.form.submittedWithoutEvent": contract(
|
|
566
|
+
"FORM_SUBMITTED_NO_EVENT",
|
|
567
|
+
CALCOM_USER_TRIGGER_CONFIG_SCHEMA,
|
|
568
|
+
),
|
|
569
|
+
"calcom.instantMeeting.accepted": contract(
|
|
570
|
+
"INSTANT_MEETING_ACCEPTED",
|
|
571
|
+
CALCOM_USER_TRIGGER_CONFIG_SCHEMA,
|
|
572
|
+
),
|
|
573
|
+
"calcom.instantMeeting.created": contract(
|
|
574
|
+
"INSTANT_MEETING",
|
|
575
|
+
CALCOM_USER_TRIGGER_CONFIG_SCHEMA,
|
|
576
|
+
),
|
|
577
|
+
"calcom.meeting.ended": contract(
|
|
578
|
+
"MEETING_ENDED",
|
|
579
|
+
CALCOM_USER_TRIGGER_CONFIG_SCHEMA,
|
|
580
|
+
),
|
|
581
|
+
"calcom.meeting.started": contract(
|
|
582
|
+
"MEETING_STARTED",
|
|
583
|
+
CALCOM_USER_TRIGGER_CONFIG_SCHEMA,
|
|
584
|
+
),
|
|
585
|
+
"calcom.outOfOffice.created": contract(
|
|
586
|
+
"OOO_CREATED",
|
|
587
|
+
CALCOM_USER_TRIGGER_CONFIG_SCHEMA,
|
|
588
|
+
),
|
|
589
|
+
"calcom.recording.ready": contract(
|
|
590
|
+
"RECORDING_READY",
|
|
591
|
+
CALCOM_USER_TRIGGER_CONFIG_SCHEMA,
|
|
592
|
+
),
|
|
485
593
|
"calcom.recording.transcriptionGenerated": contract(
|
|
486
594
|
"RECORDING_TRANSCRIPTION_GENERATED",
|
|
595
|
+
CALCOM_USER_TRIGGER_CONFIG_SCHEMA,
|
|
487
596
|
),
|
|
488
|
-
"calcom.routingForm.fallbackHit": contract(
|
|
489
|
-
|
|
490
|
-
|
|
597
|
+
"calcom.routingForm.fallbackHit": contract(
|
|
598
|
+
"ROUTING_FORM_FALLBACK_HIT",
|
|
599
|
+
CALCOM_USER_TRIGGER_CONFIG_SCHEMA,
|
|
600
|
+
),
|
|
601
|
+
"calcom.wrongAssignmentReported": contract(
|
|
602
|
+
"WRONG_ASSIGNMENT_REPORT",
|
|
603
|
+
CALCOM_USER_TRIGGER_CONFIG_SCHEMA,
|
|
604
|
+
),
|
|
605
|
+
} as const satisfies CalcomSemanticContracts
|
|
491
606
|
|
|
492
607
|
/**
|
|
493
608
|
* Builds one trigger contract for an exact Cal.com provider event.
|
|
494
609
|
*
|
|
495
610
|
* @param type Exact Cal.com provider event type.
|
|
611
|
+
* @param configSchema Trigger configuration schema.
|
|
496
612
|
*/
|
|
497
|
-
function contract<
|
|
613
|
+
function contract<
|
|
614
|
+
TType extends CalcomWebhookTriggerType,
|
|
615
|
+
TConfigSchema extends CalcomTriggerConfigSchema,
|
|
616
|
+
>(type: TType, configSchema: TConfigSchema) {
|
|
498
617
|
return {
|
|
499
|
-
configSchema
|
|
618
|
+
configSchema,
|
|
500
619
|
eventSchema: calcomSemanticEventSchema(type),
|
|
501
620
|
}
|
|
502
621
|
}
|
package/src/kit/schemas.ts
CHANGED
|
@@ -32,6 +32,12 @@ const operationData = KIT_OPENAPI_DATA as {
|
|
|
32
32
|
operations: Record<string, unknown>
|
|
33
33
|
wireDefinitions: Record<string, Record<string, unknown>>
|
|
34
34
|
}
|
|
35
|
+
for (const operation of Object.values(operationData.operations)) {
|
|
36
|
+
if (typeof operation !== "object" || operation === null) continue
|
|
37
|
+
const record = operation as Record<string, unknown>
|
|
38
|
+
normalizeObservedKitNulls(record.outputSchema)
|
|
39
|
+
normalizeObservedKitNulls(record.outputWireSchema)
|
|
40
|
+
}
|
|
35
41
|
const OPERATIONS = operationData.operations as unknown as Record<
|
|
36
42
|
KitOperationKey,
|
|
37
43
|
KitRuntimeOperation
|
|
@@ -91,3 +97,61 @@ export function kitOperationOutputSchema<TKey extends KitOperationKey>(
|
|
|
91
97
|
|
|
92
98
|
/** Provider-native component schemas used for schema-guided key mapping. */
|
|
93
99
|
export const KIT_WIRE_DEFINITIONS = operationData.wireDefinitions
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Accepts nullable fields observed in successful Kit responses but omitted from
|
|
103
|
+
* the provider's OpenAPI nullability metadata.
|
|
104
|
+
*
|
|
105
|
+
* @param value Generated operation metadata.
|
|
106
|
+
*/
|
|
107
|
+
function normalizeObservedKitNulls(value: unknown) {
|
|
108
|
+
if (Array.isArray(value)) {
|
|
109
|
+
for (const item of value) normalizeObservedKitNulls(item)
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
if (typeof value !== "object" || value === null) return
|
|
113
|
+
|
|
114
|
+
const record = value as Record<string, unknown>
|
|
115
|
+
const properties = record.properties
|
|
116
|
+
if (typeof properties === "object" && properties !== null) {
|
|
117
|
+
const propertyRecord = properties as Record<string, unknown>
|
|
118
|
+
for (const name of [
|
|
119
|
+
"bio",
|
|
120
|
+
"byline",
|
|
121
|
+
"endCursor",
|
|
122
|
+
"end_cursor",
|
|
123
|
+
"startCursor",
|
|
124
|
+
"start_cursor",
|
|
125
|
+
]) {
|
|
126
|
+
const property = propertyRecord[name]
|
|
127
|
+
if (
|
|
128
|
+
typeof property !== "object" ||
|
|
129
|
+
property === null ||
|
|
130
|
+
isNullableSchema(property)
|
|
131
|
+
) {
|
|
132
|
+
continue
|
|
133
|
+
}
|
|
134
|
+
propertyRecord[name] = { anyOf: [property, { type: "null" }] }
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
for (const child of Object.values(record)) normalizeObservedKitNulls(child)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Returns whether one JSON Schema fragment already accepts null.
|
|
143
|
+
*
|
|
144
|
+
* @param value JSON Schema fragment.
|
|
145
|
+
*/
|
|
146
|
+
function isNullableSchema(value: object) {
|
|
147
|
+
const anyOf = (value as { anyOf?: unknown }).anyOf
|
|
148
|
+
return (
|
|
149
|
+
Array.isArray(anyOf) &&
|
|
150
|
+
anyOf.some(
|
|
151
|
+
(branch) =>
|
|
152
|
+
typeof branch === "object" &&
|
|
153
|
+
branch !== null &&
|
|
154
|
+
(branch as { type?: unknown }).type === "null",
|
|
155
|
+
)
|
|
156
|
+
)
|
|
157
|
+
}
|
package/src/kit/types.ts
CHANGED
|
@@ -60,6 +60,20 @@ type EncodableValue<TValue> = unknown extends TValue
|
|
|
60
60
|
: { [TKey in keyof TValue]: EncodableValue<TValue[TKey]> }
|
|
61
61
|
: never
|
|
62
62
|
|
|
63
|
+
/** Fields whose live Kit nullability is missing from its OpenAPI document. */
|
|
64
|
+
type ObservedNullableField = "bio" | "byline" | "endCursor" | "startCursor"
|
|
65
|
+
|
|
66
|
+
/** Adds nulls returned by Kit but omitted from its generated OpenAPI types. */
|
|
67
|
+
type NormalizeObservedKitNulls<TValue> = TValue extends readonly (infer TItem)[]
|
|
68
|
+
? NormalizeObservedKitNulls<TItem>[]
|
|
69
|
+
: TValue extends object
|
|
70
|
+
? {
|
|
71
|
+
[TKey in keyof TValue]:
|
|
72
|
+
| NormalizeObservedKitNulls<TValue[TKey]>
|
|
73
|
+
| (TKey extends ObservedNullableField ? null : never)
|
|
74
|
+
}
|
|
75
|
+
: TValue
|
|
76
|
+
|
|
63
77
|
/** Flattens one generated operation's path, query, and body fields. */
|
|
64
78
|
type RawKitOperationInput<TKey extends KitOperationKey> = Simplify<
|
|
65
79
|
Parameters<Operation<TKey>, "path"> &
|
|
@@ -83,5 +97,5 @@ export type KitOperationInput<TKey extends KitOperationKey> = PublicInput<
|
|
|
83
97
|
|
|
84
98
|
/** CamelCase successful response for one frozen Kit V4 operation. */
|
|
85
99
|
export type KitOperationOutput<TKey extends KitOperationKey> = EncodableValue<
|
|
86
|
-
SuccessResponse<Operation<TKey
|
|
100
|
+
NormalizeObservedKitNulls<SuccessResponse<Operation<TKey>>>
|
|
87
101
|
>
|
package/src/krisp/api.ts
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import type { Encodable } from "@automate.ax/codec"
|
|
2
|
+
import { encodableSchema } from "@automate.ax/codec"
|
|
3
|
+
import * as z from "zod"
|
|
4
|
+
|
|
5
|
+
const KRISP_API_BASE_URL = "https://meeting-api.krisp.ai/v1/"
|
|
6
|
+
const KRISP_API_ORIGIN = new URL(KRISP_API_BASE_URL).origin
|
|
7
|
+
const KRISP_SECRET_SCHEMA = z.object({ apiKey: z.string().min(1) })
|
|
8
|
+
|
|
9
|
+
/** Options for one authenticated Krisp REST request. */
|
|
10
|
+
export interface KrispRequestOptions<TSchema extends z.ZodType> {
|
|
11
|
+
/** Public camelCase JSON body. */
|
|
12
|
+
body?: Encodable
|
|
13
|
+
|
|
14
|
+
/** HTTP verb. Defaults to `GET`. */
|
|
15
|
+
method?: "GET" | "POST"
|
|
16
|
+
|
|
17
|
+
/** Public camelCase query parameters. */
|
|
18
|
+
query?: Record<
|
|
19
|
+
string,
|
|
20
|
+
boolean | number | readonly string[] | string | undefined
|
|
21
|
+
>
|
|
22
|
+
|
|
23
|
+
/** Schema for the normalized provider response. */
|
|
24
|
+
responseSchema: TSchema
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Structured Krisp REST failure. */
|
|
28
|
+
export class KrispApiError extends Error {
|
|
29
|
+
/** Normalized provider error payload, when valid JSON was returned. */
|
|
30
|
+
readonly body?: Encodable
|
|
31
|
+
|
|
32
|
+
/** Retry delay in seconds, when Krisp returned one. */
|
|
33
|
+
readonly retryAfter?: number
|
|
34
|
+
|
|
35
|
+
/** HTTP status returned by Krisp. */
|
|
36
|
+
readonly status: number
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Creates a structured Krisp API error.
|
|
40
|
+
*
|
|
41
|
+
* @param options - Provider status, body, and retry metadata.
|
|
42
|
+
* @param options.body - Normalized provider error body.
|
|
43
|
+
* @param options.retryAfter - Retry delay in seconds.
|
|
44
|
+
* @param options.status - HTTP status.
|
|
45
|
+
*/
|
|
46
|
+
constructor(options: {
|
|
47
|
+
body?: Encodable
|
|
48
|
+
retryAfter?: number
|
|
49
|
+
status: number
|
|
50
|
+
}) {
|
|
51
|
+
super(
|
|
52
|
+
getErrorMessage(options.body) ??
|
|
53
|
+
`Krisp API request failed with status ${options.status}.`,
|
|
54
|
+
)
|
|
55
|
+
this.name = "KrispApiError"
|
|
56
|
+
this.body = options.body
|
|
57
|
+
this.retryAfter = options.retryAfter
|
|
58
|
+
this.status = options.status
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Creates an authenticated Krisp Meeting Assistant REST client.
|
|
64
|
+
*
|
|
65
|
+
* @param secret - Resolved Krisp API-key secret.
|
|
66
|
+
*/
|
|
67
|
+
export function getKrispApi(secret: Record<string, unknown>) {
|
|
68
|
+
const { apiKey } = KRISP_SECRET_SCHEMA.parse(secret)
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
/**
|
|
72
|
+
* Runs one Krisp request and returns its normalized response.
|
|
73
|
+
*
|
|
74
|
+
* @param path - API path relative to the v1 root.
|
|
75
|
+
* @param options - Method, parameters, and response schema.
|
|
76
|
+
*/
|
|
77
|
+
async request<TSchema extends z.ZodType>(
|
|
78
|
+
path: string,
|
|
79
|
+
options: KrispRequestOptions<TSchema>,
|
|
80
|
+
): Promise<z.output<TSchema>> {
|
|
81
|
+
const url = new URL(path.replace(/^\//, ""), KRISP_API_BASE_URL)
|
|
82
|
+
if (url.origin !== KRISP_API_ORIGIN) {
|
|
83
|
+
throw new Error("Krisp API paths must use the Krisp API origin.")
|
|
84
|
+
}
|
|
85
|
+
for (const [name, value] of Object.entries(options.query ?? {})) {
|
|
86
|
+
if (value === undefined) continue
|
|
87
|
+
url.searchParams.set(
|
|
88
|
+
toSnakeCase(name),
|
|
89
|
+
Array.isArray(value) ? value.join(",") : String(value),
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
const response = await fetch(url, {
|
|
93
|
+
body:
|
|
94
|
+
options.body === undefined
|
|
95
|
+
? undefined
|
|
96
|
+
: JSON.stringify(toKrisp(options.body)),
|
|
97
|
+
headers: {
|
|
98
|
+
Accept: "application/json",
|
|
99
|
+
Authorization: `Bearer ${apiKey}`,
|
|
100
|
+
...(options.body === undefined
|
|
101
|
+
? {}
|
|
102
|
+
: { "Content-Type": "application/json" }),
|
|
103
|
+
},
|
|
104
|
+
method: options.method ?? "GET",
|
|
105
|
+
})
|
|
106
|
+
const normalized = fromKrisp(parseJson(await response.text()))
|
|
107
|
+
if (!response.ok) {
|
|
108
|
+
const body = encodableSchema.safeParse(normalized)
|
|
109
|
+
throw new KrispApiError({
|
|
110
|
+
...(body.success && { body: body.data }),
|
|
111
|
+
retryAfter: parseFiniteNumber(response.headers.get("Retry-After")),
|
|
112
|
+
status: response.status,
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
return options.responseSchema.parse(normalized)
|
|
116
|
+
},
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Converts public camelCase JSON to Krisp wire keys.
|
|
122
|
+
*
|
|
123
|
+
* @param value - Public value to normalize.
|
|
124
|
+
*/
|
|
125
|
+
export function toKrisp(value: Encodable): Encodable {
|
|
126
|
+
if (value instanceof Date) return value.toISOString()
|
|
127
|
+
if (Array.isArray(value)) return value.map(toKrisp)
|
|
128
|
+
if (!isPlainObject(value)) return value
|
|
129
|
+
return Object.fromEntries(
|
|
130
|
+
Object.entries(value).map(([key, item]) => [
|
|
131
|
+
toSnakeCase(key),
|
|
132
|
+
toKrisp(item),
|
|
133
|
+
]),
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Converts Krisp wire JSON to public camelCase keys.
|
|
139
|
+
*
|
|
140
|
+
* @param value - Provider value to normalize.
|
|
141
|
+
*/
|
|
142
|
+
export function fromKrisp(value: unknown): unknown {
|
|
143
|
+
if (Array.isArray(value)) return value.map(fromKrisp)
|
|
144
|
+
if (!isPlainObject(value)) return value
|
|
145
|
+
return Object.fromEntries(
|
|
146
|
+
Object.entries(value).map(([key, item]) => [
|
|
147
|
+
toCamelCase(key),
|
|
148
|
+
fromKrisp(item),
|
|
149
|
+
]),
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Checks whether a value is a key-value object.
|
|
155
|
+
*
|
|
156
|
+
* @param value - Candidate value.
|
|
157
|
+
*/
|
|
158
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
159
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Parses a JSON response body without obscuring the HTTP status on failure.
|
|
164
|
+
*
|
|
165
|
+
* @param value - Raw response text.
|
|
166
|
+
*/
|
|
167
|
+
function parseJson(value: string): unknown {
|
|
168
|
+
try {
|
|
169
|
+
return JSON.parse(value)
|
|
170
|
+
} catch {
|
|
171
|
+
return undefined
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Parses an optional finite numeric header.
|
|
177
|
+
*
|
|
178
|
+
* @param value - Raw header value.
|
|
179
|
+
*/
|
|
180
|
+
function parseFiniteNumber(value: string | null) {
|
|
181
|
+
if (value === null) return undefined
|
|
182
|
+
const number = Number(value)
|
|
183
|
+
return Number.isFinite(number) ? number : undefined
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Extracts Krisp's human-readable error string.
|
|
188
|
+
*
|
|
189
|
+
* @param body - Normalized provider error body.
|
|
190
|
+
*/
|
|
191
|
+
function getErrorMessage(body: Encodable | undefined) {
|
|
192
|
+
return isPlainObject(body) && typeof body.error === "string"
|
|
193
|
+
? body.error
|
|
194
|
+
: undefined
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Converts one public camelCase key to Krisp snake_case.
|
|
199
|
+
*
|
|
200
|
+
* @param value - Public key.
|
|
201
|
+
*/
|
|
202
|
+
function toSnakeCase(value: string) {
|
|
203
|
+
return value.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Converts one Krisp snake_case key to public camelCase.
|
|
208
|
+
*
|
|
209
|
+
* @param value - Provider key.
|
|
210
|
+
*/
|
|
211
|
+
function toCamelCase(value: string) {
|
|
212
|
+
return value.replace(/_([a-z0-9])/g, (_, letter: string) =>
|
|
213
|
+
letter.toUpperCase(),
|
|
214
|
+
)
|
|
215
|
+
}
|