@griffin-app/griffin-cli 1.0.11 → 1.0.13
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 +85 -36
- package/dist/commands/env.d.ts +8 -0
- package/dist/commands/env.js +31 -1
- package/dist/commands/hub/integrations.d.ts +8 -6
- package/dist/commands/hub/integrations.js +155 -102
- package/dist/commands/hub/notifications.d.ts +8 -2
- package/dist/commands/hub/notifications.js +116 -7
- package/dist/commands/init.js +7 -29
- package/dist/commands/variables.d.ts +12 -0
- package/dist/commands/variables.js +87 -0
- package/dist/core/apply.test.js +1 -0
- package/dist/core/diff.test.js +2 -0
- package/dist/core/discovery.d.ts +1 -0
- package/dist/core/discovery.js +14 -4
- package/dist/core/monitor-diff.js +15 -0
- package/dist/core/variables.d.ts +2 -2
- package/dist/core/variables.js +6 -24
- package/dist/providers/registry.d.ts +24 -0
- package/dist/providers/registry.js +123 -0
- package/dist/schemas/state.d.ts +15 -5
- package/dist/schemas/state.js +3 -1
- package/dist/utils/terminal.d.ts +4 -0
- package/dist/utils/terminal.js +11 -0
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,8 @@ import { Command } from "commander";
|
|
|
3
3
|
import { executeInit } from "./commands/init.js";
|
|
4
4
|
import { executeValidate } from "./commands/validate.js";
|
|
5
5
|
import { executeGenerateKey } from "./commands/generate-key.js";
|
|
6
|
-
import { executeEnvList } from "./commands/env.js";
|
|
6
|
+
import { executeEnvList, executeEnvAdd, executeEnvRemove, } from "./commands/env.js";
|
|
7
|
+
import { executeVariablesList, executeVariablesAdd, executeVariablesRemove, } from "./commands/variables.js";
|
|
7
8
|
// Local commands
|
|
8
9
|
import { executeRunLocal } from "./commands/local/run.js";
|
|
9
10
|
// Hub commands
|
|
@@ -16,9 +17,10 @@ import { executeApply } from "./commands/hub/apply.js";
|
|
|
16
17
|
import { executeRun } from "./commands/hub/run.js";
|
|
17
18
|
import { executeLogin } from "./commands/hub/login.js";
|
|
18
19
|
import { executeLogout } from "./commands/hub/logout.js";
|
|
20
|
+
import { executeIntegrationsConnect } from "./commands/hub/integrations.js";
|
|
19
21
|
import { executeNotificationsList, executeNotificationsTest, } from "./commands/hub/notifications.js";
|
|
20
22
|
import { executeSecretsList, executeSecretsSet, executeSecretsGet, executeSecretsDelete, } from "./commands/hub/secrets.js";
|
|
21
|
-
import { executeIntegrationsList, executeIntegrationsShow,
|
|
23
|
+
import { executeIntegrationsList, executeIntegrationsShow, executeIntegrationsUpdate, executeIntegrationsRemove, } from "./commands/hub/integrations.js";
|
|
22
24
|
const program = new Command();
|
|
23
25
|
program
|
|
24
26
|
.name("griffin")
|
|
@@ -52,6 +54,43 @@ env
|
|
|
52
54
|
.action(async () => {
|
|
53
55
|
await executeEnvList();
|
|
54
56
|
});
|
|
57
|
+
env
|
|
58
|
+
.command("add <name>")
|
|
59
|
+
.description("Add a new environment")
|
|
60
|
+
.action(async (name) => {
|
|
61
|
+
await executeEnvAdd(name);
|
|
62
|
+
});
|
|
63
|
+
env
|
|
64
|
+
.command("remove <name>")
|
|
65
|
+
.description("Remove an environment")
|
|
66
|
+
.action(async (name) => {
|
|
67
|
+
await executeEnvRemove(name);
|
|
68
|
+
});
|
|
69
|
+
// Variables command group
|
|
70
|
+
const variables = program
|
|
71
|
+
.command("variables")
|
|
72
|
+
.description("Manage environment variables");
|
|
73
|
+
variables
|
|
74
|
+
.command("list")
|
|
75
|
+
.description("List all variables for an environment")
|
|
76
|
+
.requiredOption("--env <name>", "Environment name")
|
|
77
|
+
.action(async (options) => {
|
|
78
|
+
await executeVariablesList(options.env);
|
|
79
|
+
});
|
|
80
|
+
variables
|
|
81
|
+
.command("add <keyValue>")
|
|
82
|
+
.description("Add or update a variable (format: KEY=VALUE)")
|
|
83
|
+
.requiredOption("--env <name>", "Environment name")
|
|
84
|
+
.action(async (keyValue, options) => {
|
|
85
|
+
await executeVariablesAdd(keyValue, options.env);
|
|
86
|
+
});
|
|
87
|
+
variables
|
|
88
|
+
.command("remove <key>")
|
|
89
|
+
.description("Remove a variable")
|
|
90
|
+
.requiredOption("--env <name>", "Environment name")
|
|
91
|
+
.action(async (key, options) => {
|
|
92
|
+
await executeVariablesRemove(key, options.env);
|
|
93
|
+
});
|
|
55
94
|
// Local command group
|
|
56
95
|
const local = program.command("local").description("Local test execution");
|
|
57
96
|
local
|
|
@@ -155,11 +194,15 @@ notifications
|
|
|
155
194
|
});
|
|
156
195
|
notifications
|
|
157
196
|
.command("test")
|
|
158
|
-
.description("
|
|
159
|
-
.
|
|
197
|
+
.description("Send a test notification (interactive wizard, or use flags for scripts)")
|
|
198
|
+
.option("--integration <id|name>", "Integration id or name; omit to choose from a list")
|
|
199
|
+
.option("--channel <channel>", "Slack channel (e.g. #alerts); use with Slack integration")
|
|
200
|
+
.option("--to-addresses <emails>", "Email recipients, comma-separated; use with email integration")
|
|
160
201
|
.action(async (options) => {
|
|
161
202
|
await executeNotificationsTest({
|
|
162
|
-
|
|
203
|
+
integration: options.integration,
|
|
204
|
+
channel: options.channel,
|
|
205
|
+
toAddresses: options.toAddresses,
|
|
163
206
|
});
|
|
164
207
|
});
|
|
165
208
|
// Integrations command group
|
|
@@ -170,14 +213,14 @@ integrations
|
|
|
170
213
|
.command("list")
|
|
171
214
|
.description("List integrations")
|
|
172
215
|
.option("--category <cat>", "Filter by category: secrets, notifications, metrics")
|
|
173
|
-
.option("--provider
|
|
216
|
+
.option("--provider <provider>", "Filter by provider")
|
|
174
217
|
.option("--environment <env>", "Filter by environment")
|
|
175
218
|
.option("--enabled", "Filter by enabled status")
|
|
176
219
|
.option("--json", "Output as JSON")
|
|
177
220
|
.action(async (options) => {
|
|
178
221
|
await executeIntegrationsList({
|
|
179
222
|
category: options.category,
|
|
180
|
-
|
|
223
|
+
provider: options.provider,
|
|
181
224
|
environment: options.environment,
|
|
182
225
|
enabled: options.enabled ? true : undefined,
|
|
183
226
|
json: options.json,
|
|
@@ -191,24 +234,47 @@ integrations
|
|
|
191
234
|
await executeIntegrationsShow({ id, json: options.json });
|
|
192
235
|
});
|
|
193
236
|
integrations
|
|
194
|
-
.command("
|
|
195
|
-
.description("
|
|
196
|
-
.
|
|
197
|
-
.
|
|
198
|
-
.requiredOption("--name <name>", "Display name")
|
|
199
|
-
.option("--environment <env>", "Environment scope (omit for org-wide)")
|
|
200
|
-
.option("--enabled", "Enabled")
|
|
237
|
+
.command("connect [type] [provider]")
|
|
238
|
+
.description("Connect an integration.")
|
|
239
|
+
.option("--name <name>", "Display name for the integration")
|
|
240
|
+
.option("--environment <env>", "Environment scope")
|
|
201
241
|
.option("--json", "Output as JSON")
|
|
202
|
-
.action(async (options) => {
|
|
203
|
-
await
|
|
204
|
-
category:
|
|
205
|
-
|
|
242
|
+
.action(async (category, provider, options) => {
|
|
243
|
+
await executeIntegrationsConnect({
|
|
244
|
+
category: category ?? "",
|
|
245
|
+
provider: provider ?? "",
|
|
206
246
|
name: options.name,
|
|
207
247
|
environment: options.environment,
|
|
208
|
-
enabled: options.enabled,
|
|
209
248
|
json: options.json,
|
|
210
249
|
});
|
|
211
250
|
});
|
|
251
|
+
//integrations
|
|
252
|
+
// .command("add")
|
|
253
|
+
// .description(
|
|
254
|
+
// "Add an integration (no credentials in Phase 1; use API for credentials)",
|
|
255
|
+
// )
|
|
256
|
+
// .requiredOption(
|
|
257
|
+
// "--category <cat>",
|
|
258
|
+
// "Category: secrets, notifications, metrics",
|
|
259
|
+
// )
|
|
260
|
+
// .requiredOption(
|
|
261
|
+
// "--provider-type <type>",
|
|
262
|
+
// "Provider type (e.g. slack_webhook, datadog)",
|
|
263
|
+
// )
|
|
264
|
+
// .requiredOption("--name <name>", "Display name")
|
|
265
|
+
// .option("--environment <env>", "Environment scope (omit for org-wide)")
|
|
266
|
+
// .option("--enabled", "Enabled")
|
|
267
|
+
// .option("--json", "Output as JSON")
|
|
268
|
+
// .action(async (options) => {
|
|
269
|
+
// await executeIntegrationsAdd({
|
|
270
|
+
// category: options.category,
|
|
271
|
+
// providerType: options.providerType,
|
|
272
|
+
// name: options.name,
|
|
273
|
+
// environment: options.environment,
|
|
274
|
+
// enabled: options.enabled,
|
|
275
|
+
// json: options.json,
|
|
276
|
+
// });
|
|
277
|
+
// });
|
|
212
278
|
integrations
|
|
213
279
|
.command("update <id>")
|
|
214
280
|
.description("Update an integration")
|
|
@@ -232,23 +298,6 @@ integrations
|
|
|
232
298
|
.action(async (id) => {
|
|
233
299
|
await executeIntegrationsRemove({ id });
|
|
234
300
|
});
|
|
235
|
-
integrations
|
|
236
|
-
.command("connect <provider>")
|
|
237
|
-
.description("Connect an OAuth integration (opens browser)")
|
|
238
|
-
.option("--name <name>", "Display name for the integration")
|
|
239
|
-
.option("--environment <env>", "Environment scope")
|
|
240
|
-
.option("--category <cat>", "Category (default: notifications)")
|
|
241
|
-
.option("--json", "Output integration ID as JSON")
|
|
242
|
-
.action(async (provider, options) => {
|
|
243
|
-
const { executeIntegrationsConnect } = await import("./commands/hub/integrations.js");
|
|
244
|
-
await executeIntegrationsConnect({
|
|
245
|
-
providerType: provider,
|
|
246
|
-
name: options.name,
|
|
247
|
-
environment: options.environment,
|
|
248
|
-
category: options.category,
|
|
249
|
-
json: options.json,
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
301
|
// Secrets command group
|
|
253
302
|
const secrets = hub
|
|
254
303
|
.command("secrets")
|
package/dist/commands/env.d.ts
CHANGED
|
@@ -2,3 +2,11 @@
|
|
|
2
2
|
* List all available environments
|
|
3
3
|
*/
|
|
4
4
|
export declare function executeEnvList(): Promise<void>;
|
|
5
|
+
/**
|
|
6
|
+
* Add a new environment
|
|
7
|
+
*/
|
|
8
|
+
export declare function executeEnvAdd(name: string): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Remove an environment
|
|
11
|
+
*/
|
|
12
|
+
export declare function executeEnvRemove(name: string): Promise<void>;
|
package/dist/commands/env.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { loadState } from "../core/state.js";
|
|
1
|
+
import { loadState, addEnvironment, removeEnvironment } from "../core/state.js";
|
|
2
2
|
import { terminal } from "../utils/terminal.js";
|
|
3
3
|
/**
|
|
4
4
|
* List all available environments
|
|
@@ -27,3 +27,33 @@ export async function executeEnvList() {
|
|
|
27
27
|
terminal.exit(1);
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Add a new environment
|
|
32
|
+
*/
|
|
33
|
+
export async function executeEnvAdd(name) {
|
|
34
|
+
try {
|
|
35
|
+
await addEnvironment(name, {});
|
|
36
|
+
terminal.success(`Environment '${name}' added successfully.`);
|
|
37
|
+
terminal.blank();
|
|
38
|
+
terminal.dim(`Add variables with: griffin variables add KEY=VALUE --env ${name}`);
|
|
39
|
+
terminal.blank();
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
terminal.error(error.message);
|
|
43
|
+
terminal.exit(1);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Remove an environment
|
|
48
|
+
*/
|
|
49
|
+
export async function executeEnvRemove(name) {
|
|
50
|
+
try {
|
|
51
|
+
await removeEnvironment(name);
|
|
52
|
+
terminal.success(`Environment '${name}' removed successfully.`);
|
|
53
|
+
terminal.blank();
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
terminal.error(error.message);
|
|
57
|
+
terminal.exit(1);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import type { IntegrationCategory, Provider } from "@griffin-app/griffin-ts/types";
|
|
1
2
|
export interface IntegrationsListOptions {
|
|
2
|
-
category?:
|
|
3
|
-
|
|
3
|
+
category?: IntegrationCategory;
|
|
4
|
+
provider?: Provider;
|
|
4
5
|
environment?: string;
|
|
5
6
|
enabled?: boolean;
|
|
6
7
|
json?: boolean;
|
|
@@ -12,8 +13,8 @@ export interface IntegrationsShowOptions {
|
|
|
12
13
|
}
|
|
13
14
|
export declare function executeIntegrationsShow(options: IntegrationsShowOptions): Promise<void>;
|
|
14
15
|
export interface IntegrationsAddOptions {
|
|
15
|
-
category:
|
|
16
|
-
|
|
16
|
+
category: IntegrationCategory;
|
|
17
|
+
provider: Provider;
|
|
17
18
|
name: string;
|
|
18
19
|
config?: Record<string, string>;
|
|
19
20
|
environment?: string;
|
|
@@ -36,10 +37,11 @@ export interface IntegrationsRemoveOptions {
|
|
|
36
37
|
}
|
|
37
38
|
export declare function executeIntegrationsRemove(options: IntegrationsRemoveOptions): Promise<void>;
|
|
38
39
|
export interface IntegrationsConnectOptions {
|
|
39
|
-
|
|
40
|
+
category: IntegrationCategory;
|
|
41
|
+
provider: Provider;
|
|
40
42
|
name?: string;
|
|
41
43
|
environment?: string;
|
|
42
|
-
category?: string;
|
|
43
44
|
json?: boolean;
|
|
44
45
|
}
|
|
46
|
+
export declare function printConnectHelp(): void;
|
|
45
47
|
export declare function executeIntegrationsConnect(options: IntegrationsConnectOptions): Promise<void>;
|
|
@@ -1,59 +1,21 @@
|
|
|
1
1
|
import { exec } from "node:child_process";
|
|
2
2
|
import { platform } from "node:os";
|
|
3
3
|
import { loadState } from "../../core/state.js";
|
|
4
|
-
import { getHubCredentials } from "../../core/credentials.js";
|
|
5
4
|
import { terminal } from "../../utils/terminal.js";
|
|
6
|
-
|
|
5
|
+
import { getProvider, getProviders, getCategories, getAllProvidersByCategory, } from "../../providers/registry.js";
|
|
6
|
+
import { createSdkWithCredentials } from "../../core/sdk.js";
|
|
7
|
+
export async function executeIntegrationsList(options) {
|
|
7
8
|
const state = await loadState();
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"Content-Type": "application/json",
|
|
17
|
-
};
|
|
18
|
-
if (credentials?.token) {
|
|
19
|
-
headers["Authorization"] = `Bearer ${credentials.token}`;
|
|
20
|
-
}
|
|
21
|
-
const res = await fetch(url, {
|
|
22
|
-
method: options.method ?? "GET",
|
|
23
|
-
headers,
|
|
24
|
-
body: options.body ? JSON.stringify(options.body) : undefined,
|
|
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
|
+
},
|
|
25
17
|
});
|
|
26
|
-
const
|
|
27
|
-
let json;
|
|
28
|
-
try {
|
|
29
|
-
json = text ? JSON.parse(text) : {};
|
|
30
|
-
}
|
|
31
|
-
catch {
|
|
32
|
-
return { error: res.ok ? "Invalid response" : text || res.statusText };
|
|
33
|
-
}
|
|
34
|
-
if (!res.ok) {
|
|
35
|
-
return { error: json.error ?? res.statusText };
|
|
36
|
-
}
|
|
37
|
-
return json;
|
|
38
|
-
}
|
|
39
|
-
export async function executeIntegrationsList(options) {
|
|
40
|
-
const params = new URLSearchParams();
|
|
41
|
-
if (options.category)
|
|
42
|
-
params.set("category", options.category);
|
|
43
|
-
if (options.providerType)
|
|
44
|
-
params.set("providerType", options.providerType);
|
|
45
|
-
if (options.environment != null)
|
|
46
|
-
params.set("environment", options.environment);
|
|
47
|
-
if (options.enabled != null)
|
|
48
|
-
params.set("enabled", String(options.enabled));
|
|
49
|
-
const qs = params.toString();
|
|
50
|
-
const path = `/integrations${qs ? `?${qs}` : ""}`;
|
|
51
|
-
const result = await hubFetch(path);
|
|
52
|
-
if (result.error) {
|
|
53
|
-
terminal.error(result.error);
|
|
54
|
-
process.exit(1);
|
|
55
|
-
}
|
|
56
|
-
const list = result.data ?? [];
|
|
18
|
+
const list = result.data?.data ?? [];
|
|
57
19
|
if (options.json) {
|
|
58
20
|
terminal.log(JSON.stringify(list, null, 2));
|
|
59
21
|
return;
|
|
@@ -69,9 +31,9 @@ export async function executeIntegrationsList(options) {
|
|
|
69
31
|
});
|
|
70
32
|
for (const i of list) {
|
|
71
33
|
table.push([
|
|
72
|
-
i.id
|
|
34
|
+
i.id,
|
|
73
35
|
i.category,
|
|
74
|
-
i.
|
|
36
|
+
i.provider,
|
|
75
37
|
i.name,
|
|
76
38
|
i.environment ?? "(all)",
|
|
77
39
|
i.enabled ? "✓" : "✗",
|
|
@@ -80,12 +42,12 @@ export async function executeIntegrationsList(options) {
|
|
|
80
42
|
terminal.log(table.toString());
|
|
81
43
|
}
|
|
82
44
|
export async function executeIntegrationsShow(options) {
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
const integration = result.data;
|
|
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;
|
|
89
51
|
if (!integration) {
|
|
90
52
|
terminal.error("Integration not found.");
|
|
91
53
|
process.exit(1);
|
|
@@ -97,7 +59,7 @@ export async function executeIntegrationsShow(options) {
|
|
|
97
59
|
terminal.info(integration.name);
|
|
98
60
|
terminal.dim(`ID: ${integration.id}`);
|
|
99
61
|
terminal.log(`Category: ${integration.category}`);
|
|
100
|
-
terminal.log(`Provider: ${integration.
|
|
62
|
+
terminal.log(`Provider: ${integration.provider}`);
|
|
101
63
|
terminal.log(`Environment: ${integration.environment ?? "(all)"}`);
|
|
102
64
|
terminal.log(`Enabled: ${integration.enabled ? "Yes" : "No"}`);
|
|
103
65
|
terminal.log(`Has credentials: ${integration.hasCredentials ? "Yes" : "No"}`);
|
|
@@ -108,23 +70,20 @@ export async function executeIntegrationsShow(options) {
|
|
|
108
70
|
export async function executeIntegrationsAdd(options) {
|
|
109
71
|
const body = {
|
|
110
72
|
category: options.category,
|
|
111
|
-
|
|
73
|
+
provider: options.provider,
|
|
112
74
|
name: options.name,
|
|
113
75
|
config: options.config ?? {},
|
|
114
76
|
environment: options.environment ?? null,
|
|
115
77
|
enabled: options.enabled ?? true,
|
|
116
78
|
};
|
|
117
|
-
const
|
|
118
|
-
|
|
79
|
+
const state = await loadState();
|
|
80
|
+
const sdk = await createSdkWithCredentials(state.hub.baseUrl);
|
|
81
|
+
const result = await sdk.postIntegrations({
|
|
119
82
|
body,
|
|
120
83
|
});
|
|
121
|
-
|
|
122
|
-
terminal.error(result.error);
|
|
123
|
-
process.exit(1);
|
|
124
|
-
}
|
|
125
|
-
const integration = result.data;
|
|
84
|
+
const integration = result.data?.data;
|
|
126
85
|
if (!integration) {
|
|
127
|
-
terminal.error("
|
|
86
|
+
terminal.error("Integration not found.");
|
|
128
87
|
process.exit(1);
|
|
129
88
|
}
|
|
130
89
|
if (options.json) {
|
|
@@ -135,37 +94,44 @@ export async function executeIntegrationsAdd(options) {
|
|
|
135
94
|
}
|
|
136
95
|
export async function executeIntegrationsUpdate(options) {
|
|
137
96
|
const body = {};
|
|
138
|
-
if (options.name !== undefined)
|
|
139
|
-
|
|
140
|
-
if (options.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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;
|
|
152
113
|
if (!integration) {
|
|
153
|
-
terminal.error("
|
|
114
|
+
terminal.error("Integration not found.");
|
|
154
115
|
process.exit(1);
|
|
155
116
|
}
|
|
156
117
|
if (options.json) {
|
|
157
118
|
terminal.log(JSON.stringify(integration, null, 2));
|
|
158
119
|
return;
|
|
159
120
|
}
|
|
160
|
-
terminal.
|
|
121
|
+
terminal.success(`Updated integration ${integration.name}`);
|
|
161
122
|
}
|
|
162
123
|
export async function executeIntegrationsRemove(options) {
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
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;
|
|
130
|
+
if (!integration) {
|
|
131
|
+
terminal.error("Integration not found.");
|
|
166
132
|
process.exit(1);
|
|
167
133
|
}
|
|
168
|
-
terminal.
|
|
134
|
+
terminal.success(`Integration removed.`);
|
|
169
135
|
}
|
|
170
136
|
function openBrowser(url) {
|
|
171
137
|
const cmd = platform() === "darwin"
|
|
@@ -182,19 +148,32 @@ function openBrowser(url) {
|
|
|
182
148
|
const POLL_INITIAL_MS = 2000;
|
|
183
149
|
const POLL_MAX_MS = 10000;
|
|
184
150
|
const POLL_TIMEOUT_MS = 15 * 60 * 1000; // 15 minutes
|
|
185
|
-
export
|
|
186
|
-
|
|
151
|
+
export function printConnectHelp() {
|
|
152
|
+
terminal.log("Usage: griffin hub integrations connect <category> <provider> [options]");
|
|
153
|
+
terminal.blank();
|
|
154
|
+
terminal.log("Categories and Providers:");
|
|
155
|
+
const byCategory = getAllProvidersByCategory();
|
|
156
|
+
for (const category of getCategories()) {
|
|
157
|
+
const providers = byCategory.get(category) ?? [];
|
|
158
|
+
terminal.log(` ${category}`);
|
|
159
|
+
for (const p of providers) {
|
|
160
|
+
terminal.log(` ${p.provider.padEnd(14)} ${p.displayName}`);
|
|
161
|
+
}
|
|
162
|
+
terminal.blank();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
async function connectOAuth(options, _provider) {
|
|
187
166
|
const body = {
|
|
188
|
-
category,
|
|
189
|
-
|
|
190
|
-
name: options.name ??
|
|
167
|
+
category: options.category,
|
|
168
|
+
provider: options.provider,
|
|
169
|
+
name: options.name ?? options.provider,
|
|
191
170
|
environment: options.environment ?? undefined,
|
|
192
171
|
};
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
172
|
+
const state = await loadState();
|
|
173
|
+
const sdk = await createSdkWithCredentials(state.hub.baseUrl);
|
|
174
|
+
const result = await sdk.postIntegrationsOauthInitiate({
|
|
175
|
+
body,
|
|
176
|
+
});
|
|
198
177
|
const data = result.data;
|
|
199
178
|
if (!data) {
|
|
200
179
|
terminal.error("Initiate failed.");
|
|
@@ -208,10 +187,9 @@ export async function executeIntegrationsConnect(options) {
|
|
|
208
187
|
const start = Date.now();
|
|
209
188
|
let interval = POLL_INITIAL_MS;
|
|
210
189
|
const poll = async () => {
|
|
211
|
-
const statusResult = await
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
190
|
+
const statusResult = await sdk.getIntegrationsOauthStatusByTransactionId({
|
|
191
|
+
path: { transactionId: data.transactionId },
|
|
192
|
+
});
|
|
215
193
|
const status = statusResult.data;
|
|
216
194
|
if (!status) {
|
|
217
195
|
throw new Error("Invalid status response");
|
|
@@ -253,3 +231,78 @@ export async function executeIntegrationsConnect(options) {
|
|
|
253
231
|
process.exit(1);
|
|
254
232
|
}
|
|
255
233
|
}
|
|
234
|
+
async function connectCredentials(options, provider) {
|
|
235
|
+
const config = {};
|
|
236
|
+
for (const field of provider.configFields ?? []) {
|
|
237
|
+
const value = await terminal.input(`${field.label}${field.required ? " (required)" : ""}`, field.default);
|
|
238
|
+
if (field.required && !value?.trim()) {
|
|
239
|
+
terminal.error(`${field.label} is required`);
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
if (value?.trim())
|
|
243
|
+
config[field.key] = value.trim();
|
|
244
|
+
}
|
|
245
|
+
const credentials = {};
|
|
246
|
+
for (const field of provider.credentialFields ?? []) {
|
|
247
|
+
const value = field.secret
|
|
248
|
+
? await terminal.password(`${field.label}${field.required ? " (required)" : ""}`)
|
|
249
|
+
: await terminal.input(`${field.label}${field.required ? " (required)" : ""}`, field.default);
|
|
250
|
+
if (field.required && !value?.trim()) {
|
|
251
|
+
terminal.error(`${field.label} is required`);
|
|
252
|
+
process.exit(1);
|
|
253
|
+
}
|
|
254
|
+
if (value?.trim())
|
|
255
|
+
credentials[field.key] = value.trim();
|
|
256
|
+
}
|
|
257
|
+
const body = {
|
|
258
|
+
category: options.category,
|
|
259
|
+
provider: options.provider,
|
|
260
|
+
name: options.name ?? provider.displayName,
|
|
261
|
+
config,
|
|
262
|
+
credentials: Object.keys(credentials).length > 0 ? credentials : undefined,
|
|
263
|
+
environment: options.environment ?? null,
|
|
264
|
+
enabled: true,
|
|
265
|
+
};
|
|
266
|
+
const state = await loadState();
|
|
267
|
+
const sdk = await createSdkWithCredentials(state.hub.baseUrl);
|
|
268
|
+
const result = await sdk.postIntegrations({
|
|
269
|
+
body,
|
|
270
|
+
});
|
|
271
|
+
const integration = result.data?.data;
|
|
272
|
+
if (!integration) {
|
|
273
|
+
terminal.error("Integration not found.");
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
if (options.json) {
|
|
277
|
+
terminal.log(JSON.stringify(integration, null, 2));
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
terminal.success(`Integration connected: ${integration.name} (${integration.id})`);
|
|
281
|
+
}
|
|
282
|
+
export async function executeIntegrationsConnect(options) {
|
|
283
|
+
if (!options.category || !options.provider) {
|
|
284
|
+
printConnectHelp();
|
|
285
|
+
process.exit(0);
|
|
286
|
+
}
|
|
287
|
+
const providerDef = getProvider(options.category, options.provider);
|
|
288
|
+
if (!providerDef) {
|
|
289
|
+
terminal.error(`Unknown provider: ${options.category}/${options.provider}`);
|
|
290
|
+
const providersForCategory = getProviders(options.category);
|
|
291
|
+
if (providersForCategory.length > 0) {
|
|
292
|
+
terminal.info(`Available providers for ${options.category}:`);
|
|
293
|
+
for (const p of providersForCategory) {
|
|
294
|
+
terminal.log(` - ${p.provider} (${p.displayName})`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
terminal.info("Available categories: " + getCategories().join(", "));
|
|
299
|
+
}
|
|
300
|
+
process.exit(1);
|
|
301
|
+
}
|
|
302
|
+
if (providerDef.authMethod === "oauth") {
|
|
303
|
+
await connectOAuth(options, providerDef);
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
await connectCredentials(options, providerDef);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
@@ -4,7 +4,12 @@ export interface NotificationsListOptions {
|
|
|
4
4
|
json?: boolean;
|
|
5
5
|
}
|
|
6
6
|
export interface NotificationsTestOptions {
|
|
7
|
-
|
|
7
|
+
/** Integration id or name. If omitted, wizard runs (select integration + provider options). */
|
|
8
|
+
integration?: string;
|
|
9
|
+
/** Slack channel (e.g. #alerts). Use with Slack integration. */
|
|
10
|
+
channel?: string;
|
|
11
|
+
/** Email recipients, comma-separated. Use with email integration. */
|
|
12
|
+
toAddresses?: string;
|
|
8
13
|
}
|
|
9
14
|
/**
|
|
10
15
|
* List notification rules (read-only).
|
|
@@ -12,6 +17,7 @@ export interface NotificationsTestOptions {
|
|
|
12
17
|
*/
|
|
13
18
|
export declare function executeNotificationsList(options: NotificationsListOptions): Promise<void>;
|
|
14
19
|
/**
|
|
15
|
-
* Test a
|
|
20
|
+
* Test a notification integration. Without options, runs a wizard (select integration, then provider-specific prompts).
|
|
21
|
+
* With --integration and routing flags, runs non-interactively.
|
|
16
22
|
*/
|
|
17
23
|
export declare function executeNotificationsTest(options: NotificationsTestOptions): Promise<void>;
|