@getmagical/cli 0.1.3

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,2220 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli-formatting.ts
4
+ import { inspect, stripVTControlCharacters, styleText } from "util";
5
+ import { Help } from "commander";
6
+ var EMPTY_VALUE = "-";
7
+ var TABLE_GAP = " ";
8
+ function applyStyle(value, style, enabled = true) {
9
+ if (!enabled) {
10
+ return value;
11
+ }
12
+ return styleText(style, value);
13
+ }
14
+ function indent(text, level = 2) {
15
+ const padding = " ".repeat(level);
16
+ return text.split("\n").map((line) => `${padding}${line}`).join("\n");
17
+ }
18
+ function stripAnsi(value) {
19
+ return stripVTControlCharacters(value);
20
+ }
21
+ function displayWidth(value) {
22
+ return stripAnsi(value).length;
23
+ }
24
+ function padCell(value, width) {
25
+ return value.padEnd(width + value.length - displayWidth(value));
26
+ }
27
+ function formatHeading(title) {
28
+ return applyStyle(title, ["bold", "cyan"]);
29
+ }
30
+ function formatLabel(label) {
31
+ return applyStyle(label, "dim");
32
+ }
33
+ function formatCommandSnippet(command) {
34
+ return applyStyle(command, ["bold", "cyan"]);
35
+ }
36
+ function formatFlag(flag) {
37
+ return applyStyle(flag, ["bold", "yellow"]);
38
+ }
39
+ function formatErrorLabel(label) {
40
+ return applyStyle(label, ["bold", "red"], process.stderr.isTTY);
41
+ }
42
+ function splitWords(value) {
43
+ const tokens = [];
44
+ for (const word of value.replaceAll("_", " ").replaceAll("-", " ").split(" ")) {
45
+ if (word.length === 0) {
46
+ continue;
47
+ }
48
+ let currentToken = word[0] ?? "";
49
+ for (const character of word.slice(1)) {
50
+ const previousCharacter = currentToken.at(-1) ?? "";
51
+ const shouldBreak = character >= "A" && character <= "Z" && (previousCharacter >= "a" && previousCharacter <= "z" || previousCharacter >= "0" && previousCharacter <= "9");
52
+ if (shouldBreak) {
53
+ tokens.push(currentToken);
54
+ currentToken = character;
55
+ continue;
56
+ }
57
+ currentToken += character;
58
+ }
59
+ tokens.push(currentToken);
60
+ }
61
+ return tokens;
62
+ }
63
+ function startCase(value) {
64
+ return splitWords(value).map((segment) => {
65
+ const lowerSegment = segment.toLowerCase();
66
+ if (lowerSegment === "id") {
67
+ return "ID";
68
+ }
69
+ if (lowerSegment === "url") {
70
+ return "URL";
71
+ }
72
+ if (lowerSegment === "api") {
73
+ return "API";
74
+ }
75
+ return segment.charAt(0).toUpperCase() + segment.slice(1);
76
+ }).join(" ");
77
+ }
78
+ function formatScalar(value) {
79
+ if (value === null || value === void 0) {
80
+ return EMPTY_VALUE;
81
+ }
82
+ if (typeof value === "string") {
83
+ return value.length === 0 ? EMPTY_VALUE : value;
84
+ }
85
+ if (typeof value === "number" || typeof value === "boolean") {
86
+ return String(value);
87
+ }
88
+ return inspect(value, {
89
+ breakLength: Infinity,
90
+ colors: false,
91
+ compact: true,
92
+ depth: 2
93
+ });
94
+ }
95
+ function isRecord(value) {
96
+ return typeof value === "object" && value !== null && !Array.isArray(value);
97
+ }
98
+ function isPrimitiveLike(value) {
99
+ return value === null || value === void 0 || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
100
+ }
101
+ function formatTable(options) {
102
+ const widths = options.columns.map(
103
+ (column, columnIndex) => Math.max(
104
+ displayWidth(column),
105
+ ...options.rows.map((row) => displayWidth(row[columnIndex] ?? ""))
106
+ )
107
+ );
108
+ const renderRow = (cells) => cells.map((cell, columnIndex) => padCell(cell, widths[columnIndex] ?? 0)).join(TABLE_GAP).trimEnd();
109
+ const divider = widths.map((width) => "-".repeat(width)).join(TABLE_GAP);
110
+ return [
111
+ renderRow(options.columns.map((column) => applyStyle(column, "bold"))),
112
+ divider,
113
+ ...options.rows.map(renderRow)
114
+ ].join("\n");
115
+ }
116
+ function canRenderRecordArrayAsTable(items) {
117
+ if (items.length === 0 || !items.every(isRecord)) {
118
+ return false;
119
+ }
120
+ const keys = [...new Set(items.flatMap((item) => Object.keys(item)))];
121
+ if (keys.length === 0 || keys.length > 8) {
122
+ return false;
123
+ }
124
+ return items.every((item) => keys.every((key) => isPrimitiveLike(item[key])));
125
+ }
126
+ function formatRecord(record) {
127
+ const entries = Object.entries(record);
128
+ if (entries.length === 0) {
129
+ return EMPTY_VALUE;
130
+ }
131
+ const labelWidth = Math.max(
132
+ ...entries.map(([key]) => displayWidth(startCase(key)))
133
+ );
134
+ return entries.map(([key, value]) => {
135
+ const label = `${startCase(key)}:`;
136
+ if (isPrimitiveLike(value)) {
137
+ return `${padCell(formatLabel(label), labelWidth + 1)} ${formatScalar(value)}`;
138
+ }
139
+ return `${formatLabel(label)}
140
+ ${indent(formatHumanReadableOutput(value))}`;
141
+ }).join("\n");
142
+ }
143
+ function formatArray(items) {
144
+ if (items.length === 0) {
145
+ return "No results.";
146
+ }
147
+ if (items.every(isPrimitiveLike)) {
148
+ return items.map((item) => `- ${formatScalar(item)}`).join("\n");
149
+ }
150
+ if (canRenderRecordArrayAsTable(items)) {
151
+ const keys = [...new Set(items.flatMap((item) => Object.keys(item)))];
152
+ return formatTable({
153
+ columns: keys.map((key) => startCase(key)),
154
+ rows: items.map((item) => keys.map((key) => formatScalar(item[key])))
155
+ });
156
+ }
157
+ return items.map((item, index) => {
158
+ const title = formatHeading(`Item ${index + 1}`);
159
+ return `${title}
160
+ ${indent(formatHumanReadableOutput(item))}`;
161
+ }).join("\n\n");
162
+ }
163
+ function renderKeyValueBlock(entries) {
164
+ const labelWidth = Math.max(...entries.map(([label]) => displayWidth(label)));
165
+ return entries.map(
166
+ ([label, value]) => `${padCell(formatLabel(`${label}:`), labelWidth + 1)} ${value}`
167
+ ).join("\n");
168
+ }
169
+ function renderSection(title, bodyLines) {
170
+ return [formatHeading(title), "", ...bodyLines].join("\n");
171
+ }
172
+ var MagicalHelp = class extends Help {
173
+ outputHasColors = process.stdout.isTTY;
174
+ constructor() {
175
+ super();
176
+ this.helpWidth = 96;
177
+ this.showGlobalOptions = true;
178
+ this.sortSubcommands = true;
179
+ }
180
+ prepareContext(contextOptions) {
181
+ super.prepareContext(contextOptions);
182
+ this.outputHasColors = contextOptions.outputHasColors ?? process.stdout.isTTY;
183
+ }
184
+ styleTitle(str) {
185
+ return applyStyle(str, ["bold", "cyan"], this.outputHasColors);
186
+ }
187
+ styleCommandText(str) {
188
+ return applyStyle(str, ["bold", "cyan"], this.outputHasColors);
189
+ }
190
+ styleSubcommandText(str) {
191
+ return applyStyle(str, ["bold", "cyan"], this.outputHasColors);
192
+ }
193
+ styleOptionText(str) {
194
+ return applyStyle(str, ["bold", "yellow"], this.outputHasColors);
195
+ }
196
+ styleArgumentText(str) {
197
+ return applyStyle(str, ["bold", "green"], this.outputHasColors);
198
+ }
199
+ styleDescriptionText(str) {
200
+ return applyStyle(str, "dim", this.outputHasColors);
201
+ }
202
+ };
203
+ function formatAuthLoginPrompt(options) {
204
+ return renderSection("Sign In With Your Browser", [
205
+ renderKeyValueBlock([
206
+ ["Open", options.verificationUrl],
207
+ ["Code", options.userCode]
208
+ ])
209
+ ]);
210
+ }
211
+ function formatAuthLoginSuccess(options) {
212
+ return renderSection("Signed In", [
213
+ renderKeyValueBlock([
214
+ ["Account", options.account.email ?? options.account.userId],
215
+ ["Organizations", String(options.organizationCount)],
216
+ [
217
+ "Default org",
218
+ options.defaultOrganization ? `${options.defaultOrganization.name} (${options.defaultOrganization.id})` : "None selected"
219
+ ]
220
+ ])
221
+ ]);
222
+ }
223
+ function formatBrowserOpenFallback() {
224
+ return "Browser auto-open failed. Paste the URL above into any browser.";
225
+ }
226
+ function formatLogoutSuccess() {
227
+ return renderSection("Signed Out", ["Saved CLI auth state was cleared."]);
228
+ }
229
+ function formatOrganizationList(options) {
230
+ if (options.organizations.length === 0) {
231
+ return renderSection("Organizations", ["No organizations are available."]);
232
+ }
233
+ const rows = options.organizations.map((organization) => {
234
+ const status = [
235
+ options.defaultOrgId === organization.id ? "default" : null,
236
+ options.lastUsedOrgId === organization.id ? "last used" : null
237
+ ].filter((value) => value !== null).join(", ");
238
+ return [
239
+ status || EMPTY_VALUE,
240
+ organization.name,
241
+ organization.id,
242
+ organization.primaryDomain ?? EMPTY_VALUE
243
+ ];
244
+ });
245
+ return renderSection("Organizations", [
246
+ formatTable({
247
+ columns: ["Status", "Name", "ID", "Primary domain"],
248
+ rows
249
+ })
250
+ ]);
251
+ }
252
+ function formatDefaultOrganizationSelection(organization) {
253
+ return renderSection("Default Organization Updated", [
254
+ `${organization.name} (${organization.id})`
255
+ ]);
256
+ }
257
+ function formatHumanReadableOutput(value) {
258
+ if (isPrimitiveLike(value)) {
259
+ return formatScalar(value);
260
+ }
261
+ if (Array.isArray(value)) {
262
+ return formatArray(value);
263
+ }
264
+ if (isRecord(value)) {
265
+ return formatRecord(value);
266
+ }
267
+ return inspect(value, {
268
+ colors: false,
269
+ depth: null
270
+ });
271
+ }
272
+ function formatCliError(message) {
273
+ return `${formatErrorLabel("Error:")} ${message}`;
274
+ }
275
+ function formatExamples(title, examples) {
276
+ return [
277
+ formatHeading(title),
278
+ "",
279
+ ...examples.map((example) => ` ${formatCommandSnippet(example)}`)
280
+ ].join("\n");
281
+ }
282
+ function formatOutputModesNote() {
283
+ return renderSection("Output", [
284
+ `Human-readable by default.`,
285
+ `Add ${formatFlag("--json")} before or after a command for stable machine-readable output.`
286
+ ]);
287
+ }
288
+
289
+ // src/errors.ts
290
+ var CliError = class extends Error {
291
+ constructor(message, options) {
292
+ super(message, options);
293
+ this.name = "CliError";
294
+ }
295
+ };
296
+ function getErrorMessage(error) {
297
+ if (error instanceof Error) {
298
+ return error.message;
299
+ }
300
+ return "Unknown CLI error";
301
+ }
302
+
303
+ // src/program.ts
304
+ import { spawn } from "child_process";
305
+
306
+ // ../mcp-registry/src/operation-specs.ts
307
+ import { z } from "zod";
308
+
309
+ // ../mcp-registry/src/operation-spec.ts
310
+ function stripLeadingGraphqlComments(document) {
311
+ return document.replace(/^(?:\s*#.*\n)+/u, "").trimStart();
312
+ }
313
+ function assertDocumentMatchesOperation({
314
+ document,
315
+ kind,
316
+ operationName
317
+ }) {
318
+ const normalizedDocument = stripLeadingGraphqlComments(document);
319
+ const match = /^(?<actualKind>query|mutation)\s+(?<actualOperationName>[_A-Za-z][_0-9A-Za-z]*)/u.exec(
320
+ normalizedDocument
321
+ );
322
+ const actualKind = match?.groups?.["actualKind"];
323
+ const actualOperationName = match?.groups?.["actualOperationName"];
324
+ if (!actualKind || !actualOperationName) {
325
+ throw new Error(
326
+ `Operation "${operationName}" is missing a top-level GraphQL ${kind} definition.`
327
+ );
328
+ }
329
+ if (actualKind !== kind || actualOperationName !== operationName) {
330
+ throw new Error(
331
+ `Operation "${operationName}" does not match its GraphQL document (${actualKind} ${actualOperationName}).`
332
+ );
333
+ }
334
+ }
335
+ function createOperationSpec(spec) {
336
+ assertDocumentMatchesOperation(spec);
337
+ return spec;
338
+ }
339
+
340
+ // ../mcp-registry/src/operation-specs.ts
341
+ function stringOption({
342
+ description,
343
+ flagName,
344
+ inputKey,
345
+ required = false
346
+ }) {
347
+ return {
348
+ description,
349
+ flagName,
350
+ inputKey,
351
+ required,
352
+ valueType: "string"
353
+ };
354
+ }
355
+ function numberOption({
356
+ description,
357
+ flagName,
358
+ inputKey,
359
+ required = false
360
+ }) {
361
+ return {
362
+ description,
363
+ flagName,
364
+ inputKey,
365
+ required,
366
+ valueType: "number"
367
+ };
368
+ }
369
+ function booleanOption({
370
+ description,
371
+ flagName,
372
+ inputKey
373
+ }) {
374
+ return {
375
+ description,
376
+ flagName,
377
+ inputKey,
378
+ valueType: "boolean"
379
+ };
380
+ }
381
+ function stringArrayOption({
382
+ description,
383
+ flagName,
384
+ inputKey
385
+ }) {
386
+ return {
387
+ description,
388
+ flagName,
389
+ inputKey,
390
+ valueHint: "comma-separated",
391
+ valueType: "string-array"
392
+ };
393
+ }
394
+ function jsonOption({
395
+ description,
396
+ flagName,
397
+ inputKey,
398
+ required = false
399
+ }) {
400
+ return {
401
+ description,
402
+ flagName,
403
+ inputKey,
404
+ required,
405
+ valueHint: "JSON",
406
+ valueType: "json"
407
+ };
408
+ }
409
+ var jsonObjectSchema = z.record(z.string(), z.unknown());
410
+ var operationSpecs = [
411
+ createOperationSpec({
412
+ operationName: "GetAgent",
413
+ kind: "query",
414
+ inputSchema: z.object({
415
+ id: z.string().min(1)
416
+ }),
417
+ cli: {
418
+ name: "get-agent",
419
+ path: ["agents", "get"],
420
+ description: "Fetch a single agent with config and schema fields.",
421
+ resultKey: "agent",
422
+ options: [
423
+ stringOption({
424
+ inputKey: "id",
425
+ flagName: "id",
426
+ description: "Agent ID.",
427
+ required: true
428
+ })
429
+ ]
430
+ },
431
+ mcp: { fileName: "agent.graphql" },
432
+ document: `# Query to fetch a single agent with all fields
433
+ # Config selection is limited to scalar/JSON fields to keep responses flat
434
+ query GetAgent($id: ID!) {
435
+ agent(id: $id) {
436
+ id
437
+ name
438
+ description
439
+ instructions
440
+ organizationId
441
+ createdAt
442
+ updatedAt
443
+ sourceAgentId
444
+ inputSchema
445
+ outputSchema
446
+ config {
447
+ type
448
+ language
449
+ model
450
+ tools
451
+ maxSessionMessages
452
+ commonParametersSchema
453
+ taskQueue
454
+ waitUponQueueing
455
+ queueableAgentIds
456
+ additionalSystemPrompt
457
+ ignoreUnstableSessionState
458
+ }
459
+ childAgentIds
460
+ }
461
+ }`
462
+ }),
463
+ createOperationSpec({
464
+ operationName: "GetAgentRun",
465
+ kind: "query",
466
+ inputSchema: z.object({
467
+ id: z.string().min(1)
468
+ }),
469
+ cli: {
470
+ name: "get-agent-run",
471
+ path: ["agent-runs", "get"],
472
+ description: "Fetch a single agent run.",
473
+ resultKey: "agentRun",
474
+ options: [
475
+ stringOption({
476
+ inputKey: "id",
477
+ flagName: "id",
478
+ description: "Agent run ID.",
479
+ required: true
480
+ })
481
+ ]
482
+ },
483
+ mcp: { fileName: "agentRun.graphql" },
484
+ document: `# Query a single agent run
485
+ query GetAgentRun($id: ID!) {
486
+ agentRun(id: $id) {
487
+ id
488
+ agentVersionId
489
+ agentName
490
+ status
491
+ statusDetail
492
+ isChildAgent
493
+ input
494
+ startedAt
495
+ finishedAt
496
+ parentAgentRunId
497
+ }
498
+ }`
499
+ }),
500
+ createOperationSpec({
501
+ operationName: "GetAutomationAgents",
502
+ kind: "query",
503
+ inputSchema: z.object({
504
+ id: z.string().min(1)
505
+ }),
506
+ cli: {
507
+ name: "get-automation-agents",
508
+ path: ["automations", "agents"],
509
+ description: "Fetch the agents attached to an automation.",
510
+ resultKey: "automation",
511
+ options: [
512
+ stringOption({
513
+ inputKey: "id",
514
+ flagName: "id",
515
+ description: "Automation ID.",
516
+ required: true
517
+ })
518
+ ]
519
+ },
520
+ mcp: { fileName: "automationAgents.graphql" },
521
+ document: `# Query to fetch automation agents with flat fields only
522
+ # Excludes nested agent config and schemas for MCP tool simplicity
523
+ query GetAutomationAgents($id: ID!) {
524
+ automation(id: $id) {
525
+ id
526
+ name
527
+ agents {
528
+ automationId
529
+ agentId
530
+ isTrigger
531
+ agent {
532
+ id
533
+ name
534
+ description
535
+ instructions
536
+ organizationId
537
+ createdAt
538
+ updatedAt
539
+ sourceAgentId
540
+ childAgentIds
541
+ }
542
+ }
543
+ }
544
+ }`
545
+ }),
546
+ createOperationSpec({
547
+ operationName: "GetAutomationRun",
548
+ kind: "query",
549
+ inputSchema: z.object({
550
+ id: z.string().min(1)
551
+ }),
552
+ cli: {
553
+ name: "get-automation-run",
554
+ path: ["automation-runs", "get"],
555
+ description: "Fetch a single automation run.",
556
+ resultKey: "automationRun",
557
+ options: [
558
+ stringOption({
559
+ inputKey: "id",
560
+ flagName: "id",
561
+ description: "Automation run ID.",
562
+ required: true
563
+ })
564
+ ]
565
+ },
566
+ mcp: { fileName: "automationRun.graphql" },
567
+ document: `# Query a full automation run details payload
568
+ query GetAutomationRun($id: ID!) {
569
+ automationRun(id: $id) {
570
+ id
571
+ automationId
572
+ status
573
+ statusDetail
574
+ summary
575
+ invocationType
576
+ input
577
+ additionalPrompt
578
+ debugUrl
579
+ createdAt
580
+ startedAt
581
+ pausedAt
582
+ finishedAt
583
+ }
584
+ }`
585
+ }),
586
+ createOperationSpec({
587
+ operationName: "GetAutomationRunAgentRuns",
588
+ kind: "query",
589
+ inputSchema: z.object({
590
+ automationRunId: z.string().min(1)
591
+ }),
592
+ cli: {
593
+ name: "get-automation-run-agent-runs",
594
+ path: ["automation-runs", "agent-runs"],
595
+ description: "Fetch ordered agent runs for an automation run.",
596
+ resultKey: "automationRunAgentRuns",
597
+ options: [
598
+ stringOption({
599
+ inputKey: "automationRunId",
600
+ flagName: "automation-run-id",
601
+ description: "Automation run ID.",
602
+ required: true
603
+ })
604
+ ]
605
+ },
606
+ mcp: { fileName: "automationRunAgentRuns.graphql" },
607
+ document: `# Query ordered agent runs for an automation run
608
+ query GetAutomationRunAgentRuns($automationRunId: ID!) {
609
+ automationRunAgentRuns(automationRunId: $automationRunId) {
610
+ id
611
+ agentVersionId
612
+ agentName
613
+ status
614
+ statusDetail
615
+ isChildAgent
616
+ input
617
+ startedAt
618
+ finishedAt
619
+ parentAgentRunId
620
+ }
621
+ }`
622
+ }),
623
+ createOperationSpec({
624
+ operationName: "GetAutomationRunIterations",
625
+ kind: "query",
626
+ inputSchema: z.object({
627
+ automationRunId: z.string().min(1),
628
+ agentRunId: z.string().min(1).optional(),
629
+ limit: z.number().int().positive().optional(),
630
+ reverse: z.boolean().optional()
631
+ }),
632
+ cli: {
633
+ name: "get-automation-run-iterations",
634
+ path: ["automation-runs", "iterations"],
635
+ description: "Fetch ordered iterations for an automation run.",
636
+ resultKey: "automationRunIterations",
637
+ options: [
638
+ stringOption({
639
+ inputKey: "automationRunId",
640
+ flagName: "automation-run-id",
641
+ description: "Automation run ID.",
642
+ required: true
643
+ }),
644
+ stringOption({
645
+ inputKey: "agentRunId",
646
+ flagName: "agent-run-id",
647
+ description: "Optional agent run ID to filter by."
648
+ }),
649
+ numberOption({
650
+ inputKey: "limit",
651
+ flagName: "limit",
652
+ description: "Maximum number of iterations to return."
653
+ }),
654
+ booleanOption({
655
+ inputKey: "reverse",
656
+ flagName: "reverse",
657
+ description: "Return newest iterations first."
658
+ })
659
+ ]
660
+ },
661
+ mcp: { fileName: "automationRunIterations.graphql" },
662
+ document: `# Query iterations for an automation run with optional filtering by agent run
663
+ query GetAutomationRunIterations(
664
+ $automationRunId: ID!
665
+ $agentRunId: ID
666
+ $limit: Int
667
+ $reverse: Boolean
668
+ ) {
669
+ automationRunIterations(
670
+ automationRunId: $automationRunId
671
+ agentRunId: $agentRunId
672
+ limit: $limit
673
+ reverse: $reverse
674
+ ) {
675
+ id
676
+ sequence
677
+ agentRunId
678
+ metadata
679
+ startedAt
680
+ finishedAt
681
+ totalDurationMs
682
+ llmResponseDurationMs
683
+ withMarksScreenshotUrl
684
+ withMarksMimeType
685
+ memories {
686
+ id
687
+ type
688
+ value
689
+ sequence
690
+ screenshotUrl
691
+ screenshotMimeType
692
+ }
693
+ }
694
+ }`
695
+ }),
696
+ createOperationSpec({
697
+ operationName: "GetAutomationRuns",
698
+ kind: "query",
699
+ inputSchema: z.object({
700
+ automationId: z.string().min(1),
701
+ status: z.array(z.string().min(1)).optional(),
702
+ invocationType: z.array(z.string().min(1)).optional(),
703
+ limit: z.number().int().positive().optional(),
704
+ page: z.number().int().positive().optional()
705
+ }),
706
+ cli: {
707
+ name: "get-automation-runs",
708
+ path: ["automation-runs", "list"],
709
+ description: "Fetch automation run history.",
710
+ resultKey: "automationRuns",
711
+ options: [
712
+ stringOption({
713
+ inputKey: "automationId",
714
+ flagName: "automation-id",
715
+ description: "Automation ID.",
716
+ required: true
717
+ }),
718
+ stringArrayOption({
719
+ inputKey: "status",
720
+ flagName: "status",
721
+ description: "Optional run statuses."
722
+ }),
723
+ stringArrayOption({
724
+ inputKey: "invocationType",
725
+ flagName: "invocation-type",
726
+ description: "Optional invocation types."
727
+ }),
728
+ numberOption({
729
+ inputKey: "limit",
730
+ flagName: "limit",
731
+ description: "Maximum number of runs to return."
732
+ }),
733
+ numberOption({
734
+ inputKey: "page",
735
+ flagName: "page",
736
+ description: "Page number."
737
+ })
738
+ ]
739
+ },
740
+ mcp: { fileName: "automationRuns.graphql" },
741
+ document: `# Query automation run history for a specific automation with optional filters
742
+ query GetAutomationRuns(
743
+ $automationId: ID!
744
+ $status: [RunStatus!]
745
+ $invocationType: [InvocationType!]
746
+ $limit: Int
747
+ $page: Int
748
+ ) {
749
+ automationRuns(
750
+ automationId: $automationId
751
+ status: $status
752
+ invocationType: $invocationType
753
+ limit: $limit
754
+ page: $page
755
+ ) {
756
+ id
757
+ automationId
758
+ status
759
+ statusDetail
760
+ invocationType
761
+ summary
762
+ createdAt
763
+ startedAt
764
+ pausedAt
765
+ finishedAt
766
+ }
767
+ }`
768
+ }),
769
+ createOperationSpec({
770
+ operationName: "GetAutomations",
771
+ kind: "query",
772
+ inputSchema: z.object({
773
+ limit: z.number().positive().optional(),
774
+ order: jsonObjectSchema.optional(),
775
+ where: jsonObjectSchema.optional()
776
+ }),
777
+ cli: {
778
+ name: "get-automations",
779
+ path: ["automations", "list"],
780
+ description: "Fetch automations with optional list filters.",
781
+ resultKey: "automations",
782
+ options: [
783
+ numberOption({
784
+ inputKey: "limit",
785
+ flagName: "limit",
786
+ description: "Maximum number of automations to return."
787
+ }),
788
+ jsonOption({
789
+ inputKey: "order",
790
+ flagName: "order-json",
791
+ description: "GraphQL order object."
792
+ }),
793
+ jsonOption({
794
+ inputKey: "where",
795
+ flagName: "where-json",
796
+ description: "GraphQL filter object."
797
+ })
798
+ ]
799
+ },
800
+ mcp: { fileName: "automations.graphql" },
801
+ document: `# Query to fetch automations with flat fields only (no nested objects)
802
+ # Excludes config and agents for MCP tool simplicity
803
+ query GetAutomations(
804
+ $limit: Float
805
+ $order: AutomationsOrder
806
+ $where: AutomationsWhereInput
807
+ ) {
808
+ automations(limit: $limit, order: $order, where: $where) {
809
+ id
810
+ name
811
+ organizationId
812
+ createdAt
813
+ updatedAt
814
+ environment
815
+ lastSavedAt
816
+ publishedVersionId
817
+ draftVersionId
818
+ }
819
+ }`
820
+ }),
821
+ createOperationSpec({
822
+ operationName: "GetAutomationWithConfigs",
823
+ kind: "query",
824
+ inputSchema: z.object({
825
+ id: z.string().min(1)
826
+ }),
827
+ cli: {
828
+ name: "get-automation-with-configs",
829
+ path: ["automations", "get"],
830
+ description: "Fetch a single automation with config fields.",
831
+ resultKey: "automation",
832
+ options: [
833
+ stringOption({
834
+ inputKey: "id",
835
+ flagName: "id",
836
+ description: "Automation ID.",
837
+ required: true
838
+ })
839
+ ]
840
+ },
841
+ mcp: { fileName: "automationWithConfigs.graphql" },
842
+ document: `# Query to fetch a single automation with config but excluding agents
843
+ # Config selection is limited to scalar/JSON fields to keep responses flat
844
+ query GetAutomationWithConfigs($id: ID!) {
845
+ automation(id: $id) {
846
+ id
847
+ name
848
+ organizationId
849
+ createdAt
850
+ updatedAt
851
+ environment
852
+ lastSavedAt
853
+ publishedVersionId
854
+ draftVersionId
855
+ config {
856
+ cronExpression
857
+ calendars
858
+ interval
859
+ enableVisualChangeChecks
860
+ isCDPforSOM
861
+ humanInterventionTimeoutSeconds
862
+ availableSlots
863
+ additionalSystemPrompt
864
+ ignoreUnstableSessionState
865
+ }
866
+ }
867
+ }`
868
+ }),
869
+ createOperationSpec({
870
+ operationName: "GetAvailableCommands",
871
+ kind: "query",
872
+ inputSchema: z.object({
873
+ env: z.string().min(1)
874
+ }),
875
+ cli: {
876
+ name: "get-available-commands",
877
+ path: ["catalog", "commands"],
878
+ description: "Fetch available command definitions by environment.",
879
+ resultKey: "availableCommands",
880
+ options: [
881
+ stringOption({
882
+ inputKey: "env",
883
+ flagName: "env",
884
+ description: "Environment name.",
885
+ required: true
886
+ })
887
+ ]
888
+ },
889
+ mcp: { fileName: "availableCommands.graphql" },
890
+ document: `# Query to fetch available command definitions by environment
891
+ query GetAvailableCommands($env: AvailableCommandsEnv!) {
892
+ availableCommands(env: $env) {
893
+ id
894
+ description
895
+ inputSchema
896
+ }
897
+ }`
898
+ }),
899
+ createOperationSpec({
900
+ operationName: "GetAvailableModels",
901
+ kind: "query",
902
+ inputSchema: z.object({
903
+ agentId: z.string().min(1)
904
+ }),
905
+ cli: {
906
+ name: "get-available-models",
907
+ path: ["catalog", "models"],
908
+ description: "Fetch available model definitions for an agent.",
909
+ resultKey: "availableModels",
910
+ options: [
911
+ stringOption({
912
+ inputKey: "agentId",
913
+ flagName: "agent-id",
914
+ description: "Agent ID.",
915
+ required: true
916
+ })
917
+ ]
918
+ },
919
+ mcp: { fileName: "availableModels.graphql" },
920
+ document: `# Query to fetch available model definitions and pools for a specific agent
921
+ query GetAvailableModels($agentId: String!) {
922
+ availableModels(agentId: $agentId) {
923
+ availableModelPools {
924
+ name
925
+ poolId
926
+ models {
927
+ id
928
+ name
929
+ description
930
+ status
931
+ provider
932
+ }
933
+ }
934
+ allAvailableModels {
935
+ id
936
+ name
937
+ description
938
+ status
939
+ provider
940
+ }
941
+ }
942
+ }`
943
+ }),
944
+ createOperationSpec({
945
+ operationName: "GetAvailableTools",
946
+ kind: "query",
947
+ inputSchema: z.object({
948
+ env: z.string().min(1),
949
+ automationId: z.string().min(1).optional()
950
+ }),
951
+ cli: {
952
+ name: "get-available-tools",
953
+ path: ["catalog", "tools"],
954
+ description: "Fetch available tool definitions by environment.",
955
+ resultKey: "availableTools",
956
+ options: [
957
+ stringOption({
958
+ inputKey: "env",
959
+ flagName: "env",
960
+ description: "Environment name.",
961
+ required: true
962
+ }),
963
+ stringOption({
964
+ inputKey: "automationId",
965
+ flagName: "automation-id",
966
+ description: "Optional automation ID."
967
+ })
968
+ ]
969
+ },
970
+ mcp: { fileName: "availableTools.graphql" },
971
+ document: `# Query to fetch available tool definitions by environment
972
+ query GetAvailableTools($env: AvailableCommandsEnv!, $automationId: String) {
973
+ availableTools(env: $env, automationId: $automationId) {
974
+ id
975
+ description
976
+ inputSchema
977
+ }
978
+ }`
979
+ }),
980
+ createOperationSpec({
981
+ operationName: "CreateAutomation",
982
+ kind: "mutation",
983
+ inputSchema: z.object({
984
+ input: jsonObjectSchema
985
+ }),
986
+ cli: {
987
+ name: "create-automation",
988
+ path: ["automations", "create"],
989
+ description: "Create a draft automation.",
990
+ resultKey: "createAutomation",
991
+ options: [
992
+ jsonOption({
993
+ inputKey: "input",
994
+ flagName: "input-json",
995
+ description: "CreateAutomationInput payload.",
996
+ required: true
997
+ })
998
+ ]
999
+ },
1000
+ mcp: { fileName: "createAutomation.graphql" },
1001
+ document: `# Mutation to create a draft automation with optional initial values
1002
+ # Returns flat fields only for MCP tool simplicity
1003
+ mutation CreateAutomation($input: CreateAutomationInput!) {
1004
+ createAutomation(input: $input) {
1005
+ id
1006
+ name
1007
+ organizationId
1008
+ createdAt
1009
+ updatedAt
1010
+ environment
1011
+ lastSavedAt
1012
+ publishedVersionId
1013
+ draftVersionId
1014
+ }
1015
+ }`
1016
+ }),
1017
+ createOperationSpec({
1018
+ operationName: "GetGraphqlSchema",
1019
+ kind: "query",
1020
+ inputSchema: z.object({}),
1021
+ cli: {
1022
+ name: "get-graphql-schema",
1023
+ path: ["graphql", "schema"],
1024
+ description: "Fetch GraphQL schema metadata.",
1025
+ resultKey: "graphqlSchema",
1026
+ options: []
1027
+ },
1028
+ mcp: { fileName: "graphqlSchema.graphql" },
1029
+ document: `# Query to fetch full GraphQL schema metadata.
1030
+ # Uses the first-class graphqlSchema query field to avoid __schema validation
1031
+ # issues in MCP tool-load paths while still returning introspection JSON.
1032
+ # Response can be large: call sparingly (typically once per task/session) and
1033
+ # reuse cached results.
1034
+ query GetGraphqlSchema {
1035
+ graphqlSchema
1036
+ }`
1037
+ }),
1038
+ createOperationSpec({
1039
+ operationName: "PauseAutomationRun",
1040
+ kind: "mutation",
1041
+ inputSchema: z.object({
1042
+ automationRunId: z.string().min(1)
1043
+ }),
1044
+ cli: {
1045
+ name: "pause-automation-run",
1046
+ path: ["automation-runs", "pause"],
1047
+ description: "Pause an automation run.",
1048
+ resultKey: "pauseAutomationRun",
1049
+ options: [
1050
+ stringOption({
1051
+ inputKey: "automationRunId",
1052
+ flagName: "automation-run-id",
1053
+ description: "Automation run ID.",
1054
+ required: true
1055
+ })
1056
+ ]
1057
+ },
1058
+ mcp: { fileName: "pauseAutomationRun.graphql" },
1059
+ document: `# Mutation to pause an automation run
1060
+ mutation PauseAutomationRun($automationRunId: ID!) {
1061
+ pauseAutomationRun(automationRunId: $automationRunId)
1062
+ }`
1063
+ }),
1064
+ createOperationSpec({
1065
+ operationName: "RerunAutomation",
1066
+ kind: "mutation",
1067
+ inputSchema: z.object({
1068
+ automationRunId: z.string().min(1),
1069
+ additionalPrompt: z.string().min(1).optional(),
1070
+ idempotencyKey: z.string().min(1)
1071
+ }),
1072
+ cli: {
1073
+ name: "rerun-automation",
1074
+ path: ["automation-runs", "rerun"],
1075
+ description: "Rerun an existing automation run.",
1076
+ resultKey: "rerunAutomation",
1077
+ options: [
1078
+ stringOption({
1079
+ inputKey: "automationRunId",
1080
+ flagName: "automation-run-id",
1081
+ description: "Automation run ID.",
1082
+ required: true
1083
+ }),
1084
+ stringOption({
1085
+ inputKey: "additionalPrompt",
1086
+ flagName: "additional-prompt",
1087
+ description: "Optional prompt appended to the rerun."
1088
+ }),
1089
+ stringOption({
1090
+ inputKey: "idempotencyKey",
1091
+ flagName: "idempotency-key",
1092
+ description: "Idempotency key for the rerun request.",
1093
+ required: true
1094
+ })
1095
+ ]
1096
+ },
1097
+ mcp: { fileName: "rerunAutomation.graphql" },
1098
+ document: `# Mutation to rerun an existing automation run
1099
+ mutation RerunAutomation(
1100
+ $automationRunId: ID!
1101
+ $additionalPrompt: String
1102
+ $idempotencyKey: String!
1103
+ ) {
1104
+ rerunAutomation(
1105
+ automationRunId: $automationRunId
1106
+ additionalPrompt: $additionalPrompt
1107
+ idempotencyKey: $idempotencyKey
1108
+ ) {
1109
+ automationRunId
1110
+ workflowId
1111
+ workflowRunId
1112
+ }
1113
+ }`
1114
+ }),
1115
+ createOperationSpec({
1116
+ operationName: "ResumeAutomationRun",
1117
+ kind: "mutation",
1118
+ inputSchema: z.object({
1119
+ automationRunId: z.string().min(1),
1120
+ message: z.string().min(1).optional()
1121
+ }),
1122
+ cli: {
1123
+ name: "resume-automation-run",
1124
+ path: ["automation-runs", "resume"],
1125
+ description: "Resume a paused automation run.",
1126
+ resultKey: "resumeAutomationRun",
1127
+ options: [
1128
+ stringOption({
1129
+ inputKey: "automationRunId",
1130
+ flagName: "automation-run-id",
1131
+ description: "Automation run ID.",
1132
+ required: true
1133
+ }),
1134
+ stringOption({
1135
+ inputKey: "message",
1136
+ flagName: "message",
1137
+ description: "Optional resume message."
1138
+ })
1139
+ ]
1140
+ },
1141
+ mcp: { fileName: "resumeAutomationRun.graphql" },
1142
+ document: `# Mutation to resume an automation run
1143
+ mutation ResumeAutomationRun($automationRunId: ID!, $message: String) {
1144
+ resumeAutomationRun(automationRunId: $automationRunId, message: $message)
1145
+ }`
1146
+ }),
1147
+ createOperationSpec({
1148
+ operationName: "RunDraftAutomation",
1149
+ kind: "mutation",
1150
+ inputSchema: z.object({
1151
+ draftAutomationId: z.string().min(1),
1152
+ agentId: z.string().min(1),
1153
+ idempotencyKey: z.string().min(1),
1154
+ input: z.unknown().optional(),
1155
+ additionalPrompt: z.string().min(1).optional()
1156
+ }),
1157
+ cli: {
1158
+ name: "run-draft-automation",
1159
+ path: ["automations", "run-draft"],
1160
+ description: "Run a draft automation with an explicit trigger agent.",
1161
+ resultKey: "runDraftAutomation",
1162
+ options: [
1163
+ stringOption({
1164
+ inputKey: "draftAutomationId",
1165
+ flagName: "draft-automation-id",
1166
+ description: "Draft automation ID.",
1167
+ required: true
1168
+ }),
1169
+ stringOption({
1170
+ inputKey: "agentId",
1171
+ flagName: "agent-id",
1172
+ description: "Trigger agent ID.",
1173
+ required: true
1174
+ }),
1175
+ stringOption({
1176
+ inputKey: "idempotencyKey",
1177
+ flagName: "idempotency-key",
1178
+ description: "Idempotency key for the run request.",
1179
+ required: true
1180
+ }),
1181
+ jsonOption({
1182
+ inputKey: "input",
1183
+ flagName: "input-json",
1184
+ description: "Optional JSON input payload."
1185
+ }),
1186
+ stringOption({
1187
+ inputKey: "additionalPrompt",
1188
+ flagName: "additional-prompt",
1189
+ description: "Optional prompt appended to the run."
1190
+ })
1191
+ ]
1192
+ },
1193
+ mcp: { fileName: "runDraftAutomation.graphql" },
1194
+ document: `# Mutation to run a draft automation with an explicit agent.
1195
+ # \`draftAutomationId\` is the draft automation entity ID (\`Automation.id\`), not \`draftVersionId\`.
1196
+ mutation RunDraftAutomation(
1197
+ $draftAutomationId: ID!
1198
+ $agentId: ID!
1199
+ $idempotencyKey: String!
1200
+ $input: JSON
1201
+ $additionalPrompt: String
1202
+ ) {
1203
+ runDraftAutomation(
1204
+ draftAutomationId: $draftAutomationId
1205
+ agentId: $agentId
1206
+ idempotencyKey: $idempotencyKey
1207
+ input: $input
1208
+ additionalPrompt: $additionalPrompt
1209
+ ) {
1210
+ automationRunId
1211
+ workflowId
1212
+ workflowRunId
1213
+ }
1214
+ }`
1215
+ }),
1216
+ createOperationSpec({
1217
+ operationName: "StopAutomationRun",
1218
+ kind: "mutation",
1219
+ inputSchema: z.object({
1220
+ automationRunId: z.string().min(1)
1221
+ }),
1222
+ cli: {
1223
+ name: "stop-automation-run",
1224
+ path: ["automation-runs", "stop"],
1225
+ description: "Stop an automation run.",
1226
+ resultKey: "stopAutomationRun",
1227
+ options: [
1228
+ stringOption({
1229
+ inputKey: "automationRunId",
1230
+ flagName: "automation-run-id",
1231
+ description: "Automation run ID.",
1232
+ required: true
1233
+ })
1234
+ ]
1235
+ },
1236
+ mcp: { fileName: "stopAutomationRun.graphql" },
1237
+ document: `# Mutation to stop an automation run
1238
+ mutation StopAutomationRun($automationRunId: ID!) {
1239
+ stopAutomationRun(automationRunId: $automationRunId)
1240
+ }`
1241
+ }),
1242
+ createOperationSpec({
1243
+ operationName: "UpdateAutomationById",
1244
+ kind: "mutation",
1245
+ inputSchema: z.object({
1246
+ id: z.string().min(1),
1247
+ input: jsonObjectSchema
1248
+ }),
1249
+ cli: {
1250
+ name: "update-automation-by-id",
1251
+ path: ["automations", "update"],
1252
+ description: "Update an automation.",
1253
+ resultKey: "updateAutomation",
1254
+ options: [
1255
+ stringOption({
1256
+ inputKey: "id",
1257
+ flagName: "id",
1258
+ description: "Automation ID.",
1259
+ required: true
1260
+ }),
1261
+ jsonOption({
1262
+ inputKey: "input",
1263
+ flagName: "input-json",
1264
+ description: "UpdateAutomationInput payload.",
1265
+ required: true
1266
+ })
1267
+ ]
1268
+ },
1269
+ mcp: { fileName: "updateAutomation.graphql" },
1270
+ document: `# Mutation to update an automation
1271
+ # Returns only id and lastSavedAt for MCP tool simplicity
1272
+ mutation UpdateAutomationById($id: ID!, $input: UpdateAutomationInput!) {
1273
+ updateAutomation(id: $id, input: $input) {
1274
+ id
1275
+ lastSavedAt
1276
+ }
1277
+ }`
1278
+ })
1279
+ ];
1280
+
1281
+ // src/program.ts
1282
+ import { Command, InvalidArgumentError, Option } from "commander";
1283
+
1284
+ // src/api.ts
1285
+ import { z as z2 } from "zod";
1286
+ var workosOrganizationsResponseSchema = z2.object({
1287
+ organizations: z2.array(
1288
+ z2.object({
1289
+ id: z2.string().min(1),
1290
+ name: z2.string().min(1),
1291
+ primaryDomain: z2.string().nullable().optional()
1292
+ })
1293
+ ),
1294
+ success: z2.boolean()
1295
+ });
1296
+ var workosUserSchema = z2.object({
1297
+ email: z2.string().nullable(),
1298
+ id: z2.string().min(1)
1299
+ });
1300
+ var graphQlResponseSchema = z2.object({
1301
+ data: z2.record(z2.string(), z2.unknown()).optional(),
1302
+ errors: z2.array(
1303
+ z2.object({
1304
+ message: z2.string().min(1)
1305
+ })
1306
+ ).optional()
1307
+ });
1308
+ function createMagicalApiClient({
1309
+ apiBaseUrl,
1310
+ fetchFn = fetch
1311
+ }) {
1312
+ async function fetchJson({
1313
+ accessToken,
1314
+ url,
1315
+ init
1316
+ }) {
1317
+ const headers = new Headers(init?.headers);
1318
+ headers.set("authorization", `Bearer ${accessToken}`);
1319
+ const response = await fetchFn(url, {
1320
+ ...init,
1321
+ headers
1322
+ }).catch((error) => {
1323
+ throw new CliError(
1324
+ `Failed to reach Magical API at ${url}. Check MAGICAL_API_URL and make sure service-main is running.`,
1325
+ { cause: error }
1326
+ );
1327
+ });
1328
+ const responseBody = await response.json().catch((error) => {
1329
+ throw new CliError(
1330
+ `Magical API returned a non-JSON response for ${url}.`,
1331
+ {
1332
+ cause: error
1333
+ }
1334
+ );
1335
+ });
1336
+ if (!response.ok) {
1337
+ throw new CliError(
1338
+ `Magical API request to ${url} failed with status ${response.status}.`
1339
+ );
1340
+ }
1341
+ return responseBody;
1342
+ }
1343
+ return {
1344
+ async executeGraphQl({
1345
+ accessToken,
1346
+ query,
1347
+ resultKey,
1348
+ variables
1349
+ }) {
1350
+ const headers = new Headers();
1351
+ headers.set("content-type", "application/json");
1352
+ const parsedResponse = graphQlResponseSchema.safeParse(
1353
+ await fetchJson({
1354
+ accessToken,
1355
+ url: `${apiBaseUrl}/graphql`,
1356
+ init: {
1357
+ method: "POST",
1358
+ headers,
1359
+ body: JSON.stringify({
1360
+ query,
1361
+ variables
1362
+ })
1363
+ }
1364
+ })
1365
+ );
1366
+ if (!parsedResponse.success) {
1367
+ throw new CliError("GraphQL response payload was invalid.");
1368
+ }
1369
+ if (parsedResponse.data.errors && parsedResponse.data.errors.length > 0) {
1370
+ throw new CliError(
1371
+ parsedResponse.data.errors.map((error) => error.message).join("\n")
1372
+ );
1373
+ }
1374
+ return parsedResponse.data.data?.[resultKey] ?? null;
1375
+ },
1376
+ async fetchOrganizations({ accessToken }) {
1377
+ const parsedResponse = workosOrganizationsResponseSchema.safeParse(
1378
+ await fetchJson({
1379
+ accessToken,
1380
+ url: `${apiBaseUrl}/workos/organizations`
1381
+ })
1382
+ );
1383
+ if (!parsedResponse.success) {
1384
+ throw new CliError("Organization list payload was invalid.");
1385
+ }
1386
+ return parsedResponse.data.organizations.map((organization) => ({
1387
+ id: organization.id,
1388
+ name: organization.name,
1389
+ primaryDomain: organization.primaryDomain ?? null
1390
+ }));
1391
+ },
1392
+ async fetchUser({
1393
+ accessToken,
1394
+ userId
1395
+ }) {
1396
+ const parsedResponse = workosUserSchema.safeParse(
1397
+ await fetchJson({
1398
+ accessToken,
1399
+ url: `${apiBaseUrl}/workos/user/${userId}`
1400
+ })
1401
+ );
1402
+ if (!parsedResponse.success) {
1403
+ throw new CliError("User payload was invalid.");
1404
+ }
1405
+ return parsedResponse.data;
1406
+ }
1407
+ };
1408
+ }
1409
+
1410
+ // src/config.ts
1411
+ import os from "os";
1412
+ import path from "path";
1413
+ var DEFAULT_MAGICAL_API_URL = "https://api-agt.getmagical.io";
1414
+ var DEFAULT_WORKOS_API_URL = "https://api.workos.com";
1415
+ var DEFAULT_WORKOS_CLIENT_ID = "client_01JJZ6XJ1RF248P20WF63M4VAM";
1416
+ var DEFAULT_KEYCHAIN_SERVICE_NAME = "@getmagical/mcp-cli";
1417
+ var CLI_CONFIG_DIRECTORY_NAME = "magical-mcp-cli";
1418
+ var CLI_STATE_FILE_NAME = "state.json";
1419
+ function resolvePlatformConfigDirectory() {
1420
+ const customConfigDirectory = process.env["MAGICAL_CLI_CONFIG_DIR"];
1421
+ if (customConfigDirectory) {
1422
+ return customConfigDirectory;
1423
+ }
1424
+ if (process.platform === "darwin") {
1425
+ return path.join(os.homedir(), "Library", "Application Support");
1426
+ }
1427
+ if (process.platform === "win32") {
1428
+ const appDataDirectory = process.env["APPDATA"];
1429
+ return appDataDirectory ?? path.join(os.homedir(), "AppData", "Roaming");
1430
+ }
1431
+ return process.env["XDG_CONFIG_HOME"] ?? path.join(os.homedir(), ".config");
1432
+ }
1433
+ function getRuntimeConfig() {
1434
+ const configDirectory = path.join(
1435
+ resolvePlatformConfigDirectory(),
1436
+ CLI_CONFIG_DIRECTORY_NAME
1437
+ );
1438
+ return {
1439
+ magicalApiUrl: process.env["MAGICAL_API_URL"] ?? DEFAULT_MAGICAL_API_URL,
1440
+ stateFilePath: path.join(configDirectory, CLI_STATE_FILE_NAME),
1441
+ workosApiUrl: DEFAULT_WORKOS_API_URL,
1442
+ workosClientId: process.env["WORKOS_CLIENT_ID"] ?? DEFAULT_WORKOS_CLIENT_ID,
1443
+ keychainServiceName: DEFAULT_KEYCHAIN_SERVICE_NAME,
1444
+ ...process.env["WORKOS_AUTHKIT_DOMAIN"] ? { workosAuthkitDomain: process.env["WORKOS_AUTHKIT_DOMAIN"] } : {}
1445
+ };
1446
+ }
1447
+ function requireWorkosClientId(config) {
1448
+ return config.workosClientId;
1449
+ }
1450
+
1451
+ // src/keychain.ts
1452
+ import keytar from "keytar";
1453
+ function buildKeychainAccountName(account) {
1454
+ return `${account.clientId}:${account.userId}`;
1455
+ }
1456
+ function createKeytarRefreshTokenStore(serviceName) {
1457
+ return {
1458
+ async clear(account) {
1459
+ await keytar.deletePassword(
1460
+ serviceName,
1461
+ buildKeychainAccountName(account)
1462
+ );
1463
+ },
1464
+ async load(account) {
1465
+ return keytar.getPassword(serviceName, buildKeychainAccountName(account));
1466
+ },
1467
+ async save(account, refreshToken) {
1468
+ await keytar.setPassword(
1469
+ serviceName,
1470
+ buildKeychainAccountName(account),
1471
+ refreshToken
1472
+ );
1473
+ }
1474
+ };
1475
+ }
1476
+
1477
+ // src/state.ts
1478
+ import { mkdir, readFile, rm, writeFile } from "fs/promises";
1479
+ import path2 from "path";
1480
+ import { z as z3 } from "zod";
1481
+ var cliAccountSchema = z3.object({
1482
+ clientId: z3.string().min(1),
1483
+ email: z3.string().nullable(),
1484
+ userId: z3.string().min(1)
1485
+ });
1486
+ var cliOrganizationSchema = z3.object({
1487
+ id: z3.string().min(1),
1488
+ name: z3.string().min(1),
1489
+ primaryDomain: z3.string().nullable().default(null)
1490
+ });
1491
+ var cliStateSchema = z3.object({
1492
+ account: cliAccountSchema,
1493
+ defaultOrgId: z3.string().nullable(),
1494
+ lastUsedOrgId: z3.string().nullable(),
1495
+ organizations: z3.array(cliOrganizationSchema)
1496
+ });
1497
+ function createFileStateStore(stateFilePath) {
1498
+ return {
1499
+ async clear() {
1500
+ await rm(stateFilePath, { force: true });
1501
+ },
1502
+ async load() {
1503
+ try {
1504
+ const fileContents = await readFile(stateFilePath, "utf8");
1505
+ const parsed = cliStateSchema.safeParse(JSON.parse(fileContents));
1506
+ if (!parsed.success) {
1507
+ throw new CliError("Stored CLI state is invalid.");
1508
+ }
1509
+ return parsed.data;
1510
+ } catch (error) {
1511
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
1512
+ return null;
1513
+ }
1514
+ if (error instanceof CliError) {
1515
+ throw error;
1516
+ }
1517
+ throw new CliError("Failed to load CLI state.", { cause: error });
1518
+ }
1519
+ },
1520
+ async save(state) {
1521
+ await mkdir(path2.dirname(stateFilePath), { recursive: true });
1522
+ await writeFile(stateFilePath, `${JSON.stringify(state, null, 2)}
1523
+ `);
1524
+ }
1525
+ };
1526
+ }
1527
+
1528
+ // src/workos-auth.ts
1529
+ import { z as z4 } from "zod";
1530
+ var DEFAULT_DEVICE_POLL_INTERVAL_SECONDS = 5;
1531
+ function buildFormHeaders() {
1532
+ const headers = new Headers();
1533
+ headers.set("content-type", "application/x-www-form-urlencoded");
1534
+ return headers;
1535
+ }
1536
+ var deviceAuthorizationSchema = z4.object({
1537
+ device_code: z4.string().min(1),
1538
+ expires_in: z4.number().int().positive().optional(),
1539
+ interval: z4.number().int().positive().optional(),
1540
+ user_code: z4.string().min(1),
1541
+ verification_uri: z4.url(),
1542
+ verification_uri_complete: z4.url().optional()
1543
+ });
1544
+ var tokenResponseSchema = z4.object({
1545
+ access_token: z4.string().min(1),
1546
+ expires_in: z4.number().int().positive().optional(),
1547
+ refresh_token: z4.string().min(1),
1548
+ token_type: z4.string().optional()
1549
+ });
1550
+ var tokenErrorSchema = z4.object({
1551
+ error: z4.string().min(1),
1552
+ error_description: z4.string().optional()
1553
+ });
1554
+ var accessTokenClaimsSchema = z4.object({
1555
+ org_id: z4.string().min(1).optional(),
1556
+ sid: z4.string().min(1).optional(),
1557
+ sub: z4.string().min(1)
1558
+ });
1559
+ function createWorkosAuthClient({
1560
+ fetchFn = fetch,
1561
+ sleep = async (milliseconds) => {
1562
+ await new Promise((resolve) => {
1563
+ setTimeout(resolve, milliseconds);
1564
+ });
1565
+ },
1566
+ workosApiUrl
1567
+ }) {
1568
+ async function postForm({
1569
+ endpointPath,
1570
+ formData,
1571
+ responseSchema
1572
+ }) {
1573
+ const response = await fetchFn(`${workosApiUrl}${endpointPath}`, {
1574
+ method: "POST",
1575
+ headers: buildFormHeaders(),
1576
+ body: formData
1577
+ });
1578
+ const responseBody = await response.json();
1579
+ if (!response.ok) {
1580
+ const parsedError = tokenErrorSchema.safeParse(responseBody);
1581
+ if (parsedError.success) {
1582
+ throw new CliError(
1583
+ parsedError.data.error_description ?? parsedError.data.error
1584
+ );
1585
+ }
1586
+ throw new CliError(
1587
+ `WorkOS request failed with status ${response.status}.`
1588
+ );
1589
+ }
1590
+ const parsedResponse = responseSchema.safeParse(responseBody);
1591
+ if (!parsedResponse.success) {
1592
+ throw new CliError("WorkOS returned an unexpected response payload.");
1593
+ }
1594
+ return parsedResponse.data;
1595
+ }
1596
+ return {
1597
+ decodeAccessTokenClaims(accessToken) {
1598
+ const [, payloadSegment] = accessToken.split(".");
1599
+ if (!payloadSegment) {
1600
+ throw new CliError("WorkOS access token is malformed.");
1601
+ }
1602
+ const payload = JSON.parse(
1603
+ Buffer.from(payloadSegment, "base64url").toString("utf8")
1604
+ );
1605
+ const parsedClaims = accessTokenClaimsSchema.safeParse(payload);
1606
+ if (!parsedClaims.success) {
1607
+ throw new CliError("WorkOS access token is missing required claims.");
1608
+ }
1609
+ return parsedClaims.data;
1610
+ },
1611
+ async pollForDeviceTokens({
1612
+ clientId,
1613
+ deviceCode,
1614
+ expiresInSeconds,
1615
+ intervalSeconds
1616
+ }) {
1617
+ const timeoutAt = Date.now() + (expiresInSeconds ?? 15 * 60) * 1e3;
1618
+ const initialIntervalMilliseconds = (intervalSeconds ?? DEFAULT_DEVICE_POLL_INTERVAL_SECONDS) * 1e3;
1619
+ async function poll(nextIntervalMilliseconds) {
1620
+ if (Date.now() >= timeoutAt) {
1621
+ throw new CliError(
1622
+ "WorkOS device authorization expired before completion."
1623
+ );
1624
+ }
1625
+ const formData = new URLSearchParams();
1626
+ formData.set("client_id", clientId);
1627
+ formData.set("device_code", deviceCode);
1628
+ formData.set(
1629
+ "grant_type",
1630
+ "urn:ietf:params:oauth:grant-type:device_code"
1631
+ );
1632
+ const response = await fetchFn(
1633
+ `${workosApiUrl}/user_management/authenticate`,
1634
+ {
1635
+ method: "POST",
1636
+ headers: buildFormHeaders(),
1637
+ body: formData
1638
+ }
1639
+ );
1640
+ const responseBody = await response.json();
1641
+ if (response.ok) {
1642
+ const parsedTokens = tokenResponseSchema.safeParse(responseBody);
1643
+ if (!parsedTokens.success) {
1644
+ throw new CliError("WorkOS returned an unexpected token response.");
1645
+ }
1646
+ return parsedTokens.data;
1647
+ }
1648
+ const parsedError = tokenErrorSchema.safeParse(responseBody);
1649
+ if (!parsedError.success) {
1650
+ throw new CliError(
1651
+ `WorkOS device authorization failed with status ${response.status}.`
1652
+ );
1653
+ }
1654
+ if (parsedError.data.error === "authorization_pending") {
1655
+ await sleep(nextIntervalMilliseconds);
1656
+ return poll(nextIntervalMilliseconds);
1657
+ }
1658
+ if (parsedError.data.error === "slow_down") {
1659
+ const slowedIntervalMilliseconds = nextIntervalMilliseconds + 5e3;
1660
+ await sleep(slowedIntervalMilliseconds);
1661
+ return poll(slowedIntervalMilliseconds);
1662
+ }
1663
+ throw new CliError(
1664
+ parsedError.data.error_description ?? parsedError.data.error
1665
+ );
1666
+ }
1667
+ return poll(initialIntervalMilliseconds);
1668
+ },
1669
+ async refreshTokens({
1670
+ clientId,
1671
+ organizationId,
1672
+ refreshToken
1673
+ }) {
1674
+ const formData = new URLSearchParams();
1675
+ formData.set("client_id", clientId);
1676
+ formData.set("grant_type", "refresh_token");
1677
+ formData.set("refresh_token", refreshToken);
1678
+ if (organizationId) {
1679
+ formData.set("organization_id", organizationId);
1680
+ }
1681
+ return postForm({
1682
+ endpointPath: "/user_management/authenticate",
1683
+ formData,
1684
+ responseSchema: tokenResponseSchema
1685
+ });
1686
+ },
1687
+ async startDeviceAuthorization({ clientId }) {
1688
+ const formData = new URLSearchParams();
1689
+ formData.set("client_id", clientId);
1690
+ return postForm({
1691
+ endpointPath: "/user_management/authorize/device",
1692
+ formData,
1693
+ responseSchema: deviceAuthorizationSchema
1694
+ });
1695
+ }
1696
+ };
1697
+ }
1698
+
1699
+ // src/program.ts
1700
+ var rootOperationalGroupDescriptions = /* @__PURE__ */ new Map([
1701
+ ["agents", "Query agents."],
1702
+ ["agent-runs", "Query agent runs."],
1703
+ ["automations", "Manage automations."],
1704
+ ["automation-runs", "Inspect and control automation runs."],
1705
+ ["catalog", "Inspect available commands, models, and tools."],
1706
+ ["graphql", "Inspect GraphQL metadata."]
1707
+ ]);
1708
+ function configureCommandPresentation(command) {
1709
+ const originalCreateCommand = command.createCommand.bind(command);
1710
+ command.createCommand = (name) => configureCommandPresentation(originalCreateCommand(name));
1711
+ command.createHelp = () => new MagicalHelp();
1712
+ return command;
1713
+ }
1714
+ function createOperationHelpExamples(operationSpec) {
1715
+ const baseCommand = `mgcl ${operationSpec.cli.path.join(" ")}`;
1716
+ const requiredOptions = operationSpec.cli.options.filter((optionSpec) => optionSpec.required).map((optionSpec) => {
1717
+ if (optionSpec.valueType === "boolean") {
1718
+ return `--${optionSpec.flagName}`;
1719
+ }
1720
+ if (optionSpec.valueType === "number") {
1721
+ return `--${optionSpec.flagName} 10`;
1722
+ }
1723
+ if (optionSpec.valueType === "json") {
1724
+ return `--${optionSpec.flagName} '{...}'`;
1725
+ }
1726
+ if (optionSpec.valueType === "string-array") {
1727
+ return `--${optionSpec.flagName} value-a,value-b`;
1728
+ }
1729
+ return `--${optionSpec.flagName} <${optionSpec.flagName}>`;
1730
+ });
1731
+ const baseInvocation = [baseCommand, ...requiredOptions].join(" ");
1732
+ return [
1733
+ baseInvocation,
1734
+ `${baseInvocation} --org <org>`,
1735
+ `${baseInvocation} --json`
1736
+ ];
1737
+ }
1738
+ function buildCommandOptionProperty(flagName) {
1739
+ const [firstSegment, ...remainingSegments] = flagName.split("-");
1740
+ return [
1741
+ firstSegment ?? "",
1742
+ ...remainingSegments.map(
1743
+ (segment) => `${segment[0]?.toUpperCase() ?? ""}${segment.slice(1)}`
1744
+ )
1745
+ ].join("");
1746
+ }
1747
+ function parseCommandOptionValue(rawValue, optionSpec) {
1748
+ switch (optionSpec.valueType) {
1749
+ case "number": {
1750
+ const parsedNumber = Number(rawValue);
1751
+ if (Number.isNaN(parsedNumber)) {
1752
+ throw new InvalidArgumentError(
1753
+ `Expected a number for --${optionSpec.flagName}.`
1754
+ );
1755
+ }
1756
+ return parsedNumber;
1757
+ }
1758
+ case "string-array": {
1759
+ return rawValue.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
1760
+ }
1761
+ case "json": {
1762
+ try {
1763
+ return JSON.parse(rawValue);
1764
+ } catch {
1765
+ throw new InvalidArgumentError(
1766
+ `Expected valid JSON for --${optionSpec.flagName}.`
1767
+ );
1768
+ }
1769
+ }
1770
+ case "boolean": {
1771
+ return rawValue;
1772
+ }
1773
+ case "string": {
1774
+ return rawValue;
1775
+ }
1776
+ }
1777
+ }
1778
+ function isRecord2(value) {
1779
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1780
+ }
1781
+ function resolveOrganizationBySelector({
1782
+ organizations,
1783
+ selector
1784
+ }) {
1785
+ const matchedOrganization = organizations.find(
1786
+ (organization) => organization.id === selector || organization.name === selector
1787
+ );
1788
+ if (!matchedOrganization) {
1789
+ throw new CliError(`Unknown organization "${selector}".`);
1790
+ }
1791
+ return matchedOrganization;
1792
+ }
1793
+ function resolveSelectedOrganization({
1794
+ organizationSelector,
1795
+ state
1796
+ }) {
1797
+ if (organizationSelector) {
1798
+ return resolveOrganizationBySelector({
1799
+ organizations: state.organizations,
1800
+ selector: organizationSelector
1801
+ });
1802
+ }
1803
+ if (!state.defaultOrgId) {
1804
+ throw new CliError(
1805
+ 'No default organization is configured. Use "mgcl org use <org>" or pass --org.'
1806
+ );
1807
+ }
1808
+ return resolveOrganizationBySelector({
1809
+ organizations: state.organizations,
1810
+ selector: state.defaultOrgId
1811
+ });
1812
+ }
1813
+ function requireLoggedInState(state) {
1814
+ if (!state) {
1815
+ throw new CliError('You are not logged in. Run "mgcl auth login" first.');
1816
+ }
1817
+ return state;
1818
+ }
1819
+ function writeJsonOutput(io, value) {
1820
+ io.info(JSON.stringify(value, null, 2));
1821
+ }
1822
+ function collectOperationVariables(operationSpec, options) {
1823
+ const variables = Object.fromEntries(
1824
+ operationSpec.cli.options.flatMap((optionSpec) => {
1825
+ const optionValue = options[buildCommandOptionProperty(optionSpec.flagName)];
1826
+ if (optionValue === void 0) {
1827
+ return [];
1828
+ }
1829
+ return [[optionSpec.inputKey, optionValue]];
1830
+ })
1831
+ );
1832
+ const parsedVariables = operationSpec.inputSchema.safeParse(variables);
1833
+ if (!parsedVariables.success) {
1834
+ throw new CliError(
1835
+ parsedVariables.error.issues[0]?.message ?? "Invalid command input."
1836
+ );
1837
+ }
1838
+ if (!isRecord2(parsedVariables.data)) {
1839
+ throw new CliError("Invalid command input.");
1840
+ }
1841
+ return parsedVariables.data;
1842
+ }
1843
+ async function withOrgScopedAccessToken({
1844
+ dependencies,
1845
+ organizationId,
1846
+ state
1847
+ }) {
1848
+ const refreshToken = await dependencies.refreshTokenStore.load(state.account);
1849
+ if (!refreshToken) {
1850
+ throw new CliError(
1851
+ 'No refresh token was found. Run "mgcl auth login" again.'
1852
+ );
1853
+ }
1854
+ const tokenResponse = await dependencies.authClient.refreshTokens({
1855
+ clientId: state.account.clientId,
1856
+ organizationId,
1857
+ refreshToken
1858
+ }).catch((error) => {
1859
+ if (error instanceof CliError && error.message === "Refresh token already exchanged.") {
1860
+ throw new CliError(
1861
+ 'Refresh token already exchanged. Run "mgcl auth login" again.',
1862
+ { cause: error }
1863
+ );
1864
+ }
1865
+ throw error;
1866
+ });
1867
+ await dependencies.refreshTokenStore.save(
1868
+ state.account,
1869
+ tokenResponse.refresh_token
1870
+ );
1871
+ return {
1872
+ accessToken: tokenResponse.access_token
1873
+ };
1874
+ }
1875
+ async function handleAuthLogin(dependencies, options) {
1876
+ const clientId = requireWorkosClientId(dependencies.runtimeConfig);
1877
+ const deviceAuthorization = await dependencies.authClient.startDeviceAuthorization({ clientId });
1878
+ const verificationUrl = deviceAuthorization.verification_uri_complete ?? deviceAuthorization.verification_uri;
1879
+ const promptWriter = options.json ? dependencies.io.error : dependencies.io.info;
1880
+ promptWriter(
1881
+ formatAuthLoginPrompt({
1882
+ userCode: deviceAuthorization.user_code,
1883
+ verificationUrl
1884
+ })
1885
+ );
1886
+ await dependencies.openExternalUrl(verificationUrl).catch(() => {
1887
+ promptWriter(formatBrowserOpenFallback());
1888
+ });
1889
+ const tokenResponse = await dependencies.authClient.pollForDeviceTokens({
1890
+ clientId,
1891
+ deviceCode: deviceAuthorization.device_code,
1892
+ ...deviceAuthorization.expires_in ? { expiresInSeconds: deviceAuthorization.expires_in } : {},
1893
+ ...deviceAuthorization.interval ? { intervalSeconds: deviceAuthorization.interval } : {}
1894
+ });
1895
+ const accessTokenClaims = dependencies.authClient.decodeAccessTokenClaims(
1896
+ tokenResponse.access_token
1897
+ );
1898
+ const [user, organizations] = await Promise.all([
1899
+ dependencies.apiClient.fetchUser({
1900
+ accessToken: tokenResponse.access_token,
1901
+ userId: accessTokenClaims.sub
1902
+ }),
1903
+ dependencies.apiClient.fetchOrganizations({
1904
+ accessToken: tokenResponse.access_token
1905
+ })
1906
+ ]);
1907
+ const defaultOrgId = accessTokenClaims.org_id ?? organizations[0]?.id ?? null;
1908
+ const nextState = {
1909
+ account: {
1910
+ clientId,
1911
+ email: user.email,
1912
+ userId: user.id
1913
+ },
1914
+ defaultOrgId,
1915
+ lastUsedOrgId: defaultOrgId,
1916
+ organizations
1917
+ };
1918
+ await Promise.all([
1919
+ dependencies.refreshTokenStore.save(
1920
+ nextState.account,
1921
+ tokenResponse.refresh_token
1922
+ ),
1923
+ dependencies.stateStore.save(nextState)
1924
+ ]);
1925
+ const defaultOrganization = organizations.find((organization) => organization.id === defaultOrgId) ?? null;
1926
+ if (options.json) {
1927
+ writeJsonOutput(dependencies.io, nextState);
1928
+ return;
1929
+ }
1930
+ dependencies.io.info(
1931
+ formatAuthLoginSuccess({
1932
+ account: nextState.account,
1933
+ defaultOrganization,
1934
+ organizationCount: organizations.length
1935
+ })
1936
+ );
1937
+ }
1938
+ async function handleAuthLogout(dependencies, options) {
1939
+ const state = await dependencies.stateStore.load();
1940
+ if (state) {
1941
+ await dependencies.refreshTokenStore.clear(state.account);
1942
+ }
1943
+ await dependencies.stateStore.clear();
1944
+ if (options.json) {
1945
+ writeJsonOutput(dependencies.io, { loggedOut: true });
1946
+ return;
1947
+ }
1948
+ dependencies.io.info(formatLogoutSuccess());
1949
+ }
1950
+ async function handleOrgList(dependencies, options) {
1951
+ const state = requireLoggedInState(await dependencies.stateStore.load());
1952
+ if (options.json) {
1953
+ writeJsonOutput(dependencies.io, state.organizations);
1954
+ return;
1955
+ }
1956
+ dependencies.io.info(
1957
+ formatOrganizationList({
1958
+ defaultOrgId: state.defaultOrgId,
1959
+ lastUsedOrgId: state.lastUsedOrgId,
1960
+ organizations: state.organizations
1961
+ })
1962
+ );
1963
+ }
1964
+ async function handleOrgUse(dependencies, organizationSelector, options) {
1965
+ const state = requireLoggedInState(await dependencies.stateStore.load());
1966
+ const organization = resolveOrganizationBySelector({
1967
+ organizations: state.organizations,
1968
+ selector: organizationSelector
1969
+ });
1970
+ const nextState = {
1971
+ ...state,
1972
+ defaultOrgId: organization.id,
1973
+ lastUsedOrgId: organization.id
1974
+ };
1975
+ await dependencies.stateStore.save(nextState);
1976
+ if (options.json) {
1977
+ writeJsonOutput(dependencies.io, {
1978
+ defaultOrgId: organization.id,
1979
+ organization
1980
+ });
1981
+ return;
1982
+ }
1983
+ dependencies.io.info(formatDefaultOrganizationSelection(organization));
1984
+ }
1985
+ async function handleOperationCommand(dependencies, operationSpec, commandOptions) {
1986
+ const state = requireLoggedInState(await dependencies.stateStore.load());
1987
+ const organization = resolveSelectedOrganization({
1988
+ state,
1989
+ ...commandOptions.org ? { organizationSelector: commandOptions.org } : {}
1990
+ });
1991
+ const variables = collectOperationVariables(operationSpec, commandOptions);
1992
+ const { accessToken } = await withOrgScopedAccessToken({
1993
+ dependencies,
1994
+ organizationId: organization.id,
1995
+ state
1996
+ });
1997
+ const result = await dependencies.apiClient.executeGraphQl({
1998
+ accessToken,
1999
+ query: operationSpec.document,
2000
+ resultKey: operationSpec.cli.resultKey,
2001
+ variables
2002
+ });
2003
+ await dependencies.stateStore.save({
2004
+ ...state,
2005
+ lastUsedOrgId: organization.id
2006
+ });
2007
+ if (commandOptions.json) {
2008
+ writeJsonOutput(dependencies.io, result);
2009
+ return;
2010
+ }
2011
+ dependencies.io.info(formatHumanReadableOutput(result));
2012
+ }
2013
+ function addRegistryOperationCommand({
2014
+ program: program2,
2015
+ commandGroups,
2016
+ dependencies,
2017
+ operationSpec
2018
+ }) {
2019
+ const commandPath = [...operationSpec.cli.path];
2020
+ const leafCommandName = commandPath.pop();
2021
+ if (!leafCommandName) {
2022
+ throw new CliError(
2023
+ `CLI path is missing a leaf command for ${operationSpec.operationName}.`
2024
+ );
2025
+ }
2026
+ let parentCommand = program2;
2027
+ const currentGroupPath = [];
2028
+ for (const groupSegment of commandPath) {
2029
+ currentGroupPath.push(groupSegment);
2030
+ const groupPathKey = currentGroupPath.join(" ");
2031
+ const existingGroup = commandGroups.get(groupPathKey);
2032
+ if (existingGroup) {
2033
+ parentCommand = existingGroup;
2034
+ continue;
2035
+ }
2036
+ const nextGroup = parentCommand.command(groupSegment);
2037
+ if (currentGroupPath.length === 1) {
2038
+ nextGroup.helpGroup("Operation groups:");
2039
+ }
2040
+ const rootGroupDescription = currentGroupPath.length === 1 ? rootOperationalGroupDescriptions.get(groupSegment) : void 0;
2041
+ if (rootGroupDescription) {
2042
+ nextGroup.description(rootGroupDescription);
2043
+ }
2044
+ commandGroups.set(groupPathKey, nextGroup);
2045
+ parentCommand = nextGroup;
2046
+ }
2047
+ const command = parentCommand.command(leafCommandName);
2048
+ configureOperationalCommand(command, dependencies, operationSpec);
2049
+ const legacyAliasCommand = program2.command(operationSpec.cli.name, {
2050
+ hidden: true
2051
+ });
2052
+ configureOperationalCommand(legacyAliasCommand, dependencies, operationSpec);
2053
+ }
2054
+ function configureOperationalCommand(command, dependencies, operationSpec) {
2055
+ command.description(operationSpec.cli.description).addOption(
2056
+ new Option(
2057
+ "--org <org>",
2058
+ "Organization ID or exact organization name to use."
2059
+ ).helpGroup("Shared options:")
2060
+ ).addHelpText(
2061
+ "after",
2062
+ `
2063
+ ${formatExamples(
2064
+ "Examples",
2065
+ createOperationHelpExamples(operationSpec)
2066
+ )}`
2067
+ );
2068
+ for (const optionSpec of operationSpec.cli.options) {
2069
+ const optionFlags = optionSpec.valueType === "boolean" ? `--${optionSpec.flagName}` : `--${optionSpec.flagName} <value>`;
2070
+ const optionDescription = optionSpec.valueHint ? `${optionSpec.description} (${optionSpec.valueHint})` : optionSpec.description;
2071
+ const nextOption = new Option(optionFlags, optionDescription).helpGroup(
2072
+ operationSpec.kind === "query" ? "Query options:" : "Mutation options:"
2073
+ );
2074
+ if (optionSpec.valueType === "boolean") {
2075
+ command.addOption(nextOption);
2076
+ continue;
2077
+ }
2078
+ const parser = (rawValue) => parseCommandOptionValue(rawValue, optionSpec);
2079
+ nextOption.argParser(parser);
2080
+ if (optionSpec.required) {
2081
+ nextOption.makeOptionMandatory(true);
2082
+ command.addOption(nextOption);
2083
+ continue;
2084
+ }
2085
+ command.addOption(nextOption);
2086
+ }
2087
+ command.action(async function() {
2088
+ await handleOperationCommand(
2089
+ dependencies,
2090
+ operationSpec,
2091
+ this.optsWithGlobals()
2092
+ );
2093
+ });
2094
+ }
2095
+ function createConsoleIo() {
2096
+ return {
2097
+ error(message) {
2098
+ process.stderr.write(`${message}
2099
+ `);
2100
+ },
2101
+ info(message) {
2102
+ process.stdout.write(`${message}
2103
+ `);
2104
+ }
2105
+ };
2106
+ }
2107
+ async function openExternalUrl(url) {
2108
+ const command = resolveOpenCommand(url);
2109
+ const childProcess = spawn(command.executable, command.args, {
2110
+ detached: true,
2111
+ stdio: "ignore"
2112
+ });
2113
+ await new Promise((resolve, reject) => {
2114
+ childProcess.on("error", reject);
2115
+ childProcess.on("spawn", () => {
2116
+ childProcess.unref();
2117
+ resolve();
2118
+ });
2119
+ });
2120
+ }
2121
+ function resolveOpenCommand(url) {
2122
+ if (process.platform === "darwin") {
2123
+ return { executable: "open", args: [url] };
2124
+ }
2125
+ if (process.platform === "win32") {
2126
+ return { executable: "cmd", args: ["/c", "start", "", url] };
2127
+ }
2128
+ return { executable: "xdg-open", args: [url] };
2129
+ }
2130
+ function createDefaultDependencies() {
2131
+ const runtimeConfig = getRuntimeConfig();
2132
+ return {
2133
+ apiClient: createMagicalApiClient({
2134
+ apiBaseUrl: runtimeConfig.magicalApiUrl
2135
+ }),
2136
+ authClient: createWorkosAuthClient({
2137
+ workosApiUrl: runtimeConfig.workosApiUrl
2138
+ }),
2139
+ io: createConsoleIo(),
2140
+ openExternalUrl,
2141
+ refreshTokenStore: createKeytarRefreshTokenStore(
2142
+ runtimeConfig.keychainServiceName
2143
+ ),
2144
+ runtimeConfig,
2145
+ stateStore: createFileStateStore(runtimeConfig.stateFilePath)
2146
+ };
2147
+ }
2148
+ function buildProgram(dependencies) {
2149
+ const program2 = configureCommandPresentation(
2150
+ new Command().name("mgcl").description("Run Magical shared MCP operations from the command line.")
2151
+ ).addOption(new Option("--json", "Render JSON output.")).showHelpAfterError().showSuggestionAfterError().addHelpText(
2152
+ "after",
2153
+ `
2154
+ ${formatExamples("Quick start", [
2155
+ "mgcl auth login",
2156
+ "mgcl org list",
2157
+ "mgcl automations list --limit 10",
2158
+ "mgcl automation-runs get --id <run-id>"
2159
+ ])}
2160
+
2161
+ ${formatOutputModesNote()}`
2162
+ );
2163
+ const authCommand = configureCommandPresentation(
2164
+ new Command("auth").description("Manage CLI authentication.")
2165
+ ).helpGroup("Workspace:").addHelpText(
2166
+ "after",
2167
+ `
2168
+ ${formatExamples("Examples", [
2169
+ "mgcl auth login",
2170
+ "mgcl auth logout"
2171
+ ])}`
2172
+ );
2173
+ authCommand.command("login").description("Authenticate with Magical and load organization memberships.").action(async function() {
2174
+ await handleAuthLogin(dependencies, this.optsWithGlobals());
2175
+ });
2176
+ authCommand.command("logout").description("Remove local CLI auth state.").action(async function() {
2177
+ await handleAuthLogout(dependencies, this.optsWithGlobals());
2178
+ });
2179
+ program2.addCommand(authCommand);
2180
+ const orgCommand = configureCommandPresentation(
2181
+ new Command("org").description("Manage organization selection.")
2182
+ ).helpGroup("Workspace:").addHelpText(
2183
+ "after",
2184
+ `
2185
+ ${formatExamples("Examples", [
2186
+ "mgcl org list",
2187
+ "mgcl org use <org>"
2188
+ ])}`
2189
+ );
2190
+ orgCommand.command("list").description("List organizations available to the saved identity.").action(async function() {
2191
+ await handleOrgList(dependencies, this.optsWithGlobals());
2192
+ });
2193
+ orgCommand.command("use <org>").description("Set the default organization by ID or exact name.").action(async function(organizationSelector) {
2194
+ await handleOrgUse(
2195
+ dependencies,
2196
+ organizationSelector,
2197
+ this.optsWithGlobals()
2198
+ );
2199
+ });
2200
+ program2.addCommand(orgCommand);
2201
+ const operationalCommandGroups = /* @__PURE__ */ new Map();
2202
+ for (const operationSpec of operationSpecs) {
2203
+ addRegistryOperationCommand({
2204
+ program: program2,
2205
+ commandGroups: operationalCommandGroups,
2206
+ dependencies,
2207
+ operationSpec
2208
+ });
2209
+ }
2210
+ return program2;
2211
+ }
2212
+
2213
+ // src/index.ts
2214
+ var program = buildProgram(createDefaultDependencies());
2215
+ program.parseAsync(process.argv).catch((error) => {
2216
+ process.stderr.write(`${formatCliError(getErrorMessage(error))}
2217
+ `);
2218
+ process.exitCode = 1;
2219
+ });
2220
+ //# sourceMappingURL=index.js.map