@driftgate/cli 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,1046 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { mkdir, readFile, writeFile } from "fs/promises";
5
+ import { existsSync } from "fs";
6
+ import path from "path";
7
+ import process from "process";
8
+ import { randomUUID } from "crypto";
9
+ import {
10
+ DriftGateClient,
11
+ DriftGateError
12
+ } from "@driftgate/sdk";
13
+ import { compileWorkflowYaml } from "@driftgate/workflow-compiler";
14
+ var REQUIRED_V4_ERROR_CODES = /* @__PURE__ */ new Set([
15
+ "AUTH_INVALID",
16
+ "POLICY_DENIED",
17
+ "RISK_EXCEEDED",
18
+ "ROUTE_UNAVAILABLE",
19
+ "TOOL_BLOCKED",
20
+ "RATE_LIMITED",
21
+ "TIMEOUT",
22
+ "INTERNAL",
23
+ "INVALID_REQUEST"
24
+ ]);
25
+ async function main() {
26
+ const [, , command, ...rest] = process.argv;
27
+ switch (command) {
28
+ case "login":
29
+ await handleLogin(rest);
30
+ return;
31
+ case "init":
32
+ await handleInit(rest);
33
+ return;
34
+ case "deploy":
35
+ await handleDeploy(rest);
36
+ return;
37
+ case "publish":
38
+ await handlePublish(rest);
39
+ return;
40
+ case "session":
41
+ await handleSession(rest);
42
+ return;
43
+ case "execute":
44
+ await handleExecute(rest);
45
+ return;
46
+ case "execution":
47
+ await handleExecution(rest);
48
+ return;
49
+ case "run":
50
+ throw deprecatedCommandError("run", "driftgate session execute <sessionId> --input '{...}'");
51
+ case "status":
52
+ throw deprecatedCommandError("status", "driftgate execution status <executionId>");
53
+ case "approvals":
54
+ await handleApprovals(rest);
55
+ return;
56
+ case "connectors":
57
+ await handleConnectors(rest);
58
+ return;
59
+ case "secrets":
60
+ await handleSecrets(rest);
61
+ return;
62
+ case "webhooks":
63
+ await handleWebhooks(rest);
64
+ return;
65
+ case "help":
66
+ case "--help":
67
+ case "-h":
68
+ case void 0:
69
+ printHelp();
70
+ return;
71
+ default:
72
+ throw new Error(`unknown command: ${command}`);
73
+ }
74
+ }
75
+ function parseArgs(input) {
76
+ const positionals = [];
77
+ const options = {};
78
+ for (let index = 0; index < input.length; index += 1) {
79
+ const item = input[index];
80
+ if (!item.startsWith("--")) {
81
+ positionals.push(item);
82
+ continue;
83
+ }
84
+ const key = item.slice(2);
85
+ const next = input[index + 1];
86
+ if (!next || next.startsWith("--")) {
87
+ options[key] = true;
88
+ continue;
89
+ }
90
+ options[key] = next;
91
+ index += 1;
92
+ }
93
+ return { positionals, options };
94
+ }
95
+ function resolveBaseUrl(args, config) {
96
+ const option = args.options["api-base"];
97
+ if (typeof option === "string" && option.length > 0) {
98
+ return option.replace(/\/$/, "");
99
+ }
100
+ const envBase = process.env.DG_API_BASE ?? process.env.DRIFTGATE_API_BASE;
101
+ if (envBase && envBase.length > 0) {
102
+ return envBase.replace(/\/$/, "");
103
+ }
104
+ if (config?.baseUrl) {
105
+ return config.baseUrl.replace(/\/$/, "");
106
+ }
107
+ return "http://127.0.0.1:3001";
108
+ }
109
+ function getConfigDir() {
110
+ const custom = process.env.DRIFTGATE_CLI_CONFIG_DIR;
111
+ if (custom && custom.length > 0) {
112
+ return custom;
113
+ }
114
+ const home = process.env.HOME;
115
+ if (!home) {
116
+ throw new Error("HOME is required for CLI config storage");
117
+ }
118
+ return path.join(home, ".config", "driftgate");
119
+ }
120
+ function getConfigPath() {
121
+ return path.join(getConfigDir(), "credentials.json");
122
+ }
123
+ async function loadConfig() {
124
+ const configPath = getConfigPath();
125
+ if (!existsSync(configPath)) {
126
+ return null;
127
+ }
128
+ const raw = await readFile(configPath, "utf8");
129
+ return JSON.parse(raw);
130
+ }
131
+ async function saveConfig(config) {
132
+ const configDir = getConfigDir();
133
+ await mkdir(configDir, { recursive: true });
134
+ await writeFile(getConfigPath(), JSON.stringify(config, null, 2));
135
+ }
136
+ function buildClient(baseUrl, config) {
137
+ const apiKey = process.env.DRIFTGATE_API_KEY ?? config?.apiKey;
138
+ const sessionToken = apiKey ? void 0 : process.env.DRIFTGATE_SESSION_TOKEN ?? config?.sessionToken;
139
+ if (!apiKey && !sessionToken) {
140
+ throw new Error("no credentials found; run `driftgate login` or set DRIFTGATE_API_KEY");
141
+ }
142
+ return new DriftGateClient({
143
+ baseUrl,
144
+ apiKey,
145
+ sessionToken
146
+ });
147
+ }
148
+ function requireWorkspaceId(args, usage) {
149
+ const workspaceId = typeof args.options.workspace === "string" && args.options.workspace || process.env.DRIFTGATE_WORKSPACE_ID;
150
+ if (!workspaceId) {
151
+ throw new Error(usage);
152
+ }
153
+ return workspaceId;
154
+ }
155
+ function parseBooleanValue(value, label) {
156
+ if (value === void 0) {
157
+ return void 0;
158
+ }
159
+ if (value === true) {
160
+ return true;
161
+ }
162
+ const normalized = value.trim().toLowerCase();
163
+ if (["1", "true", "yes", "on"].includes(normalized)) {
164
+ return true;
165
+ }
166
+ if (["0", "false", "no", "off"].includes(normalized)) {
167
+ return false;
168
+ }
169
+ throw new Error(`${label} must be one of: true|false|1|0|yes|no|on|off`);
170
+ }
171
+ async function parseJsonObjectOption(value, label, fallback) {
172
+ if (typeof value !== "string" || value.length === 0) {
173
+ return fallback;
174
+ }
175
+ const raw = value.startsWith("@") ? await readFile(value.slice(1), "utf8") : value;
176
+ const parsed = JSON.parse(raw);
177
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
178
+ throw new Error(`${label} must be a JSON object`);
179
+ }
180
+ return parsed;
181
+ }
182
+ function parseNullableConnectorId(value) {
183
+ if (value === void 0 || value === true) {
184
+ return void 0;
185
+ }
186
+ if (value.trim().toLowerCase() === "null") {
187
+ return null;
188
+ }
189
+ return value;
190
+ }
191
+ function optionString(value) {
192
+ return typeof value === "string" && value.length > 0 ? value : void 0;
193
+ }
194
+ function deprecatedCommandError(command, replacement) {
195
+ return new Error(`command '${command}' was removed in V4 CLI; use '${replacement}'`);
196
+ }
197
+ function nowNs() {
198
+ return process.hrtime.bigint();
199
+ }
200
+ function elapsedMs(startNs) {
201
+ const elapsed = process.hrtime.bigint() - startNs;
202
+ return Number((Number(elapsed) / 1e6).toFixed(3));
203
+ }
204
+ function parseNumberOption(value, label, { integer = false, minimum } = {}) {
205
+ const raw = optionString(value);
206
+ if (!raw) {
207
+ return void 0;
208
+ }
209
+ const parsed = Number(raw);
210
+ if (!Number.isFinite(parsed)) {
211
+ throw new Error(`${label} must be a valid number`);
212
+ }
213
+ if (integer && !Number.isInteger(parsed)) {
214
+ throw new Error(`${label} must be an integer`);
215
+ }
216
+ if (minimum !== void 0 && parsed < minimum) {
217
+ throw new Error(`${label} must be >= ${minimum}`);
218
+ }
219
+ return parsed;
220
+ }
221
+ async function parseRequiredInputOption(args) {
222
+ const rawInput = optionString(args.options.input);
223
+ if (!rawInput) {
224
+ throw new Error("usage: --input '{...}' or --input @input.json is required");
225
+ }
226
+ const source = rawInput.startsWith("@") ? await readFile(rawInput.slice(1), "utf8") : rawInput;
227
+ const parsed = JSON.parse(source);
228
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
229
+ throw new Error("--input must parse to a JSON object");
230
+ }
231
+ return parsed;
232
+ }
233
+ async function parsePolicyOption(args) {
234
+ const policyOption = optionString(args.options.policy);
235
+ if (policyOption) {
236
+ const value = await parseJsonObjectOption(policyOption, "--policy", {});
237
+ const ref2 = typeof value.ref === "string" ? value.ref : void 0;
238
+ const version2 = typeof value.version === "string" ? value.version : void 0;
239
+ if (!ref2 || !version2) {
240
+ throw new Error("--policy requires JSON object with string fields 'ref' and 'version'");
241
+ }
242
+ return { ref: ref2, version: version2 };
243
+ }
244
+ const ref = optionString(args.options["policy-ref"]);
245
+ const version = optionString(args.options["policy-version"]);
246
+ if (!ref && !version) {
247
+ return void 0;
248
+ }
249
+ if (!ref || !version) {
250
+ throw new Error("--policy-ref and --policy-version must be provided together");
251
+ }
252
+ return { ref, version };
253
+ }
254
+ async function parseRouteOption(args) {
255
+ const routeOption = optionString(args.options.route);
256
+ if (routeOption) {
257
+ const value = await parseJsonObjectOption(routeOption, "--route", {});
258
+ const provider2 = typeof value.provider === "string" ? value.provider : void 0;
259
+ const model2 = typeof value.model === "string" ? value.model : void 0;
260
+ const region2 = typeof value.region === "string" ? value.region : void 0;
261
+ if (!provider2 && !model2 && !region2) {
262
+ throw new Error("--route requires at least one of provider/model/region");
263
+ }
264
+ return { provider: provider2, model: model2, region: region2 };
265
+ }
266
+ const provider = optionString(args.options["route-provider"]);
267
+ const model = optionString(args.options["route-model"]);
268
+ const region = optionString(args.options["route-region"]);
269
+ if (!provider && !model && !region) {
270
+ return void 0;
271
+ }
272
+ return { provider, model, region };
273
+ }
274
+ async function parseRiskOption(args) {
275
+ const riskOption = optionString(args.options.risk);
276
+ if (riskOption) {
277
+ const value = await parseJsonObjectOption(riskOption, "--risk", {});
278
+ const score2 = typeof value.score === "number" ? value.score : void 0;
279
+ const decision2 = value.decision === "allow" || value.decision === "deny" || value.decision === "review" ? value.decision : void 0;
280
+ if (score2 === void 0 && decision2 === void 0) {
281
+ throw new Error("--risk requires at least one of score/decision");
282
+ }
283
+ return { score: score2, decision: decision2 };
284
+ }
285
+ const score = parseNumberOption(args.options["risk-score"], "--risk-score");
286
+ const decisionRaw = optionString(args.options["risk-decision"]);
287
+ if (decisionRaw && !["allow", "deny", "review"].includes(decisionRaw)) {
288
+ throw new Error("--risk-decision must be one of: allow|deny|review");
289
+ }
290
+ const decision = decisionRaw;
291
+ if (score === void 0 && decision === void 0) {
292
+ return void 0;
293
+ }
294
+ return { score, decision };
295
+ }
296
+ async function parseV4ExecutionDefaults(args) {
297
+ return {
298
+ policy: await parsePolicyOption(args),
299
+ route: await parseRouteOption(args),
300
+ risk: await parseRiskOption(args),
301
+ workflowVersionId: optionString(args.options["workflow-version-id"])
302
+ };
303
+ }
304
+ function canonicalOutput(response) {
305
+ return {
306
+ ok: response.ok,
307
+ data: response.data,
308
+ meta: response.meta,
309
+ error: response.error
310
+ };
311
+ }
312
+ function printJson(value) {
313
+ console.log(JSON.stringify(value, null, 2));
314
+ }
315
+ function mapStableErrorCode(inputCode, status) {
316
+ const normalized = inputCode.trim().toUpperCase();
317
+ if (REQUIRED_V4_ERROR_CODES.has(normalized)) {
318
+ return normalized;
319
+ }
320
+ switch (normalized) {
321
+ case "FORBIDDEN":
322
+ case "POLICY_DENIED":
323
+ case "ENTITLEMENT_DENIED":
324
+ return "POLICY_DENIED";
325
+ case "UNAUTHORIZED":
326
+ case "AUTH_INVALID":
327
+ return "AUTH_INVALID";
328
+ case "FIREWALL_DENIED":
329
+ case "TOOL_BLOCKED":
330
+ return "TOOL_BLOCKED";
331
+ case "NOT_FOUND":
332
+ return "ROUTE_UNAVAILABLE";
333
+ case "TIMEOUT":
334
+ return "TIMEOUT";
335
+ case "RATE_LIMITED":
336
+ return "RATE_LIMITED";
337
+ case "RISK_EXCEEDED":
338
+ return "RISK_EXCEEDED";
339
+ case "INVALID_REQUEST":
340
+ return "INVALID_REQUEST";
341
+ default:
342
+ break;
343
+ }
344
+ if (status === 401 || status === 403) {
345
+ return "AUTH_INVALID";
346
+ }
347
+ if (status === 404) {
348
+ return "ROUTE_UNAVAILABLE";
349
+ }
350
+ if (status === 408 || status === 504) {
351
+ return "TIMEOUT";
352
+ }
353
+ if (status === 429) {
354
+ return "RATE_LIMITED";
355
+ }
356
+ return "INTERNAL";
357
+ }
358
+ function renderErrorEnvelope(error) {
359
+ if (error instanceof DriftGateError) {
360
+ const code2 = mapStableErrorCode(error.code, error.status);
361
+ const retryable = code2 === "RATE_LIMITED" || code2 === "TIMEOUT" || error.status >= 500 || error.status === 429;
362
+ return {
363
+ ok: false,
364
+ data: null,
365
+ meta: {
366
+ requestId: error.correlationId ?? `cli_${randomUUID()}`,
367
+ timingMs: { total: 0 }
368
+ },
369
+ error: {
370
+ code: code2,
371
+ message: error.message,
372
+ status: error.status,
373
+ retryable,
374
+ ...error.details && typeof error.details === "object" ? { details: error.details } : {}
375
+ }
376
+ };
377
+ }
378
+ const message = error instanceof Error ? error.message : String(error);
379
+ const isUsageError = message.startsWith("usage:") || message.includes("removed in V4 CLI");
380
+ const status = isUsageError ? 400 : 500;
381
+ const code = isUsageError ? "INVALID_REQUEST" : "INTERNAL";
382
+ return {
383
+ ok: false,
384
+ data: null,
385
+ meta: {
386
+ requestId: `cli_${randomUUID()}`,
387
+ timingMs: { total: 0 }
388
+ },
389
+ error: {
390
+ code,
391
+ message,
392
+ status,
393
+ retryable: false
394
+ }
395
+ };
396
+ }
397
+ async function handleLogin(rest) {
398
+ const args = parseArgs(rest);
399
+ const existingConfig = await loadConfig();
400
+ const baseUrl = resolveBaseUrl(args, existingConfig ?? void 0);
401
+ const directApiKey = process.env.DRIFTGATE_API_KEY;
402
+ if (directApiKey && directApiKey.length > 0) {
403
+ await saveConfig({ baseUrl, apiKey: directApiKey });
404
+ console.log("Saved API key credentials for DriftGate CLI.");
405
+ return;
406
+ }
407
+ const auth0Domain = process.env.AUTH0_DOMAIN;
408
+ const auth0ClientId = process.env.AUTH0_CLIENT_ID;
409
+ if (!auth0Domain || !auth0ClientId) {
410
+ throw new Error("AUTH0_DOMAIN and AUTH0_CLIENT_ID are required for device-code login");
411
+ }
412
+ const auth0Audience = process.env.AUTH0_AUDIENCE;
413
+ const deviceCodeResponse = await fetch(`https://${auth0Domain}/oauth/device/code`, {
414
+ method: "POST",
415
+ headers: {
416
+ "content-type": "application/x-www-form-urlencoded"
417
+ },
418
+ body: new URLSearchParams({
419
+ client_id: auth0ClientId,
420
+ scope: "openid profile email offline_access",
421
+ ...auth0Audience ? { audience: auth0Audience } : {}
422
+ })
423
+ });
424
+ if (!deviceCodeResponse.ok) {
425
+ const body = await deviceCodeResponse.text();
426
+ throw new Error(`device-code start failed (${deviceCodeResponse.status}): ${body}`);
427
+ }
428
+ const deviceBody = await deviceCodeResponse.json();
429
+ console.log("Complete DriftGate login in your browser:");
430
+ console.log(deviceBody.verification_uri_complete ?? deviceBody.verification_uri);
431
+ console.log(`User code: ${deviceBody.user_code}`);
432
+ const tokenEndpoint = `https://${auth0Domain}/oauth/token`;
433
+ const pollIntervalMs = (deviceBody.interval ?? 5) * 1e3;
434
+ const timeoutAt = Date.now() + deviceBody.expires_in * 1e3;
435
+ let idToken = null;
436
+ while (Date.now() < timeoutAt) {
437
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
438
+ const tokenResponse = await fetch(tokenEndpoint, {
439
+ method: "POST",
440
+ headers: {
441
+ "content-type": "application/x-www-form-urlencoded"
442
+ },
443
+ body: new URLSearchParams({
444
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
445
+ device_code: deviceBody.device_code,
446
+ client_id: auth0ClientId
447
+ })
448
+ });
449
+ const tokenJson = await tokenResponse.json();
450
+ if (tokenResponse.ok) {
451
+ idToken = tokenJson.id_token ?? null;
452
+ break;
453
+ }
454
+ if (tokenJson.error === "authorization_pending" || tokenJson.error === "slow_down") {
455
+ continue;
456
+ }
457
+ throw new Error(
458
+ `device-code login failed: ${tokenJson.error ?? "unknown_error"} ${tokenJson.error_description ?? ""}`
459
+ );
460
+ }
461
+ if (!idToken) {
462
+ throw new Error("device-code login timed out or missing id_token in token response");
463
+ }
464
+ const exchangeResponse = await fetch(`${baseUrl}/v1/auth/session/exchange`, {
465
+ method: "POST",
466
+ headers: {
467
+ "content-type": "application/json"
468
+ },
469
+ body: JSON.stringify({ idToken })
470
+ });
471
+ const exchangeBody = await exchangeResponse.json();
472
+ if (!exchangeResponse.ok || typeof exchangeBody.sessionToken !== "string") {
473
+ throw new Error(`session exchange failed (${exchangeResponse.status}): ${JSON.stringify(exchangeBody)}`);
474
+ }
475
+ await saveConfig({
476
+ baseUrl,
477
+ sessionToken: exchangeBody.sessionToken,
478
+ expiresAt: typeof exchangeBody.expiresAt === "string" ? exchangeBody.expiresAt : void 0
479
+ });
480
+ console.log("Device-code login successful. Session token stored for DriftGate CLI.");
481
+ }
482
+ async function handleInit(rest) {
483
+ const args = parseArgs(rest);
484
+ const outputPath = typeof args.options["out"] === "string" && args.options["out"] || "workflow.yaml";
485
+ if (existsSync(outputPath) && args.options.force !== true) {
486
+ throw new Error(`${outputPath} already exists. Pass --force to overwrite.`);
487
+ }
488
+ const template = `apiVersion: driftgate.ai/v1
489
+ kind: Workflow
490
+ metadata:
491
+ name: starter-workflow
492
+ workspace: workspace_id_here
493
+ spec:
494
+ governance:
495
+ policyBindings: []
496
+ slaBindings: []
497
+ nodes:
498
+ - id: intake
499
+ type: http
500
+ config:
501
+ method: POST
502
+ path: /intake
503
+ - id: complete
504
+ type: task
505
+ config: {}
506
+ edges:
507
+ - from: intake
508
+ to: complete
509
+ `;
510
+ await writeFile(outputPath, template, "utf8");
511
+ console.log(`Created ${outputPath}`);
512
+ }
513
+ async function handleDeploy(rest) {
514
+ const args = parseArgs(rest);
515
+ const [yamlPath] = args.positionals;
516
+ if (!yamlPath) {
517
+ throw new Error("usage: driftgate deploy <workflow.yaml> [--workspace <workspaceId>] [--project <name>] [--workflow <name>]");
518
+ }
519
+ const config = await loadConfig();
520
+ const baseUrl = resolveBaseUrl(args, config ?? void 0);
521
+ const client = buildClient(baseUrl, config);
522
+ const workflowYaml = await readFile(yamlPath, "utf8");
523
+ const compiled = compileWorkflowYaml(workflowYaml);
524
+ const workspaceId = typeof args.options.workspace === "string" && args.options.workspace || process.env.DRIFTGATE_WORKSPACE_ID;
525
+ if (!workspaceId) {
526
+ throw new Error("workspace id is required (--workspace or DRIFTGATE_WORKSPACE_ID)");
527
+ }
528
+ const response = await client.deployWorkflow({
529
+ workspaceId,
530
+ projectName: typeof args.options.project === "string" && args.options.project || compiled.workflow.metadata.name,
531
+ workflowName: typeof args.options.workflow === "string" && args.options.workflow || compiled.workflow.metadata.name,
532
+ workflowYaml
533
+ });
534
+ console.log(
535
+ JSON.stringify(
536
+ {
537
+ workflowId: response.workflow.id,
538
+ projectId: response.project.id,
539
+ draftVersion: response.draft.version,
540
+ checksum: response.compile.checksum,
541
+ mutationNodeIds: response.compile.mutationNodeIds
542
+ },
543
+ null,
544
+ 2
545
+ )
546
+ );
547
+ }
548
+ async function handlePublish(rest) {
549
+ const args = parseArgs(rest);
550
+ const [workflowId] = args.positionals;
551
+ if (!workflowId) {
552
+ throw new Error("usage: driftgate publish <workflowId> [--yaml <workflow.yaml>]");
553
+ }
554
+ const config = await loadConfig();
555
+ const baseUrl = resolveBaseUrl(args, config ?? void 0);
556
+ const client = buildClient(baseUrl, config);
557
+ const yamlPath = typeof args.options.yaml === "string" ? args.options.yaml : null;
558
+ const workflowYaml = yamlPath ? await readFile(yamlPath, "utf8") : void 0;
559
+ const version = await client.publishWorkflow(workflowId, workflowYaml);
560
+ console.log(JSON.stringify(version, null, 2));
561
+ }
562
+ async function handleSession(rest) {
563
+ const [subcommand, ...tail] = rest;
564
+ switch (subcommand) {
565
+ case "start":
566
+ await handleSessionStart(tail);
567
+ return;
568
+ case "execute":
569
+ await handleSessionExecute(tail);
570
+ return;
571
+ default:
572
+ throw new Error(
573
+ "usage: driftgate session start --agent <agent> [--workspace <workspaceId>] [--metadata '{...}'] [--policy '{...}'] [--route '{...}'] [--risk '{...}'] [--workflow-version-id <id>] [--expires-at <ISO-8601>] | driftgate session execute <sessionId> --input '{...}' [--policy '{...}'] [--route '{...}'] [--risk '{...}'] [--workflow-version-id <id>]"
574
+ );
575
+ }
576
+ }
577
+ async function handleSessionStart(rest) {
578
+ const args = parseArgs(rest);
579
+ const config = await loadConfig();
580
+ const baseUrl = resolveBaseUrl(args, config ?? void 0);
581
+ const client = buildClient(baseUrl, config);
582
+ const agent = optionString(args.options.agent);
583
+ if (!agent) {
584
+ throw new Error(
585
+ "usage: driftgate session start --agent <agent> [--workspace <workspaceId>] [--metadata '{...}'] [--policy '{...}'] [--route '{...}'] [--risk '{...}'] [--workflow-version-id <id>] [--expires-at <ISO-8601>]"
586
+ );
587
+ }
588
+ const metadata = typeof args.options.metadata === "string" ? await parseJsonObjectOption(args.options.metadata, "--metadata", {}) : void 0;
589
+ const workspaceId = optionString(args.options.workspace) ?? process.env.DRIFTGATE_WORKSPACE_ID;
590
+ const input = {
591
+ ...workspaceId ? { workspaceId } : {},
592
+ agent,
593
+ ...optionString(args.options.subject) ? { subject: optionString(args.options.subject) } : {},
594
+ ...metadata ? { metadata } : {},
595
+ ...await parseV4ExecutionDefaults(args),
596
+ ...optionString(args.options["expires-at"]) ? { expiresAt: optionString(args.options["expires-at"]) } : {}
597
+ };
598
+ const session = await client.session.start(input);
599
+ printJson(canonicalOutput(session.startEnvelope));
600
+ }
601
+ async function handleSessionExecute(rest) {
602
+ const args = parseArgs(rest);
603
+ const [sessionId] = args.positionals;
604
+ if (!sessionId) {
605
+ throw new Error(
606
+ "usage: driftgate session execute <sessionId> --input '{...}' [--policy '{...}'] [--route '{...}'] [--risk '{...}'] [--workflow-version-id <id>]"
607
+ );
608
+ }
609
+ const config = await loadConfig();
610
+ const baseUrl = resolveBaseUrl(args, config ?? void 0);
611
+ const client = buildClient(baseUrl, config);
612
+ const input = {
613
+ input: await parseRequiredInputOption(args),
614
+ ...await parseV4ExecutionDefaults(args)
615
+ };
616
+ const response = await client.executeSession(sessionId, input);
617
+ printJson(canonicalOutput(response));
618
+ }
619
+ async function handleExecute(rest) {
620
+ const args = parseArgs(rest);
621
+ const config = await loadConfig();
622
+ const baseUrl = resolveBaseUrl(args, config ?? void 0);
623
+ const client = buildClient(baseUrl, config);
624
+ const agent = optionString(args.options.agent);
625
+ if (!agent) {
626
+ throw new Error(
627
+ "usage: driftgate execute --agent <agent> --input '{...}' [--workspace <workspaceId>] [--metadata '{...}'] [--policy '{...}'] [--route '{...}'] [--risk '{...}'] [--workflow-version-id <id>]"
628
+ );
629
+ }
630
+ const metadata = typeof args.options.metadata === "string" ? await parseJsonObjectOption(args.options.metadata, "--metadata", {}) : void 0;
631
+ const workspaceId = optionString(args.options.workspace) ?? process.env.DRIFTGATE_WORKSPACE_ID;
632
+ const input = {
633
+ ...workspaceId ? { workspaceId } : {},
634
+ agent,
635
+ ...optionString(args.options.subject) ? { subject: optionString(args.options.subject) } : {},
636
+ ...metadata ? { metadata } : {},
637
+ ...await parseV4ExecutionDefaults(args),
638
+ input: await parseRequiredInputOption(args)
639
+ };
640
+ const response = await client.execute(input);
641
+ printJson(canonicalOutput(response));
642
+ }
643
+ async function handleExecution(rest) {
644
+ const [subcommand, ...tail] = rest;
645
+ switch (subcommand) {
646
+ case "status":
647
+ await handleExecutionStatus(tail);
648
+ return;
649
+ case "events":
650
+ await handleExecutionEvents(tail);
651
+ return;
652
+ case "wait":
653
+ await handleExecutionWait(tail);
654
+ return;
655
+ default:
656
+ throw new Error(
657
+ "usage: driftgate execution status <executionId> | driftgate execution events <executionId> | driftgate execution wait <executionId> [--interval-ms <ms>] [--timeout-ms <ms>]"
658
+ );
659
+ }
660
+ }
661
+ async function handleExecutionStatus(rest) {
662
+ const args = parseArgs(rest);
663
+ const [executionId] = args.positionals;
664
+ if (!executionId) {
665
+ throw new Error("usage: driftgate execution status <executionId>");
666
+ }
667
+ const config = await loadConfig();
668
+ const baseUrl = resolveBaseUrl(args, config ?? void 0);
669
+ const client = buildClient(baseUrl, config);
670
+ const startedAtNs = nowNs();
671
+ const response = await client.status(executionId);
672
+ const events = await client.events(executionId);
673
+ const requestId = typeof response.run.correlationId === "string" && response.run.correlationId.length > 0 ? response.run.correlationId : `cli_${randomUUID()}`;
674
+ printJson({
675
+ ok: true,
676
+ data: {
677
+ run: response.run,
678
+ approval: response.approval ?? null,
679
+ latestEvent: events.length > 0 ? events[events.length - 1] : null,
680
+ sourcePath: "/v1/headless/runs/:runId"
681
+ },
682
+ meta: {
683
+ requestId,
684
+ executionId,
685
+ timingMs: { total: elapsedMs(startedAtNs) }
686
+ },
687
+ error: null
688
+ });
689
+ }
690
+ async function handleExecutionEvents(rest) {
691
+ const args = parseArgs(rest);
692
+ const [executionId] = args.positionals;
693
+ if (!executionId) {
694
+ throw new Error("usage: driftgate execution events <executionId>");
695
+ }
696
+ const config = await loadConfig();
697
+ const baseUrl = resolveBaseUrl(args, config ?? void 0);
698
+ const client = buildClient(baseUrl, config);
699
+ const startedAtNs = nowNs();
700
+ const events = await client.events(executionId);
701
+ printJson({
702
+ ok: true,
703
+ data: {
704
+ events,
705
+ sourcePath: "/v1/headless/runs/:runId/events"
706
+ },
707
+ meta: {
708
+ requestId: `cli_${randomUUID()}`,
709
+ executionId,
710
+ timingMs: { total: elapsedMs(startedAtNs) }
711
+ },
712
+ error: null
713
+ });
714
+ }
715
+ async function handleExecutionWait(rest) {
716
+ const args = parseArgs(rest);
717
+ const [executionId] = args.positionals;
718
+ if (!executionId) {
719
+ throw new Error("usage: driftgate execution wait <executionId> [--interval-ms <ms>] [--timeout-ms <ms>]");
720
+ }
721
+ const config = await loadConfig();
722
+ const baseUrl = resolveBaseUrl(args, config ?? void 0);
723
+ const client = buildClient(baseUrl, config);
724
+ const intervalMs = parseNumberOption(args.options["interval-ms"], "--interval-ms", {
725
+ integer: true,
726
+ minimum: 1
727
+ });
728
+ const timeoutMs = parseNumberOption(args.options["timeout-ms"], "--timeout-ms", {
729
+ integer: true,
730
+ minimum: 1
731
+ });
732
+ const startedAtNs = nowNs();
733
+ const response = await client.waitForTerminal(executionId, {
734
+ ...intervalMs !== void 0 ? { intervalMs } : {},
735
+ ...timeoutMs !== void 0 ? { timeoutMs } : {}
736
+ });
737
+ const requestId = typeof response.run.correlationId === "string" && response.run.correlationId.length > 0 ? response.run.correlationId : `cli_${randomUUID()}`;
738
+ printJson({
739
+ ok: true,
740
+ data: {
741
+ run: response.run,
742
+ approval: response.approval ?? null,
743
+ sourcePath: "/v1/headless/runs/:runId"
744
+ },
745
+ meta: {
746
+ requestId,
747
+ executionId,
748
+ timingMs: { total: elapsedMs(startedAtNs) }
749
+ },
750
+ error: null
751
+ });
752
+ }
753
+ async function handleApprovals(rest) {
754
+ const args = parseArgs(rest);
755
+ const [subcommand, subject] = args.positionals;
756
+ const config = await loadConfig();
757
+ const baseUrl = resolveBaseUrl(args, config ?? void 0);
758
+ const client = buildClient(baseUrl, config);
759
+ if (subcommand === "list") {
760
+ const workspaceId = typeof args.options.workspace === "string" && args.options.workspace || process.env.DRIFTGATE_WORKSPACE_ID;
761
+ if (!workspaceId) {
762
+ throw new Error("usage: driftgate approvals list --workspace <workspaceId>");
763
+ }
764
+ const status = typeof args.options.status === "string" ? args.options.status : void 0;
765
+ const approvals = await client.approvals.list(workspaceId, status);
766
+ console.log(JSON.stringify(approvals, null, 2));
767
+ return;
768
+ }
769
+ if (subcommand === "approve") {
770
+ if (!subject) {
771
+ throw new Error("usage: driftgate approvals approve <approvalId>");
772
+ }
773
+ const result = await client.approvals.approve(subject);
774
+ console.log(JSON.stringify(result, null, 2));
775
+ return;
776
+ }
777
+ if (subcommand === "deny") {
778
+ if (!subject) {
779
+ throw new Error("usage: driftgate approvals deny <approvalId>");
780
+ }
781
+ const result = await client.approvals.deny(subject);
782
+ console.log(JSON.stringify(result, null, 2));
783
+ return;
784
+ }
785
+ throw new Error("usage: driftgate approvals list|approve|deny ...");
786
+ }
787
+ async function handleConnectors(rest) {
788
+ const args = parseArgs(rest);
789
+ const [subcommand, connectorId] = args.positionals;
790
+ const config = await loadConfig();
791
+ const baseUrl = resolveBaseUrl(args, config ?? void 0);
792
+ const client = buildClient(baseUrl, config);
793
+ if (subcommand === "list") {
794
+ const workspaceId = requireWorkspaceId(args, "usage: driftgate connectors list --workspace <workspaceId>");
795
+ const connectors = await client.connectors.list(workspaceId);
796
+ console.log(JSON.stringify(connectors, null, 2));
797
+ return;
798
+ }
799
+ if (subcommand === "create") {
800
+ const workspaceId = requireWorkspaceId(
801
+ args,
802
+ "usage: driftgate connectors create --workspace <workspaceId> --name <name> --type <connectorType> [--status active|disabled] [--config '{...}']"
803
+ );
804
+ const name = typeof args.options.name === "string" ? args.options.name : "";
805
+ const connectorType = typeof args.options.type === "string" ? args.options.type : "";
806
+ if (!name || !connectorType) {
807
+ throw new Error("usage: driftgate connectors create --workspace <workspaceId> --name <name> --type <connectorType>");
808
+ }
809
+ const configJson = await parseJsonObjectOption(args.options.config, "--config", {});
810
+ const status = typeof args.options.status === "string" ? args.options.status : void 0;
811
+ const connector = await client.connectors.create(workspaceId, {
812
+ name,
813
+ connectorType,
814
+ status,
815
+ config: configJson
816
+ });
817
+ console.log(JSON.stringify(connector, null, 2));
818
+ return;
819
+ }
820
+ if (subcommand === "update") {
821
+ if (!connectorId) {
822
+ throw new Error("usage: driftgate connectors update <connectorId> --workspace <workspaceId> [--name <name>] [--type <connectorType>] [--status active|disabled] [--config '{...}']");
823
+ }
824
+ const workspaceId = requireWorkspaceId(args, "usage: driftgate connectors update <connectorId> --workspace <workspaceId> ...");
825
+ const configPatch = typeof args.options.config === "string" ? await parseJsonObjectOption(args.options.config, "--config", {}) : void 0;
826
+ const input = {
827
+ ...typeof args.options.name === "string" ? { name: args.options.name } : {},
828
+ ...typeof args.options.type === "string" ? { connectorType: args.options.type } : {},
829
+ ...typeof args.options.status === "string" ? { status: args.options.status } : {},
830
+ ...configPatch ? { config: configPatch } : {}
831
+ };
832
+ if (Object.keys(input).length === 0) {
833
+ throw new Error("usage: driftgate connectors update <connectorId> --workspace <workspaceId> requires at least one field");
834
+ }
835
+ const connector = await client.connectors.update(workspaceId, connectorId, input);
836
+ console.log(JSON.stringify(connector, null, 2));
837
+ return;
838
+ }
839
+ if (subcommand === "delete") {
840
+ if (!connectorId) {
841
+ throw new Error("usage: driftgate connectors delete <connectorId> --workspace <workspaceId>");
842
+ }
843
+ const workspaceId = requireWorkspaceId(args, "usage: driftgate connectors delete <connectorId> --workspace <workspaceId>");
844
+ const connector = await client.connectors.delete(workspaceId, connectorId);
845
+ console.log(JSON.stringify(connector, null, 2));
846
+ return;
847
+ }
848
+ throw new Error("usage: driftgate connectors list|create|update|delete ...");
849
+ }
850
+ async function handleSecrets(rest) {
851
+ const args = parseArgs(rest);
852
+ const [subcommand, secretId] = args.positionals;
853
+ const config = await loadConfig();
854
+ const baseUrl = resolveBaseUrl(args, config ?? void 0);
855
+ const client = buildClient(baseUrl, config);
856
+ if (subcommand === "list") {
857
+ const workspaceId = requireWorkspaceId(args, "usage: driftgate secrets list --workspace <workspaceId>");
858
+ const secrets = await client.secrets.list(workspaceId);
859
+ console.log(JSON.stringify(secrets, null, 2));
860
+ return;
861
+ }
862
+ if (subcommand === "create") {
863
+ const workspaceId = requireWorkspaceId(
864
+ args,
865
+ "usage: driftgate secrets create --workspace <workspaceId> --name <name> --value <value> [--connector-id <id|null>] [--metadata '{...}']"
866
+ );
867
+ const name = typeof args.options.name === "string" ? args.options.name : "";
868
+ const value = typeof args.options.value === "string" ? args.options.value : "";
869
+ if (!name || !value) {
870
+ throw new Error("usage: driftgate secrets create --workspace <workspaceId> --name <name> --value <value>");
871
+ }
872
+ const metadata = await parseJsonObjectOption(args.options.metadata, "--metadata", {});
873
+ const secret = await client.secrets.create(workspaceId, {
874
+ name,
875
+ value,
876
+ connectorId: parseNullableConnectorId(args.options["connector-id"]),
877
+ keyVersion: typeof args.options["key-version"] === "string" ? args.options["key-version"] : void 0,
878
+ metadata
879
+ });
880
+ console.log(JSON.stringify(secret, null, 2));
881
+ return;
882
+ }
883
+ if (subcommand === "update") {
884
+ if (!secretId) {
885
+ throw new Error("usage: driftgate secrets update <secretId> --workspace <workspaceId> [--name <name>] [--value <value>] [--connector-id <id|null>] [--metadata '{...}']");
886
+ }
887
+ const workspaceId = requireWorkspaceId(args, "usage: driftgate secrets update <secretId> --workspace <workspaceId> ...");
888
+ const metadataPatch = typeof args.options.metadata === "string" ? await parseJsonObjectOption(args.options.metadata, "--metadata", {}) : void 0;
889
+ const input = {
890
+ ...typeof args.options.name === "string" ? { name: args.options.name } : {},
891
+ ...typeof args.options.value === "string" ? { value: args.options.value } : {},
892
+ ...args.options["connector-id"] !== void 0 ? { connectorId: parseNullableConnectorId(args.options["connector-id"]) } : {},
893
+ ...typeof args.options["key-version"] === "string" ? { keyVersion: args.options["key-version"] } : {},
894
+ ...metadataPatch ? { metadata: metadataPatch } : {}
895
+ };
896
+ if (Object.keys(input).length === 0) {
897
+ throw new Error("usage: driftgate secrets update <secretId> --workspace <workspaceId> requires at least one field");
898
+ }
899
+ const secret = await client.secrets.update(workspaceId, secretId, input);
900
+ console.log(JSON.stringify(secret, null, 2));
901
+ return;
902
+ }
903
+ if (subcommand === "delete") {
904
+ if (!secretId) {
905
+ throw new Error("usage: driftgate secrets delete <secretId> --workspace <workspaceId>");
906
+ }
907
+ const workspaceId = requireWorkspaceId(args, "usage: driftgate secrets delete <secretId> --workspace <workspaceId>");
908
+ const secret = await client.secrets.delete(workspaceId, secretId);
909
+ console.log(JSON.stringify(secret, null, 2));
910
+ return;
911
+ }
912
+ throw new Error("usage: driftgate secrets list|create|update|delete ...");
913
+ }
914
+ async function handleWebhooks(rest) {
915
+ const args = parseArgs(rest);
916
+ const [subcommand, webhookId] = args.positionals;
917
+ const config = await loadConfig();
918
+ const baseUrl = resolveBaseUrl(args, config ?? void 0);
919
+ const client = buildClient(baseUrl, config);
920
+ if (subcommand === "list") {
921
+ const workspaceId = requireWorkspaceId(args, "usage: driftgate webhooks list --workspace <workspaceId>");
922
+ const webhooks = await client.webhooks.list(workspaceId);
923
+ console.log(JSON.stringify(webhooks, null, 2));
924
+ return;
925
+ }
926
+ if (subcommand === "create") {
927
+ const workspaceId = requireWorkspaceId(
928
+ args,
929
+ "usage: driftgate webhooks create --workspace <workspaceId> --name <name> --path </hook> --target-workflow <workflowId> --signing-secret <secret>"
930
+ );
931
+ const name = typeof args.options.name === "string" ? args.options.name : "";
932
+ const hookPath = typeof args.options.path === "string" ? args.options.path : "";
933
+ const targetWorkflowId = typeof args.options["target-workflow"] === "string" ? args.options["target-workflow"] : "";
934
+ const signingSecret = typeof args.options["signing-secret"] === "string" ? args.options["signing-secret"] : "";
935
+ if (!name || !hookPath || !targetWorkflowId || !signingSecret) {
936
+ throw new Error(
937
+ "usage: driftgate webhooks create --workspace <workspaceId> --name <name> --path </hook> --target-workflow <workflowId> --signing-secret <secret>"
938
+ );
939
+ }
940
+ const eventFilter = await parseJsonObjectOption(args.options["event-filter"], "--event-filter", {});
941
+ const executionJson = await parseJsonObjectOption(args.options.execution, "--execution", {});
942
+ const requiresApproval = parseBooleanValue(args.options["requires-approval"], "--requires-approval");
943
+ const execution = {
944
+ ...executionJson,
945
+ ...requiresApproval !== void 0 ? { requiresApproval } : {},
946
+ ...typeof args.options["required-role"] === "string" ? { requiredRole: args.options["required-role"] } : {},
947
+ ...typeof args.options["sla-policy-id"] === "string" ? { slaPolicyId: args.options["sla-policy-id"] } : {}
948
+ };
949
+ const webhook = await client.webhooks.create(workspaceId, {
950
+ connectorId: parseNullableConnectorId(args.options["connector-id"]),
951
+ name,
952
+ path: hookPath,
953
+ targetWorkflowId,
954
+ status: typeof args.options.status === "string" ? args.options.status : void 0,
955
+ eventFilter,
956
+ execution,
957
+ signingSecret
958
+ });
959
+ console.log(JSON.stringify(webhook, null, 2));
960
+ return;
961
+ }
962
+ if (subcommand === "update") {
963
+ if (!webhookId) {
964
+ throw new Error("usage: driftgate webhooks update <webhookId> --workspace <workspaceId> [--name ...]");
965
+ }
966
+ const workspaceId = requireWorkspaceId(args, "usage: driftgate webhooks update <webhookId> --workspace <workspaceId> ...");
967
+ const eventFilterPatch = typeof args.options["event-filter"] === "string" ? await parseJsonObjectOption(args.options["event-filter"], "--event-filter", {}) : void 0;
968
+ const executionPatch = typeof args.options.execution === "string" ? await parseJsonObjectOption(args.options.execution, "--execution", {}) : void 0;
969
+ const requiresApprovalPatch = parseBooleanValue(
970
+ args.options["requires-approval"],
971
+ "--requires-approval"
972
+ );
973
+ const execution = {
974
+ ...executionPatch ?? {},
975
+ ...requiresApprovalPatch !== void 0 ? { requiresApproval: requiresApprovalPatch } : {},
976
+ ...typeof args.options["required-role"] === "string" ? { requiredRole: args.options["required-role"] } : {},
977
+ ...typeof args.options["sla-policy-id"] === "string" ? { slaPolicyId: args.options["sla-policy-id"] } : {}
978
+ };
979
+ const input = {
980
+ ...args.options["connector-id"] !== void 0 ? { connectorId: parseNullableConnectorId(args.options["connector-id"]) } : {},
981
+ ...typeof args.options.name === "string" ? { name: args.options.name } : {},
982
+ ...typeof args.options.path === "string" ? { path: args.options.path } : {},
983
+ ...typeof args.options["target-workflow"] === "string" ? { targetWorkflowId: args.options["target-workflow"] } : {},
984
+ ...typeof args.options.status === "string" ? { status: args.options.status } : {},
985
+ ...eventFilterPatch ? { eventFilter: eventFilterPatch } : {},
986
+ ...Object.keys(execution).length > 0 ? { execution } : {},
987
+ ...typeof args.options["signing-secret"] === "string" ? { signingSecret: args.options["signing-secret"] } : {}
988
+ };
989
+ if (Object.keys(input).length === 0) {
990
+ throw new Error("usage: driftgate webhooks update <webhookId> --workspace <workspaceId> requires at least one field");
991
+ }
992
+ const webhook = await client.webhooks.update(workspaceId, webhookId, input);
993
+ console.log(JSON.stringify(webhook, null, 2));
994
+ return;
995
+ }
996
+ if (subcommand === "delete") {
997
+ if (!webhookId) {
998
+ throw new Error("usage: driftgate webhooks delete <webhookId> --workspace <workspaceId>");
999
+ }
1000
+ const workspaceId = requireWorkspaceId(args, "usage: driftgate webhooks delete <webhookId> --workspace <workspaceId>");
1001
+ const webhook = await client.webhooks.delete(workspaceId, webhookId);
1002
+ console.log(JSON.stringify(webhook, null, 2));
1003
+ return;
1004
+ }
1005
+ throw new Error("usage: driftgate webhooks list|create|update|delete ...");
1006
+ }
1007
+ function printHelp() {
1008
+ console.log(`driftgate CLI
1009
+
1010
+ Commands:
1011
+ driftgate login [--api-base <url>]
1012
+ driftgate init [--out workflow.yaml] [--force]
1013
+ driftgate deploy <workflow.yaml> --workspace <workspaceId> [--project <name>] [--workflow <name>]
1014
+ driftgate publish <workflowId> [--yaml workflow.yaml]
1015
+ driftgate session start --agent <agent> [--workspace <workspaceId>] [--subject <subject>] [--metadata '{"key":"value"}' | --metadata @metadata.json] [--policy '{"ref":"default","version":"latest"}'] [--route '{"provider":"openai","model":"gpt-4.1-mini","region":"us-east-1"}'] [--risk '{"score":12.5,"decision":"allow"}'] [--workflow-version-id <id>] [--expires-at <ISO-8601>]
1016
+ driftgate session execute <sessionId> --input '{"key":"value"}' | --input @input.json [--policy '{"ref":"default","version":"latest"}'] [--route '{"provider":"openai"}'] [--risk '{"score":12.5,"decision":"allow"}'] [--workflow-version-id <id>]
1017
+ driftgate execute --agent <agent> --input '{"key":"value"}' | --input @input.json [--workspace <workspaceId>] [--subject <subject>] [--metadata '{"key":"value"}' | --metadata @metadata.json] [--policy '{"ref":"default","version":"latest"}'] [--route '{"provider":"openai"}'] [--risk '{"score":12.5,"decision":"allow"}'] [--workflow-version-id <id>]
1018
+ driftgate execution status <executionId>
1019
+ driftgate execution events <executionId>
1020
+ driftgate execution wait <executionId> [--interval-ms <ms>] [--timeout-ms <ms>]
1021
+ driftgate approvals list --workspace <workspaceId> [--status pending|approved|denied]
1022
+ driftgate approvals approve <approvalId>
1023
+ driftgate approvals deny <approvalId>
1024
+ driftgate connectors list --workspace <workspaceId>
1025
+ driftgate connectors create --workspace <workspaceId> --name <name> --type <connectorType> [--status active|disabled] [--config '{...}']
1026
+ driftgate connectors update <connectorId> --workspace <workspaceId> [--name <name>] [--type <connectorType>] [--status active|disabled] [--config '{...}']
1027
+ driftgate connectors delete <connectorId> --workspace <workspaceId>
1028
+ driftgate secrets list --workspace <workspaceId>
1029
+ driftgate secrets create --workspace <workspaceId> --name <name> --value <value> [--connector-id <id|null>] [--key-version <v>] [--metadata '{...}']
1030
+ driftgate secrets update <secretId> --workspace <workspaceId> [--name <name>] [--value <value>] [--connector-id <id|null>] [--key-version <v>] [--metadata '{...}']
1031
+ driftgate secrets delete <secretId> --workspace <workspaceId>
1032
+ driftgate webhooks list --workspace <workspaceId>
1033
+ driftgate webhooks create --workspace <workspaceId> --name <name> --path </hook> --target-workflow <workflowId> --signing-secret <secret> [--connector-id <id|null>] [--status active|disabled] [--event-filter '{...}'] [--execution '{...}'] [--requires-approval true|false] [--required-role <role>] [--sla-policy-id <id>]
1034
+ driftgate webhooks update <webhookId> --workspace <workspaceId> [--name <name>] [--path </hook>] [--target-workflow <workflowId>] [--status active|disabled] [--event-filter '{...}'] [--execution '{...}'] [--requires-approval true|false] [--required-role <role>] [--sla-policy-id <id>] [--signing-secret <secret>] [--connector-id <id|null>]
1035
+ driftgate webhooks delete <webhookId> --workspace <workspaceId>
1036
+
1037
+ Removed in V4 CLI:
1038
+ driftgate run ...
1039
+ driftgate status ...
1040
+ `);
1041
+ }
1042
+ void main().catch((error) => {
1043
+ console.error(JSON.stringify(renderErrorEnvelope(error), null, 2));
1044
+ process.exitCode = 1;
1045
+ });
1046
+ //# sourceMappingURL=index.js.map