@griffin-app/griffin-cli 1.0.8 → 1.0.10
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 +129 -34
- package/dist/commands/env.js +2 -7
- package/dist/commands/hub/apply.js +4 -2
- package/dist/commands/hub/integrations.d.ts +37 -0
- package/dist/commands/hub/integrations.js +167 -0
- package/dist/commands/hub/login.js +12 -25
- package/dist/commands/hub/monitor.js +4 -2
- package/dist/commands/hub/notifications.d.ts +2 -21
- package/dist/commands/hub/notifications.js +7 -119
- package/dist/commands/hub/run.js +1 -1
- package/dist/commands/hub/secrets.d.ts +37 -0
- package/dist/commands/hub/secrets.js +187 -0
- package/dist/commands/init.js +1 -6
- package/dist/commands/local/run.d.ts +1 -1
- package/dist/core/apply.test.js +3 -1
- package/dist/core/secrets.d.ts +37 -0
- package/dist/core/secrets.js +96 -0
- package/dist/core/state.d.ts +0 -4
- package/dist/core/state.js +8 -26
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/monitor-discovery.d.ts +4 -0
- package/dist/monitor-discovery.js +25 -0
- package/dist/monitor-runner.d.ts +6 -0
- package/dist/monitor-runner.js +56 -0
- package/dist/schemas/state.d.ts +16 -17
- package/dist/schemas/state.js +22 -16
- package/dist/test-runner.js +5 -4
- package/dist/utils/sdk-error.js +0 -2
- package/package.json +6 -5
package/dist/cli.js
CHANGED
|
@@ -16,7 +16,9 @@ import { executeApply } from "./commands/hub/apply.js";
|
|
|
16
16
|
import { executeRun } from "./commands/hub/run.js";
|
|
17
17
|
import { executeLogin } from "./commands/hub/login.js";
|
|
18
18
|
import { executeLogout } from "./commands/hub/logout.js";
|
|
19
|
-
import { executeNotificationsList,
|
|
19
|
+
import { executeNotificationsList, executeNotificationsTest, } from "./commands/hub/notifications.js";
|
|
20
|
+
import { executeSecretsList, executeSecretsSet, executeSecretsGet, executeSecretsDelete, } from "./commands/hub/secrets.js";
|
|
21
|
+
import { executeIntegrationsList, executeIntegrationsShow, executeIntegrationsAdd, executeIntegrationsUpdate, executeIntegrationsRemove, } from "./commands/hub/integrations.js";
|
|
20
22
|
const program = new Command();
|
|
21
23
|
program
|
|
22
24
|
.name("griffin")
|
|
@@ -53,7 +55,7 @@ env
|
|
|
53
55
|
// Local command group
|
|
54
56
|
const local = program.command("local").description("Local test execution");
|
|
55
57
|
local
|
|
56
|
-
.command("run
|
|
58
|
+
.command("run [env]")
|
|
57
59
|
.description("Run tests locally against an environment")
|
|
58
60
|
.action(async (env, options) => {
|
|
59
61
|
await executeRunLocal({ env });
|
|
@@ -75,15 +77,14 @@ hub
|
|
|
75
77
|
await executeStatus();
|
|
76
78
|
});
|
|
77
79
|
hub
|
|
78
|
-
.command("metrics")
|
|
80
|
+
.command("metrics [env]")
|
|
79
81
|
.description("Show metrics summary from the hub")
|
|
80
82
|
.option("--period <period>", "Time window: 1h, 6h, 24h, 7d, 30d", "24h")
|
|
81
|
-
.option("--environment <env>", "Filter by environment")
|
|
82
83
|
.option("--json", "Output as JSON")
|
|
83
|
-
.action(async (options) => {
|
|
84
|
+
.action(async (env, options) => {
|
|
84
85
|
await executeMetrics({
|
|
85
86
|
period: options.period,
|
|
86
|
-
environment:
|
|
87
|
+
environment: env,
|
|
87
88
|
json: options.json,
|
|
88
89
|
});
|
|
89
90
|
});
|
|
@@ -99,14 +100,14 @@ hub
|
|
|
99
100
|
});
|
|
100
101
|
});
|
|
101
102
|
hub
|
|
102
|
-
.command("
|
|
103
|
+
.command("plan [env]")
|
|
103
104
|
.description("Show what changes would be applied")
|
|
104
105
|
.option("--json", "Output in JSON format")
|
|
105
106
|
.action(async (env, options) => {
|
|
106
107
|
await executeMonitor({ ...options, env });
|
|
107
108
|
});
|
|
108
109
|
hub
|
|
109
|
-
.command("apply
|
|
110
|
+
.command("apply [env]")
|
|
110
111
|
.description("Apply changes to the hub")
|
|
111
112
|
.option("--auto-approve", "Skip confirmation prompt")
|
|
112
113
|
.option("--dry-run", "Show what would be done without making changes")
|
|
@@ -115,7 +116,7 @@ hub
|
|
|
115
116
|
await executeApply({ ...options, env });
|
|
116
117
|
});
|
|
117
118
|
hub
|
|
118
|
-
.command("run
|
|
119
|
+
.command("run [env]")
|
|
119
120
|
.description("Trigger a monitor run on the hub")
|
|
120
121
|
.requiredOption("--monitor <name>", "Monitor name to run")
|
|
121
122
|
.option("--wait", "Wait for run to complete")
|
|
@@ -138,7 +139,7 @@ hub
|
|
|
138
139
|
// Notifications command group
|
|
139
140
|
const notifications = hub
|
|
140
141
|
.command("notifications")
|
|
141
|
-
.description("
|
|
142
|
+
.description("View notification rules (rules are defined in monitor DSL and synced via griffin hub apply)");
|
|
142
143
|
notifications
|
|
143
144
|
.command("list")
|
|
144
145
|
.description("List notification rules")
|
|
@@ -153,39 +154,133 @@ notifications
|
|
|
153
154
|
});
|
|
154
155
|
});
|
|
155
156
|
notifications
|
|
157
|
+
.command("test")
|
|
158
|
+
.description("Test a webhook configuration")
|
|
159
|
+
.requiredOption("--webhook <url>", "Webhook URL to test")
|
|
160
|
+
.action(async (options) => {
|
|
161
|
+
await executeNotificationsTest({
|
|
162
|
+
webhook: options.webhook,
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
// Integrations command group
|
|
166
|
+
const integrations = hub
|
|
167
|
+
.command("integrations")
|
|
168
|
+
.description("Manage integrations (notifications, secrets, metrics)");
|
|
169
|
+
integrations
|
|
170
|
+
.command("list")
|
|
171
|
+
.description("List integrations")
|
|
172
|
+
.option("--category <cat>", "Filter by category: secrets, notifications, metrics")
|
|
173
|
+
.option("--provider-type <type>", "Filter by provider type")
|
|
174
|
+
.option("--environment <env>", "Filter by environment")
|
|
175
|
+
.option("--enabled", "Filter by enabled status")
|
|
176
|
+
.option("--json", "Output as JSON")
|
|
177
|
+
.action(async (options) => {
|
|
178
|
+
await executeIntegrationsList({
|
|
179
|
+
category: options.category,
|
|
180
|
+
providerType: options.providerType,
|
|
181
|
+
environment: options.environment,
|
|
182
|
+
enabled: options.enabled ? true : undefined,
|
|
183
|
+
json: options.json,
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
integrations
|
|
187
|
+
.command("show <id>")
|
|
188
|
+
.description("Show integration details")
|
|
189
|
+
.option("--json", "Output as JSON")
|
|
190
|
+
.action(async (id, options) => {
|
|
191
|
+
await executeIntegrationsShow({ id, json: options.json });
|
|
192
|
+
});
|
|
193
|
+
integrations
|
|
156
194
|
.command("add")
|
|
157
|
-
.description("
|
|
158
|
-
.requiredOption("--
|
|
159
|
-
.
|
|
160
|
-
.
|
|
161
|
-
.option("--
|
|
162
|
-
.
|
|
163
|
-
.
|
|
164
|
-
.option("--cooldown <minutes>", "Cooldown period in minutes", "15")
|
|
195
|
+
.description("Add an integration (no credentials in Phase 1; use API for credentials)")
|
|
196
|
+
.requiredOption("--category <cat>", "Category: secrets, notifications, metrics")
|
|
197
|
+
.requiredOption("--provider-type <type>", "Provider type (e.g. slack_webhook, datadog)")
|
|
198
|
+
.requiredOption("--name <name>", "Display name")
|
|
199
|
+
.option("--environment <env>", "Environment scope (omit for org-wide)")
|
|
200
|
+
.option("--enabled/--no-enabled", "Enabled", true)
|
|
201
|
+
.option("--json", "Output as JSON")
|
|
165
202
|
.action(async (options) => {
|
|
166
|
-
await
|
|
203
|
+
await executeIntegrationsAdd({
|
|
204
|
+
category: options.category,
|
|
205
|
+
providerType: options.providerType,
|
|
167
206
|
name: options.name,
|
|
168
|
-
monitor: options.monitor,
|
|
169
207
|
environment: options.environment,
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
webhook: options.webhook,
|
|
173
|
-
cooldown: options.cooldown ? parseInt(options.cooldown, 10) : undefined,
|
|
208
|
+
enabled: options.enabled,
|
|
209
|
+
json: options.json,
|
|
174
210
|
});
|
|
175
211
|
});
|
|
176
|
-
|
|
177
|
-
.command("
|
|
178
|
-
.description("
|
|
212
|
+
integrations
|
|
213
|
+
.command("update <id>")
|
|
214
|
+
.description("Update an integration")
|
|
215
|
+
.option("--name <name>", "Display name")
|
|
216
|
+
.option("--environment <env>", "Environment scope")
|
|
217
|
+
.option("--enabled/--no-enabled", "Enabled")
|
|
218
|
+
.option("--json", "Output as JSON")
|
|
219
|
+
.action(async (id, options) => {
|
|
220
|
+
await executeIntegrationsUpdate({
|
|
221
|
+
id,
|
|
222
|
+
name: options.name,
|
|
223
|
+
environment: options.environment,
|
|
224
|
+
enabled: options.enabled,
|
|
225
|
+
json: options.json,
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
integrations
|
|
229
|
+
.command("remove <id>")
|
|
230
|
+
.description("Remove an integration")
|
|
231
|
+
.option("--force", "Skip confirmation")
|
|
179
232
|
.action(async (id) => {
|
|
180
|
-
await
|
|
233
|
+
await executeIntegrationsRemove({ id });
|
|
181
234
|
});
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
.
|
|
185
|
-
.
|
|
235
|
+
// Secrets command group
|
|
236
|
+
const secrets = hub
|
|
237
|
+
.command("secrets")
|
|
238
|
+
.description("Manage secrets (platform storage)");
|
|
239
|
+
secrets
|
|
240
|
+
.command("list")
|
|
241
|
+
.description("List secrets for an environment")
|
|
242
|
+
.option("--environment <env>", "Environment name", "default")
|
|
243
|
+
.option("--json", "Output as JSON")
|
|
186
244
|
.action(async (options) => {
|
|
187
|
-
await
|
|
188
|
-
|
|
245
|
+
await executeSecretsList({
|
|
246
|
+
environment: options.environment,
|
|
247
|
+
json: options.json,
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
secrets
|
|
251
|
+
.command("set <name>")
|
|
252
|
+
.description("Create or update a secret (prompts for value)")
|
|
253
|
+
.option("--environment <env>", "Environment name", "default")
|
|
254
|
+
.option("--value <value>", "Secret value (avoid for sensitive data)")
|
|
255
|
+
.action(async (name, options) => {
|
|
256
|
+
await executeSecretsSet({
|
|
257
|
+
name,
|
|
258
|
+
environment: options.environment,
|
|
259
|
+
value: options.value,
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
secrets
|
|
263
|
+
.command("get <name>")
|
|
264
|
+
.description("Show secret metadata (not the value)")
|
|
265
|
+
.option("--environment <env>", "Environment name", "default")
|
|
266
|
+
.option("--json", "Output as JSON")
|
|
267
|
+
.action(async (name, options) => {
|
|
268
|
+
await executeSecretsGet({
|
|
269
|
+
name,
|
|
270
|
+
environment: options.environment,
|
|
271
|
+
json: options.json,
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
secrets
|
|
275
|
+
.command("delete <name>")
|
|
276
|
+
.description("Delete a secret")
|
|
277
|
+
.option("--environment <env>", "Environment name", "default")
|
|
278
|
+
.option("--force", "Skip confirmation prompt")
|
|
279
|
+
.action(async (name, options) => {
|
|
280
|
+
await executeSecretsDelete({
|
|
281
|
+
name,
|
|
282
|
+
environment: options.environment,
|
|
283
|
+
force: options.force,
|
|
189
284
|
});
|
|
190
285
|
});
|
|
191
286
|
// Parse arguments
|
package/dist/commands/env.js
CHANGED
|
@@ -16,13 +16,8 @@ export async function executeEnvList() {
|
|
|
16
16
|
terminal.info("Available environments:");
|
|
17
17
|
terminal.blank();
|
|
18
18
|
for (const envName of environments) {
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
? terminal.colors.green("●")
|
|
22
|
-
: terminal.colors.dim("○");
|
|
23
|
-
const envDisplay = isDefault
|
|
24
|
-
? terminal.colors.cyan(envName) + terminal.colors.dim(" (default)")
|
|
25
|
-
: envName;
|
|
19
|
+
const marker = terminal.colors.green("●");
|
|
20
|
+
const envDisplay = terminal.colors.cyan(envName);
|
|
26
21
|
terminal.log(` ${marker} ${envDisplay}`);
|
|
27
22
|
}
|
|
28
23
|
terminal.blank();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { loadState, resolveEnvironment } from "../../core/state.js";
|
|
2
|
-
import { discoverMonitors, formatDiscoveryErrors } from "../../core/discovery.js";
|
|
2
|
+
import { discoverMonitors, formatDiscoveryErrors, } from "../../core/discovery.js";
|
|
3
3
|
import { computeDiff, formatDiff } from "../../core/diff.js";
|
|
4
4
|
import { applyDiff, formatApplyResult } from "../../core/apply.js";
|
|
5
5
|
import { createSdkWithCredentials } from "../../core/sdk.js";
|
|
@@ -42,7 +42,9 @@ export async function executeApply(options) {
|
|
|
42
42
|
}
|
|
43
43
|
spinner.succeed(`Found ${monitors.length} local monitor(s)`);
|
|
44
44
|
// Fetch remote monitors for this project + environment
|
|
45
|
-
const fetchSpinner = terminal
|
|
45
|
+
const fetchSpinner = terminal
|
|
46
|
+
.spinner("Fetching remote monitors...")
|
|
47
|
+
.start();
|
|
46
48
|
const response = await withSDKErrorHandling(() => sdk.getMonitor({
|
|
47
49
|
query: {
|
|
48
50
|
projectId: state.projectId,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface IntegrationsListOptions {
|
|
2
|
+
category?: string;
|
|
3
|
+
providerType?: string;
|
|
4
|
+
environment?: string;
|
|
5
|
+
enabled?: boolean;
|
|
6
|
+
json?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function executeIntegrationsList(options: IntegrationsListOptions): Promise<void>;
|
|
9
|
+
export interface IntegrationsShowOptions {
|
|
10
|
+
id: string;
|
|
11
|
+
json?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function executeIntegrationsShow(options: IntegrationsShowOptions): Promise<void>;
|
|
14
|
+
export interface IntegrationsAddOptions {
|
|
15
|
+
category: string;
|
|
16
|
+
providerType: string;
|
|
17
|
+
name: string;
|
|
18
|
+
config?: Record<string, string>;
|
|
19
|
+
environment?: string;
|
|
20
|
+
enabled?: boolean;
|
|
21
|
+
json?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare function executeIntegrationsAdd(options: IntegrationsAddOptions): Promise<void>;
|
|
24
|
+
export interface IntegrationsUpdateOptions {
|
|
25
|
+
id: string;
|
|
26
|
+
name?: string;
|
|
27
|
+
config?: Record<string, string>;
|
|
28
|
+
environment?: string;
|
|
29
|
+
enabled?: boolean;
|
|
30
|
+
json?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export declare function executeIntegrationsUpdate(options: IntegrationsUpdateOptions): Promise<void>;
|
|
33
|
+
export interface IntegrationsRemoveOptions {
|
|
34
|
+
id: string;
|
|
35
|
+
force?: boolean;
|
|
36
|
+
}
|
|
37
|
+
export declare function executeIntegrationsRemove(options: IntegrationsRemoveOptions): Promise<void>;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { loadState } from "../../core/state.js";
|
|
2
|
+
import { getHubCredentials } from "../../core/credentials.js";
|
|
3
|
+
import { terminal } from "../../utils/terminal.js";
|
|
4
|
+
async function hubFetch(path, options = {}) {
|
|
5
|
+
const state = await loadState();
|
|
6
|
+
if (!state.hub?.baseUrl) {
|
|
7
|
+
terminal.error("Hub connection not configured.");
|
|
8
|
+
terminal.dim("Connect with: griffin hub connect --url <url> --token <token>");
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
const credentials = await getHubCredentials();
|
|
12
|
+
const url = `${state.hub.baseUrl.replace(/\/$/, "")}${path}`;
|
|
13
|
+
const headers = {
|
|
14
|
+
"Content-Type": "application/json",
|
|
15
|
+
};
|
|
16
|
+
if (credentials?.token) {
|
|
17
|
+
headers["Authorization"] = `Bearer ${credentials.token}`;
|
|
18
|
+
}
|
|
19
|
+
const res = await fetch(url, {
|
|
20
|
+
method: options.method ?? "GET",
|
|
21
|
+
headers,
|
|
22
|
+
body: options.body ? JSON.stringify(options.body) : undefined,
|
|
23
|
+
});
|
|
24
|
+
const text = await res.text();
|
|
25
|
+
let json;
|
|
26
|
+
try {
|
|
27
|
+
json = text ? JSON.parse(text) : {};
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return { error: res.ok ? "Invalid response" : text || res.statusText };
|
|
31
|
+
}
|
|
32
|
+
if (!res.ok) {
|
|
33
|
+
return { error: json.error ?? res.statusText };
|
|
34
|
+
}
|
|
35
|
+
return json;
|
|
36
|
+
}
|
|
37
|
+
export async function executeIntegrationsList(options) {
|
|
38
|
+
const params = new URLSearchParams();
|
|
39
|
+
if (options.category)
|
|
40
|
+
params.set("category", options.category);
|
|
41
|
+
if (options.providerType)
|
|
42
|
+
params.set("providerType", options.providerType);
|
|
43
|
+
if (options.environment != null)
|
|
44
|
+
params.set("environment", options.environment);
|
|
45
|
+
if (options.enabled != null)
|
|
46
|
+
params.set("enabled", String(options.enabled));
|
|
47
|
+
const qs = params.toString();
|
|
48
|
+
const path = `/integrations${qs ? `?${qs}` : ""}`;
|
|
49
|
+
const result = await hubFetch(path);
|
|
50
|
+
if (result.error) {
|
|
51
|
+
terminal.error(result.error);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
const list = result.data ?? [];
|
|
55
|
+
if (options.json) {
|
|
56
|
+
terminal.log(JSON.stringify(list, null, 2));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (list.length === 0) {
|
|
60
|
+
terminal.info("No integrations found.");
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
terminal.info("Integrations");
|
|
64
|
+
terminal.blank();
|
|
65
|
+
const table = terminal.table({
|
|
66
|
+
head: ["ID", "Category", "Provider", "Name", "Environment", "Enabled"],
|
|
67
|
+
});
|
|
68
|
+
for (const i of list) {
|
|
69
|
+
table.push([
|
|
70
|
+
i.id.substring(0, 12) + (i.id.length > 12 ? "…" : ""),
|
|
71
|
+
i.category,
|
|
72
|
+
i.providerType,
|
|
73
|
+
i.name,
|
|
74
|
+
i.environment ?? "(all)",
|
|
75
|
+
i.enabled ? "✓" : "✗",
|
|
76
|
+
]);
|
|
77
|
+
}
|
|
78
|
+
terminal.log(table.toString());
|
|
79
|
+
}
|
|
80
|
+
export async function executeIntegrationsShow(options) {
|
|
81
|
+
const result = await hubFetch(`/integrations/${encodeURIComponent(options.id)}`);
|
|
82
|
+
if (result.error) {
|
|
83
|
+
terminal.error(result.error);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
const integration = result.data;
|
|
87
|
+
if (!integration) {
|
|
88
|
+
terminal.error("Integration not found.");
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
if (options.json) {
|
|
92
|
+
terminal.log(JSON.stringify(integration, null, 2));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
terminal.info(integration.name);
|
|
96
|
+
terminal.dim(`ID: ${integration.id}`);
|
|
97
|
+
terminal.log(`Category: ${integration.category}`);
|
|
98
|
+
terminal.log(`Provider: ${integration.providerType}`);
|
|
99
|
+
terminal.log(`Environment: ${integration.environment ?? "(all)"}`);
|
|
100
|
+
terminal.log(`Enabled: ${integration.enabled ? "Yes" : "No"}`);
|
|
101
|
+
terminal.log(`Has credentials: ${integration.hasCredentials ? "Yes" : "No"}`);
|
|
102
|
+
if (Object.keys(integration.config).length > 0) {
|
|
103
|
+
terminal.log("Config: " + JSON.stringify(integration.config));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export async function executeIntegrationsAdd(options) {
|
|
107
|
+
const body = {
|
|
108
|
+
category: options.category,
|
|
109
|
+
providerType: options.providerType,
|
|
110
|
+
name: options.name,
|
|
111
|
+
config: options.config ?? {},
|
|
112
|
+
environment: options.environment ?? null,
|
|
113
|
+
enabled: options.enabled ?? true,
|
|
114
|
+
};
|
|
115
|
+
const result = await hubFetch("/integrations", {
|
|
116
|
+
method: "POST",
|
|
117
|
+
body,
|
|
118
|
+
});
|
|
119
|
+
if (result.error) {
|
|
120
|
+
terminal.error(result.error);
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
const integration = result.data;
|
|
124
|
+
if (!integration) {
|
|
125
|
+
terminal.error("Create failed.");
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
if (options.json) {
|
|
129
|
+
terminal.log(JSON.stringify(integration, null, 2));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
terminal.info(`Created integration ${integration.name} (${integration.id})`);
|
|
133
|
+
}
|
|
134
|
+
export async function executeIntegrationsUpdate(options) {
|
|
135
|
+
const body = {};
|
|
136
|
+
if (options.name !== undefined)
|
|
137
|
+
body.name = options.name;
|
|
138
|
+
if (options.config !== undefined)
|
|
139
|
+
body.config = options.config;
|
|
140
|
+
if (options.environment !== undefined)
|
|
141
|
+
body.environment = options.environment;
|
|
142
|
+
if (options.enabled !== undefined)
|
|
143
|
+
body.enabled = options.enabled;
|
|
144
|
+
const result = await hubFetch(`/integrations/${encodeURIComponent(options.id)}`, { method: "PATCH", body });
|
|
145
|
+
if (result.error) {
|
|
146
|
+
terminal.error(result.error);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
const integration = result.data;
|
|
150
|
+
if (!integration) {
|
|
151
|
+
terminal.error("Update failed.");
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
if (options.json) {
|
|
155
|
+
terminal.log(JSON.stringify(integration, null, 2));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
terminal.info(`Updated integration ${integration.name}`);
|
|
159
|
+
}
|
|
160
|
+
export async function executeIntegrationsRemove(options) {
|
|
161
|
+
const result = await hubFetch(`/integrations/${encodeURIComponent(options.id)}`, { method: "DELETE" });
|
|
162
|
+
if (result.error) {
|
|
163
|
+
terminal.error(result.error);
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
terminal.info("Integration removed.");
|
|
167
|
+
}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
// CLI implementation
|
|
2
2
|
import { createAuthClient } from "better-auth/client";
|
|
3
3
|
import { deviceAuthorizationClient, jwtClient, } from "better-auth/client/plugins";
|
|
4
|
-
import {
|
|
4
|
+
import { loadState, saveState } from "../../core/state.js";
|
|
5
5
|
import { saveHubCredentials } from "../../core/credentials.js";
|
|
6
6
|
import { terminal } from "../../utils/terminal.js";
|
|
7
7
|
import { randomBytes } from "crypto";
|
|
8
|
-
import { createEmptyState } from "../../schemas/state.js";
|
|
9
|
-
const baseURL = "http://localhost:4000/api/auth";
|
|
10
|
-
const hubBaseUrl = "http://localhost:3000";
|
|
11
|
-
//const baseURL = "https://cloud.griffin.app"
|
|
12
8
|
const oauthGrant = "urn:ietf:params:oauth:grant-type:device_code";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
function createAuthClientFromState(state) {
|
|
10
|
+
return createAuthClient({
|
|
11
|
+
baseURL: state.cloud.authUrl,
|
|
12
|
+
plugins: [deviceAuthorizationClient(), jwtClient()],
|
|
13
|
+
});
|
|
14
|
+
}
|
|
17
15
|
async function pollForToken(clientId, deviceCode, interval) {
|
|
16
|
+
const state = await loadState();
|
|
17
|
+
const authClient = createAuthClientFromState(state);
|
|
18
18
|
const { data, error } = await authClient.device.token({
|
|
19
19
|
grant_type: oauthGrant,
|
|
20
20
|
device_code: deviceCode,
|
|
@@ -39,16 +39,9 @@ async function pollForToken(clientId, deviceCode, interval) {
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
export async function executeLogin() {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
state = await loadState();
|
|
46
|
-
clientId = state.hub?.clientId;
|
|
47
|
-
}
|
|
48
|
-
catch (error) { }
|
|
49
|
-
if (!clientId) {
|
|
50
|
-
clientId = randomBytes(16).toString("hex");
|
|
51
|
-
}
|
|
42
|
+
const state = await loadState();
|
|
43
|
+
const clientId = state.hub?.clientId ?? randomBytes(16).toString("hex");
|
|
44
|
+
const authClient = createAuthClientFromState(state);
|
|
52
45
|
const { data } = await authClient.device.code({
|
|
53
46
|
client_id: clientId,
|
|
54
47
|
});
|
|
@@ -69,17 +62,11 @@ export async function executeLogin() {
|
|
|
69
62
|
terminal.success("Login successful");
|
|
70
63
|
terminal.log(` Token saved to user credentials`);
|
|
71
64
|
}
|
|
72
|
-
if (!state) {
|
|
73
|
-
const projectId = await getProjectId();
|
|
74
|
-
state = createEmptyState(projectId);
|
|
75
|
-
}
|
|
76
|
-
// Save hub config to project state (without token)
|
|
77
65
|
await saveState({
|
|
78
66
|
...state,
|
|
79
67
|
hub: {
|
|
80
68
|
...state.hub,
|
|
81
69
|
clientId: clientId,
|
|
82
|
-
baseUrl: hubBaseUrl,
|
|
83
70
|
},
|
|
84
71
|
});
|
|
85
72
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { loadState, resolveEnvironment } from "../../core/state.js";
|
|
2
|
-
import { discoverMonitors, formatDiscoveryErrors } from "../../core/discovery.js";
|
|
2
|
+
import { discoverMonitors, formatDiscoveryErrors, } from "../../core/discovery.js";
|
|
3
3
|
import { createSdkWithCredentials } from "../../core/sdk.js";
|
|
4
4
|
import { computeDiff, formatDiff, formatDiffJson } from "../../core/diff.js";
|
|
5
5
|
import { terminal } from "../../utils/terminal.js";
|
|
@@ -38,7 +38,9 @@ export async function executeMonitor(options) {
|
|
|
38
38
|
// Create SDK clients with credentials
|
|
39
39
|
const sdk = await createSdkWithCredentials(state.hub.baseUrl);
|
|
40
40
|
// Fetch remote monitors for this project + environment
|
|
41
|
-
const fetchSpinner = terminal
|
|
41
|
+
const fetchSpinner = terminal
|
|
42
|
+
.spinner("Fetching remote monitors...")
|
|
43
|
+
.start();
|
|
42
44
|
const response = await withSDKErrorHandling(() => sdk.getMonitor({
|
|
43
45
|
query: {
|
|
44
46
|
projectId: state.projectId,
|
|
@@ -3,33 +3,14 @@ export interface NotificationsListOptions {
|
|
|
3
3
|
enabled?: boolean;
|
|
4
4
|
json?: boolean;
|
|
5
5
|
}
|
|
6
|
-
export interface NotificationsAddOptions {
|
|
7
|
-
name: string;
|
|
8
|
-
monitor?: string;
|
|
9
|
-
environment?: string;
|
|
10
|
-
location?: string;
|
|
11
|
-
trigger: string;
|
|
12
|
-
webhook: string;
|
|
13
|
-
cooldown?: number;
|
|
14
|
-
}
|
|
15
|
-
export interface NotificationsDeleteOptions {
|
|
16
|
-
id: string;
|
|
17
|
-
}
|
|
18
6
|
export interface NotificationsTestOptions {
|
|
19
7
|
webhook: string;
|
|
20
8
|
}
|
|
21
9
|
/**
|
|
22
|
-
* List notification rules
|
|
10
|
+
* List notification rules (read-only).
|
|
11
|
+
* Rules are defined in monitor DSL and synced via `griffin hub apply`.
|
|
23
12
|
*/
|
|
24
13
|
export declare function executeNotificationsList(options: NotificationsListOptions): Promise<void>;
|
|
25
|
-
/**
|
|
26
|
-
* Add a notification rule
|
|
27
|
-
*/
|
|
28
|
-
export declare function executeNotificationsAdd(options: NotificationsAddOptions): Promise<void>;
|
|
29
|
-
/**
|
|
30
|
-
* Delete a notification rule
|
|
31
|
-
*/
|
|
32
|
-
export declare function executeNotificationsDelete(options: NotificationsDeleteOptions): Promise<void>;
|
|
33
14
|
/**
|
|
34
15
|
* Test a webhook
|
|
35
16
|
*/
|