@catandbox/schrodinger-contracts 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.
@@ -0,0 +1,74 @@
1
+ import type { ApiRequestMap, ApiResponseMap } from "./types";
2
+
3
+ export interface ClientOptions {
4
+ baseUrl: string;
5
+ defaultHeaders?: Record<string, string>;
6
+ fetchImpl?: typeof fetch;
7
+ }
8
+
9
+ export interface ClientRequestOptions {
10
+ headers?: Record<string, string>;
11
+ query?: Record<string, string | number | boolean | undefined>;
12
+ pathParams?: Record<string, string>;
13
+ }
14
+
15
+ export class SchrodingerApiClient {
16
+ private readonly baseUrl: string;
17
+ private readonly defaultHeaders: Record<string, string>;
18
+ private readonly fetchImpl: typeof fetch;
19
+
20
+ public constructor(options: ClientOptions) {
21
+ this.baseUrl = options.baseUrl.replace(/\/$/, "");
22
+ this.defaultHeaders = options.defaultHeaders ?? {};
23
+ this.fetchImpl = options.fetchImpl ?? fetch;
24
+ }
25
+
26
+ public async request<TPath extends keyof ApiRequestMap & keyof ApiResponseMap>(
27
+ methodAndPath: TPath,
28
+ payload: ApiRequestMap[TPath],
29
+ options?: ClientRequestOptions
30
+ ): Promise<ApiResponseMap[TPath]> {
31
+ const [method, pathTemplate] = methodAndPath.split(" ") as [string, string];
32
+ const path = this.interpolatePath(pathTemplate, options?.pathParams);
33
+ const url = new URL(`${this.baseUrl}${path}`);
34
+
35
+ for (const [key, value] of Object.entries(options?.query ?? {})) {
36
+ if (value !== undefined) {
37
+ url.searchParams.set(key, String(value));
38
+ }
39
+ }
40
+
41
+ const requestInit: RequestInit = {
42
+ method,
43
+ headers: {
44
+ "content-type": "application/json",
45
+ ...this.defaultHeaders,
46
+ ...(options?.headers ?? {})
47
+ }
48
+ };
49
+
50
+ if (payload !== undefined) {
51
+ requestInit.body = JSON.stringify(payload);
52
+ }
53
+
54
+ const response = await this.fetchImpl(url, requestInit);
55
+
56
+ if (!response.ok) {
57
+ const body = await response.text();
58
+ throw new Error(`Request failed (${response.status}): ${body}`);
59
+ }
60
+
61
+ return (await response.json()) as ApiResponseMap[TPath];
62
+ }
63
+
64
+ private interpolatePath(pathTemplate: string, pathParams?: Record<string, string>): string {
65
+ return pathTemplate.replace(/\{([^}]+)\}/g, (_, rawKey: string) => {
66
+ const value = pathParams?.[rawKey];
67
+ if (!value) {
68
+ throw new Error(`Missing path parameter: ${rawKey}`);
69
+ }
70
+
71
+ return encodeURIComponent(value);
72
+ });
73
+ }
74
+ }
@@ -0,0 +1,543 @@
1
+ export type TicketStatus = "Active" | "InProgress" | "AwaitingResponse" | "Closed" | "Archived";
2
+
3
+ export interface ErrorResponse {
4
+ error: string;
5
+ message: string;
6
+ requestId?: string;
7
+ }
8
+
9
+ export interface Ticket {
10
+ id: string;
11
+ tenantId: string;
12
+ principalId: string;
13
+ categoryId: string | null;
14
+ status: TicketStatus;
15
+ title: string;
16
+ assignedAliasId: string | null;
17
+ createdAt: number;
18
+ updatedAt: number;
19
+ }
20
+
21
+ export interface Message {
22
+ id: string;
23
+ ticketId: string;
24
+ authorType: "customer" | "agent" | "admin" | "system";
25
+ isPublic: boolean;
26
+ bodyPlain: string;
27
+ authorAliasId: string | null;
28
+ createdAt: number;
29
+ }
30
+
31
+ export interface Category {
32
+ id: string;
33
+ integrationId: string;
34
+ name: string;
35
+ isDeleted: boolean;
36
+ }
37
+
38
+ export interface PublicCategory {
39
+ id: string;
40
+ name: string;
41
+ }
42
+
43
+ export interface Alias {
44
+ id: string;
45
+ integrationId: string;
46
+ department: string;
47
+ displayName: string;
48
+ avatarUrl: string | null;
49
+ toneStyle: unknown;
50
+ isActive: boolean;
51
+ }
52
+
53
+ export interface Macro {
54
+ id: string;
55
+ integrationId: string;
56
+ categoryId: string | null;
57
+ title: string;
58
+ bodyTemplate: string;
59
+ isActive: boolean;
60
+ createdAt: number;
61
+ updatedAt: number;
62
+ }
63
+
64
+ export interface RagSource {
65
+ id: string;
66
+ ragScope: "shopify_common" | "app_specific";
67
+ integrationId: string | null;
68
+ url: string;
69
+ type: string;
70
+ status: string;
71
+ lastFetchedAt: number | null;
72
+ }
73
+
74
+ export interface RagGoldenTest {
75
+ id: string;
76
+ ragScope: "shopify_common" | "app_specific";
77
+ integrationId: string | null;
78
+ question: string;
79
+ expectedPoints: unknown;
80
+ severity: "critical" | "normal";
81
+ isActive: boolean;
82
+ }
83
+
84
+ export interface AnalyticsStatusCount {
85
+ status: TicketStatus;
86
+ count: number;
87
+ }
88
+
89
+ export interface AnalyticsModeCount {
90
+ mode: "manual" | "semi" | "auto";
91
+ count: number;
92
+ }
93
+
94
+ export interface AnalyticsBucketCount {
95
+ bucketStart: number;
96
+ count: number;
97
+ }
98
+
99
+ export interface AnalyticsFirstResponseByPriority {
100
+ priority: "low" | "medium" | "high" | null;
101
+ count: number;
102
+ avgSeconds: number;
103
+ }
104
+
105
+ export interface AnalyticsFirstResponseByCategory {
106
+ categoryId: string | null;
107
+ categoryName: string;
108
+ count: number;
109
+ avgSeconds: number;
110
+ }
111
+
112
+ export interface AnalyticsDistributionPoint {
113
+ stars: number;
114
+ count: number;
115
+ }
116
+
117
+ export interface AnalyticsCategoryPoint {
118
+ categoryId: string | null;
119
+ categoryName: string;
120
+ count: number;
121
+ }
122
+
123
+ export interface AnalyticsModeTimelinePoint {
124
+ bucketStart: number;
125
+ mode: "manual" | "semi" | "auto";
126
+ count: number;
127
+ }
128
+
129
+ export interface AnalyticsRagOutcomePoint {
130
+ bucketStart: number;
131
+ status: "running" | "passed" | "declined" | "error";
132
+ count: number;
133
+ }
134
+
135
+ export interface AnalyticsRagVersion {
136
+ id: string;
137
+ ragScope: "shopify_common" | "app_specific";
138
+ integrationId: string | null;
139
+ vectorizeIndexName: string;
140
+ status: "candidate" | "active" | "deprecated" | "declined";
141
+ createdAt: number;
142
+ promotedAt: number | null;
143
+ deprecatedAt: number | null;
144
+ declineReason: string | null;
145
+ }
146
+
147
+ export interface CreateTicketRequest {
148
+ tenantExternalId: string;
149
+ principalExternalId: string;
150
+ categoryId?: string | null;
151
+ title: string;
152
+ body: string;
153
+ clientMessageId: string;
154
+ notifyEmail?: string | null;
155
+ }
156
+
157
+ export interface TicketEvent {
158
+ id: string;
159
+ ticketId: string;
160
+ eventType: string;
161
+ payload: unknown;
162
+ createdAt: number;
163
+ }
164
+
165
+ export interface CreateMessageRequest {
166
+ tenantExternalId: string;
167
+ principalExternalId: string;
168
+ body: string;
169
+ clientMessageId: string;
170
+ }
171
+
172
+ export interface RatingRequest {
173
+ tenantExternalId: string;
174
+ principalExternalId: string;
175
+ stars: number;
176
+ comment?: string;
177
+ }
178
+
179
+ export interface UploadInitRequest {
180
+ tenantExternalId: string;
181
+ principalExternalId: string;
182
+ ticketId: string;
183
+ files: Array<{
184
+ filename: string;
185
+ mime: string;
186
+ sizeBytes: number;
187
+ sha256: string;
188
+ }>;
189
+ }
190
+
191
+ export interface UploadCompleteRequest {
192
+ tenantExternalId: string;
193
+ principalExternalId: string;
194
+ ticketId: string;
195
+ uploads: Array<{
196
+ uploadId: string;
197
+ sizeBytes: number;
198
+ sha256: string;
199
+ }>;
200
+ messageId?: string | null;
201
+ }
202
+
203
+ export interface CreateIntegrationRequest {
204
+ name: string;
205
+ platform: string;
206
+ }
207
+
208
+ export interface Integration {
209
+ id: string;
210
+ name: string;
211
+ platform: string;
212
+ status: "active" | "disabled";
213
+ createdAt: number;
214
+ }
215
+
216
+ export interface CreatedKey {
217
+ keyId: string;
218
+ secret: string;
219
+ createdAt: number;
220
+ }
221
+
222
+ export interface GdprDeletionStats {
223
+ tenants: number;
224
+ principals: number;
225
+ tickets: number;
226
+ messages: number;
227
+ events: number;
228
+ attachments: number;
229
+ agentRuns: number;
230
+ aliasRatings: number;
231
+ }
232
+
233
+ export interface GdprShopRedactRequest {
234
+ integrationId?: string | null;
235
+ tenantExternalId?: string | null;
236
+ shopDomain?: string | null;
237
+ shop_domain?: string;
238
+ shop_id?: string | number;
239
+ }
240
+
241
+ export interface GdprCustomerRedactRequest extends GdprShopRedactRequest {
242
+ principalExternalId?: string;
243
+ customer?: {
244
+ id?: string | number;
245
+ email?: string;
246
+ };
247
+ }
248
+
249
+ export interface GdprDataRequest extends GdprCustomerRedactRequest {}
250
+
251
+ export interface ApiRequestMap {
252
+ "POST /v1/tickets": CreateTicketRequest;
253
+ "GET /v1/tickets": undefined;
254
+ "GET /v1/tickets/{ticketId}": undefined;
255
+ "POST /v1/tickets/{ticketId}/messages": CreateMessageRequest;
256
+ "POST /v1/tickets/{ticketId}/ratings": RatingRequest;
257
+ "POST /v1/messages/{messageId}/ratings": RatingRequest;
258
+ "GET /v1/categories": undefined;
259
+ "GET /v1/portal-config": undefined;
260
+ "GET /v1/tickets/{ticketId}/history": undefined;
261
+ "POST /v1/uploads/init": UploadInitRequest;
262
+ "POST /v1/uploads/complete": UploadCompleteRequest;
263
+ "PUT /v1/uploads/{uploadId}/data": undefined;
264
+ "POST /admin/integrations": CreateIntegrationRequest;
265
+ "POST /admin/integrations/{id}/keys": undefined;
266
+ "POST /admin/integrations/{id}/aliases": {
267
+ department: string;
268
+ displayName: string;
269
+ avatarUrl?: string | null;
270
+ toneStyle?: unknown;
271
+ isActive?: boolean;
272
+ };
273
+ "PATCH /admin/aliases/{aliasId}": {
274
+ department?: string;
275
+ displayName?: string;
276
+ avatarUrl?: string | null;
277
+ toneStyle?: unknown;
278
+ isActive?: boolean;
279
+ };
280
+ "POST /admin/integrations/{id}/macros": {
281
+ categoryId?: string | null;
282
+ title: string;
283
+ bodyTemplate: string;
284
+ isActive?: boolean;
285
+ };
286
+ "PATCH /admin/macros/{macroId}": {
287
+ categoryId?: string | null;
288
+ title?: string;
289
+ bodyTemplate?: string;
290
+ isActive?: boolean;
291
+ };
292
+ "POST /admin/tickets/{ticketId}/pause": { pausedBy?: string; reason?: string };
293
+ "POST /admin/tickets/{ticketId}/resume": { resumedBy?: string };
294
+ "POST /admin/tickets/{ticketId}/escalate": { escalatedBy?: string; reason?: string };
295
+ "POST /admin/tickets/{ticketId}/draft": { requestedBy?: string };
296
+ "POST /admin/tickets/{ticketId}/respond": { responder?: string; body: string };
297
+ "POST /admin/gdpr/shop-redact": GdprShopRedactRequest;
298
+ "POST /admin/gdpr/customer-redact": GdprCustomerRedactRequest;
299
+ "POST /admin/gdpr/data-request": GdprDataRequest;
300
+ "GET /admin/analytics/summary": undefined;
301
+ "GET /admin/analytics/tickets": undefined;
302
+ "GET /admin/analytics/csat": undefined;
303
+ "GET /admin/analytics/categories": undefined;
304
+ "GET /admin/analytics/modes": undefined;
305
+ "GET /admin/analytics/rag": undefined;
306
+ "GET /admin/rag/sources": undefined;
307
+ "POST /admin/rag/sources": {
308
+ ragScope: "shopify_common" | "app_specific";
309
+ integrationId?: string | null;
310
+ url: string;
311
+ type: string;
312
+ };
313
+ "POST /admin/rag/build": {
314
+ ragScope: "shopify_common" | "app_specific";
315
+ integrationId?: string | null;
316
+ };
317
+ "POST /admin/rag/golden/run": {
318
+ ragScope: "shopify_common" | "app_specific";
319
+ integrationId?: string | null;
320
+ candidateVersionId: string;
321
+ baselineVersionId?: string | null;
322
+ judgeModel?: string;
323
+ };
324
+ "PUT /admin/integrations/{id}/manual-categories": { manualCategoryIds: string[] };
325
+ "POST /admin/rag/promote/{versionId}": undefined;
326
+ "POST /admin/rag/deprecate/{versionId}": { reason?: string };
327
+ "GET /admin/rag/golden/tests": undefined;
328
+ "POST /admin/rag/golden/tests": {
329
+ ragScope: "shopify_common" | "app_specific";
330
+ integrationId?: string | null;
331
+ question: string;
332
+ expectedPoints: unknown;
333
+ severity: "critical" | "normal";
334
+ isActive?: boolean;
335
+ };
336
+ "PATCH /admin/rag/golden/tests/{testId}": {
337
+ question?: string;
338
+ expectedPoints?: unknown;
339
+ severity?: "critical" | "normal";
340
+ isActive?: boolean;
341
+ };
342
+ "DELETE /admin/rag/golden/tests/{testId}": undefined;
343
+ }
344
+
345
+ export interface ApiResponseMap {
346
+ "POST /v1/tickets": Ticket;
347
+ "GET /v1/tickets": { items: Ticket[] };
348
+ "GET /v1/tickets/{ticketId}": Ticket;
349
+ "POST /v1/tickets/{ticketId}/messages": Message;
350
+ "POST /v1/tickets/{ticketId}/ratings": { ok: true };
351
+ "POST /v1/messages/{messageId}/ratings": { ok: true; aliasRated: boolean };
352
+ "GET /v1/categories": { items: PublicCategory[] };
353
+ "GET /v1/portal-config": { categories: Array<{ id: string; name: string }>; aliases: Alias[] };
354
+ "GET /v1/tickets/{ticketId}/history": { items: TicketEvent[] };
355
+ "POST /v1/uploads/init": {
356
+ uploads: Array<{ uploadId: string; attachmentId: string; filename: string; putUrl: string }>;
357
+ };
358
+ "POST /v1/uploads/complete": { completed: number; scanStatus: "pending" };
359
+ "PUT /v1/uploads/{uploadId}/data": { ok: true };
360
+ "POST /admin/integrations": Integration;
361
+ "POST /admin/integrations/{id}/keys": CreatedKey;
362
+ "POST /admin/integrations/{id}/aliases": Alias;
363
+ "PATCH /admin/aliases/{aliasId}": { ok: true };
364
+ "POST /admin/integrations/{id}/macros": Macro;
365
+ "PATCH /admin/macros/{macroId}": { ok: true };
366
+ "POST /admin/tickets/{ticketId}/pause": { ok: true };
367
+ "POST /admin/tickets/{ticketId}/resume": { ok: true };
368
+ "POST /admin/tickets/{ticketId}/escalate": { ok: true };
369
+ "POST /admin/tickets/{ticketId}/draft": { draftBody: string };
370
+ "POST /admin/tickets/{ticketId}/respond": { ok: true };
371
+ "POST /admin/gdpr/shop-redact": {
372
+ ok: true;
373
+ matchedTenants: number;
374
+ deleted: GdprDeletionStats;
375
+ };
376
+ "POST /admin/gdpr/customer-redact": {
377
+ ok: true;
378
+ matchedTenants: number;
379
+ matchedPrincipals: number;
380
+ deleted: GdprDeletionStats;
381
+ };
382
+ "POST /admin/gdpr/data-request": {
383
+ ok: true;
384
+ found: boolean;
385
+ data: {
386
+ tenants: Array<{
387
+ id: string;
388
+ integrationId: string;
389
+ externalTenantId: string;
390
+ shopDomain: string | null;
391
+ }>;
392
+ principals: Array<{
393
+ id: string;
394
+ tenantId: string;
395
+ externalPrincipalId: string;
396
+ email: string | null;
397
+ displayName: string | null;
398
+ createdAt: number;
399
+ lastSeenAt: number | null;
400
+ }>;
401
+ tickets: Array<{
402
+ id: string;
403
+ tenantId: string;
404
+ principalId: string;
405
+ status: string;
406
+ title: string;
407
+ createdAt: number;
408
+ updatedAt: number;
409
+ }>;
410
+ messages: Array<{
411
+ id: string;
412
+ ticketId: string;
413
+ authorType: string;
414
+ isPublic: boolean;
415
+ bodyPlain: string;
416
+ createdAt: number;
417
+ }>;
418
+ events: Array<{
419
+ id: string;
420
+ ticketId: string;
421
+ eventType: string;
422
+ payloadJson: string;
423
+ createdAt: number;
424
+ }>;
425
+ attachments: Array<{
426
+ id: string;
427
+ ticketId: string;
428
+ messageId: string | null;
429
+ filename: string;
430
+ mime: string;
431
+ sizeBytes: number;
432
+ sha256: string;
433
+ createdAt: number;
434
+ }>;
435
+ aliasRatings: Array<{
436
+ id: string;
437
+ aliasId: string;
438
+ tenantId: string;
439
+ principalId: string;
440
+ starsAvg: number;
441
+ starsCount: number;
442
+ lastStar: number | null;
443
+ isBlockedForPrincipal: boolean;
444
+ }>;
445
+ };
446
+ };
447
+ "GET /admin/analytics/summary": {
448
+ window: { from: number; to: number };
449
+ tickets: {
450
+ opened: number;
451
+ byStatus: {
452
+ Active: number;
453
+ InProgress: number;
454
+ AwaitingResponse: number;
455
+ Closed: number;
456
+ Archived: number;
457
+ };
458
+ };
459
+ csat: {
460
+ ticketAverage: number | null;
461
+ messageAverage: number | null;
462
+ };
463
+ response: {
464
+ avgFirstResponseSeconds: number | null;
465
+ };
466
+ rag: {
467
+ runsTotal: number;
468
+ running: number;
469
+ passed: number;
470
+ declined: number;
471
+ error: number;
472
+ };
473
+ };
474
+ "GET /admin/analytics/tickets": {
475
+ window: { from: number; to: number; bucketSeconds: number };
476
+ openedSeries: AnalyticsBucketCount[];
477
+ byStatus: AnalyticsStatusCount[];
478
+ avgFirstResponseByPriority: AnalyticsFirstResponseByPriority[];
479
+ avgFirstResponseByCategory: AnalyticsFirstResponseByCategory[];
480
+ };
481
+ "GET /admin/analytics/csat": {
482
+ window: { from: number; to: number };
483
+ ticketDistribution: AnalyticsDistributionPoint[];
484
+ messageDistribution: AnalyticsDistributionPoint[];
485
+ ticketAverage: number | null;
486
+ messageAverage: number | null;
487
+ };
488
+ "GET /admin/analytics/categories": {
489
+ window: { from: number; to: number };
490
+ overall: AnalyticsCategoryPoint[];
491
+ perIntegration: Array<{
492
+ integrationId: string;
493
+ items: AnalyticsCategoryPoint[];
494
+ }>;
495
+ };
496
+ "GET /admin/analytics/modes": {
497
+ window: { from: number; to: number; bucketSeconds: number };
498
+ snapshot: AnalyticsModeCount[];
499
+ timeline: AnalyticsModeTimelinePoint[];
500
+ };
501
+ "GET /admin/analytics/rag": {
502
+ window: { from: number; to: number; bucketSeconds: number };
503
+ versions: AnalyticsRagVersion[];
504
+ outcomesTimeline: AnalyticsRagOutcomePoint[];
505
+ runStatus: Array<{
506
+ status: "running" | "passed" | "declined" | "error";
507
+ count: number;
508
+ }>;
509
+ };
510
+ "GET /admin/rag/sources": { items: RagSource[] };
511
+ "POST /admin/rag/sources": {
512
+ id: string;
513
+ ragScope: "shopify_common" | "app_specific";
514
+ integrationId: string | null;
515
+ url: string;
516
+ type: string;
517
+ status: string;
518
+ };
519
+ "POST /admin/rag/build": { versionId: string; vectorizeIndexName: string; status: "candidate" };
520
+ "POST /admin/rag/golden/run": {
521
+ runId: string;
522
+ status: "passed" | "declined";
523
+ summary: {
524
+ total: number;
525
+ criticalFailures: number;
526
+ avgCandidateScore: number;
527
+ avgBaselineScore: number;
528
+ avgDrop: number;
529
+ threshold: number;
530
+ };
531
+ };
532
+ "PUT /admin/integrations/{id}/manual-categories": { ok: true };
533
+ "POST /admin/rag/promote/{versionId}": {
534
+ ok: true;
535
+ activatedVersionId: string;
536
+ deprecatedVersionId: string | null;
537
+ };
538
+ "POST /admin/rag/deprecate/{versionId}": { ok: true };
539
+ "GET /admin/rag/golden/tests": { items: RagGoldenTest[] };
540
+ "POST /admin/rag/golden/tests": { id: string };
541
+ "PATCH /admin/rag/golden/tests/{testId}": { ok: true };
542
+ "DELETE /admin/rag/golden/tests/{testId}": { ok: true };
543
+ }