@griffin-app/griffin-cli 1.0.18 → 1.0.20

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/dist/cli.js +183 -196
  2. package/dist/commands/env.d.ts +3 -3
  3. package/dist/commands/env.js +28 -45
  4. package/dist/commands/hub/apply.d.ts +1 -1
  5. package/dist/commands/hub/apply.js +54 -95
  6. package/dist/commands/hub/connect.d.ts +1 -1
  7. package/dist/commands/hub/connect.js +2 -6
  8. package/dist/commands/hub/destroy.d.ts +10 -0
  9. package/dist/commands/hub/destroy.js +141 -0
  10. package/dist/commands/hub/integrations.d.ts +9 -9
  11. package/dist/commands/hub/integrations.js +104 -115
  12. package/dist/commands/hub/login.js +3 -0
  13. package/dist/commands/hub/metrics.d.ts +2 -2
  14. package/dist/commands/hub/metrics.js +41 -69
  15. package/dist/commands/hub/monitor.d.ts +1 -1
  16. package/dist/commands/hub/monitor.js +30 -70
  17. package/dist/commands/hub/notifications.d.ts +3 -3
  18. package/dist/commands/hub/notifications.js +74 -105
  19. package/dist/commands/hub/run.d.ts +1 -1
  20. package/dist/commands/hub/run.js +118 -144
  21. package/dist/commands/hub/runs.d.ts +1 -1
  22. package/dist/commands/hub/runs.js +60 -81
  23. package/dist/commands/hub/secrets.d.ts +8 -8
  24. package/dist/commands/hub/secrets.js +88 -148
  25. package/dist/commands/hub/status.d.ts +1 -1
  26. package/dist/commands/hub/status.js +13 -26
  27. package/dist/commands/init.js +3 -3
  28. package/dist/commands/local/run.d.ts +2 -2
  29. package/dist/commands/local/run.js +37 -42
  30. package/dist/commands/validate.d.ts +1 -1
  31. package/dist/commands/validate.js +19 -39
  32. package/dist/commands/variables.d.ts +3 -3
  33. package/dist/commands/variables.js +51 -69
  34. package/dist/core/discovery.js +0 -2
  35. package/dist/core/monitor-helpers.d.ts +19 -0
  36. package/dist/core/monitor-helpers.js +48 -0
  37. package/dist/core/sdk.d.ts +5 -0
  38. package/dist/core/sdk.js +11 -0
  39. package/dist/core/state.d.ts +1 -1
  40. package/dist/core/state.js +0 -3
  41. package/dist/index.d.ts +1 -0
  42. package/dist/index.js +1 -0
  43. package/dist/schemas/state.js +1 -1
  44. package/dist/utils/command-wrapper.d.ts +10 -0
  45. package/dist/utils/command-wrapper.js +27 -0
  46. package/dist/utils/sdk-error.js +1 -1
  47. package/dist/utils/terminal.d.ts +4 -0
  48. package/dist/utils/terminal.js +17 -0
  49. package/package.json +4 -4
@@ -1,25 +1,25 @@
1
- import { exec } from "node:child_process";
2
- import { platform } from "node:os";
3
- import { loadState } from "../../core/state.js";
1
+ import { loadState, resolveEnvironment } from "../../core/state.js";
4
2
  import { terminal } from "../../utils/terminal.js";
5
3
  import { getProvider, getProviders, getCategories, getAllProvidersByCategory, } from "../../providers/registry.js";
6
- import { createSdkWithCredentials } from "../../core/sdk.js";
7
- export async function executeIntegrationsList(options) {
8
- const state = await loadState();
9
- const sdk = await createSdkWithCredentials(state.hub.baseUrl);
10
- const result = await sdk.getIntegrations({
11
- query: {
12
- category: options.category,
13
- provider: options.provider,
14
- environment: options.environment,
15
- enabled: options.enabled,
16
- },
17
- });
18
- const list = result.data?.data ?? [];
19
- if (options.json) {
20
- terminal.log(JSON.stringify(list, null, 2));
4
+ import { createSdkFromState, createSdkWithCredentials, } from "../../core/sdk.js";
5
+ import { withSDKErrorHandling } from "../../utils/sdk-error.js";
6
+ import { withCommandErrorHandler, outputJsonOrContinue, } from "../../utils/command-wrapper.js";
7
+ export const executeIntegrationsList = withCommandErrorHandler(async (options) => {
8
+ const sdk = await createSdkFromState();
9
+ const env = await resolveEnvironment(options.environment);
10
+ const response = await withSDKErrorHandling(async () => {
11
+ return sdk.getIntegrations({
12
+ query: {
13
+ category: options.category,
14
+ provider: options.provider,
15
+ environment: env,
16
+ enabled: options.enabled,
17
+ },
18
+ });
19
+ }, "Failed to list integrations");
20
+ const list = response?.data?.data ?? [];
21
+ if (outputJsonOrContinue(list, options.json))
21
22
  return;
22
- }
23
23
  if (list.length === 0) {
24
24
  terminal.info("No integrations found.");
25
25
  return;
@@ -40,22 +40,21 @@ export async function executeIntegrationsList(options) {
40
40
  ]);
41
41
  }
42
42
  terminal.log(table.toString());
43
- }
44
- export async function executeIntegrationsShow(options) {
45
- const state = await loadState();
46
- const sdk = await createSdkWithCredentials(state.hub.baseUrl);
47
- const result = await sdk.getIntegrationsById({
48
- path: { id: options.id },
49
- });
50
- const integration = result.data?.data;
43
+ });
44
+ export const executeIntegrationsShow = withCommandErrorHandler(async (options) => {
45
+ const sdk = await createSdkFromState();
46
+ const response = await withSDKErrorHandling(async () => {
47
+ return sdk.getIntegrationsById({
48
+ path: { id: options.id },
49
+ });
50
+ }, "Failed to show integration");
51
+ const integration = response?.data?.data;
51
52
  if (!integration) {
52
53
  terminal.error("Integration not found.");
53
- process.exit(1);
54
+ terminal.exit(1);
54
55
  }
55
- if (options.json) {
56
- terminal.log(JSON.stringify(integration, null, 2));
56
+ if (outputJsonOrContinue(integration, options.json))
57
57
  return;
58
- }
59
58
  terminal.info(integration.name);
60
59
  terminal.dim(`ID: ${integration.id}`);
61
60
  terminal.log(`Category: ${integration.category}`);
@@ -66,90 +65,74 @@ export async function executeIntegrationsShow(options) {
66
65
  if (Object.keys(integration.config).length > 0) {
67
66
  terminal.log("Config: " + JSON.stringify(integration.config));
68
67
  }
69
- }
70
- export async function executeIntegrationsAdd(options) {
68
+ });
69
+ export const executeIntegrationsAdd = withCommandErrorHandler(async (options) => {
70
+ const env = await resolveEnvironment(options.environment);
71
+ const sdk = await createSdkFromState();
71
72
  const body = {
72
73
  category: options.category,
73
74
  provider: options.provider,
74
75
  name: options.name,
75
76
  config: options.config ?? {},
76
- environment: options.environment ?? null,
77
+ environment: env,
77
78
  enabled: options.enabled ?? true,
78
79
  };
79
- const state = await loadState();
80
- const sdk = await createSdkWithCredentials(state.hub.baseUrl);
81
- const result = await sdk.postIntegrations({
82
- body,
83
- });
84
- const integration = result.data?.data;
80
+ const response = await withSDKErrorHandling(async () => {
81
+ return sdk.postIntegrations({
82
+ body,
83
+ });
84
+ }, "Failed to add integration");
85
+ const integration = response?.data?.data;
85
86
  if (!integration) {
86
- terminal.error("Integration not found.");
87
- process.exit(1);
88
- }
89
- if (options.json) {
90
- terminal.log(JSON.stringify(integration, null, 2));
87
+ terminal.error("Failed to add integration.");
91
88
  return;
92
89
  }
93
- terminal.info(`Created integration ${integration.name} (${integration.id})`);
94
- }
95
- export async function executeIntegrationsUpdate(options) {
96
- const body = {};
97
- //if (options.name !== undefined) body.name = options.name;
98
- //if (options.config !== undefined) body.config = options.config;
99
- //if (options.environment !== undefined) body.environment = options.environment;
100
- //if (options.enabled !== undefined) body.enabled = options.enabled;
101
- const state = await loadState();
102
- const sdk = await createSdkWithCredentials(state.hub.baseUrl);
103
- const result = await sdk.patchIntegrationsById({
104
- path: { id: options.id },
105
- body: {
106
- name: options.name,
107
- config: options.config,
108
- environment: options.environment,
109
- enabled: options.enabled,
110
- },
111
- });
112
- const integration = result.data?.data;
90
+ terminal.success(`Created integration ${integration.name} (${integration.id})`);
91
+ if (outputJsonOrContinue(integration, options.json))
92
+ return;
93
+ });
94
+ export const executeIntegrationsUpdate = withCommandErrorHandler(async (options) => {
95
+ const env = await resolveEnvironment(options.environment);
96
+ const sdk = await createSdkFromState();
97
+ const response = await withSDKErrorHandling(async () => {
98
+ return sdk.patchIntegrationsById({
99
+ path: { id: options.id },
100
+ body: {
101
+ name: options.name,
102
+ config: options.config,
103
+ environment: env,
104
+ enabled: options.enabled,
105
+ },
106
+ });
107
+ }, "Failed to update integration");
108
+ const integration = response?.data?.data;
113
109
  if (!integration) {
114
110
  terminal.error("Integration not found.");
115
- process.exit(1);
111
+ terminal.exit(1);
116
112
  }
117
- if (options.json) {
118
- terminal.log(JSON.stringify(integration, null, 2));
113
+ if (outputJsonOrContinue(integration, options.json))
119
114
  return;
120
- }
121
115
  terminal.success(`Updated integration ${integration.name}`);
122
- }
123
- export async function executeIntegrationsRemove(options) {
124
- const state = await loadState();
125
- const sdk = await createSdkWithCredentials(state.hub.baseUrl);
126
- const result = await sdk.deleteIntegrationsById({
127
- path: { id: options.id },
128
- });
129
- const integration = result.data?.data;
116
+ });
117
+ export const executeIntegrationsRemove = withCommandErrorHandler(async (options) => {
118
+ const sdk = await createSdkFromState();
119
+ const response = await withSDKErrorHandling(async () => {
120
+ return sdk.deleteIntegrationsById({
121
+ path: { id: options.id },
122
+ });
123
+ }, "Failed to remove integration");
124
+ const integration = response?.data?.data;
130
125
  if (!integration) {
131
126
  terminal.error("Integration not found.");
132
- process.exit(1);
127
+ terminal.exit(1);
133
128
  }
134
129
  terminal.success(`Integration removed.`);
135
- }
136
- function openBrowser(url) {
137
- const cmd = platform() === "darwin"
138
- ? "open"
139
- : platform() === "win32"
140
- ? "start"
141
- : "xdg-open";
142
- exec(`${cmd} "${url.replace(/"/g, '\\"')}"`, (err) => {
143
- if (err) {
144
- terminal.dim(`Could not open browser: ${err.message}`);
145
- }
146
- });
147
- }
130
+ });
148
131
  const POLL_INITIAL_MS = 2000;
149
132
  const POLL_MAX_MS = 10000;
150
133
  const POLL_TIMEOUT_MS = 15 * 60 * 1000; // 15 minutes
151
134
  export function printConnectHelp() {
152
- terminal.log("Usage: griffin hub integrations connect <category> <provider> [options]");
135
+ terminal.log("Usage: griffin integrations connect <category> <provider> [options]");
153
136
  terminal.blank();
154
137
  terminal.log("Categories and Providers:");
155
138
  const byCategory = getAllProvidersByCategory();
@@ -163,26 +146,33 @@ export function printConnectHelp() {
163
146
  }
164
147
  }
165
148
  async function connectOAuth(options, _provider) {
149
+ const env = await resolveEnvironment(options.environment);
166
150
  const body = {
167
151
  category: options.category,
168
152
  provider: options.provider,
169
153
  name: options.name ?? options.provider,
170
- environment: options.environment ?? undefined,
154
+ environment: env,
171
155
  };
172
- const state = await loadState();
173
- const sdk = await createSdkWithCredentials(state.hub.baseUrl);
174
- const result = await sdk.postIntegrationsOauthInitiate({
175
- body,
176
- });
177
- const data = result.data;
156
+ const sdk = await createSdkFromState();
157
+ const response = await withSDKErrorHandling(async () => {
158
+ return sdk.postIntegrationsOauthInitiate({
159
+ body,
160
+ });
161
+ }, "Failed to initiate OAuth");
162
+ const data = response?.data;
178
163
  if (!data) {
179
164
  terminal.error("Initiate failed.");
180
- process.exit(1);
165
+ return terminal.exit(1);
181
166
  }
182
167
  terminal.info("Open this URL in your browser to authorize:");
183
168
  terminal.log(data.authUrl);
184
169
  terminal.blank();
185
- openBrowser(data.authUrl);
170
+ terminal.openBrowser(data.authUrl);
171
+ terminal.info("Waiting for authorization… (polling)");
172
+ terminal.info("Open this URL in your browser to authorize:");
173
+ terminal.log(data.authUrl);
174
+ terminal.blank();
175
+ terminal.openBrowser(data.authUrl);
186
176
  terminal.info("Waiting for authorization… (polling)");
187
177
  const start = Date.now();
188
178
  let interval = POLL_INITIAL_MS;
@@ -217,27 +207,28 @@ async function connectOAuth(options, _provider) {
217
207
  }
218
208
  if (status.status === "failed") {
219
209
  terminal.error(status.errorMessage ?? "Authorization failed");
220
- process.exit(1);
210
+ terminal.exit(1);
221
211
  }
222
212
  if (status.status === "expired") {
223
213
  terminal.error(status.errorMessage ?? "Transaction expired");
224
- process.exit(1);
214
+ terminal.exit(1);
225
215
  }
226
216
  terminal.error("Unexpected status");
227
- process.exit(1);
217
+ terminal.exit(1);
228
218
  }
229
219
  catch (err) {
230
220
  terminal.error(err instanceof Error ? err.message : "Polling failed");
231
- process.exit(1);
221
+ terminal.exit(1);
232
222
  }
233
223
  }
234
224
  async function connectCredentials(options, provider) {
225
+ const env = await resolveEnvironment(options.environment);
235
226
  const config = {};
236
227
  for (const field of provider.configFields ?? []) {
237
228
  const value = await terminal.input(`${field.label}${field.required ? " (required)" : ""}`, field.default);
238
229
  if (field.required && !value?.trim()) {
239
230
  terminal.error(`${field.label} is required`);
240
- process.exit(1);
231
+ terminal.exit(1);
241
232
  }
242
233
  if (value?.trim())
243
234
  config[field.key] = value.trim();
@@ -249,7 +240,7 @@ async function connectCredentials(options, provider) {
249
240
  : await terminal.input(`${field.label}${field.required ? " (required)" : ""}`, field.default);
250
241
  if (field.required && !value?.trim()) {
251
242
  terminal.error(`${field.label} is required`);
252
- process.exit(1);
243
+ terminal.exit(1);
253
244
  }
254
245
  if (value?.trim())
255
246
  credentials[field.key] = value.trim();
@@ -260,29 +251,27 @@ async function connectCredentials(options, provider) {
260
251
  name: options.name ?? provider.displayName,
261
252
  config,
262
253
  credentials: Object.keys(credentials).length > 0 ? credentials : undefined,
263
- environment: options.environment ?? null,
254
+ environment: env,
264
255
  enabled: true,
265
256
  };
266
257
  const state = await loadState();
267
258
  const sdk = await createSdkWithCredentials(state.hub.baseUrl);
268
259
  const result = await sdk.postIntegrations({
269
- body,
260
+ body: body,
270
261
  });
271
262
  const integration = result.data?.data;
272
263
  if (!integration) {
273
264
  terminal.error("Integration not found.");
274
- process.exit(1);
265
+ terminal.exit(1);
275
266
  }
276
- if (options.json) {
277
- terminal.log(JSON.stringify(integration, null, 2));
267
+ if (outputJsonOrContinue(integration, options.json))
278
268
  return;
279
- }
280
269
  terminal.success(`Integration connected: ${integration.name} (${integration.id})`);
281
270
  }
282
271
  export async function executeIntegrationsConnect(options) {
283
272
  if (!options.category || !options.provider) {
284
273
  printConnectHelp();
285
- process.exit(0);
274
+ return terminal.exit(0);
286
275
  }
287
276
  const providerDef = getProvider(options.category, options.provider);
288
277
  if (!providerDef) {
@@ -297,7 +286,7 @@ export async function executeIntegrationsConnect(options) {
297
286
  else {
298
287
  terminal.info("Available categories: " + getCategories().join(", "));
299
288
  }
300
- process.exit(1);
289
+ return terminal.exit(1);
301
290
  }
302
291
  if (providerDef.authMethod === "oauth") {
303
292
  await connectOAuth(options, providerDef);
@@ -47,6 +47,9 @@ export async function executeLogin() {
47
47
  });
48
48
  terminal.info(`Go to: ${data?.verification_uri_complete}`);
49
49
  terminal.info(`Or enter code: ${data?.user_code}`);
50
+ if (data?.verification_uri_complete) {
51
+ terminal.openBrowser(data.verification_uri_complete);
52
+ }
50
53
  // 2. Poll for authorization
51
54
  const sessionToken = await pollForToken(clientId, data?.device_code, (data?.interval ?? 5) * 1000);
52
55
  const { data: jwtData } = await authClient.token({
@@ -2,11 +2,11 @@ declare const METRICS_PERIODS: readonly ["1h", "6h", "24h", "7d", "30d"];
2
2
  type MetricsPeriod = (typeof METRICS_PERIODS)[number];
3
3
  export interface MetricsOptions {
4
4
  period?: MetricsPeriod;
5
- environment?: string;
5
+ environment: string;
6
6
  json?: boolean;
7
7
  }
8
8
  /**
9
9
  * Show metrics summary from the hub
10
10
  */
11
- export declare function executeMetrics(options: MetricsOptions): Promise<void>;
11
+ export declare const executeMetrics: (options: MetricsOptions) => Promise<void>;
12
12
  export {};
@@ -1,78 +1,50 @@
1
- import { loadState } from "../../core/state.js";
2
- import { getHubCredentials } from "../../core/credentials.js";
1
+ import { resolveEnvironment } from "../../core/state.js";
3
2
  import { terminal } from "../../utils/terminal.js";
3
+ import { createSdkFromState } from "../../core/sdk.js";
4
+ import { withSDKErrorHandling } from "../../utils/sdk-error.js";
5
+ import { withCommandErrorHandler, outputJsonOrContinue, } from "../../utils/command-wrapper.js";
4
6
  const METRICS_PERIODS = ["1h", "6h", "24h", "7d", "30d"];
5
7
  /**
6
8
  * Show metrics summary from the hub
7
9
  */
8
- export async function executeMetrics(options) {
9
- try {
10
- const state = await loadState();
11
- if (!state.hub?.baseUrl) {
12
- terminal.error("No hub connection configured.");
13
- terminal.dim("Connect with:");
14
- terminal.dim(" griffin hub connect --url <url> --token <token>");
15
- terminal.exit(1);
16
- }
17
- const credentials = await getHubCredentials();
18
- const token = credentials?.token;
19
- if (!token) {
20
- terminal.error("No API token. Run 'griffin hub login' or connect with --token.");
21
- terminal.exit(1);
22
- }
23
- const baseUrl = state.hub.baseUrl;
24
- const period = options.period ?? "24h";
25
- const url = new URL(baseUrl);
26
- url.pathname = "/metrics/summary";
27
- url.searchParams.set("period", period);
28
- if (options.environment) {
29
- url.searchParams.set("environment", options.environment);
30
- }
31
- const spinner = terminal.spinner("Fetching metrics...").start();
32
- const response = await fetch(url.toString(), {
33
- headers: { Authorization: `Bearer ${token}` },
10
+ export const executeMetrics = withCommandErrorHandler(async (options) => {
11
+ const sdk = await createSdkFromState();
12
+ const env = await resolveEnvironment(options.environment);
13
+ const spinner = terminal.spinner("Fetching metrics...").start();
14
+ const response = await withSDKErrorHandling(async () => {
15
+ return sdk.getMetricsSummary({
16
+ query: {
17
+ environment: env,
18
+ period: options.period,
19
+ },
34
20
  });
35
- spinner.stop();
36
- if (!response.ok) {
37
- const text = await response.text();
38
- terminal.error(`Failed to fetch metrics: ${response.status} ${response.statusText}`);
39
- if (text)
40
- terminal.dim(text);
41
- terminal.exit(1);
42
- }
43
- const body = (await response.json());
44
- const data = body.data;
45
- if (options.json) {
46
- terminal.log(JSON.stringify(data, null, 2));
47
- return;
48
- }
49
- terminal.info(`Metrics (${period})`);
50
- terminal.dim(`${new Date(data.periodStart).toLocaleString()} ${new Date(data.periodEnd).toLocaleString()}`);
51
- terminal.blank();
52
- terminal.log("Monitors:");
53
- terminal.log(` Total: ${data.monitors.total} Passing: ${terminal.colors.green(String(data.monitors.passing))} Failing: ${data.monitors.failing > 0 ? terminal.colors.red(String(data.monitors.failing)) : "0"} No recent runs: ${data.monitors.noRecentRuns}`);
21
+ }, "Failed to fetch metrics");
22
+ spinner.stop();
23
+ const data = response?.data?.data;
24
+ if (outputJsonOrContinue(data, options.json))
25
+ return;
26
+ terminal.info(`Metrics (${options.period})`);
27
+ terminal.dim(`${new Date(data.periodStart).toLocaleString()} – ${new Date(data.periodEnd).toLocaleString()}`);
28
+ terminal.blank();
29
+ terminal.log("Monitors:");
30
+ terminal.log(` Total: ${data.monitors.total} Passing: ${terminal.colors.green(String(data.monitors.passing))} Failing: ${data.monitors.failing > 0 ? terminal.colors.red(String(data.monitors.failing)) : "0"} No recent runs: ${data.monitors.noRecentRuns}`);
31
+ terminal.blank();
32
+ terminal.log("Runs:");
33
+ terminal.log(` Total: ${data.runs.total} Success rate: ${data.runs.successRate.toFixed(1)}%`);
34
+ terminal.blank();
35
+ if (data.latency.p50DurationMs != null ||
36
+ data.latency.p95DurationMs != null ||
37
+ data.latency.p99DurationMs != null) {
38
+ terminal.log("Latency (completed runs):");
39
+ terminal.log(` p50: ${data.latency.p50DurationMs ?? "-"} ms p95: ${data.latency.p95DurationMs ?? "-"} ms p99: ${data.latency.p99DurationMs ?? "-"} ms`);
54
40
  terminal.blank();
55
- terminal.log("Runs:");
56
- terminal.log(` Total: ${data.runs.total} Success rate: ${data.runs.successRate.toFixed(1)}%`);
57
- terminal.blank();
58
- if (data.latency.p50DurationMs != null ||
59
- data.latency.p95DurationMs != null ||
60
- data.latency.p99DurationMs != null) {
61
- terminal.log("Latency (completed runs):");
62
- terminal.log(` p50: ${data.latency.p50DurationMs ?? "-"} ms p95: ${data.latency.p95DurationMs ?? "-"} ms p99: ${data.latency.p99DurationMs ?? "-"} ms`);
63
- terminal.blank();
64
- }
65
- terminal.log(`Uptime: ${data.uptimePercent.toFixed(1)}%`);
66
- terminal.blank();
67
- if (data.failingMonitors.length > 0) {
68
- terminal.warn("Failing monitors:");
69
- for (const m of data.failingMonitors) {
70
- terminal.log(` ${terminal.colors.red("✗")} ${m.monitorName} (${m.monitorId}) – ${m.consecutiveFailures} consecutive failure(s), last at ${new Date(m.lastFailureAt).toLocaleString()}`);
71
- }
72
- }
73
41
  }
74
- catch (error) {
75
- terminal.error(error.message);
76
- terminal.exit(1);
42
+ terminal.log(`Uptime: ${data.uptimePercent.toFixed(1)}%`);
43
+ terminal.blank();
44
+ if (data.failingMonitors.length > 0) {
45
+ terminal.warn("Failing monitors:");
46
+ for (const m of data.failingMonitors) {
47
+ terminal.log(` ${terminal.colors.red("✗")} ${m.monitorName} (${m.monitorId}) – ${m.consecutiveFailures} consecutive failure(s), last at ${new Date(m.lastFailureAt).toLocaleString()}`);
48
+ }
77
49
  }
78
- }
50
+ });
@@ -5,4 +5,4 @@ export interface PlanOptions {
5
5
  /**
6
6
  * Show what changes would be applied
7
7
  */
8
- export declare function executePlan(options: PlanOptions): Promise<void>;
8
+ export declare const executePlan: (options: PlanOptions) => Promise<void>;
@@ -1,77 +1,37 @@
1
- import { loadState, resolveEnvironment } from "../../core/state.js";
2
- import { discoverMonitors, formatDiscoveryErrors, } from "../../core/discovery.js";
3
- import { createSdkWithCredentials } from "../../core/sdk.js";
1
+ import { resolveEnvironment } from "../../core/state.js";
2
+ import { createSdkAndState } from "../../core/sdk.js";
4
3
  import { computeDiff, formatDiff, formatDiffJson } from "../../core/diff.js";
5
4
  import { terminal } from "../../utils/terminal.js";
6
- import { withSDKErrorHandling } from "../../utils/sdk-error.js";
7
- import { loadVariables } from "../../core/variables.js";
8
- import { resolveMonitor } from "../../resolve.js";
5
+ import { withCommandErrorHandler } from "../../utils/command-wrapper.js";
6
+ import { discoverLocalMonitors, fetchRemoteMonitors, resolveLocalMonitors, } from "../../core/monitor-helpers.js";
9
7
  /**
10
8
  * Show what changes would be applied
11
9
  */
12
- export async function executePlan(options) {
13
- try {
14
- // Load state
15
- const state = await loadState();
16
- // Resolve environment
17
- const envName = await resolveEnvironment(options.env);
18
- if (!state.hub?.baseUrl) {
19
- terminal.error("Hub connection not configured.");
20
- terminal.dim("Connect with:");
21
- terminal.dim(" griffin hub connect --url <url> --token <token>");
22
- terminal.exit(1);
23
- }
24
- // Discover local monitors
25
- const discoveryPattern = state.discovery?.pattern || "**/__griffin__/*.{ts,js}";
26
- const discoveryIgnore = state.discovery?.ignore || [
27
- "node_modules/**",
28
- "dist/**",
29
- ];
30
- const spinner = terminal.spinner("Discovering local monitors...").start();
31
- const { monitors, errors } = await discoverMonitors(discoveryPattern, discoveryIgnore);
32
- if (errors.length > 0) {
33
- spinner.fail("Discovery failed");
34
- terminal.error(formatDiscoveryErrors(errors));
35
- terminal.exit(1);
36
- }
37
- spinner.succeed(`Found ${monitors.length} local monitor(s)`);
38
- // Create SDK clients with credentials
39
- const sdk = await createSdkWithCredentials(state.hub.baseUrl);
40
- // Fetch remote monitors for this project + environment
41
- const fetchSpinner = terminal
42
- .spinner("Fetching remote monitors...")
43
- .start();
44
- const response = await withSDKErrorHandling(() => sdk.getMonitor({
45
- query: {
46
- projectId: state.projectId,
47
- environment: envName,
48
- },
49
- }), "Failed to fetch remote monitors");
50
- const remoteMonitors = response?.data?.data;
51
- fetchSpinner.succeed(`Found ${remoteMonitors.length} remote monitor(s)`);
52
- // Load variables and resolve local monitors before computing diff
53
- const variables = await loadVariables(envName);
54
- const resolvedMonitors = monitors.map((p) => resolveMonitor(p.monitor, state.projectId, envName, variables));
55
- // Compute diff (no deletions shown by default)
56
- const diff = computeDiff(resolvedMonitors, remoteMonitors, {
57
- includeDeletions: false,
58
- });
59
- terminal.blank();
60
- // Output
61
- if (options.json) {
62
- terminal.log(formatDiffJson(diff));
63
- }
64
- else {
65
- terminal.log(formatDiff(diff));
66
- }
67
- // Exit with error code if there are changes
68
- if (diff.summary.creates + diff.summary.updates + diff.summary.deletes >
69
- 0) {
70
- terminal.exit(2); // Exit code 2 indicates changes pending
71
- }
10
+ export const executePlan = withCommandErrorHandler(async (options) => {
11
+ const { sdk, state } = await createSdkAndState();
12
+ // Resolve environment
13
+ const envName = await resolveEnvironment(options.env);
14
+ // Discover local monitors
15
+ const { monitors } = await discoverLocalMonitors(state);
16
+ // Fetch remote monitors for this project + environment
17
+ const remoteMonitors = await fetchRemoteMonitors(sdk, state.projectId, envName);
18
+ // Load variables and resolve local monitors before computing diff
19
+ const resolvedMonitors = await resolveLocalMonitors(monitors, state.projectId, envName);
20
+ // Compute diff (no deletions shown by default)
21
+ const diff = computeDiff(resolvedMonitors, remoteMonitors, {
22
+ includeDeletions: false,
23
+ });
24
+ terminal.blank();
25
+ // Output
26
+ if (options.json) {
27
+ terminal.log(formatDiffJson(diff));
72
28
  }
73
- catch (error) {
74
- terminal.error(error.message);
75
- terminal.exit(1);
29
+ else {
30
+ terminal.log(formatDiff(diff));
76
31
  }
77
- }
32
+ // Exit with error code if there are changes
33
+ if (diff.summary.creates + diff.summary.updates + diff.summary.deletes >
34
+ 0) {
35
+ terminal.exit(2); // Exit code 2 indicates changes pending
36
+ }
37
+ });
@@ -13,11 +13,11 @@ export interface NotificationsTestOptions {
13
13
  }
14
14
  /**
15
15
  * List notification rules (read-only).
16
- * Rules are defined in monitor DSL and synced via `griffin hub apply`.
16
+ * Rules are defined in monitor DSL and synced via `griffin apply`.
17
17
  */
18
- export declare function executeNotificationsList(options: NotificationsListOptions): Promise<void>;
18
+ export declare const executeNotificationsList: (options: NotificationsListOptions) => Promise<void>;
19
19
  /**
20
20
  * Test a notification integration. Without options, runs a wizard (select integration, then provider-specific prompts).
21
21
  * With --integration and routing flags, runs non-interactively.
22
22
  */
23
- export declare function executeNotificationsTest(options: NotificationsTestOptions): Promise<void>;
23
+ export declare const executeNotificationsTest: (options: NotificationsTestOptions) => Promise<void>;