@driftgate/sdk 0.1.0-rc.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/index.js ADDED
@@ -0,0 +1,765 @@
1
+ // src/index.ts
2
+ import { setTimeout as sleep } from "timers/promises";
3
+ import { z } from "zod";
4
+ import {
5
+ CanonicalPolicyRefSchema,
6
+ CanonicalRiskMetaSchema,
7
+ CanonicalRouteRefSchema,
8
+ DataBoundaryDecisionSchema,
9
+ EdgeInterceptorRegistrationSchema,
10
+ EdgeInterceptorStatusSchema,
11
+ FirewallEventsResponseSchema,
12
+ FirewallInspectRequestSchema,
13
+ FirewallInspectResponseSchema,
14
+ FirewallInspectResultSchema,
15
+ RunStateSchema,
16
+ V4ExecutionRequestSchema,
17
+ V4SessionResourceSchema,
18
+ V4SessionStartRequestSchema,
19
+ WorkflowVersionSchema
20
+ } from "@driftgate/contracts";
21
+ var HeadlessErrorEnvelopeSchema = z.object({
22
+ code: z.string(),
23
+ message: z.string(),
24
+ correlation_id: z.string().optional(),
25
+ details: z.unknown().optional()
26
+ });
27
+ var LegacyErrorEnvelopeSchema = z.object({
28
+ error: z.string(),
29
+ message: z.string(),
30
+ issues: z.unknown().optional()
31
+ });
32
+ var CanonicalTimingMsSchema = z.object({
33
+ total: z.number(),
34
+ policy: z.number().optional(),
35
+ route: z.number().optional(),
36
+ tool: z.number().optional()
37
+ });
38
+ var CanonicalMetaSchema = z.object({
39
+ requestId: z.string(),
40
+ sessionId: z.string().optional(),
41
+ executionId: z.string().optional(),
42
+ lineageId: z.string().optional(),
43
+ policy: CanonicalPolicyRefSchema.optional(),
44
+ route: CanonicalRouteRefSchema.optional(),
45
+ risk: CanonicalRiskMetaSchema.optional(),
46
+ timingMs: CanonicalTimingMsSchema
47
+ });
48
+ var CanonicalErrorSchema = z.object({
49
+ code: z.string(),
50
+ message: z.string(),
51
+ status: z.number(),
52
+ retryable: z.boolean(),
53
+ details: z.record(z.unknown()).optional()
54
+ });
55
+ var RunRecordSchema = z.object({
56
+ id: z.string(),
57
+ workspaceId: z.string(),
58
+ workflowVersionId: z.string(),
59
+ state: RunStateSchema,
60
+ correlationId: z.string(),
61
+ idempotencyKey: z.string().nullable().optional(),
62
+ triggerSource: z.enum(["ui", "api", "sdk", "cli", "hosted", "webhook"]),
63
+ requestedBy: z.string(),
64
+ requestedAt: z.string(),
65
+ startedAt: z.string().nullable().optional(),
66
+ completedAt: z.string().nullable().optional(),
67
+ slaPolicyId: z.string().nullable().optional(),
68
+ slaDueAt: z.string().nullable().optional(),
69
+ slaViolatedAt: z.string().nullable().optional()
70
+ });
71
+ var ApprovalSchema = z.object({
72
+ id: z.string(),
73
+ runId: z.string(),
74
+ requiredRole: z.string(),
75
+ status: z.enum(["pending", "approved", "denied"]),
76
+ createdAt: z.string(),
77
+ decidedAt: z.string().nullable().optional(),
78
+ decidedBy: z.string().nullable().optional()
79
+ });
80
+ var RunResponseSchema = z.object({
81
+ run: RunRecordSchema,
82
+ approval: ApprovalSchema.nullable().optional(),
83
+ blocked: z.boolean().optional(),
84
+ policyDecisions: z.array(
85
+ z.object({
86
+ mode: z.enum(["monitor", "enforce"]),
87
+ decision: z.enum(["allow", "deny"]),
88
+ policyId: z.string(),
89
+ ruleId: z.string(),
90
+ reasonCode: z.string(),
91
+ reasonText: z.string(),
92
+ correlationId: z.string(),
93
+ trace: z.record(z.unknown())
94
+ })
95
+ ).optional(),
96
+ entitlementDecision: z.object({
97
+ id: z.string(),
98
+ reasonCode: z.string(),
99
+ reasonText: z.string(),
100
+ entitled: z.boolean()
101
+ }).optional(),
102
+ usageEntry: z.object({
103
+ id: z.string(),
104
+ quantity: z.number()
105
+ }).optional(),
106
+ boundaryDecision: DataBoundaryDecisionSchema.nullable().optional(),
107
+ firewallDecision: FirewallInspectResultSchema.nullable().optional()
108
+ });
109
+ var V4SessionStartDataSchema = z.object({
110
+ session: V4SessionResourceSchema
111
+ });
112
+ var CanonicalEnvelopeSchema = (dataSchema) => z.object({
113
+ ok: z.boolean(),
114
+ data: dataSchema.nullable(),
115
+ meta: CanonicalMetaSchema,
116
+ error: CanonicalErrorSchema.nullable()
117
+ });
118
+ var V4SessionStartResponseSchema = CanonicalEnvelopeSchema(V4SessionStartDataSchema);
119
+ var V4ExecutionResponseSchema = CanonicalEnvelopeSchema(RunResponseSchema);
120
+ var V4EphemeralExecuteRequestBodySchema = V4SessionStartRequestSchema.extend({
121
+ input: z.record(z.unknown())
122
+ });
123
+ var V4EphemeralExecuteDataSchema = z.object({
124
+ session: V4SessionResourceSchema,
125
+ execution: RunResponseSchema
126
+ });
127
+ var V4EphemeralExecutionResponseSchema = CanonicalEnvelopeSchema(V4EphemeralExecuteDataSchema);
128
+ var CanonicalErrorEnvelopeSchema = z.object({
129
+ ok: z.literal(false),
130
+ data: z.null(),
131
+ meta: CanonicalMetaSchema,
132
+ error: CanonicalErrorSchema
133
+ });
134
+ var RunEventsResponseSchema = z.object({
135
+ events: z.array(
136
+ z.object({
137
+ id: z.string(),
138
+ runId: z.string(),
139
+ type: z.string(),
140
+ payload: z.record(z.unknown()),
141
+ createdAt: z.string()
142
+ })
143
+ )
144
+ });
145
+ var ApprovalsListSchema = z.object({
146
+ approvals: z.array(
147
+ z.object({
148
+ approval: ApprovalSchema,
149
+ run: RunRecordSchema
150
+ })
151
+ )
152
+ });
153
+ var DeployResponseSchema = z.object({
154
+ project: z.object({
155
+ id: z.string(),
156
+ workspaceId: z.string(),
157
+ name: z.string(),
158
+ createdBy: z.string(),
159
+ createdAt: z.string(),
160
+ updatedAt: z.string()
161
+ }),
162
+ workflow: z.object({
163
+ id: z.string(),
164
+ projectId: z.string(),
165
+ workspaceId: z.string(),
166
+ name: z.string(),
167
+ status: z.enum(["draft", "published", "archived"]),
168
+ createdBy: z.string(),
169
+ createdAt: z.string(),
170
+ updatedAt: z.string()
171
+ }),
172
+ draft: z.object({
173
+ workflowId: z.string(),
174
+ workspaceId: z.string(),
175
+ version: z.number(),
176
+ nodes: z.array(z.unknown()),
177
+ edges: z.array(z.unknown()),
178
+ viewport: z.object({ x: z.number(), y: z.number(), zoom: z.number() }),
179
+ updatedAt: z.string()
180
+ }),
181
+ compile: z.object({
182
+ checksum: z.string(),
183
+ mutationNodeIds: z.array(z.string()),
184
+ compiledPlan: z.record(z.unknown())
185
+ })
186
+ });
187
+ var PublishResponseSchema = z.object({
188
+ version: WorkflowVersionSchema
189
+ });
190
+ var ConnectorRecordSchema = z.object({
191
+ id: z.string(),
192
+ workspaceId: z.string(),
193
+ name: z.string(),
194
+ connectorType: z.string(),
195
+ status: z.enum(["active", "disabled"]),
196
+ config: z.record(z.unknown()),
197
+ createdBy: z.string(),
198
+ createdAt: z.string(),
199
+ updatedAt: z.string()
200
+ });
201
+ var WorkspaceSecretRecordSchema = z.object({
202
+ id: z.string(),
203
+ workspaceId: z.string(),
204
+ connectorId: z.string().nullable(),
205
+ name: z.string(),
206
+ keyVersion: z.string(),
207
+ metadata: z.record(z.unknown()),
208
+ createdBy: z.string(),
209
+ createdAt: z.string(),
210
+ rotatedAt: z.string().nullable(),
211
+ revokedAt: z.string().nullable(),
212
+ maskedValue: z.string()
213
+ });
214
+ var WorkspaceWebhookRecordSchema = z.object({
215
+ id: z.string(),
216
+ workspaceId: z.string(),
217
+ connectorId: z.string().nullable(),
218
+ name: z.string(),
219
+ path: z.string(),
220
+ targetWorkflowId: z.string(),
221
+ status: z.enum(["active", "disabled"]),
222
+ eventFilter: z.record(z.unknown()),
223
+ createdBy: z.string(),
224
+ createdAt: z.string(),
225
+ updatedAt: z.string(),
226
+ lastReceivedAt: z.string().nullable(),
227
+ revokedAt: z.string().nullable(),
228
+ signingSecretConfigured: z.boolean()
229
+ });
230
+ var ConnectorListSchema = z.object({
231
+ connectors: z.array(ConnectorRecordSchema)
232
+ });
233
+ var ConnectorMutationSchema = z.object({
234
+ connector: ConnectorRecordSchema
235
+ });
236
+ var SecretListSchema = z.object({
237
+ secrets: z.array(WorkspaceSecretRecordSchema)
238
+ });
239
+ var SecretMutationSchema = z.object({
240
+ secret: WorkspaceSecretRecordSchema
241
+ });
242
+ var WebhookListSchema = z.object({
243
+ webhooks: z.array(WorkspaceWebhookRecordSchema)
244
+ });
245
+ var WebhookMutationSchema = z.object({
246
+ webhook: WorkspaceWebhookRecordSchema
247
+ });
248
+ var EdgeInterceptorListSchema = z.object({
249
+ registrations: z.array(EdgeInterceptorRegistrationSchema)
250
+ });
251
+ var EdgeInterceptorMutationSchema = z.object({
252
+ registration: EdgeInterceptorRegistrationSchema
253
+ });
254
+ var FirewallInspectBodySchema = FirewallInspectRequestSchema.omit({
255
+ workspaceId: true
256
+ });
257
+ var DriftGateError = class extends Error {
258
+ constructor(code, message, status, correlationId, details) {
259
+ super(message);
260
+ this.code = code;
261
+ this.status = status;
262
+ this.correlationId = correlationId;
263
+ this.details = details;
264
+ this.name = "DriftGateError";
265
+ }
266
+ };
267
+ var DriftGateSessionHandle = class {
268
+ constructor(client, session, startEnvelope) {
269
+ this.client = client;
270
+ this.session = session;
271
+ this.startEnvelope = startEnvelope;
272
+ }
273
+ get sessionId() {
274
+ return this.session.sessionId;
275
+ }
276
+ get rawEnvelope() {
277
+ return this.startEnvelope.raw;
278
+ }
279
+ async execute(input) {
280
+ return this.client.executeSession(this.session.sessionId, input);
281
+ }
282
+ };
283
+ function isTerminalState(state) {
284
+ return ["succeeded", "failed", "denied", "timed_out", "canceled", "aborted"].includes(state);
285
+ }
286
+ var DriftGateClient = class {
287
+ baseUrl;
288
+ sessionToken;
289
+ apiKey;
290
+ fetchImpl;
291
+ edgeInterceptorState = null;
292
+ session;
293
+ approvals;
294
+ connectors;
295
+ secrets;
296
+ webhooks;
297
+ edgeInterceptors;
298
+ firewall;
299
+ constructor(options) {
300
+ this.baseUrl = options.baseUrl.replace(/\/$/, "");
301
+ this.sessionToken = options.sessionToken;
302
+ this.apiKey = options.apiKey;
303
+ this.fetchImpl = options.fetchImpl ?? fetch;
304
+ this.session = {
305
+ start: async (input) => {
306
+ const payload = V4SessionStartRequestSchema.parse(input);
307
+ const raw = await this.request("/v4/sessions.start", {
308
+ method: "POST",
309
+ body: JSON.stringify(payload)
310
+ });
311
+ const parsed = V4SessionStartResponseSchema.parse(raw);
312
+ if (!parsed.ok || !parsed.data) {
313
+ const canonicalCode = parsed.error?.code ?? "INTERNAL";
314
+ throw new DriftGateError(
315
+ canonicalCode,
316
+ parsed.error?.message ?? "session.start failed",
317
+ parsed.error?.status ?? 500,
318
+ parsed.meta.requestId,
319
+ parsed.error?.details
320
+ );
321
+ }
322
+ const envelope = {
323
+ ok: parsed.ok,
324
+ data: parsed.data,
325
+ meta: parsed.meta,
326
+ error: parsed.error,
327
+ raw
328
+ };
329
+ return new DriftGateSessionHandle(this, parsed.data.session, envelope);
330
+ }
331
+ };
332
+ this.approvals = {
333
+ list: async (workspaceId, status) => {
334
+ const query = status ? `?status=${encodeURIComponent(status)}` : "";
335
+ const response = await this.request(`/v1/headless/workspaces/${encodeURIComponent(workspaceId)}/approvals${query}`);
336
+ return ApprovalsListSchema.parse(response).approvals;
337
+ },
338
+ approve: async (approvalId) => {
339
+ const response = await this.request(`/v1/headless/approvals/${encodeURIComponent(approvalId)}/approve`, {
340
+ method: "POST"
341
+ });
342
+ return RunResponseSchema.parse(response);
343
+ },
344
+ deny: async (approvalId) => {
345
+ const response = await this.request(`/v1/headless/approvals/${encodeURIComponent(approvalId)}/deny`, {
346
+ method: "POST"
347
+ });
348
+ return RunResponseSchema.parse(response);
349
+ }
350
+ };
351
+ this.connectors = {
352
+ list: async (workspaceId) => {
353
+ const response = await this.request(
354
+ `/v1/workspaces/${encodeURIComponent(workspaceId)}/connectors`
355
+ );
356
+ return ConnectorListSchema.parse(response).connectors;
357
+ },
358
+ create: async (workspaceId, input) => {
359
+ const response = await this.request(
360
+ `/v1/workspaces/${encodeURIComponent(workspaceId)}/connectors`,
361
+ {
362
+ method: "POST",
363
+ body: JSON.stringify(input)
364
+ }
365
+ );
366
+ return ConnectorMutationSchema.parse(response).connector;
367
+ },
368
+ update: async (workspaceId, connectorId, input) => {
369
+ const response = await this.request(
370
+ `/v1/workspaces/${encodeURIComponent(workspaceId)}/connectors/${encodeURIComponent(
371
+ connectorId
372
+ )}`,
373
+ {
374
+ method: "PATCH",
375
+ body: JSON.stringify(input)
376
+ }
377
+ );
378
+ return ConnectorMutationSchema.parse(response).connector;
379
+ },
380
+ delete: async (workspaceId, connectorId) => {
381
+ const response = await this.request(
382
+ `/v1/workspaces/${encodeURIComponent(workspaceId)}/connectors/${encodeURIComponent(
383
+ connectorId
384
+ )}`,
385
+ {
386
+ method: "DELETE"
387
+ }
388
+ );
389
+ return ConnectorMutationSchema.parse(response).connector;
390
+ }
391
+ };
392
+ this.secrets = {
393
+ list: async (workspaceId) => {
394
+ const response = await this.request(
395
+ `/v1/workspaces/${encodeURIComponent(workspaceId)}/secrets`
396
+ );
397
+ return SecretListSchema.parse(response).secrets;
398
+ },
399
+ create: async (workspaceId, input) => {
400
+ const response = await this.request(
401
+ `/v1/workspaces/${encodeURIComponent(workspaceId)}/secrets`,
402
+ {
403
+ method: "POST",
404
+ body: JSON.stringify(input)
405
+ }
406
+ );
407
+ return SecretMutationSchema.parse(response).secret;
408
+ },
409
+ update: async (workspaceId, secretId, input) => {
410
+ const response = await this.request(
411
+ `/v1/workspaces/${encodeURIComponent(workspaceId)}/secrets/${encodeURIComponent(secretId)}`,
412
+ {
413
+ method: "PATCH",
414
+ body: JSON.stringify(input)
415
+ }
416
+ );
417
+ return SecretMutationSchema.parse(response).secret;
418
+ },
419
+ delete: async (workspaceId, secretId) => {
420
+ const response = await this.request(
421
+ `/v1/workspaces/${encodeURIComponent(workspaceId)}/secrets/${encodeURIComponent(secretId)}`,
422
+ {
423
+ method: "DELETE"
424
+ }
425
+ );
426
+ return SecretMutationSchema.parse(response).secret;
427
+ }
428
+ };
429
+ this.webhooks = {
430
+ list: async (workspaceId) => {
431
+ const response = await this.request(
432
+ `/v1/workspaces/${encodeURIComponent(workspaceId)}/webhooks`
433
+ );
434
+ return WebhookListSchema.parse(response).webhooks;
435
+ },
436
+ create: async (workspaceId, input) => {
437
+ const response = await this.request(
438
+ `/v1/workspaces/${encodeURIComponent(workspaceId)}/webhooks`,
439
+ {
440
+ method: "POST",
441
+ body: JSON.stringify(input)
442
+ }
443
+ );
444
+ return WebhookMutationSchema.parse(response).webhook;
445
+ },
446
+ update: async (workspaceId, webhookId, input) => {
447
+ const response = await this.request(
448
+ `/v1/workspaces/${encodeURIComponent(workspaceId)}/webhooks/${encodeURIComponent(
449
+ webhookId
450
+ )}`,
451
+ {
452
+ method: "PATCH",
453
+ body: JSON.stringify(input)
454
+ }
455
+ );
456
+ return WebhookMutationSchema.parse(response).webhook;
457
+ },
458
+ delete: async (workspaceId, webhookId) => {
459
+ const response = await this.request(
460
+ `/v1/workspaces/${encodeURIComponent(workspaceId)}/webhooks/${encodeURIComponent(
461
+ webhookId
462
+ )}`,
463
+ {
464
+ method: "DELETE"
465
+ }
466
+ );
467
+ return WebhookMutationSchema.parse(response).webhook;
468
+ }
469
+ };
470
+ this.edgeInterceptors = {
471
+ list: async (workspaceId) => {
472
+ const response = await this.request(
473
+ `/v1/workspaces/${encodeURIComponent(workspaceId)}/edge/interceptors`
474
+ );
475
+ return EdgeInterceptorListSchema.parse(response).registrations;
476
+ },
477
+ register: async (workspaceId, input) => {
478
+ const response = await this.request(
479
+ `/v1/workspaces/${encodeURIComponent(workspaceId)}/edge/interceptors`,
480
+ {
481
+ method: "POST",
482
+ body: JSON.stringify({
483
+ ...input,
484
+ workspaceId
485
+ })
486
+ }
487
+ );
488
+ return EdgeInterceptorMutationSchema.parse(response).registration;
489
+ },
490
+ setStatus: async (workspaceId, registrationId, status) => {
491
+ const parsedStatus = EdgeInterceptorStatusSchema.parse(status);
492
+ const response = await this.request(
493
+ `/v1/workspaces/${encodeURIComponent(workspaceId)}/edge/interceptors/${encodeURIComponent(
494
+ registrationId
495
+ )}/status`,
496
+ {
497
+ method: "PATCH",
498
+ body: JSON.stringify({ status: parsedStatus })
499
+ }
500
+ );
501
+ return EdgeInterceptorMutationSchema.parse(response).registration;
502
+ }
503
+ };
504
+ this.firewall = {
505
+ inspect: async (workspaceId, input) => {
506
+ const parsedInput = FirewallInspectBodySchema.parse(input);
507
+ const response = await this.request(
508
+ `/v1/workspaces/${encodeURIComponent(workspaceId)}/firewall/inspect`,
509
+ {
510
+ method: "POST",
511
+ body: JSON.stringify(parsedInput)
512
+ }
513
+ );
514
+ return FirewallInspectResponseSchema.parse(response);
515
+ },
516
+ events: async (workspaceId) => {
517
+ const response = await this.request(
518
+ `/v1/workspaces/${encodeURIComponent(workspaceId)}/firewall/events`
519
+ );
520
+ return FirewallEventsResponseSchema.parse(response).events;
521
+ }
522
+ };
523
+ }
524
+ async executeSession(sessionId, input) {
525
+ const payload = V4ExecutionRequestSchema.parse(input);
526
+ const raw = await this.request(`/v4/sessions/${encodeURIComponent(sessionId)}/executions.execute`, {
527
+ method: "POST",
528
+ body: JSON.stringify(payload)
529
+ });
530
+ const parsed = V4ExecutionResponseSchema.parse(raw);
531
+ if (!parsed.ok || !parsed.data) {
532
+ const canonicalCode = parsed.error?.code ?? "INTERNAL";
533
+ throw new DriftGateError(
534
+ canonicalCode,
535
+ parsed.error?.message ?? "session.execute failed",
536
+ parsed.error?.status ?? 500,
537
+ parsed.meta.requestId,
538
+ parsed.error?.details
539
+ );
540
+ }
541
+ return {
542
+ ok: parsed.ok,
543
+ data: parsed.data,
544
+ meta: parsed.meta,
545
+ error: parsed.error,
546
+ raw
547
+ };
548
+ }
549
+ async execute(input) {
550
+ const payload = V4EphemeralExecuteRequestBodySchema.parse(input);
551
+ const raw = await this.request("/v4/execute", {
552
+ method: "POST",
553
+ body: JSON.stringify(payload)
554
+ });
555
+ const parsed = V4EphemeralExecutionResponseSchema.parse(raw);
556
+ if (!parsed.ok || !parsed.data) {
557
+ const canonicalCode = parsed.error?.code ?? "INTERNAL";
558
+ throw new DriftGateError(
559
+ canonicalCode,
560
+ parsed.error?.message ?? "execute failed",
561
+ parsed.error?.status ?? 500,
562
+ parsed.meta.requestId,
563
+ parsed.error?.details
564
+ );
565
+ }
566
+ return {
567
+ ok: parsed.ok,
568
+ data: parsed.data,
569
+ meta: parsed.meta,
570
+ error: parsed.error,
571
+ raw
572
+ };
573
+ }
574
+ async enableEdgeMode(input) {
575
+ const registration = await this.edgeInterceptors.register(input.workspaceId, input.registration);
576
+ this.edgeInterceptorState = {
577
+ workspaceId: input.workspaceId,
578
+ registration,
579
+ enforcement: input.enforcement ?? "monitor",
580
+ hooks: input.hooks
581
+ };
582
+ return registration;
583
+ }
584
+ disableEdgeMode() {
585
+ this.edgeInterceptorState = null;
586
+ }
587
+ async run(input) {
588
+ const edgeDecision = this.evaluateEdgeDecision(input);
589
+ const edgeState = this.edgeInterceptorState;
590
+ if (edgeDecision && edgeState?.hooks?.beforeRun) {
591
+ await edgeState.hooks.beforeRun(input, edgeDecision);
592
+ }
593
+ if (edgeDecision && !edgeDecision.allowed) {
594
+ if (edgeState?.hooks?.onBlocked) {
595
+ await edgeState.hooks.onBlocked(input, edgeDecision);
596
+ }
597
+ if (edgeState?.enforcement === "enforce") {
598
+ throw new DriftGateError(
599
+ "edge_interceptor_denied",
600
+ edgeDecision.reasonText,
601
+ 403,
602
+ void 0,
603
+ {
604
+ reasonCode: edgeDecision.reasonCode,
605
+ requiredCapabilities: edgeDecision.requiredCapabilities,
606
+ grantedCapabilities: edgeDecision.grantedCapabilities,
607
+ registrationId: edgeState.registration.registrationId
608
+ }
609
+ );
610
+ }
611
+ }
612
+ const response = await this.request("/v1/headless/runs", {
613
+ method: "POST",
614
+ body: JSON.stringify(input)
615
+ });
616
+ const parsed = RunResponseSchema.parse(response);
617
+ if (edgeDecision && edgeState?.hooks?.afterRun) {
618
+ await edgeState.hooks.afterRun(input, edgeDecision, parsed);
619
+ }
620
+ return parsed;
621
+ }
622
+ async status(runId) {
623
+ const response = await this.request(`/v1/headless/runs/${encodeURIComponent(runId)}`);
624
+ return RunResponseSchema.parse(response);
625
+ }
626
+ async events(runId) {
627
+ const response = await this.request(`/v1/headless/runs/${encodeURIComponent(runId)}/events`);
628
+ return RunEventsResponseSchema.parse(response).events;
629
+ }
630
+ async waitForTerminal(runId, options = {}) {
631
+ const intervalMs = options.intervalMs ?? 1500;
632
+ const timeoutMs = options.timeoutMs ?? 12e4;
633
+ const startedAt = Date.now();
634
+ while (true) {
635
+ const current = await this.status(runId);
636
+ if (isTerminalState(current.run.state)) {
637
+ return current;
638
+ }
639
+ if (Date.now() - startedAt >= timeoutMs) {
640
+ throw new DriftGateError("timeout", `run ${runId} did not reach terminal state before timeout`, 408);
641
+ }
642
+ await sleep(intervalMs);
643
+ }
644
+ }
645
+ async deployWorkflow(input) {
646
+ const response = await this.request("/v1/headless/workflows/deploy", {
647
+ method: "POST",
648
+ body: JSON.stringify(input)
649
+ });
650
+ return DeployResponseSchema.parse(response);
651
+ }
652
+ async publishWorkflow(workflowId, workflowYaml) {
653
+ const response = await this.request(`/v1/headless/workflows/${encodeURIComponent(workflowId)}/publish`, {
654
+ method: "POST",
655
+ body: JSON.stringify(workflowYaml ? { workflowYaml } : {})
656
+ });
657
+ const parsed = PublishResponseSchema.parse(response);
658
+ return parsed.version;
659
+ }
660
+ evaluateEdgeDecision(input) {
661
+ const state = this.edgeInterceptorState;
662
+ if (!state || state.workspaceId !== input.workspaceId) {
663
+ return null;
664
+ }
665
+ if (state.registration.status !== "active") {
666
+ return {
667
+ allowed: true,
668
+ reasonCode: "edge.interceptor.disabled",
669
+ reasonText: "Edge interceptor registration is disabled; enforcement skipped.",
670
+ requiredCapabilities: [],
671
+ grantedCapabilities: state.registration.capabilities
672
+ };
673
+ }
674
+ const requiredCapabilities = ["runs:create"];
675
+ const grantedCapabilities = state.registration.capabilities;
676
+ const grantedSet = new Set(grantedCapabilities);
677
+ const missingCapabilities = requiredCapabilities.filter(
678
+ (capability) => !grantedSet.has(capability)
679
+ );
680
+ if (missingCapabilities.length === 0) {
681
+ return {
682
+ allowed: true,
683
+ reasonCode: "edge.interceptor.allow",
684
+ reasonText: "Edge interceptor capability checks passed.",
685
+ requiredCapabilities,
686
+ grantedCapabilities
687
+ };
688
+ }
689
+ return {
690
+ allowed: false,
691
+ reasonCode: "edge.interceptor.denied.missing_capability",
692
+ reasonText: `Missing required edge capabilities: ${missingCapabilities.join(", ")}`,
693
+ requiredCapabilities,
694
+ grantedCapabilities
695
+ };
696
+ }
697
+ async request(path, init = {}) {
698
+ const headers = new Headers(init.headers ?? {});
699
+ if (!headers.has("content-type") && init.body) {
700
+ headers.set("content-type", "application/json");
701
+ }
702
+ if (this.apiKey) {
703
+ headers.set("x-driftgate-api-key", this.apiKey);
704
+ } else if (this.sessionToken) {
705
+ headers.set("authorization", `Bearer ${this.sessionToken}`);
706
+ }
707
+ const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
708
+ ...init,
709
+ headers
710
+ });
711
+ const rawText = await response.text();
712
+ const body = rawText.length > 0 ? safelyParseJson(rawText) : null;
713
+ if (!response.ok) {
714
+ const canonicalEnvelope = CanonicalErrorEnvelopeSchema.safeParse(body);
715
+ if (canonicalEnvelope.success) {
716
+ throw new DriftGateError(
717
+ canonicalEnvelope.data.error.code,
718
+ canonicalEnvelope.data.error.message,
719
+ canonicalEnvelope.data.error.status,
720
+ canonicalEnvelope.data.meta.requestId,
721
+ canonicalEnvelope.data.error.details
722
+ );
723
+ }
724
+ const envelope = HeadlessErrorEnvelopeSchema.safeParse(body);
725
+ if (envelope.success) {
726
+ throw new DriftGateError(
727
+ envelope.data.code,
728
+ envelope.data.message,
729
+ response.status,
730
+ envelope.data.correlation_id,
731
+ envelope.data.details
732
+ );
733
+ }
734
+ const legacyEnvelope = LegacyErrorEnvelopeSchema.safeParse(body);
735
+ if (legacyEnvelope.success) {
736
+ throw new DriftGateError(
737
+ legacyEnvelope.data.error,
738
+ legacyEnvelope.data.message,
739
+ response.status,
740
+ void 0,
741
+ legacyEnvelope.data.issues
742
+ );
743
+ }
744
+ throw new DriftGateError(
745
+ "http_error",
746
+ `request failed (${response.status})${rawText ? `: ${rawText}` : ""}`,
747
+ response.status
748
+ );
749
+ }
750
+ return body;
751
+ }
752
+ };
753
+ function safelyParseJson(input) {
754
+ try {
755
+ return JSON.parse(input);
756
+ } catch {
757
+ return { raw: input };
758
+ }
759
+ }
760
+ export {
761
+ DriftGateClient,
762
+ DriftGateError,
763
+ DriftGateSessionHandle
764
+ };
765
+ //# sourceMappingURL=index.js.map