@griffin-app/griffin-cli 1.0.25 → 1.0.28

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.
Files changed (49) hide show
  1. package/README.md +122 -367
  2. package/dist/cli.js +48 -29
  3. package/dist/commands/env.d.ts +14 -3
  4. package/dist/commands/env.js +24 -22
  5. package/dist/commands/generate-key.d.ts +5 -6
  6. package/dist/commands/generate-key.js +20 -26
  7. package/dist/commands/hub/apply.d.ts +1 -0
  8. package/dist/commands/hub/apply.js +44 -29
  9. package/dist/commands/hub/connect.d.ts +2 -1
  10. package/dist/commands/hub/connect.js +18 -22
  11. package/dist/commands/hub/destroy.d.ts +2 -1
  12. package/dist/commands/hub/destroy.js +123 -119
  13. package/dist/commands/hub/integrations.d.ts +4 -1
  14. package/dist/commands/hub/integrations.js +160 -141
  15. package/dist/commands/hub/login.d.ts +13 -1
  16. package/dist/commands/hub/login.js +81 -15
  17. package/dist/commands/hub/logout.d.ts +2 -1
  18. package/dist/commands/hub/logout.js +7 -12
  19. package/dist/commands/hub/metrics.js +29 -27
  20. package/dist/commands/hub/monitor.js +16 -16
  21. package/dist/commands/hub/notifications.d.ts +3 -6
  22. package/dist/commands/hub/notifications.js +52 -38
  23. package/dist/commands/hub/run.d.ts +1 -0
  24. package/dist/commands/hub/run.js +114 -87
  25. package/dist/commands/hub/runs.d.ts +2 -0
  26. package/dist/commands/hub/runs.js +47 -45
  27. package/dist/commands/hub/secrets.d.ts +5 -5
  28. package/dist/commands/hub/secrets.js +80 -72
  29. package/dist/commands/hub/status.d.ts +4 -1
  30. package/dist/commands/hub/status.js +15 -9
  31. package/dist/commands/init.d.ts +2 -1
  32. package/dist/commands/init.js +31 -25
  33. package/dist/commands/local/run.d.ts +1 -0
  34. package/dist/commands/local/run.js +34 -26
  35. package/dist/commands/validate.d.ts +4 -1
  36. package/dist/commands/validate.js +23 -14
  37. package/dist/commands/variables.d.ts +17 -3
  38. package/dist/commands/variables.js +29 -28
  39. package/dist/core/credentials.d.ts +15 -0
  40. package/dist/core/credentials.js +37 -0
  41. package/dist/core/variables.js +4 -0
  42. package/dist/monitor-runner.js +0 -12
  43. package/dist/utils/command-wrapper.d.ts +9 -0
  44. package/dist/utils/command-wrapper.js +23 -0
  45. package/dist/utils/output.d.ts +66 -0
  46. package/dist/utils/output.js +202 -0
  47. package/dist/utils/sdk-error.d.ts +6 -1
  48. package/dist/utils/sdk-error.js +107 -77
  49. package/package.json +3 -3
package/dist/cli.js CHANGED
@@ -24,33 +24,34 @@ const program = new Command();
24
24
  program
25
25
  .name("griffin")
26
26
  .description("Griffin CLI - API Monitoring as Code")
27
- .version("1.0.0");
27
+ .version("1.0.0")
28
+ .option("--json", "Output as JSON (for scripts and AI agents)");
28
29
  // Top-level commands
29
30
  program
30
31
  .command("init")
31
32
  .description("Initialize griffin in the current directory")
32
33
  .option("--project <name>", "Project ID (defaults to package.json name or directory name)")
33
34
  .action(async (options) => {
34
- await executeInit(options);
35
+ await executeInit({ ...options, json: program.opts().json });
35
36
  });
36
37
  program
37
38
  .command("validate")
38
39
  .description("Check monitor files for errors")
39
40
  .action(async () => {
40
- await executeValidate();
41
+ await executeValidate({ json: program.opts().json });
41
42
  });
42
43
  program
43
44
  .command("status")
44
45
  .description("Show project and connection status")
45
46
  .action(async () => {
46
- await executeStatus();
47
+ await executeStatus({ json: program.opts().json });
47
48
  });
48
49
  program
49
50
  .command("test")
50
51
  .description("Run monitors locally")
51
52
  .option("--env <name>", "Environment name", "default")
52
53
  .action(async (options) => {
53
- await executeRunLocal({ env: options.env });
54
+ await executeRunLocal({ env: options.env, json: program.opts().json });
54
55
  });
55
56
  program
56
57
  .command("plan")
@@ -58,7 +59,7 @@ program
58
59
  .option("--env <name>", "Environment name", "default")
59
60
  .option("--json", "Output in JSON format")
60
61
  .action(async (options) => {
61
- await executePlan({ ...options, env: options.env });
62
+ await executePlan({ ...options, env: options.env, json: program.opts().json ?? options.json });
62
63
  });
63
64
  program
64
65
  .command("apply")
@@ -68,7 +69,7 @@ program
68
69
  .option("--dry-run", "Show what would be done without making changes")
69
70
  .option("--prune", "Delete monitors on hub that don't exist locally")
70
71
  .action(async (options) => {
71
- await executeApply({ ...options, env: options.env });
72
+ await executeApply({ ...options, env: options.env, json: program.opts().json });
72
73
  });
73
74
  program
74
75
  .command("runs")
@@ -81,6 +82,7 @@ program
81
82
  ...options,
82
83
  env: options.env,
83
84
  limit: parseInt(options.limit, 10),
85
+ json: program.opts().json,
84
86
  });
85
87
  });
86
88
  program
@@ -91,7 +93,7 @@ program
91
93
  .option("--wait", "Wait for run to complete")
92
94
  .option("--force", "Run even if local monitor differs from hub")
93
95
  .action(async (options) => {
94
- await executeRun({ ...options, env: options.env });
96
+ await executeRun({ ...options, env: options.env, json: program.opts().json });
95
97
  });
96
98
  program
97
99
  .command("metrics")
@@ -103,7 +105,7 @@ program
103
105
  await executeMetrics({
104
106
  period: options.period,
105
107
  environment: options.env,
106
- json: options.json,
108
+ json: program.opts().json ?? options.json,
107
109
  });
108
110
  });
109
111
  program
@@ -114,21 +116,27 @@ program
114
116
  .option("--auto-approve", "Skip confirmation prompt")
115
117
  .option("--dry-run", "Show what would be destroyed without making changes")
116
118
  .action(async (env, options) => {
117
- await executeDestroy({ ...options, env });
119
+ await executeDestroy({ ...options, env, json: program.opts().json });
118
120
  });
119
121
  // Auth command group
120
122
  const auth = program.command("auth").description("Manage authentication");
121
123
  auth
122
124
  .command("login")
123
125
  .description("Authenticate with Griffin Cloud")
124
- .action(async () => {
125
- await executeLogin();
126
+ .option("--no-poll", "Only output auth URL and exit; run again with --poll after completing authorization")
127
+ .option("--poll", "Poll for completion after user has authorized in browser (use after --no-poll)")
128
+ .action(async (options) => {
129
+ await executeLogin({
130
+ json: program.opts().json,
131
+ noPoll: options.noPoll,
132
+ poll: options.poll,
133
+ });
126
134
  });
127
135
  auth
128
136
  .command("logout")
129
137
  .description("Clear stored credentials")
130
138
  .action(async (options) => {
131
- await executeLogout(options);
139
+ await executeLogout({ ...options, json: program.opts().json });
132
140
  });
133
141
  auth
134
142
  .command("connect")
@@ -136,13 +144,13 @@ auth
136
144
  .requiredOption("--url <url>", "Hub URL")
137
145
  .requiredOption("--token <token>", "API authentication token")
138
146
  .action(async (options) => {
139
- await executeConnect(options);
147
+ await executeConnect({ ...options, json: program.opts().json });
140
148
  });
141
149
  auth
142
150
  .command("generate-key")
143
151
  .description("Generate an API key")
144
152
  .action(async () => {
145
- await executeGenerateKey();
153
+ await executeGenerateKey({ json: program.opts().json });
146
154
  });
147
155
  // Environment command group
148
156
  const env = program.command("env").description("Manage environments");
@@ -150,19 +158,19 @@ env
150
158
  .command("list")
151
159
  .description("List configured environments")
152
160
  .action(async () => {
153
- await executeEnvList();
161
+ await executeEnvList({ json: program.opts().json });
154
162
  });
155
163
  env
156
164
  .command("add <name>")
157
165
  .description("Add an environment")
158
166
  .action(async (name) => {
159
- await executeEnvAdd(name);
167
+ await executeEnvAdd({ name, json: program.opts().json });
160
168
  });
161
169
  env
162
170
  .command("remove <name>")
163
171
  .description("Remove an environment")
164
172
  .action(async (name) => {
165
- await executeEnvRemove(name);
173
+ await executeEnvRemove({ name, json: program.opts().json });
166
174
  });
167
175
  // Variables command group
168
176
  const variables = program
@@ -173,21 +181,29 @@ variables
173
181
  .description("List variables for an environment")
174
182
  .option("--env <name>", "Environment name", "default")
175
183
  .action(async (options) => {
176
- await executeVariablesList(options.env);
184
+ await executeVariablesList({ env: options.env, json: program.opts().json });
177
185
  });
178
186
  variables
179
187
  .command("add <keyValue>")
180
188
  .description("Set a variable (KEY=VALUE)")
181
189
  .option("--env <name>", "Environment name", "default")
182
190
  .action(async (keyValue, options) => {
183
- await executeVariablesAdd(keyValue, options.env);
191
+ await executeVariablesAdd({
192
+ keyValue,
193
+ env: options.env,
194
+ json: program.opts().json,
195
+ });
184
196
  });
185
197
  variables
186
198
  .command("remove <key>")
187
199
  .description("Remove a variable")
188
200
  .option("--env <name>", "Environment name", "default")
189
201
  .action(async (key, options) => {
190
- await executeVariablesRemove(key, options.env);
202
+ await executeVariablesRemove({
203
+ key,
204
+ env: options.env,
205
+ json: program.opts().json,
206
+ });
191
207
  });
192
208
  // Secrets command group
193
209
  const secrets = program
@@ -201,7 +217,7 @@ secrets
201
217
  .action(async (options) => {
202
218
  await executeSecretsList({
203
219
  environment: options.env,
204
- json: options.json,
220
+ json: program.opts().json ?? options.json,
205
221
  });
206
222
  });
207
223
  secrets
@@ -214,6 +230,7 @@ secrets
214
230
  name,
215
231
  environment: options.env,
216
232
  value: options.value,
233
+ json: program.opts().json,
217
234
  });
218
235
  });
219
236
  secrets
@@ -225,7 +242,7 @@ secrets
225
242
  await executeSecretsGet({
226
243
  name,
227
244
  environment: options.env,
228
- json: options.json,
245
+ json: program.opts().json ?? options.json,
229
246
  });
230
247
  });
231
248
  secrets
@@ -238,6 +255,7 @@ secrets
238
255
  name,
239
256
  environment: options.env,
240
257
  force: options.force,
258
+ json: program.opts().json,
241
259
  });
242
260
  });
243
261
  // Integrations command group
@@ -258,7 +276,7 @@ integrations
258
276
  provider: options.provider,
259
277
  environment: options.env,
260
278
  enabled: options.enabled ? true : undefined,
261
- json: options.json,
279
+ json: program.opts().json ?? options.json,
262
280
  });
263
281
  });
264
282
  integrations
@@ -266,7 +284,7 @@ integrations
266
284
  .description("Show integration details")
267
285
  .option("--json", "Output as JSON")
268
286
  .action(async (id, options) => {
269
- await executeIntegrationsShow({ id, json: options.json });
287
+ await executeIntegrationsShow({ id, json: program.opts().json ?? options.json });
270
288
  });
271
289
  integrations
272
290
  .command("connect [type] [provider]")
@@ -280,7 +298,7 @@ integrations
280
298
  provider: provider ?? "",
281
299
  name: options.name,
282
300
  environment: options.env,
283
- json: options.json,
301
+ json: program.opts().json ?? options.json,
284
302
  });
285
303
  });
286
304
  integrations
@@ -296,7 +314,7 @@ integrations
296
314
  name: options.name,
297
315
  environment: options.env,
298
316
  enabled: options.enabled,
299
- json: options.json,
317
+ json: program.opts().json ?? options.json,
300
318
  });
301
319
  });
302
320
  integrations
@@ -304,7 +322,7 @@ integrations
304
322
  .description("Remove an integration")
305
323
  .option("--force", "Skip confirmation")
306
324
  .action(async (id, options) => {
307
- await executeIntegrationsRemove({ id, force: options.force });
325
+ await executeIntegrationsRemove({ id, force: options.force, json: program.opts().json });
308
326
  });
309
327
  // Notifications command group
310
328
  const notifications = program
@@ -320,7 +338,7 @@ notifications
320
338
  await executeNotificationsList({
321
339
  monitor: options.monitor,
322
340
  enabled: options.enabled ? true : undefined,
323
- json: options.json,
341
+ json: program.opts().json ?? options.json,
324
342
  });
325
343
  });
326
344
  notifications
@@ -334,6 +352,7 @@ notifications
334
352
  integration: options.integration,
335
353
  channel: options.channel,
336
354
  toAddresses: options.toAddresses,
355
+ json: program.opts().json,
337
356
  });
338
357
  });
339
358
  // Parse arguments
@@ -1,12 +1,23 @@
1
+ export interface EnvListOptions {
2
+ json?: boolean;
3
+ }
1
4
  /**
2
5
  * List all available environments
3
6
  */
4
- export declare const executeEnvList: () => Promise<void>;
7
+ export declare const executeEnvList: (options: EnvListOptions) => Promise<void>;
8
+ export interface EnvAddOptions {
9
+ name: string;
10
+ json?: boolean;
11
+ }
5
12
  /**
6
13
  * Add a new environment
7
14
  */
8
- export declare const executeEnvAdd: (name: string) => Promise<void>;
15
+ export declare const executeEnvAdd: (options: EnvAddOptions) => Promise<void>;
16
+ export interface EnvRemoveOptions {
17
+ name: string;
18
+ json?: boolean;
19
+ }
9
20
  /**
10
21
  * Remove an environment
11
22
  */
12
- export declare const executeEnvRemove: (name: string) => Promise<void>;
23
+ export declare const executeEnvRemove: (options: EnvRemoveOptions) => Promise<void>;
@@ -1,42 +1,44 @@
1
1
  import { loadState, addEnvironment, removeEnvironment } from "../core/state.js";
2
- import { terminal } from "../utils/terminal.js";
3
- import { withCommandErrorHandler } from "../utils/command-wrapper.js";
2
+ import { createCommandHandler } from "../utils/command-wrapper.js";
4
3
  /**
5
4
  * List all available environments
6
5
  */
7
- export const executeEnvList = withCommandErrorHandler(async () => {
6
+ export const executeEnvList = createCommandHandler("env list", async (_options, output) => {
8
7
  const state = await loadState();
9
8
  const environments = Object.keys(state.environments);
9
+ output.setData({ environments });
10
10
  if (environments.length === 0) {
11
- terminal.warn("No environments configured.");
12
- terminal.blank();
13
- terminal.dim("Run 'griffin init' to set up your project.");
11
+ output.warn("No environments configured.");
12
+ output.blank();
13
+ output.dim("Run 'griffin init' to set up your project.");
14
14
  return;
15
15
  }
16
- terminal.info("Available environments:");
17
- terminal.blank();
16
+ output.info("Available environments:");
17
+ output.blank();
18
18
  for (const envName of environments) {
19
- const marker = terminal.colors.green("●");
20
- const envDisplay = terminal.colors.cyan(envName);
21
- terminal.log(` ${marker} ${envDisplay}`);
19
+ const marker = output.colors.green("●");
20
+ const envDisplay = output.colors.cyan(envName);
21
+ output.log(` ${marker} ${envDisplay}`);
22
22
  }
23
- terminal.blank();
23
+ output.blank();
24
24
  });
25
25
  /**
26
26
  * Add a new environment
27
27
  */
28
- export const executeEnvAdd = withCommandErrorHandler(async (name) => {
29
- await addEnvironment(name, {});
30
- terminal.success(`Environment '${name}' added successfully.`);
31
- terminal.blank();
32
- terminal.dim(`Add variables with: griffin variables add KEY=VALUE --env ${name}`);
33
- terminal.blank();
28
+ export const executeEnvAdd = createCommandHandler("env add", async (options, output) => {
29
+ await addEnvironment(options.name, {});
30
+ output.setData({ environment: options.name, added: true });
31
+ output.success(`Environment '${options.name}' added successfully.`);
32
+ output.blank();
33
+ output.dim(`Add variables with: griffin variables add KEY=VALUE --env ${options.name}`);
34
+ output.blank();
34
35
  });
35
36
  /**
36
37
  * Remove an environment
37
38
  */
38
- export const executeEnvRemove = withCommandErrorHandler(async (name) => {
39
- await removeEnvironment(name);
40
- terminal.success(`Environment '${name}' removed successfully.`);
41
- terminal.blank();
39
+ export const executeEnvRemove = createCommandHandler("env remove", async (options, output) => {
40
+ await removeEnvironment(options.name);
41
+ output.setData({ environment: options.name, removed: true });
42
+ output.success(`Environment '${options.name}' removed successfully.`);
43
+ output.blank();
42
44
  });
@@ -1,9 +1,8 @@
1
+ export interface GenerateKeyOptions {
2
+ json?: boolean;
3
+ }
1
4
  /**
2
5
  * Generate a cryptographically secure API key for griffin-runner authentication.
3
- *
4
- * The key format is: grfn_sk_<48-character-hex-string>
5
- * - grfn: Griffin prefix
6
- * - sk: Secret Key
7
- * - 48 hex chars: 24 random bytes = 192 bits of entropy
6
+ * Format: grfn_sk_<48-character-hex-string>
8
7
  */
9
- export declare function executeGenerateKey(): Promise<void>;
8
+ export declare const executeGenerateKey: (options: GenerateKeyOptions) => Promise<void>;
@@ -1,33 +1,27 @@
1
1
  import { randomBytes } from "node:crypto";
2
- import { terminal } from "../utils/terminal.js";
2
+ import { createCommandHandler } from "../utils/command-wrapper.js";
3
3
  /**
4
4
  * Generate a cryptographically secure API key for griffin-runner authentication.
5
- *
6
- * The key format is: grfn_sk_<48-character-hex-string>
7
- * - grfn: Griffin prefix
8
- * - sk: Secret Key
9
- * - 48 hex chars: 24 random bytes = 192 bits of entropy
5
+ * Format: grfn_sk_<48-character-hex-string>
10
6
  */
11
- export async function executeGenerateKey() {
12
- // Generate 24 random bytes (192 bits of entropy)
7
+ export const executeGenerateKey = createCommandHandler("generate-key", async (_options, output) => {
13
8
  const keyBytes = randomBytes(24);
14
- // Convert to hex string
15
9
  const keySecret = keyBytes.toString("hex");
16
- // Add prefix following the pattern: grfn_sk_<secret>
17
10
  const apiKey = `grfn_sk_${keySecret}`;
18
- terminal.blank();
19
- terminal.success("Generated API key:");
20
- terminal.blank();
21
- terminal.log(` ${terminal.colors.cyan(apiKey)}`);
22
- terminal.blank();
23
- terminal.warn("Store this key securely - it cannot be retrieved later.");
24
- terminal.blank();
25
- terminal.info("To use this key:");
26
- terminal.dim(" 1. Add it to your runner's AUTH_API_KEYS environment variable:");
27
- terminal.dim(` AUTH_API_KEYS=${apiKey}`);
28
- terminal.dim(" 2. Or add it to your .griffinrc.json:");
29
- terminal.dim(` { "runner": { "apiToken": "${apiKey}" } }`);
30
- terminal.dim(" 3. Or pass it via environment variable:");
31
- terminal.dim(` GRIFFIN_API_TOKEN=${apiKey}`);
32
- terminal.blank();
33
- }
11
+ output.setData({ apiKey });
12
+ output.blank();
13
+ output.success("Generated API key:");
14
+ output.blank();
15
+ output.log(` ${output.colors.cyan(apiKey)}`);
16
+ output.blank();
17
+ output.warn("Store this key securely - it cannot be retrieved later.");
18
+ output.blank();
19
+ output.info("To use this key:");
20
+ output.dim(" 1. Add it to your runner's AUTH_API_KEYS environment variable:");
21
+ output.dim(` AUTH_API_KEYS=${apiKey}`);
22
+ output.dim(" 2. Or add it to your .griffinrc.json:");
23
+ output.dim(` { "runner": { "apiToken": "${apiKey}" } }`);
24
+ output.dim(" 3. Or pass it via environment variable:");
25
+ output.dim(` GRIFFIN_API_TOKEN=${apiKey}`);
26
+ output.blank();
27
+ });
@@ -3,6 +3,7 @@ export interface ApplyOptions {
3
3
  dryRun?: boolean;
4
4
  env: string;
5
5
  prune?: boolean;
6
+ json?: boolean;
6
7
  }
7
8
  /**
8
9
  * Apply changes to the hub
@@ -2,53 +2,54 @@ import { resolveEnvironment } from "../../core/state.js";
2
2
  import { computeDiff, formatDiff } from "../../core/diff.js";
3
3
  import { applyDiff, formatApplyResult } from "../../core/apply.js";
4
4
  import { createSdkAndState } from "../../core/sdk.js";
5
- import { terminal } from "../../utils/terminal.js";
6
- import { withCommandErrorHandler } from "../../utils/command-wrapper.js";
5
+ import { createCommandHandler } from "../../utils/command-wrapper.js";
7
6
  import { discoverLocalMonitors, fetchRemoteMonitors, resolveLocalMonitors, } from "../../core/monitor-helpers.js";
8
7
  /**
9
8
  * Apply changes to the hub
10
9
  */
11
- export const executeApply = withCommandErrorHandler(async (options) => {
10
+ export const executeApply = createCommandHandler("apply", async (options, output) => {
12
11
  const { sdk, state } = await createSdkAndState();
13
- // Resolve environment
14
12
  const envName = await resolveEnvironment(options.env);
15
- terminal.info(`Applying to ${terminal.colors.cyan(envName)} environment`);
16
- terminal.blank();
17
- // Discover local monitors
13
+ output.info(`Applying to ${output.colors.cyan(envName)} environment`);
14
+ output.blank();
18
15
  const { monitors } = await discoverLocalMonitors(state);
19
- // Fetch remote monitors for this project + environment
20
16
  const remoteMonitors = await fetchRemoteMonitors(sdk, state.projectId, envName);
21
- // Load variables and resolve local monitors before computing diff
22
17
  const resolvedMonitors = await resolveLocalMonitors(monitors, state.projectId, envName);
23
- // Compute diff (include deletions if --prune)
24
18
  const diff = computeDiff(resolvedMonitors, remoteMonitors, {
25
19
  includeDeletions: options.prune || false,
26
20
  });
27
- // Show monitor
28
- terminal.blank();
29
- terminal.log(formatDiff(diff));
30
- terminal.blank();
31
- // Check if there are changes
32
- if (diff.summary.creates + diff.summary.updates + diff.summary.deletes ===
33
- 0) {
34
- terminal.success("No changes to apply.");
21
+ output.blank();
22
+ output.log(formatDiff(diff));
23
+ output.blank();
24
+ const changeCount = diff.summary.creates + diff.summary.updates + diff.summary.deletes;
25
+ if (changeCount === 0) {
26
+ output.success("No changes to apply.");
27
+ output.setData({
28
+ summary: diff.summary,
29
+ applied: [],
30
+ success: true,
31
+ message: "No changes to apply.",
32
+ });
35
33
  return;
36
34
  }
37
- // Show deletions warning if --prune
38
35
  if (options.prune && diff.summary.deletes > 0) {
39
- terminal.warn(`--prune will DELETE ${diff.summary.deletes} monitor(s) from the hub`);
40
- terminal.blank();
36
+ output.warn(`--prune will DELETE ${diff.summary.deletes} monitor(s) from the hub`);
37
+ output.blank();
41
38
  }
42
- // Ask for confirmation unless auto-approved
43
39
  if (!options.autoApprove && !options.dryRun) {
44
- const confirmed = await terminal.confirm("Do you want to perform these actions?");
40
+ const confirmed = await output.confirm("Do you want to perform these actions?");
45
41
  if (!confirmed) {
46
- terminal.warn("Apply cancelled.");
42
+ output.warn("Apply cancelled.");
43
+ output.setData({
44
+ summary: diff.summary,
45
+ applied: [],
46
+ success: false,
47
+ message: "Apply cancelled.",
48
+ });
47
49
  return;
48
50
  }
49
51
  }
50
- // Apply changes with environment injection
51
- const applySpinner = terminal.spinner("Applying changes...").start();
52
+ const applySpinner = output.spinner("Applying changes...").start();
52
53
  const result = await applyDiff(diff, sdk, {
53
54
  dryRun: options.dryRun,
54
55
  });
@@ -58,9 +59,23 @@ export const executeApply = withCommandErrorHandler(async (options) => {
58
59
  else {
59
60
  applySpinner.fail("Apply failed");
60
61
  }
61
- terminal.blank();
62
- terminal.log(formatApplyResult(result));
62
+ output.setData({
63
+ summary: diff.summary,
64
+ applied: result.applied.map((a) => ({
65
+ type: a.type,
66
+ monitorName: a.monitorName,
67
+ success: a.success,
68
+ error: a.error,
69
+ })),
70
+ errors: result.errors.map((e) => ({
71
+ action: e.action.type,
72
+ error: e.error.message,
73
+ })),
74
+ success: result.success,
75
+ });
76
+ output.blank();
77
+ output.log(formatApplyResult(result));
63
78
  if (!result.success) {
64
- terminal.exit(1);
79
+ output.exit(1);
65
80
  }
66
81
  });
@@ -1,8 +1,9 @@
1
1
  export interface ConnectOptions {
2
2
  url: string;
3
3
  token: string;
4
+ json?: boolean;
4
5
  }
5
6
  /**
6
7
  * Configure hub connection settings
7
8
  */
8
- export declare function executeConnect(options: ConnectOptions): Promise<void>;
9
+ export declare const executeConnect: (options: ConnectOptions) => Promise<void>;
@@ -1,28 +1,24 @@
1
1
  import { randomBytes } from "crypto";
2
2
  import { loadState, saveState } from "../../core/state.js";
3
3
  import { saveHubCredentials } from "../../core/credentials.js";
4
- import { terminal } from "../../utils/terminal.js";
4
+ import { createCommandHandler } from "../../utils/command-wrapper.js";
5
5
  /**
6
6
  * Configure hub connection settings
7
7
  */
8
- export async function executeConnect(options) {
9
- try {
10
- const state = await loadState();
11
- // Save token to user-level credentials file if provided
12
- await saveHubCredentials(options.token);
13
- // Update hub config in project state (without token)
14
- state.hub = {
15
- baseUrl: options.url,
16
- clientId: randomBytes(16).toString("hex"),
17
- };
18
- await saveState(state);
19
- terminal.success("Hub connection configured");
20
- terminal.log(` URL: ${terminal.colors.cyan(options.url)}`);
21
- terminal.log(` API Token: ${terminal.colors.dim("***")} (saved to user credentials)`);
22
- terminal.blank();
23
- }
24
- catch (error) {
25
- terminal.error(error.message);
26
- terminal.exit(1);
27
- }
28
- }
8
+ export const executeConnect = createCommandHandler("auth connect", async (options, output) => {
9
+ const state = await loadState();
10
+ await saveHubCredentials(options.token);
11
+ state.hub = {
12
+ baseUrl: options.url,
13
+ clientId: randomBytes(16).toString("hex"),
14
+ };
15
+ await saveState(state);
16
+ output.setData({
17
+ url: options.url,
18
+ configured: true,
19
+ });
20
+ output.success("Hub connection configured");
21
+ output.log(` URL: ${output.colors.cyan(options.url)}`);
22
+ output.log(` API Token: ${output.colors.dim("***")} (saved to user credentials)`);
23
+ output.blank();
24
+ });
@@ -3,8 +3,9 @@ export interface DestroyOptions {
3
3
  dryRun?: boolean;
4
4
  env: string;
5
5
  monitor?: string;
6
+ json?: boolean;
6
7
  }
7
8
  /**
8
9
  * Destroy monitors on the hub
9
10
  */
10
- export declare function executeDestroy(options: DestroyOptions): Promise<void>;
11
+ export declare const executeDestroy: (options: DestroyOptions) => Promise<void>;