@griffin-app/griffin-cli 1.0.9 → 1.0.11

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 CHANGED
@@ -18,6 +18,7 @@ import { executeLogin } from "./commands/hub/login.js";
18
18
  import { executeLogout } from "./commands/hub/logout.js";
19
19
  import { executeNotificationsList, executeNotificationsTest, } from "./commands/hub/notifications.js";
20
20
  import { executeSecretsList, executeSecretsSet, executeSecretsGet, executeSecretsDelete, } from "./commands/hub/secrets.js";
21
+ import { executeIntegrationsList, executeIntegrationsShow, executeIntegrationsAdd, executeIntegrationsUpdate, executeIntegrationsRemove, } from "./commands/hub/integrations.js";
21
22
  const program = new Command();
22
23
  program
23
24
  .name("griffin")
@@ -161,6 +162,93 @@ notifications
161
162
  webhook: options.webhook,
162
163
  });
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
194
+ .command("add")
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", "Enabled")
201
+ .option("--json", "Output as JSON")
202
+ .action(async (options) => {
203
+ await executeIntegrationsAdd({
204
+ category: options.category,
205
+ providerType: options.providerType,
206
+ name: options.name,
207
+ environment: options.environment,
208
+ enabled: options.enabled,
209
+ json: options.json,
210
+ });
211
+ });
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", "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")
232
+ .action(async (id) => {
233
+ await executeIntegrationsRemove({ id });
234
+ });
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
+ });
164
252
  // Secrets command group
165
253
  const secrets = hub
166
254
  .command("secrets")
@@ -0,0 +1,45 @@
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>;
38
+ export interface IntegrationsConnectOptions {
39
+ providerType: string;
40
+ name?: string;
41
+ environment?: string;
42
+ category?: string;
43
+ json?: boolean;
44
+ }
45
+ export declare function executeIntegrationsConnect(options: IntegrationsConnectOptions): Promise<void>;
@@ -0,0 +1,255 @@
1
+ import { exec } from "node:child_process";
2
+ import { platform } from "node:os";
3
+ import { loadState } from "../../core/state.js";
4
+ import { getHubCredentials } from "../../core/credentials.js";
5
+ import { terminal } from "../../utils/terminal.js";
6
+ async function hubFetch(path, options = {}) {
7
+ const state = await loadState();
8
+ if (!state.hub?.baseUrl) {
9
+ terminal.error("Hub connection not configured.");
10
+ terminal.dim("Connect with: griffin hub connect --url <url> --token <token>");
11
+ process.exit(1);
12
+ }
13
+ const credentials = await getHubCredentials();
14
+ const url = `${state.hub.baseUrl.replace(/\/$/, "")}${path}`;
15
+ const headers = {
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,
25
+ });
26
+ const text = await res.text();
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 ?? [];
57
+ if (options.json) {
58
+ terminal.log(JSON.stringify(list, null, 2));
59
+ return;
60
+ }
61
+ if (list.length === 0) {
62
+ terminal.info("No integrations found.");
63
+ return;
64
+ }
65
+ terminal.info("Integrations");
66
+ terminal.blank();
67
+ const table = terminal.table({
68
+ head: ["ID", "Category", "Provider", "Name", "Environment", "Enabled"],
69
+ });
70
+ for (const i of list) {
71
+ table.push([
72
+ i.id.substring(0, 12) + (i.id.length > 12 ? "…" : ""),
73
+ i.category,
74
+ i.providerType,
75
+ i.name,
76
+ i.environment ?? "(all)",
77
+ i.enabled ? "✓" : "✗",
78
+ ]);
79
+ }
80
+ terminal.log(table.toString());
81
+ }
82
+ export async function executeIntegrationsShow(options) {
83
+ const result = await hubFetch(`/integrations/${encodeURIComponent(options.id)}`);
84
+ if (result.error) {
85
+ terminal.error(result.error);
86
+ process.exit(1);
87
+ }
88
+ const integration = result.data;
89
+ if (!integration) {
90
+ terminal.error("Integration not found.");
91
+ process.exit(1);
92
+ }
93
+ if (options.json) {
94
+ terminal.log(JSON.stringify(integration, null, 2));
95
+ return;
96
+ }
97
+ terminal.info(integration.name);
98
+ terminal.dim(`ID: ${integration.id}`);
99
+ terminal.log(`Category: ${integration.category}`);
100
+ terminal.log(`Provider: ${integration.providerType}`);
101
+ terminal.log(`Environment: ${integration.environment ?? "(all)"}`);
102
+ terminal.log(`Enabled: ${integration.enabled ? "Yes" : "No"}`);
103
+ terminal.log(`Has credentials: ${integration.hasCredentials ? "Yes" : "No"}`);
104
+ if (Object.keys(integration.config).length > 0) {
105
+ terminal.log("Config: " + JSON.stringify(integration.config));
106
+ }
107
+ }
108
+ export async function executeIntegrationsAdd(options) {
109
+ const body = {
110
+ category: options.category,
111
+ providerType: options.providerType,
112
+ name: options.name,
113
+ config: options.config ?? {},
114
+ environment: options.environment ?? null,
115
+ enabled: options.enabled ?? true,
116
+ };
117
+ const result = await hubFetch("/integrations", {
118
+ method: "POST",
119
+ body,
120
+ });
121
+ if (result.error) {
122
+ terminal.error(result.error);
123
+ process.exit(1);
124
+ }
125
+ const integration = result.data;
126
+ if (!integration) {
127
+ terminal.error("Create failed.");
128
+ process.exit(1);
129
+ }
130
+ if (options.json) {
131
+ terminal.log(JSON.stringify(integration, null, 2));
132
+ return;
133
+ }
134
+ terminal.info(`Created integration ${integration.name} (${integration.id})`);
135
+ }
136
+ export async function executeIntegrationsUpdate(options) {
137
+ const body = {};
138
+ if (options.name !== undefined)
139
+ body.name = options.name;
140
+ if (options.config !== undefined)
141
+ body.config = options.config;
142
+ if (options.environment !== undefined)
143
+ body.environment = options.environment;
144
+ if (options.enabled !== undefined)
145
+ body.enabled = options.enabled;
146
+ const result = await hubFetch(`/integrations/${encodeURIComponent(options.id)}`, { method: "PATCH", body });
147
+ if (result.error) {
148
+ terminal.error(result.error);
149
+ process.exit(1);
150
+ }
151
+ const integration = result.data;
152
+ if (!integration) {
153
+ terminal.error("Update failed.");
154
+ process.exit(1);
155
+ }
156
+ if (options.json) {
157
+ terminal.log(JSON.stringify(integration, null, 2));
158
+ return;
159
+ }
160
+ terminal.info(`Updated integration ${integration.name}`);
161
+ }
162
+ export async function executeIntegrationsRemove(options) {
163
+ const result = await hubFetch(`/integrations/${encodeURIComponent(options.id)}`, { method: "DELETE" });
164
+ if (result.error) {
165
+ terminal.error(result.error);
166
+ process.exit(1);
167
+ }
168
+ terminal.info("Integration removed.");
169
+ }
170
+ function openBrowser(url) {
171
+ const cmd = platform() === "darwin"
172
+ ? "open"
173
+ : platform() === "win32"
174
+ ? "start"
175
+ : "xdg-open";
176
+ exec(`${cmd} "${url.replace(/"/g, '\\"')}"`, (err) => {
177
+ if (err) {
178
+ terminal.dim(`Could not open browser: ${err.message}`);
179
+ }
180
+ });
181
+ }
182
+ const POLL_INITIAL_MS = 2000;
183
+ const POLL_MAX_MS = 10000;
184
+ const POLL_TIMEOUT_MS = 15 * 60 * 1000; // 15 minutes
185
+ export async function executeIntegrationsConnect(options) {
186
+ const category = options.category ?? "notifications";
187
+ const body = {
188
+ category,
189
+ providerType: options.providerType,
190
+ name: options.name ?? undefined,
191
+ environment: options.environment ?? undefined,
192
+ };
193
+ const result = await hubFetch("/integrations/oauth/initiate", { method: "POST", body });
194
+ if (result.error) {
195
+ terminal.error(result.error);
196
+ process.exit(1);
197
+ }
198
+ const data = result.data;
199
+ if (!data) {
200
+ terminal.error("Initiate failed.");
201
+ process.exit(1);
202
+ }
203
+ terminal.info("Open this URL in your browser to authorize:");
204
+ terminal.log(data.authUrl);
205
+ terminal.blank();
206
+ openBrowser(data.authUrl);
207
+ terminal.info("Waiting for authorization… (polling)");
208
+ const start = Date.now();
209
+ let interval = POLL_INITIAL_MS;
210
+ const poll = async () => {
211
+ const statusResult = await hubFetch(`/integrations/oauth/status/${encodeURIComponent(data.transactionId)}`);
212
+ if (statusResult.error) {
213
+ throw new Error(statusResult.error);
214
+ }
215
+ const status = statusResult.data;
216
+ if (!status) {
217
+ throw new Error("Invalid status response");
218
+ }
219
+ if (status.status !== "pending") {
220
+ return status;
221
+ }
222
+ if (Date.now() - start >= POLL_TIMEOUT_MS) {
223
+ return { status: "expired", errorMessage: "Polling timed out" };
224
+ }
225
+ await new Promise((resolve) => setTimeout(resolve, interval));
226
+ interval = Math.min(interval + 1000, POLL_MAX_MS);
227
+ return poll();
228
+ };
229
+ try {
230
+ const status = await poll();
231
+ if (status.status === "completed" && status.integrationId) {
232
+ if (options.json) {
233
+ terminal.log(JSON.stringify({ integrationId: status.integrationId }, null, 2));
234
+ }
235
+ else {
236
+ terminal.success(`Integration connected: ${status.integrationId}`);
237
+ }
238
+ return;
239
+ }
240
+ if (status.status === "failed") {
241
+ terminal.error(status.errorMessage ?? "Authorization failed");
242
+ process.exit(1);
243
+ }
244
+ if (status.status === "expired") {
245
+ terminal.error(status.errorMessage ?? "Transaction expired");
246
+ process.exit(1);
247
+ }
248
+ terminal.error("Unexpected status");
249
+ process.exit(1);
250
+ }
251
+ catch (err) {
252
+ terminal.error(err instanceof Error ? err.message : "Polling failed");
253
+ process.exit(1);
254
+ }
255
+ }
@@ -18,7 +18,9 @@ export interface SecretsDeleteOptions {
18
18
  force?: boolean;
19
19
  }
20
20
  /**
21
- * List secrets (metadata only)
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).
22
24
  */
23
25
  export declare function executeSecretsList(options: SecretsListOptions): Promise<void>;
24
26
  /**
@@ -5,7 +5,9 @@ import { withSDKErrorHandling } from "../../utils/sdk-error.js";
5
5
  import { createSdkWithCredentials } from "../../core/sdk.js";
6
6
  const SECRET_NAME_REGEX = /^[A-Za-z_][A-Za-z0-9_]*$/;
7
7
  /**
8
- * List secrets (metadata only)
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).
9
11
  */
10
12
  export async function executeSecretsList(options) {
11
13
  try {
@@ -1,5 +1,5 @@
1
- import { findTestFiles } from "../../test-discovery.js";
2
- import { runTestFile } from "../../test-runner.js";
1
+ import { findTestFiles } from "../../monitor-discovery.js";
2
+ import { runTestFile } from "../../monitor-runner.js";
3
3
  import { resolveEnvironment } from "../../core/state.js";
4
4
  import { terminal } from "../../utils/terminal.js";
5
5
  import { basename } from "path";
@@ -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
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Discovers test files in __griffin__ directories.
3
+ */
4
+ export declare function findTestFiles(basePath?: string): string[];
@@ -0,0 +1,25 @@
1
+ import * as path from "path";
2
+ import { glob } from "glob";
3
+ /**
4
+ * Discovers test files in __griffin__ directories.
5
+ */
6
+ export function findTestFiles(basePath = ".") {
7
+ const absolutePath = path.resolve(basePath);
8
+ // Find all __griffin__ directories
9
+ const griffinDirs = findgriffinDirectories(absolutePath);
10
+ // Find all .ts files in those directories
11
+ const testFiles = [];
12
+ for (const griffinDir of griffinDirs) {
13
+ const tsFiles = findTsFiles(griffinDir);
14
+ testFiles.push(...tsFiles);
15
+ }
16
+ return testFiles;
17
+ }
18
+ function findgriffinDirectories(basePath) {
19
+ const pattern = path.join(basePath, "**", "__griffin__");
20
+ return glob.sync(pattern, { absolute: true });
21
+ }
22
+ function findTsFiles(griffinDir) {
23
+ const pattern = path.join(griffinDir, "*.ts");
24
+ return glob.sync(pattern, { absolute: true });
25
+ }
@@ -0,0 +1,6 @@
1
+ import "tsx";
2
+ import { ExecutionResult } from "@griffin-app/griffin-plan-executor";
3
+ /**
4
+ * Runs a TypeScript test file and executes the resulting JSON monitor.
5
+ */
6
+ export declare function runTestFile(filePath: string, envName: string): Promise<ExecutionResult>;
@@ -0,0 +1,56 @@
1
+ import "tsx";
2
+ import { Value } from "typebox/value";
3
+ import { executeMonitorV1, AxiosAdapter, } from "@griffin-app/griffin-plan-executor";
4
+ import { MonitorDSLSchema } from "@griffin-app/griffin-ts/schema";
5
+ import { randomUUID } from "crypto";
6
+ import { loadVariables } from "./core/variables.js";
7
+ import { getProjectId } from "./core/state.js";
8
+ import { resolveMonitor } from "./resolve.js";
9
+ import { terminal } from "./utils/terminal.js";
10
+ import { createSecretsProviderForCLI } from "./core/secrets.js";
11
+ function validateDsl(monitor) {
12
+ const errors = Value.Errors(MonitorDSLSchema, monitor);
13
+ if (errors.length > 0) {
14
+ throw new Error(`Invalid monitor: ${JSON.stringify([...errors], null, 2)}`);
15
+ }
16
+ return monitor;
17
+ }
18
+ /**
19
+ * Runs a TypeScript test file and executes the resulting JSON monitor.
20
+ */
21
+ export async function runTestFile(filePath, envName) {
22
+ const variables = await loadVariables(envName);
23
+ const projectId = await getProjectId();
24
+ const defaultExport = await import(filePath);
25
+ const rawMonitor = validateDsl(defaultExport.default);
26
+ terminal.dim(`Project ID: ${projectId}`);
27
+ const resolvedMonitor = resolveMonitor(rawMonitor, projectId, envName, variables);
28
+ // Create secret provider from environment configuration
29
+ const secretProvider = await createSecretsProviderForCLI();
30
+ try {
31
+ const result = await executeMonitorV1({
32
+ ...resolvedMonitor,
33
+ id: randomUUID(),
34
+ }, "default-org", {
35
+ mode: "local",
36
+ httpClient: new AxiosAdapter(),
37
+ secretProvider,
38
+ });
39
+ return result;
40
+ }
41
+ catch (error) {
42
+ throw new Error(`Error executing monitor: ${error instanceof Error ? error.message : String(error)}`);
43
+ }
44
+ }
45
+ //function findWorkspaceRoot(): string {
46
+ // let current = process.cwd();
47
+ // while (current !== path.dirname(current)) {
48
+ // const testCliPath = path.join(current, "griffin-cli");
49
+ // const testSystemPath = path.join(current, "griffin-ts");
50
+ // if (fs.existsSync(testCliPath) && fs.existsSync(testSystemPath)) {
51
+ // return current;
52
+ // }
53
+ // current = path.dirname(current);
54
+ // }
55
+ // return process.cwd();
56
+ //}
@@ -1,12 +1,13 @@
1
1
  import "tsx";
2
2
  import { Value } from "typebox/value";
3
- import { executeMonitorV1, AxiosAdapter, EnvSecretProvider, SecretProviderRegistry, } from "@griffin-app/griffin-plan-executor";
3
+ import { executeMonitorV1, AxiosAdapter, } from "@griffin-app/griffin-plan-executor";
4
4
  import { MonitorDSLSchema } from "@griffin-app/griffin-ts/schema";
5
5
  import { randomUUID } from "crypto";
6
6
  import { loadVariables } from "./core/variables.js";
7
7
  import { getProjectId } from "./core/state.js";
8
8
  import { resolveMonitor } from "./resolve.js";
9
9
  import { terminal } from "./utils/terminal.js";
10
+ import { createSecretsProviderForCLI } from "./core/secrets.js";
10
11
  function validateDsl(monitor) {
11
12
  const errors = Value.Errors(MonitorDSLSchema, monitor);
12
13
  if (errors.length > 0) {
@@ -24,8 +25,8 @@ export async function runTestFile(filePath, envName) {
24
25
  const rawMonitor = validateDsl(defaultExport.default);
25
26
  terminal.dim(`Project ID: ${projectId}`);
26
27
  const resolvedMonitor = resolveMonitor(rawMonitor, projectId, envName, variables);
27
- const secretRegistry = new SecretProviderRegistry();
28
- secretRegistry.setProvider(new EnvSecretProvider());
28
+ // Create secret provider from environment configuration
29
+ const secretProvider = await createSecretsProviderForCLI();
29
30
  try {
30
31
  const result = await executeMonitorV1({
31
32
  ...resolvedMonitor,
@@ -33,7 +34,7 @@ export async function runTestFile(filePath, envName) {
33
34
  }, "default-org", {
34
35
  mode: "local",
35
36
  httpClient: new AxiosAdapter(),
36
- secretRegistry: secretRegistry,
37
+ secretProvider,
37
38
  });
38
39
  return result;
39
40
  }
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@griffin-app/griffin-cli",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "CLI tool for running and managing griffin API tests",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
8
- "griffin": "./dist/cli.js"
8
+ "griffin": "./dist/cli.js",
9
+ "gr": "./dist/cli.js"
9
10
  },
10
11
  "scripts": {
11
12
  "build": "tsc",
@@ -23,9 +24,9 @@
23
24
  "author": "",
24
25
  "license": "MIT",
25
26
  "dependencies": {
26
- "@griffin-app/griffin-hub-sdk": "1.0.10",
27
- "@griffin-app/griffin-plan-executor": "0.1.16",
28
- "@griffin-app/griffin-ts": "0.1.15",
27
+ "@griffin-app/griffin-hub-sdk": "1.0.14",
28
+ "@griffin-app/griffin-plan-executor": "0.1.18",
29
+ "@griffin-app/griffin-ts": "0.1.16",
29
30
  "better-auth": "^1.4.17",
30
31
  "cli-table3": "^0.6.5",
31
32
  "commander": "^12.1.0",