@griffin-app/griffin-cli 1.0.30 → 1.0.32
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 +3 -15
- package/dist/commands/hub/secrets.js +2 -6
- package/dist/core/secrets.d.ts +2 -13
- package/dist/core/secrets.js +3 -24
- package/dist/core/variables.js +50 -2
- package/dist/core/variables.test.d.ts +1 -0
- package/dist/core/variables.test.js +127 -0
- package/dist/monitor-runner.js +6 -12
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -18,7 +18,7 @@ import { executeLogin } from "./commands/hub/login.js";
|
|
|
18
18
|
import { executeLogout } from "./commands/hub/logout.js";
|
|
19
19
|
import { executeIntegrationsConnect } from "./commands/hub/integrations.js";
|
|
20
20
|
import { executeNotificationsList, executeNotificationsTest, } from "./commands/hub/notifications.js";
|
|
21
|
-
import { executeSecretsList, executeSecretsSet,
|
|
21
|
+
import { executeSecretsList, executeSecretsSet, executeSecretsDelete, } from "./commands/hub/secrets.js";
|
|
22
22
|
import { executeIntegrationsList, executeIntegrationsShow, executeIntegrationsUpdate, executeIntegrationsRemove, } from "./commands/hub/integrations.js";
|
|
23
23
|
import packageInfo from "../package.json" with { type: "json" };
|
|
24
24
|
const program = new Command();
|
|
@@ -128,8 +128,8 @@ program
|
|
|
128
128
|
.option("--monitor <nameOrId>", "Specific monitor name or ID to destroy")
|
|
129
129
|
.option("--auto-approve", "Skip confirmation prompt")
|
|
130
130
|
.option("--dry-run", "Show what would be destroyed without making changes")
|
|
131
|
-
.action(async (
|
|
132
|
-
await executeDestroy({ ...options,
|
|
131
|
+
.action(async (options) => {
|
|
132
|
+
await executeDestroy({ ...options, json: program.opts().json });
|
|
133
133
|
});
|
|
134
134
|
// Auth command group
|
|
135
135
|
const auth = program.command("auth").description("Manage authentication");
|
|
@@ -246,18 +246,6 @@ secrets
|
|
|
246
246
|
json: program.opts().json,
|
|
247
247
|
});
|
|
248
248
|
});
|
|
249
|
-
secrets
|
|
250
|
-
.command("get <name>")
|
|
251
|
-
.description("Show secret metadata")
|
|
252
|
-
.option("--env <name>", "Environment name", "default")
|
|
253
|
-
.option("--json", "Output as JSON")
|
|
254
|
-
.action(async (name, options) => {
|
|
255
|
-
await executeSecretsGet({
|
|
256
|
-
name,
|
|
257
|
-
environment: options.env,
|
|
258
|
-
json: program.opts().json ?? options.json,
|
|
259
|
-
});
|
|
260
|
-
});
|
|
261
249
|
secrets
|
|
262
250
|
.command("delete <name>")
|
|
263
251
|
.description("Delete a secret")
|
|
@@ -28,13 +28,9 @@ export const executeSecretsList = createCommandHandler("secrets list", async (op
|
|
|
28
28
|
}
|
|
29
29
|
output.info(`Secrets (environment: ${env})`);
|
|
30
30
|
output.blank();
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
});
|
|
34
|
-
for (const s of secrets) {
|
|
35
|
-
table.push([s.name]);
|
|
31
|
+
for (const { name } of secrets) {
|
|
32
|
+
output.log(name);
|
|
36
33
|
}
|
|
37
|
-
output.log(table.toString());
|
|
38
34
|
});
|
|
39
35
|
function promptSecret(promptText) {
|
|
40
36
|
return new Promise((resolve) => {
|
package/dist/core/secrets.d.ts
CHANGED
|
@@ -5,20 +5,9 @@
|
|
|
5
5
|
* - `env`: reads secrets from environment variables (local dev, no hub needed)
|
|
6
6
|
* - `hub`: resolves secrets via POST /secrets/resolve on the hub API
|
|
7
7
|
*/
|
|
8
|
-
import { type SecretProvider } from "@griffin-app/griffin-executor";
|
|
9
|
-
|
|
8
|
+
import { HubSecretProvider, type SecretProvider } from "@griffin-app/griffin-executor";
|
|
9
|
+
export { HubSecretProvider };
|
|
10
10
|
/**
|
|
11
11
|
* Create an env-based secret provider for fully-local CLI runs (no hub).
|
|
12
12
|
*/
|
|
13
13
|
export declare function createEnvSecretsProvider(): SecretProvider;
|
|
14
|
-
/**
|
|
15
|
-
* SecretProvider that resolves secrets via the hub's POST /secrets/resolve endpoint.
|
|
16
|
-
* The hub is the sole gateway for AWS Secrets Manager — no credentials leave the hub.
|
|
17
|
-
*/
|
|
18
|
-
export declare class HubSecretProvider implements SecretProvider {
|
|
19
|
-
readonly name = "hub";
|
|
20
|
-
private readonly sdk;
|
|
21
|
-
private readonly environment;
|
|
22
|
-
constructor(sdk: GriffinHubSdk, environment: string);
|
|
23
|
-
resolve(ref: string): Promise<string>;
|
|
24
|
-
}
|
package/dist/core/secrets.js
CHANGED
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
* - `env`: reads secrets from environment variables (local dev, no hub needed)
|
|
6
6
|
* - `hub`: resolves secrets via POST /secrets/resolve on the hub API
|
|
7
7
|
*/
|
|
8
|
-
import { EnvSecretProvider, } from "@griffin-app/griffin-executor";
|
|
8
|
+
import { EnvSecretProvider, HubSecretProvider, } from "@griffin-app/griffin-executor";
|
|
9
|
+
// Re-export so existing imports from this module continue to work
|
|
10
|
+
export { HubSecretProvider };
|
|
9
11
|
/**
|
|
10
12
|
* Environment variable name for selecting the local secrets provider.
|
|
11
13
|
* Only "env" is supported for fully-local runs without a hub.
|
|
@@ -20,26 +22,3 @@ export function createEnvSecretsProvider() {
|
|
|
20
22
|
env: process.env,
|
|
21
23
|
});
|
|
22
24
|
}
|
|
23
|
-
/**
|
|
24
|
-
* SecretProvider that resolves secrets via the hub's POST /secrets/resolve endpoint.
|
|
25
|
-
* The hub is the sole gateway for AWS Secrets Manager — no credentials leave the hub.
|
|
26
|
-
*/
|
|
27
|
-
export class HubSecretProvider {
|
|
28
|
-
name = "hub";
|
|
29
|
-
sdk;
|
|
30
|
-
environment;
|
|
31
|
-
constructor(sdk, environment) {
|
|
32
|
-
this.sdk = sdk;
|
|
33
|
-
this.environment = environment;
|
|
34
|
-
}
|
|
35
|
-
async resolve(ref) {
|
|
36
|
-
const response = await this.sdk.postSecretsResolve({
|
|
37
|
-
body: { name: ref, environment: this.environment },
|
|
38
|
-
});
|
|
39
|
-
const value = response.data?.data?.value;
|
|
40
|
-
if (value === undefined) {
|
|
41
|
-
throw new Error(`Secret "${ref}" not found for environment "${this.environment}"`);
|
|
42
|
-
}
|
|
43
|
-
return value;
|
|
44
|
-
}
|
|
45
|
-
}
|
package/dist/core/variables.js
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import { getEnvironment } from "./state.js";
|
|
2
2
|
import { isNodeRef } from "@griffin-app/griffin-core/schema";
|
|
3
|
+
function isTemplateRef(value) {
|
|
4
|
+
if (typeof value !== "object" || value === null) {
|
|
5
|
+
return false;
|
|
6
|
+
}
|
|
7
|
+
const obj = value;
|
|
8
|
+
if (!("$template" in obj) ||
|
|
9
|
+
typeof obj.$template !== "object" ||
|
|
10
|
+
obj.$template === null) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const tmpl = obj.$template;
|
|
14
|
+
return Array.isArray(tmpl.strings) && Array.isArray(tmpl.values);
|
|
15
|
+
}
|
|
3
16
|
/**
|
|
4
17
|
* Load variables from state file for a specific environment.
|
|
5
18
|
*
|
|
@@ -61,6 +74,38 @@ function resolveVariable(varRef, variables) {
|
|
|
61
74
|
const placeholder = `\${${key}}`;
|
|
62
75
|
return template.replace(placeholder, value);
|
|
63
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Resolve variables in a template, using partial application.
|
|
79
|
+
* Resolves $variable values, merges them into adjacent strings.
|
|
80
|
+
* Returns a plain string if all values resolved, or a reduced $template.
|
|
81
|
+
*/
|
|
82
|
+
function resolveTemplateVariables(tmpl, variables) {
|
|
83
|
+
const { strings, values } = tmpl.$template;
|
|
84
|
+
const newStrings = [strings[0]];
|
|
85
|
+
const newValues = [];
|
|
86
|
+
for (let i = 0; i < values.length; i++) {
|
|
87
|
+
const val = values[i];
|
|
88
|
+
if (isVariableRef(val)) {
|
|
89
|
+
const resolved = resolveVariable(val, variables);
|
|
90
|
+
// Merge resolved value into the last string segment
|
|
91
|
+
newStrings[newStrings.length - 1] += resolved + strings[i + 1];
|
|
92
|
+
}
|
|
93
|
+
else if (isStringLiteral(val)) {
|
|
94
|
+
// Unwrap literals and merge
|
|
95
|
+
newStrings[newStrings.length - 1] += val.$literal + strings[i + 1];
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
// Keep this value and its trailing string
|
|
99
|
+
newValues.push(val);
|
|
100
|
+
newStrings.push(strings[i + 1]);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// If all values resolved, collapse to a $literal
|
|
104
|
+
if (newValues.length === 0) {
|
|
105
|
+
return { $literal: newStrings[0] };
|
|
106
|
+
}
|
|
107
|
+
return { $template: { strings: newStrings, values: newValues } };
|
|
108
|
+
}
|
|
64
109
|
/**
|
|
65
110
|
* Recursively walk a monitor object and resolve all variable references.
|
|
66
111
|
*
|
|
@@ -74,10 +119,13 @@ function resolveVariable(varRef, variables) {
|
|
|
74
119
|
export function resolveVariablesInMonitor(obj, variables) {
|
|
75
120
|
// Check if this is a variable reference
|
|
76
121
|
if (isVariableRef(obj)) {
|
|
77
|
-
return resolveVariable(obj, variables);
|
|
122
|
+
return { $literal: resolveVariable(obj, variables) };
|
|
123
|
+
}
|
|
124
|
+
if (isTemplateRef(obj)) {
|
|
125
|
+
return resolveTemplateVariables(obj, variables);
|
|
78
126
|
}
|
|
79
127
|
if (isStringLiteral(obj)) {
|
|
80
|
-
return obj
|
|
128
|
+
return obj;
|
|
81
129
|
}
|
|
82
130
|
if (isNodeRef(obj)) {
|
|
83
131
|
return obj;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { resolveVariablesInMonitor } from "./variables.js";
|
|
3
|
+
describe("resolveVariablesInMonitor", () => {
|
|
4
|
+
const variables = { env: "staging", version: "v2" };
|
|
5
|
+
it("should resolve a simple variable ref to $literal", () => {
|
|
6
|
+
const result = resolveVariablesInMonitor({ $variable: { key: "env" } }, variables);
|
|
7
|
+
expect(result).toEqual({ $literal: "staging" });
|
|
8
|
+
});
|
|
9
|
+
it("should resolve a variable with template to $literal", () => {
|
|
10
|
+
const result = resolveVariablesInMonitor({ $variable: { key: "env", template: "https://${env}.api.com" } }, variables);
|
|
11
|
+
expect(result).toEqual({ $literal: "https://staging.api.com" });
|
|
12
|
+
});
|
|
13
|
+
it("should pass through $literal unchanged", () => {
|
|
14
|
+
const literal = { $literal: "hello" };
|
|
15
|
+
const result = resolveVariablesInMonitor(literal, variables);
|
|
16
|
+
expect(result).toEqual(literal);
|
|
17
|
+
});
|
|
18
|
+
it("should pass through $nodeRef unchanged", () => {
|
|
19
|
+
const nodeRef = {
|
|
20
|
+
$nodeRef: { nodeId: "step1", subject: "body", path: ["id"] },
|
|
21
|
+
};
|
|
22
|
+
const result = resolveVariablesInMonitor(nodeRef, variables);
|
|
23
|
+
expect(result).toEqual(nodeRef);
|
|
24
|
+
});
|
|
25
|
+
describe("$template resolution", () => {
|
|
26
|
+
it("should fully collapse a template with only variables to $literal", () => {
|
|
27
|
+
const tmpl = {
|
|
28
|
+
$template: {
|
|
29
|
+
strings: ["https://", ".api.com/", "/health"],
|
|
30
|
+
values: [
|
|
31
|
+
{ $variable: { key: "env" } },
|
|
32
|
+
{ $variable: { key: "version" } },
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
const result = resolveVariablesInMonitor(tmpl, variables);
|
|
37
|
+
expect(result).toEqual({ $literal: "https://staging.api.com/v2/health" });
|
|
38
|
+
});
|
|
39
|
+
it("should partially resolve a template with mixed variable and secret", () => {
|
|
40
|
+
const tmpl = {
|
|
41
|
+
$template: {
|
|
42
|
+
strings: ["https://", ".api.com?key=", ""],
|
|
43
|
+
values: [
|
|
44
|
+
{ $variable: { key: "env" } },
|
|
45
|
+
{ $secret: { ref: "API_KEY" } },
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
const result = resolveVariablesInMonitor(tmpl, variables);
|
|
50
|
+
expect(result).toEqual({
|
|
51
|
+
$template: {
|
|
52
|
+
strings: ["https://staging.api.com?key=", ""],
|
|
53
|
+
values: [{ $secret: { ref: "API_KEY" } }],
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
it("should partially resolve a template with mixed variable and nodeRef", () => {
|
|
58
|
+
const tmpl = {
|
|
59
|
+
$template: {
|
|
60
|
+
strings: ["/api/", "/resource/", ""],
|
|
61
|
+
values: [
|
|
62
|
+
{ $variable: { key: "version" } },
|
|
63
|
+
{ $nodeRef: { nodeId: "step1", subject: "body", path: ["id"] } },
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
const result = resolveVariablesInMonitor(tmpl, variables);
|
|
68
|
+
expect(result).toEqual({
|
|
69
|
+
$template: {
|
|
70
|
+
strings: ["/api/v2/resource/", ""],
|
|
71
|
+
values: [
|
|
72
|
+
{ $nodeRef: { nodeId: "step1", subject: "body", path: ["id"] } },
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
it("should pass through a template with no variables unchanged", () => {
|
|
78
|
+
const tmpl = {
|
|
79
|
+
$template: {
|
|
80
|
+
strings: ["Bearer ", ""],
|
|
81
|
+
values: [{ $secret: { ref: "TOKEN" } }],
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
const result = resolveVariablesInMonitor(tmpl, variables);
|
|
85
|
+
expect(result).toEqual(tmpl);
|
|
86
|
+
});
|
|
87
|
+
it("should handle adjacent variable interpolations", () => {
|
|
88
|
+
const tmpl = {
|
|
89
|
+
$template: {
|
|
90
|
+
strings: ["", "", "/path"],
|
|
91
|
+
values: [
|
|
92
|
+
{ $variable: { key: "env" } },
|
|
93
|
+
{ $variable: { key: "version" } },
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
const result = resolveVariablesInMonitor(tmpl, variables);
|
|
98
|
+
expect(result).toEqual({ $literal: "stagingv2/path" });
|
|
99
|
+
});
|
|
100
|
+
it("should resolve templates nested in monitor structure", () => {
|
|
101
|
+
const monitor = {
|
|
102
|
+
nodes: [
|
|
103
|
+
{
|
|
104
|
+
type: "HTTP_REQUEST",
|
|
105
|
+
path: {
|
|
106
|
+
$template: {
|
|
107
|
+
strings: ["/api/", "/health"],
|
|
108
|
+
values: [{ $variable: { key: "version" } }],
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
base: {
|
|
112
|
+
$template: {
|
|
113
|
+
strings: ["https://", ".api.com"],
|
|
114
|
+
values: [{ $variable: { key: "env" } }],
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
};
|
|
120
|
+
const result = resolveVariablesInMonitor(monitor, variables);
|
|
121
|
+
expect(result.nodes[0].path).toEqual({ $literal: "/api/v2/health" });
|
|
122
|
+
expect(result.nodes[0].base).toEqual({
|
|
123
|
+
$literal: "https://staging.api.com",
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|
package/dist/monitor-runner.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "tsx";
|
|
2
2
|
import { Value } from "typebox/value";
|
|
3
|
-
import { executeMonitorV1, AxiosAdapter,
|
|
3
|
+
import { executeMonitorV1, AxiosAdapter, } from "@griffin-app/griffin-executor";
|
|
4
4
|
import { MonitorDSLSchema } from "@griffin-app/griffin-core/schema";
|
|
5
5
|
import { randomUUID } from "crypto";
|
|
6
6
|
import { loadVariables } from "./core/variables.js";
|
|
@@ -28,19 +28,13 @@ export async function runTestFile(filePath, envName) {
|
|
|
28
28
|
const resolvedMonitor = resolveMonitor(rawMonitor, projectId, envName, variables);
|
|
29
29
|
// Create secret provider: prefer hub (authenticated) for cloud secrets,
|
|
30
30
|
// fall back to env provider for fully-local runs.
|
|
31
|
-
let secretProvider;
|
|
32
31
|
const monitorV1 = { ...resolvedMonitor, id: randomUUID() };
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
// Hub not available — fall back to env provider
|
|
40
|
-
secretProvider = createEnvSecretsProvider();
|
|
41
|
-
}
|
|
32
|
+
let secretProvider;
|
|
33
|
+
try {
|
|
34
|
+
const sdk = await createSdkFromState();
|
|
35
|
+
secretProvider = new HubSecretProvider(sdk, envName);
|
|
42
36
|
}
|
|
43
|
-
|
|
37
|
+
catch {
|
|
44
38
|
secretProvider = createEnvSecretsProvider();
|
|
45
39
|
}
|
|
46
40
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@griffin-app/griffin-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.32",
|
|
4
4
|
"description": "CLI tool for running and managing griffin API tests",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
"author": "",
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@griffin-app/griffin-
|
|
28
|
-
"@griffin-app/griffin-executor": "0.1.
|
|
29
|
-
"@griffin-app/griffin-
|
|
27
|
+
"@griffin-app/griffin-core": "0.2.4",
|
|
28
|
+
"@griffin-app/griffin-executor": "0.1.7",
|
|
29
|
+
"@griffin-app/griffin-hub-sdk": "1.0.29",
|
|
30
30
|
"better-auth": "^1.4.17",
|
|
31
31
|
"cli-table3": "^0.6.5",
|
|
32
32
|
"commander": "^12.1.0",
|