@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
|
@@ -3,7 +3,8 @@ import { createSdkWithCredentials } from "../../core/sdk.js";
|
|
|
3
3
|
import { terminal } from "../../utils/terminal.js";
|
|
4
4
|
import { withSDKErrorHandling } from "../../utils/sdk-error.js";
|
|
5
5
|
/**
|
|
6
|
-
* List notification rules
|
|
6
|
+
* List notification rules (read-only).
|
|
7
|
+
* Rules are defined in monitor DSL and synced via `griffin hub apply`.
|
|
7
8
|
*/
|
|
8
9
|
export async function executeNotificationsList(options) {
|
|
9
10
|
try {
|
|
@@ -28,26 +29,22 @@ export async function executeNotificationsList(options) {
|
|
|
28
29
|
}
|
|
29
30
|
if (rules.length === 0) {
|
|
30
31
|
terminal.info("No notification rules found.");
|
|
32
|
+
terminal.dim("Define notifications in your monitor DSL and run griffin hub apply.");
|
|
31
33
|
return;
|
|
32
34
|
}
|
|
33
|
-
terminal.info("Notification Rules");
|
|
35
|
+
terminal.info("Notification Rules (synced from monitor DSL)");
|
|
34
36
|
terminal.blank();
|
|
35
37
|
const table = terminal.table({
|
|
36
|
-
head: ["ID", "
|
|
38
|
+
head: ["ID", "Monitor", "Integration", "Trigger", "Enabled"],
|
|
37
39
|
});
|
|
38
40
|
for (const rule of rules) {
|
|
39
41
|
const triggerDesc = formatTrigger(rule.trigger);
|
|
40
|
-
const channels = rule.channels
|
|
41
|
-
.map((ch) => (ch.type === "webhook" ? "webhook" : ch.type))
|
|
42
|
-
.join(", ");
|
|
43
|
-
const monitorName = rule.monitorId || "all monitors";
|
|
44
42
|
const enabled = rule.enabled ? "✓" : "✗";
|
|
45
43
|
table.push([
|
|
46
44
|
rule.id.substring(0, 8) + "...",
|
|
47
|
-
rule.
|
|
48
|
-
|
|
45
|
+
rule.monitorId ?? "-",
|
|
46
|
+
rule.integrationName ?? "-",
|
|
49
47
|
triggerDesc,
|
|
50
|
-
channels,
|
|
51
48
|
enabled,
|
|
52
49
|
]);
|
|
53
50
|
}
|
|
@@ -58,76 +55,6 @@ export async function executeNotificationsList(options) {
|
|
|
58
55
|
terminal.exit(1);
|
|
59
56
|
}
|
|
60
57
|
}
|
|
61
|
-
/**
|
|
62
|
-
* Add a notification rule
|
|
63
|
-
*/
|
|
64
|
-
export async function executeNotificationsAdd(options) {
|
|
65
|
-
try {
|
|
66
|
-
const state = await loadState();
|
|
67
|
-
if (!state.hub?.baseUrl) {
|
|
68
|
-
terminal.error("Hub connection not configured.");
|
|
69
|
-
terminal.dim("Connect with:");
|
|
70
|
-
terminal.dim(" griffin hub connect --url <url> --token <token>");
|
|
71
|
-
terminal.exit(1);
|
|
72
|
-
}
|
|
73
|
-
// Parse trigger
|
|
74
|
-
const trigger = parseTrigger(options.trigger);
|
|
75
|
-
if (!trigger) {
|
|
76
|
-
terminal.error(`Invalid trigger: ${options.trigger}. Expected format: type[:value]`);
|
|
77
|
-
terminal.dim("Examples:");
|
|
78
|
-
terminal.dim(" run_failed");
|
|
79
|
-
terminal.dim(" consecutive_failures:3");
|
|
80
|
-
terminal.dim(" success_rate_below:80:60");
|
|
81
|
-
terminal.exit(1);
|
|
82
|
-
}
|
|
83
|
-
const sdk = await createSdkWithCredentials(state.hub.baseUrl);
|
|
84
|
-
const response = await withSDKErrorHandling(() => sdk.postNotificationsRules({
|
|
85
|
-
body: {
|
|
86
|
-
name: options.name,
|
|
87
|
-
monitorId: options.monitor,
|
|
88
|
-
environment: options.environment,
|
|
89
|
-
location: options.location,
|
|
90
|
-
trigger,
|
|
91
|
-
channels: [
|
|
92
|
-
{
|
|
93
|
-
type: "webhook",
|
|
94
|
-
url: options.webhook,
|
|
95
|
-
},
|
|
96
|
-
],
|
|
97
|
-
cooldownMinutes: options.cooldown,
|
|
98
|
-
},
|
|
99
|
-
}), "Failed to create notification rule");
|
|
100
|
-
const rule = response?.data?.data;
|
|
101
|
-
if (rule) {
|
|
102
|
-
terminal.success(`Created notification rule: ${rule.id}`);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
catch (error) {
|
|
106
|
-
terminal.error(error.message);
|
|
107
|
-
terminal.exit(1);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Delete a notification rule
|
|
112
|
-
*/
|
|
113
|
-
export async function executeNotificationsDelete(options) {
|
|
114
|
-
try {
|
|
115
|
-
const state = await loadState();
|
|
116
|
-
if (!state.hub?.baseUrl) {
|
|
117
|
-
terminal.error("Hub connection not configured.");
|
|
118
|
-
terminal.dim("Connect with:");
|
|
119
|
-
terminal.dim(" griffin hub connect --url <url> --token <token>");
|
|
120
|
-
terminal.exit(1);
|
|
121
|
-
}
|
|
122
|
-
const sdk = await createSdkWithCredentials(state.hub.baseUrl);
|
|
123
|
-
await withSDKErrorHandling(() => sdk.deleteNotificationsRulesById({ path: { id: options.id } }), "Failed to delete notification rule");
|
|
124
|
-
terminal.success(`Deleted notification rule: ${options.id}`);
|
|
125
|
-
}
|
|
126
|
-
catch (error) {
|
|
127
|
-
terminal.error(error.message);
|
|
128
|
-
terminal.exit(1);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
58
|
/**
|
|
132
59
|
* Test a webhook
|
|
133
60
|
*/
|
|
@@ -165,45 +92,6 @@ export async function executeNotificationsTest(options) {
|
|
|
165
92
|
terminal.exit(1);
|
|
166
93
|
}
|
|
167
94
|
}
|
|
168
|
-
/**
|
|
169
|
-
* Parse trigger string into trigger object
|
|
170
|
-
*/
|
|
171
|
-
function parseTrigger(triggerStr) {
|
|
172
|
-
const parts = triggerStr.split(":");
|
|
173
|
-
const type = parts[0];
|
|
174
|
-
switch (type) {
|
|
175
|
-
case "run_failed":
|
|
176
|
-
return { type: "run_failed" };
|
|
177
|
-
case "run_recovered":
|
|
178
|
-
return { type: "run_recovered" };
|
|
179
|
-
case "consecutive_failures":
|
|
180
|
-
if (parts.length < 2)
|
|
181
|
-
return null;
|
|
182
|
-
return {
|
|
183
|
-
type: "consecutive_failures",
|
|
184
|
-
threshold: parseInt(parts[1], 10),
|
|
185
|
-
};
|
|
186
|
-
case "success_rate_below":
|
|
187
|
-
if (parts.length < 3)
|
|
188
|
-
return null;
|
|
189
|
-
return {
|
|
190
|
-
type: "success_rate_below",
|
|
191
|
-
threshold: parseFloat(parts[1]),
|
|
192
|
-
window_minutes: parseInt(parts[2], 10),
|
|
193
|
-
};
|
|
194
|
-
case "latency_above":
|
|
195
|
-
if (parts.length < 4)
|
|
196
|
-
return null;
|
|
197
|
-
return {
|
|
198
|
-
type: "latency_above",
|
|
199
|
-
threshold_ms: parseInt(parts[1], 10),
|
|
200
|
-
percentile: parts[2],
|
|
201
|
-
window_minutes: parseInt(parts[3], 10),
|
|
202
|
-
};
|
|
203
|
-
default:
|
|
204
|
-
return null;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
95
|
/**
|
|
208
96
|
* Format trigger for display
|
|
209
97
|
*/
|
package/dist/commands/hub/run.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { loadState, resolveEnvironment } from "../../core/state.js";
|
|
2
2
|
import { createSdkWithCredentials } from "../../core/sdk.js";
|
|
3
|
-
import { discoverMonitors, formatDiscoveryErrors } from "../../core/discovery.js";
|
|
3
|
+
import { discoverMonitors, formatDiscoveryErrors, } from "../../core/discovery.js";
|
|
4
4
|
import { computeDiff } from "../../core/diff.js";
|
|
5
5
|
import { terminal } from "../../utils/terminal.js";
|
|
6
6
|
import { withSDKErrorHandling } from "../../utils/sdk-error.js";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface SecretsListOptions {
|
|
2
|
+
environment?: string;
|
|
3
|
+
json?: boolean;
|
|
4
|
+
}
|
|
5
|
+
export interface SecretsSetOptions {
|
|
6
|
+
name: string;
|
|
7
|
+
environment?: string;
|
|
8
|
+
value?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SecretsGetOptions {
|
|
11
|
+
name: string;
|
|
12
|
+
environment?: string;
|
|
13
|
+
json?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface SecretsDeleteOptions {
|
|
16
|
+
name: string;
|
|
17
|
+
environment?: string;
|
|
18
|
+
force?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* List secrets (metadata only).
|
|
22
|
+
* Uses the resolved secrets integration for the organization and environment
|
|
23
|
+
* (from hub integrations API or platform default / legacy config).
|
|
24
|
+
*/
|
|
25
|
+
export declare function executeSecretsList(options: SecretsListOptions): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Set a secret (prompts for value if not provided)
|
|
28
|
+
*/
|
|
29
|
+
export declare function executeSecretsSet(options: SecretsSetOptions): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Get secret metadata only
|
|
32
|
+
*/
|
|
33
|
+
export declare function executeSecretsGet(options: SecretsGetOptions): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Delete a secret (with confirmation unless --force)
|
|
36
|
+
*/
|
|
37
|
+
export declare function executeSecretsDelete(options: SecretsDeleteOptions): Promise<void>;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { createInterface } from "node:readline";
|
|
2
|
+
import { loadState } from "../../core/state.js";
|
|
3
|
+
import { terminal } from "../../utils/terminal.js";
|
|
4
|
+
import { withSDKErrorHandling } from "../../utils/sdk-error.js";
|
|
5
|
+
import { createSdkWithCredentials } from "../../core/sdk.js";
|
|
6
|
+
const SECRET_NAME_REGEX = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
7
|
+
/**
|
|
8
|
+
* List secrets (metadata only).
|
|
9
|
+
* Uses the resolved secrets integration for the organization and environment
|
|
10
|
+
* (from hub integrations API or platform default / legacy config).
|
|
11
|
+
*/
|
|
12
|
+
export async function executeSecretsList(options) {
|
|
13
|
+
try {
|
|
14
|
+
const state = await loadState();
|
|
15
|
+
if (!state.hub?.baseUrl) {
|
|
16
|
+
terminal.error("Hub connection not configured.");
|
|
17
|
+
terminal.dim("Connect with:");
|
|
18
|
+
terminal.dim(" griffin hub connect --url <url> --token <token>");
|
|
19
|
+
terminal.exit(1);
|
|
20
|
+
}
|
|
21
|
+
const baseUrl = state.hub.baseUrl;
|
|
22
|
+
const sdk = await createSdkWithCredentials(baseUrl);
|
|
23
|
+
const env = options.environment ?? "default";
|
|
24
|
+
const response = await withSDKErrorHandling(async () => {
|
|
25
|
+
return sdk.getSecrets({
|
|
26
|
+
query: {
|
|
27
|
+
environment: env,
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
}, "Failed to list secrets");
|
|
31
|
+
const secrets = response?.data?.data ?? [];
|
|
32
|
+
if (options.json) {
|
|
33
|
+
terminal.log(JSON.stringify(secrets, null, 2));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (secrets.length === 0) {
|
|
37
|
+
terminal.info(`No secrets found for environment "${env}".`);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
terminal.info(`Secrets (environment: ${env})`);
|
|
41
|
+
terminal.blank();
|
|
42
|
+
const table = terminal.table({
|
|
43
|
+
head: ["Name", "Created", "Updated"],
|
|
44
|
+
});
|
|
45
|
+
for (const s of secrets) {
|
|
46
|
+
table.push([s.name, s.createdAt ?? "-", s.updatedAt ?? "-"]);
|
|
47
|
+
}
|
|
48
|
+
terminal.log(table.toString());
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
terminal.error(error.message);
|
|
52
|
+
terminal.exit(1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Set a secret (prompts for value if not provided)
|
|
57
|
+
*/
|
|
58
|
+
export async function executeSecretsSet(options) {
|
|
59
|
+
try {
|
|
60
|
+
if (!SECRET_NAME_REGEX.test(options.name)) {
|
|
61
|
+
terminal.error("Secret name must start with a letter or underscore and contain only letters, numbers, and underscores.");
|
|
62
|
+
terminal.exit(1);
|
|
63
|
+
}
|
|
64
|
+
const state = await loadState();
|
|
65
|
+
if (!state.hub?.baseUrl) {
|
|
66
|
+
terminal.error("Hub connection not configured.");
|
|
67
|
+
terminal.dim("Connect with:");
|
|
68
|
+
terminal.dim(" griffin hub connect --url <url> --token <token>");
|
|
69
|
+
terminal.exit(1);
|
|
70
|
+
}
|
|
71
|
+
let value = options.value;
|
|
72
|
+
if (value === undefined) {
|
|
73
|
+
value = await promptSecret("Enter secret value:");
|
|
74
|
+
if (!value) {
|
|
75
|
+
terminal.error("Secret value cannot be empty.");
|
|
76
|
+
terminal.exit(1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const baseUrl = state.hub.baseUrl;
|
|
80
|
+
const env = options.environment ?? "default";
|
|
81
|
+
const sdk = await createSdkWithCredentials(baseUrl);
|
|
82
|
+
const response = await withSDKErrorHandling(async () => {
|
|
83
|
+
return sdk.putSecretsByName({
|
|
84
|
+
path: { name: options.name },
|
|
85
|
+
body: { value, environment: env },
|
|
86
|
+
});
|
|
87
|
+
}, "Failed to set secret");
|
|
88
|
+
const result = response?.data;
|
|
89
|
+
terminal.success(`Secret "${result.data.name}" saved.`);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
terminal.error(error.message);
|
|
93
|
+
terminal.exit(1);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function promptSecret(promptText) {
|
|
97
|
+
return new Promise((resolve) => {
|
|
98
|
+
const rl = createInterface({
|
|
99
|
+
input: process.stdin,
|
|
100
|
+
output: process.stdout,
|
|
101
|
+
});
|
|
102
|
+
rl.question(promptText, (answer) => {
|
|
103
|
+
rl.close();
|
|
104
|
+
resolve(answer.trim());
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get secret metadata only
|
|
110
|
+
*/
|
|
111
|
+
export async function executeSecretsGet(options) {
|
|
112
|
+
try {
|
|
113
|
+
const state = await loadState();
|
|
114
|
+
if (!state.hub?.baseUrl) {
|
|
115
|
+
terminal.error("Hub connection not configured.");
|
|
116
|
+
terminal.dim("Connect with:");
|
|
117
|
+
terminal.dim(" griffin hub connect --url <url> --token <token>");
|
|
118
|
+
terminal.exit(1);
|
|
119
|
+
}
|
|
120
|
+
const baseUrl = state.hub.baseUrl;
|
|
121
|
+
const env = options.environment ?? "default";
|
|
122
|
+
const sdk = await createSdkWithCredentials(baseUrl);
|
|
123
|
+
const response = await withSDKErrorHandling(async () => {
|
|
124
|
+
return sdk.getSecretsByName({
|
|
125
|
+
path: { name: options.name },
|
|
126
|
+
query: { environment: env },
|
|
127
|
+
});
|
|
128
|
+
}, "Failed to get secret");
|
|
129
|
+
const secret = response?.data?.data;
|
|
130
|
+
if (options.json) {
|
|
131
|
+
terminal.log(JSON.stringify(secret, null, 2));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
terminal.info(`Secret: ${secret.name}`);
|
|
135
|
+
terminal.dim(`Created: ${secret.createdAt ?? "-"}`);
|
|
136
|
+
terminal.dim(`Updated: ${secret.updatedAt ?? "-"}`);
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
terminal.error(error.message);
|
|
140
|
+
terminal.exit(1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Delete a secret (with confirmation unless --force)
|
|
145
|
+
*/
|
|
146
|
+
export async function executeSecretsDelete(options) {
|
|
147
|
+
try {
|
|
148
|
+
const state = await loadState();
|
|
149
|
+
if (!state.hub?.baseUrl) {
|
|
150
|
+
terminal.error("Hub connection not configured.");
|
|
151
|
+
terminal.dim("Connect with:");
|
|
152
|
+
terminal.dim(" griffin hub connect --url <url> --token <token>");
|
|
153
|
+
terminal.exit(1);
|
|
154
|
+
}
|
|
155
|
+
const baseUrl = state.hub.baseUrl;
|
|
156
|
+
const env = options.environment ?? "default";
|
|
157
|
+
const sdk = await createSdkWithCredentials(baseUrl);
|
|
158
|
+
if (!options.force) {
|
|
159
|
+
const rl = createInterface({
|
|
160
|
+
input: process.stdin,
|
|
161
|
+
output: process.stdout,
|
|
162
|
+
});
|
|
163
|
+
const answer = await new Promise((resolve) => {
|
|
164
|
+
rl.question(`Delete secret "${options.name}" (environment: ${env})? [y/N] `, (a) => {
|
|
165
|
+
rl.close();
|
|
166
|
+
resolve(a.trim().toLowerCase());
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
if (answer !== "y" && answer !== "yes") {
|
|
170
|
+
terminal.info("Aborted.");
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const response = await withSDKErrorHandling(async () => {
|
|
175
|
+
return sdk.deleteSecretsByName({
|
|
176
|
+
path: { name: options.name },
|
|
177
|
+
query: { environment: env },
|
|
178
|
+
});
|
|
179
|
+
}, "Failed to delete secret");
|
|
180
|
+
const result = response?.data;
|
|
181
|
+
terminal.success(`Secret deleted.`);
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
terminal.error(error.message);
|
|
185
|
+
terminal.exit(1);
|
|
186
|
+
}
|
|
187
|
+
}
|
package/dist/commands/init.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { initState, stateExists, getStateFilePath,
|
|
3
|
+
import { initState, stateExists, getStateFilePath, } from "../core/state.js";
|
|
4
4
|
import { detectProjectId } from "../core/project.js";
|
|
5
5
|
import { terminal } from "../utils/terminal.js";
|
|
6
6
|
const VARIABLES_FILE = "variables.yaml";
|
|
@@ -34,11 +34,6 @@ export async function executeInit(options) {
|
|
|
34
34
|
// Initialize state file
|
|
35
35
|
await initState(projectId);
|
|
36
36
|
terminal.success(`Created state file: ${terminal.colors.dim(getStateFilePath())}`);
|
|
37
|
-
// Create default environments
|
|
38
|
-
await addEnvironment("dev", {});
|
|
39
|
-
await addEnvironment("staging", {});
|
|
40
|
-
await addEnvironment("production", {});
|
|
41
|
-
terminal.success("Created default environments (dev, staging, production)");
|
|
42
37
|
// Create variables.yaml if it doesn't exist
|
|
43
38
|
const variablesPath = path.join(process.cwd(), VARIABLES_FILE);
|
|
44
39
|
try {
|
package/dist/core/apply.test.js
CHANGED
|
@@ -116,7 +116,9 @@ describe("applyDiff", () => {
|
|
|
116
116
|
it("should apply delete action", async () => {
|
|
117
117
|
const remoteMonitor = createMonitor("old-monitor");
|
|
118
118
|
const diff = {
|
|
119
|
-
actions: [
|
|
119
|
+
actions: [
|
|
120
|
+
{ type: "delete", monitor: null, remoteMonitor, reason: "removed" },
|
|
121
|
+
],
|
|
120
122
|
summary: { creates: 0, updates: 0, deletes: 1, noops: 0 },
|
|
121
123
|
};
|
|
122
124
|
const mockMonitorApi = {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secrets configuration for CLI local runs.
|
|
3
|
+
* Reads configuration from environment variables to construct a secret provider.
|
|
4
|
+
*/
|
|
5
|
+
import { type SecretProvider, type SecretProviderConfig } from "@griffin-app/griffin-plan-executor";
|
|
6
|
+
/**
|
|
7
|
+
* Read secrets provider configuration from environment variables.
|
|
8
|
+
*
|
|
9
|
+
* Supported configurations:
|
|
10
|
+
*
|
|
11
|
+
* 1. Environment provider (default if no config):
|
|
12
|
+
* GRIFFIN_SECRETS_PROVIDER=env
|
|
13
|
+
* GRIFFIN_SECRETS_ENV_PREFIX=APP_
|
|
14
|
+
*
|
|
15
|
+
* 2. AWS Secrets Manager:
|
|
16
|
+
* GRIFFIN_SECRETS_PROVIDER=aws
|
|
17
|
+
* GRIFFIN_SECRETS_AWS_REGION=us-east-1
|
|
18
|
+
* GRIFFIN_SECRETS_AWS_PREFIX=myapp/
|
|
19
|
+
* GRIFFIN_SECRETS_AWS_ROLE_ARN=arn:aws:iam::123:role/griffin
|
|
20
|
+
* GRIFFIN_SECRETS_AWS_EXTERNAL_ID=xyz
|
|
21
|
+
* GRIFFIN_SECRETS_AWS_ACCESS_KEY_ID=...
|
|
22
|
+
* GRIFFIN_SECRETS_AWS_SECRET_ACCESS_KEY=...
|
|
23
|
+
*
|
|
24
|
+
* 3. HashiCorp Vault:
|
|
25
|
+
* GRIFFIN_SECRETS_PROVIDER=vault
|
|
26
|
+
* GRIFFIN_SECRETS_VAULT_ADDRESS=https://vault.example.com
|
|
27
|
+
* GRIFFIN_SECRETS_VAULT_TOKEN=...
|
|
28
|
+
* GRIFFIN_SECRETS_VAULT_NAMESPACE=myapp
|
|
29
|
+
* GRIFFIN_SECRETS_VAULT_KV_VERSION=2
|
|
30
|
+
* GRIFFIN_SECRETS_VAULT_PREFIX=myapp/
|
|
31
|
+
*/
|
|
32
|
+
export declare function getSecretsConfigFromEnv(): SecretProviderConfig;
|
|
33
|
+
/**
|
|
34
|
+
* Create a secret provider for CLI local runs.
|
|
35
|
+
* Reads configuration from environment variables.
|
|
36
|
+
*/
|
|
37
|
+
export declare function createSecretsProviderForCLI(): Promise<SecretProvider>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secrets configuration for CLI local runs.
|
|
3
|
+
* Reads configuration from environment variables to construct a secret provider.
|
|
4
|
+
*/
|
|
5
|
+
import { createSecretProvider, } from "@griffin-app/griffin-plan-executor";
|
|
6
|
+
/**
|
|
7
|
+
* Environment variable prefixes for secrets configuration.
|
|
8
|
+
*/
|
|
9
|
+
const ENV_PREFIX = "GRIFFIN_SECRETS_";
|
|
10
|
+
/**
|
|
11
|
+
* Read secrets provider configuration from environment variables.
|
|
12
|
+
*
|
|
13
|
+
* Supported configurations:
|
|
14
|
+
*
|
|
15
|
+
* 1. Environment provider (default if no config):
|
|
16
|
+
* GRIFFIN_SECRETS_PROVIDER=env
|
|
17
|
+
* GRIFFIN_SECRETS_ENV_PREFIX=APP_
|
|
18
|
+
*
|
|
19
|
+
* 2. AWS Secrets Manager:
|
|
20
|
+
* GRIFFIN_SECRETS_PROVIDER=aws
|
|
21
|
+
* GRIFFIN_SECRETS_AWS_REGION=us-east-1
|
|
22
|
+
* GRIFFIN_SECRETS_AWS_PREFIX=myapp/
|
|
23
|
+
* GRIFFIN_SECRETS_AWS_ROLE_ARN=arn:aws:iam::123:role/griffin
|
|
24
|
+
* GRIFFIN_SECRETS_AWS_EXTERNAL_ID=xyz
|
|
25
|
+
* GRIFFIN_SECRETS_AWS_ACCESS_KEY_ID=...
|
|
26
|
+
* GRIFFIN_SECRETS_AWS_SECRET_ACCESS_KEY=...
|
|
27
|
+
*
|
|
28
|
+
* 3. HashiCorp Vault:
|
|
29
|
+
* GRIFFIN_SECRETS_PROVIDER=vault
|
|
30
|
+
* GRIFFIN_SECRETS_VAULT_ADDRESS=https://vault.example.com
|
|
31
|
+
* GRIFFIN_SECRETS_VAULT_TOKEN=...
|
|
32
|
+
* GRIFFIN_SECRETS_VAULT_NAMESPACE=myapp
|
|
33
|
+
* GRIFFIN_SECRETS_VAULT_KV_VERSION=2
|
|
34
|
+
* GRIFFIN_SECRETS_VAULT_PREFIX=myapp/
|
|
35
|
+
*/
|
|
36
|
+
export function getSecretsConfigFromEnv() {
|
|
37
|
+
const provider = process.env[`${ENV_PREFIX}PROVIDER`] || "env";
|
|
38
|
+
switch (provider) {
|
|
39
|
+
case "env":
|
|
40
|
+
return {
|
|
41
|
+
provider: "env",
|
|
42
|
+
prefix: process.env[`${ENV_PREFIX}ENV_PREFIX`] || "",
|
|
43
|
+
env: process.env,
|
|
44
|
+
};
|
|
45
|
+
case "aws": {
|
|
46
|
+
const region = process.env[`${ENV_PREFIX}AWS_REGION`];
|
|
47
|
+
if (!region) {
|
|
48
|
+
throw new Error(`${ENV_PREFIX}AWS_REGION is required when GRIFFIN_SECRETS_PROVIDER=aws`);
|
|
49
|
+
}
|
|
50
|
+
const accessKeyId = process.env[`${ENV_PREFIX}AWS_ACCESS_KEY_ID`];
|
|
51
|
+
const secretAccessKey = process.env[`${ENV_PREFIX}AWS_SECRET_ACCESS_KEY`];
|
|
52
|
+
return {
|
|
53
|
+
provider: "aws",
|
|
54
|
+
region,
|
|
55
|
+
prefix: process.env[`${ENV_PREFIX}AWS_PREFIX`],
|
|
56
|
+
roleArn: process.env[`${ENV_PREFIX}AWS_ROLE_ARN`],
|
|
57
|
+
externalId: process.env[`${ENV_PREFIX}AWS_EXTERNAL_ID`],
|
|
58
|
+
credentials: accessKeyId && secretAccessKey
|
|
59
|
+
? { accessKeyId, secretAccessKey }
|
|
60
|
+
: undefined,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
case "vault": {
|
|
64
|
+
const address = process.env[`${ENV_PREFIX}VAULT_ADDRESS`];
|
|
65
|
+
const token = process.env[`${ENV_PREFIX}VAULT_TOKEN`];
|
|
66
|
+
if (!address) {
|
|
67
|
+
throw new Error(`${ENV_PREFIX}VAULT_ADDRESS is required when GRIFFIN_SECRETS_PROVIDER=vault`);
|
|
68
|
+
}
|
|
69
|
+
if (!token) {
|
|
70
|
+
throw new Error(`${ENV_PREFIX}VAULT_TOKEN is required when GRIFFIN_SECRETS_PROVIDER=vault`);
|
|
71
|
+
}
|
|
72
|
+
const kvVersionStr = process.env[`${ENV_PREFIX}VAULT_KV_VERSION`];
|
|
73
|
+
const kvVersion = kvVersionStr === "1" || kvVersionStr === "2"
|
|
74
|
+
? parseInt(kvVersionStr)
|
|
75
|
+
: 2;
|
|
76
|
+
return {
|
|
77
|
+
provider: "vault",
|
|
78
|
+
address,
|
|
79
|
+
token,
|
|
80
|
+
namespace: process.env[`${ENV_PREFIX}VAULT_NAMESPACE`],
|
|
81
|
+
kvVersion,
|
|
82
|
+
prefix: process.env[`${ENV_PREFIX}VAULT_PREFIX`],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
default:
|
|
86
|
+
throw new Error(`Unknown secrets provider: ${provider}. Supported: env, aws, vault`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Create a secret provider for CLI local runs.
|
|
91
|
+
* Reads configuration from environment variables.
|
|
92
|
+
*/
|
|
93
|
+
export async function createSecretsProviderForCLI() {
|
|
94
|
+
const config = getSecretsConfigFromEnv();
|
|
95
|
+
return createSecretProvider(config);
|
|
96
|
+
}
|
package/dist/core/state.d.ts
CHANGED
|
@@ -34,10 +34,6 @@ export declare function addEnvironment(name: string, config: EnvironmentConfig):
|
|
|
34
34
|
* Remove an environment
|
|
35
35
|
*/
|
|
36
36
|
export declare function removeEnvironment(name: string): Promise<void>;
|
|
37
|
-
/**
|
|
38
|
-
* Set the default environment
|
|
39
|
-
*/
|
|
40
|
-
export declare function setDefaultEnvironment(name: string): Promise<void>;
|
|
41
37
|
/**
|
|
42
38
|
* Get the current environment name (from flag, env var, or default)
|
|
43
39
|
*/
|
package/dist/core/state.js
CHANGED
|
@@ -84,10 +84,6 @@ export async function initState(projectId) {
|
|
|
84
84
|
export async function addEnvironment(name, config) {
|
|
85
85
|
const state = await loadState();
|
|
86
86
|
state.environments[name] = config;
|
|
87
|
-
// Set as default if it's the first environment
|
|
88
|
-
if (Object.keys(state.environments).length === 1) {
|
|
89
|
-
state.defaultEnvironment = name;
|
|
90
|
-
}
|
|
91
87
|
await saveState(state);
|
|
92
88
|
}
|
|
93
89
|
/**
|
|
@@ -95,26 +91,13 @@ export async function addEnvironment(name, config) {
|
|
|
95
91
|
*/
|
|
96
92
|
export async function removeEnvironment(name) {
|
|
97
93
|
const state = await loadState();
|
|
98
|
-
if (
|
|
99
|
-
throw new Error(
|
|
94
|
+
if (name === "default") {
|
|
95
|
+
throw new Error("Cannot remove default environment");
|
|
100
96
|
}
|
|
101
|
-
delete state.environments[name];
|
|
102
|
-
// Update default if we removed it
|
|
103
|
-
if (state.defaultEnvironment === name) {
|
|
104
|
-
const remaining = Object.keys(state.environments);
|
|
105
|
-
state.defaultEnvironment = remaining.length > 0 ? remaining[0] : undefined;
|
|
106
|
-
}
|
|
107
|
-
await saveState(state);
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Set the default environment
|
|
111
|
-
*/
|
|
112
|
-
export async function setDefaultEnvironment(name) {
|
|
113
|
-
const state = await loadState();
|
|
114
97
|
if (!(name in state.environments)) {
|
|
115
98
|
throw new Error(`Environment '${name}' does not exist`);
|
|
116
99
|
}
|
|
117
|
-
state.
|
|
100
|
+
delete state.environments[name];
|
|
118
101
|
await saveState(state);
|
|
119
102
|
}
|
|
120
103
|
/**
|
|
@@ -123,14 +106,13 @@ export async function setDefaultEnvironment(name) {
|
|
|
123
106
|
export async function resolveEnvironment(envFlag) {
|
|
124
107
|
const state = await loadState();
|
|
125
108
|
// Priority: CLI flag > ENV var > default > error
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
throw new Error("No environment specified. Pass the environment name as the first argument (e.g. griffin hub apply production). You can also set GRIFFIN_ENV or a default with 'griffin env default <name>'.");
|
|
109
|
+
if (!envFlag) {
|
|
110
|
+
return "default";
|
|
129
111
|
}
|
|
130
|
-
if (!(
|
|
131
|
-
throw new Error(`Environment '${
|
|
112
|
+
if (!(envFlag in state.environments)) {
|
|
113
|
+
throw new Error(`Environment '${envFlag}' not found. Available: ${Object.keys(state.environments).join(", ")}`);
|
|
132
114
|
}
|
|
133
|
-
return
|
|
115
|
+
return envFlag;
|
|
134
116
|
}
|
|
135
117
|
/**
|
|
136
118
|
* Get environment configuration
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export type { DiffAction, DiffResult } from "./core/diff.js";
|
|
|
5
5
|
export type { ApplyResult, ApplyAction, ApplyError } from "./core/apply.js";
|
|
6
6
|
export { createEmptyState, StateFileSchema, HubConfigSchema, } from "./schemas/state.js";
|
|
7
7
|
export { createEmptyCredentials, CredentialsFileSchema, HubCredentialsSchema, } from "./schemas/credentials.js";
|
|
8
|
-
export { getStateDirPath, getStateFilePath, stateExists, loadState, saveState, initState, addEnvironment, removeEnvironment,
|
|
8
|
+
export { getStateDirPath, getStateFilePath, stateExists, loadState, saveState, initState, addEnvironment, removeEnvironment, resolveEnvironment, getEnvironment, } from "./core/state.js";
|
|
9
9
|
export { getCredentialsDirPath, getCredentialsFilePath, credentialsExist, loadCredentials, saveCredentials, saveHubCredentials, getHubCredentials, removeHubCredentials, } from "./core/credentials.js";
|
|
10
10
|
export { discoverMonitors, formatDiscoveryErrors } from "./core/discovery.js";
|
|
11
11
|
export { computeDiff, formatDiff, formatDiffJson } from "./core/diff.js";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Export core functions
|
|
2
2
|
export { createEmptyState, StateFileSchema, HubConfigSchema, } from "./schemas/state.js";
|
|
3
3
|
export { createEmptyCredentials, CredentialsFileSchema, HubCredentialsSchema, } from "./schemas/credentials.js";
|
|
4
|
-
export { getStateDirPath, getStateFilePath, stateExists, loadState, saveState, initState, addEnvironment, removeEnvironment,
|
|
4
|
+
export { getStateDirPath, getStateFilePath, stateExists, loadState, saveState, initState, addEnvironment, removeEnvironment, resolveEnvironment, getEnvironment, } from "./core/state.js";
|
|
5
5
|
export { getCredentialsDirPath, getCredentialsFilePath, credentialsExist, loadCredentials, saveCredentials, saveHubCredentials, getHubCredentials, removeHubCredentials, } from "./core/credentials.js";
|
|
6
6
|
export { discoverMonitors, formatDiscoveryErrors } from "./core/discovery.js";
|
|
7
7
|
export { computeDiff, formatDiff, formatDiffJson } from "./core/diff.js";
|