@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.
- package/dist/cli.js +183 -196
- package/dist/commands/env.d.ts +3 -3
- package/dist/commands/env.js +28 -45
- package/dist/commands/hub/apply.d.ts +1 -1
- package/dist/commands/hub/apply.js +54 -95
- package/dist/commands/hub/connect.d.ts +1 -1
- package/dist/commands/hub/connect.js +2 -6
- package/dist/commands/hub/destroy.d.ts +10 -0
- package/dist/commands/hub/destroy.js +141 -0
- package/dist/commands/hub/integrations.d.ts +9 -9
- package/dist/commands/hub/integrations.js +104 -115
- package/dist/commands/hub/login.js +3 -0
- package/dist/commands/hub/metrics.d.ts +2 -2
- package/dist/commands/hub/metrics.js +41 -69
- package/dist/commands/hub/monitor.d.ts +1 -1
- package/dist/commands/hub/monitor.js +30 -70
- package/dist/commands/hub/notifications.d.ts +3 -3
- package/dist/commands/hub/notifications.js +74 -105
- package/dist/commands/hub/run.d.ts +1 -1
- package/dist/commands/hub/run.js +118 -144
- package/dist/commands/hub/runs.d.ts +1 -1
- package/dist/commands/hub/runs.js +60 -81
- package/dist/commands/hub/secrets.d.ts +8 -8
- package/dist/commands/hub/secrets.js +88 -148
- package/dist/commands/hub/status.d.ts +1 -1
- package/dist/commands/hub/status.js +13 -26
- package/dist/commands/init.js +3 -3
- package/dist/commands/local/run.d.ts +2 -2
- package/dist/commands/local/run.js +37 -42
- package/dist/commands/validate.d.ts +1 -1
- package/dist/commands/validate.js +19 -39
- package/dist/commands/variables.d.ts +3 -3
- package/dist/commands/variables.js +51 -69
- package/dist/core/discovery.js +0 -2
- package/dist/core/monitor-helpers.d.ts +19 -0
- package/dist/core/monitor-helpers.js +48 -0
- package/dist/core/sdk.d.ts +5 -0
- package/dist/core/sdk.js +11 -0
- package/dist/core/state.d.ts +1 -1
- package/dist/core/state.js +0 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/schemas/state.js +1 -1
- package/dist/utils/command-wrapper.d.ts +10 -0
- package/dist/utils/command-wrapper.js +27 -0
- package/dist/utils/sdk-error.js +1 -1
- package/dist/utils/terminal.d.ts +4 -0
- package/dist/utils/terminal.js +17 -0
- package/package.json +4 -4
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
77
|
+
environment: env,
|
|
77
78
|
enabled: options.enabled ?? true,
|
|
78
79
|
};
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
});
|
|
84
|
-
const integration =
|
|
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("
|
|
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.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
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
|
|
124
|
-
const
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
154
|
+
environment: env,
|
|
171
155
|
};
|
|
172
|
-
const
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
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
|
-
|
|
210
|
+
terminal.exit(1);
|
|
221
211
|
}
|
|
222
212
|
if (status.status === "expired") {
|
|
223
213
|
terminal.error(status.errorMessage ?? "Transaction expired");
|
|
224
|
-
|
|
214
|
+
terminal.exit(1);
|
|
225
215
|
}
|
|
226
216
|
terminal.error("Unexpected status");
|
|
227
|
-
|
|
217
|
+
terminal.exit(1);
|
|
228
218
|
}
|
|
229
219
|
catch (err) {
|
|
230
220
|
terminal.error(err instanceof Error ? err.message : "Polling failed");
|
|
231
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
5
|
+
environment: string;
|
|
6
6
|
json?: boolean;
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
9
|
* Show metrics summary from the hub
|
|
10
10
|
*/
|
|
11
|
-
export declare
|
|
11
|
+
export declare const executeMetrics: (options: MetricsOptions) => Promise<void>;
|
|
12
12
|
export {};
|
|
@@ -1,78 +1,50 @@
|
|
|
1
|
-
import {
|
|
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
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
terminal.log("
|
|
53
|
-
terminal.log(`
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
+
});
|
|
@@ -1,77 +1,37 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
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 {
|
|
7
|
-
import {
|
|
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
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
74
|
-
terminal.
|
|
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
|
|
16
|
+
* Rules are defined in monitor DSL and synced via `griffin apply`.
|
|
17
17
|
*/
|
|
18
|
-
export declare
|
|
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
|
|
23
|
+
export declare const executeNotificationsTest: (options: NotificationsTestOptions) => Promise<void>;
|