@griffin-app/griffin-cli 1.0.18 → 1.0.19

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.
Files changed (48) hide show
  1. package/dist/cli.js +183 -196
  2. package/dist/commands/env.d.ts +3 -3
  3. package/dist/commands/env.js +28 -45
  4. package/dist/commands/hub/apply.d.ts +1 -1
  5. package/dist/commands/hub/apply.js +54 -95
  6. package/dist/commands/hub/connect.d.ts +1 -1
  7. package/dist/commands/hub/connect.js +2 -6
  8. package/dist/commands/hub/destroy.d.ts +10 -0
  9. package/dist/commands/hub/destroy.js +141 -0
  10. package/dist/commands/hub/integrations.d.ts +9 -9
  11. package/dist/commands/hub/integrations.js +104 -115
  12. package/dist/commands/hub/login.js +3 -0
  13. package/dist/commands/hub/metrics.d.ts +2 -2
  14. package/dist/commands/hub/metrics.js +41 -69
  15. package/dist/commands/hub/monitor.d.ts +1 -1
  16. package/dist/commands/hub/monitor.js +30 -70
  17. package/dist/commands/hub/notifications.d.ts +3 -3
  18. package/dist/commands/hub/notifications.js +61 -89
  19. package/dist/commands/hub/run.d.ts +1 -1
  20. package/dist/commands/hub/run.js +118 -144
  21. package/dist/commands/hub/runs.d.ts +1 -1
  22. package/dist/commands/hub/runs.js +60 -81
  23. package/dist/commands/hub/secrets.d.ts +8 -8
  24. package/dist/commands/hub/secrets.js +88 -148
  25. package/dist/commands/hub/status.d.ts +1 -1
  26. package/dist/commands/hub/status.js +13 -26
  27. package/dist/commands/init.js +3 -3
  28. package/dist/commands/local/run.d.ts +2 -2
  29. package/dist/commands/local/run.js +37 -42
  30. package/dist/commands/validate.d.ts +1 -1
  31. package/dist/commands/validate.js +19 -39
  32. package/dist/commands/variables.d.ts +3 -3
  33. package/dist/commands/variables.js +51 -69
  34. package/dist/core/discovery.js +0 -2
  35. package/dist/core/monitor-helpers.d.ts +19 -0
  36. package/dist/core/monitor-helpers.js +48 -0
  37. package/dist/core/sdk.d.ts +5 -0
  38. package/dist/core/sdk.js +11 -0
  39. package/dist/core/state.d.ts +1 -1
  40. package/dist/core/state.js +0 -3
  41. package/dist/index.d.ts +1 -0
  42. package/dist/index.js +1 -0
  43. package/dist/utils/command-wrapper.d.ts +10 -0
  44. package/dist/utils/command-wrapper.js +27 -0
  45. package/dist/utils/sdk-error.js +1 -1
  46. package/dist/utils/terminal.d.ts +4 -0
  47. package/dist/utils/terminal.js +17 -0
  48. package/package.json +1 -1
@@ -1,20 +1,20 @@
1
1
  export interface SecretsListOptions {
2
- environment?: string;
2
+ environment: string;
3
3
  json?: boolean;
4
4
  }
5
5
  export interface SecretsSetOptions {
6
6
  name: string;
7
- environment?: string;
7
+ environment: string;
8
8
  value?: string;
9
9
  }
10
10
  export interface SecretsGetOptions {
11
11
  name: string;
12
- environment?: string;
12
+ environment: string;
13
13
  json?: boolean;
14
14
  }
15
15
  export interface SecretsDeleteOptions {
16
16
  name: string;
17
- environment?: string;
17
+ environment: string;
18
18
  force?: boolean;
19
19
  }
20
20
  /**
@@ -22,16 +22,16 @@ export interface SecretsDeleteOptions {
22
22
  * Uses the resolved secrets integration for the organization and environment
23
23
  * (from hub integrations API or platform default / legacy config).
24
24
  */
25
- export declare function executeSecretsList(options: SecretsListOptions): Promise<void>;
25
+ export declare const executeSecretsList: (options: SecretsListOptions) => Promise<void>;
26
26
  /**
27
27
  * Set a secret (prompts for value if not provided)
28
28
  */
29
- export declare function executeSecretsSet(options: SecretsSetOptions): Promise<void>;
29
+ export declare const executeSecretsSet: (options: SecretsSetOptions) => Promise<void>;
30
30
  /**
31
31
  * Get secret metadata only
32
32
  */
33
- export declare function executeSecretsGet(options: SecretsGetOptions): Promise<void>;
33
+ export declare const executeSecretsGet: (options: SecretsGetOptions) => Promise<void>;
34
34
  /**
35
35
  * Delete a secret (with confirmation unless --force)
36
36
  */
37
- export declare function executeSecretsDelete(options: SecretsDeleteOptions): Promise<void>;
37
+ export declare const executeSecretsDelete: (options: SecretsDeleteOptions) => Promise<void>;
@@ -1,98 +1,69 @@
1
1
  import { createInterface } from "node:readline";
2
- import { loadState } from "../../core/state.js";
2
+ import { resolveEnvironment } from "../../core/state.js";
3
3
  import { terminal } from "../../utils/terminal.js";
4
4
  import { withSDKErrorHandling } from "../../utils/sdk-error.js";
5
- import { createSdkWithCredentials } from "../../core/sdk.js";
5
+ import { createSdkFromState } from "../../core/sdk.js";
6
+ import { withCommandErrorHandler, outputJsonOrContinue, } from "../../utils/command-wrapper.js";
6
7
  const SECRET_NAME_REGEX = /^[A-Za-z_][A-Za-z0-9_]*$/;
7
8
  /**
8
9
  * List secrets (metadata only).
9
10
  * Uses the resolved secrets integration for the organization and environment
10
11
  * (from hub integrations API or platform default / legacy config).
11
12
  */
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"],
13
+ export const executeSecretsList = withCommandErrorHandler(async (options) => {
14
+ const sdk = await createSdkFromState();
15
+ const env = await resolveEnvironment(options.environment);
16
+ const response = await withSDKErrorHandling(async () => {
17
+ return sdk.getSecrets({
18
+ query: {
19
+ environment: env,
20
+ },
44
21
  });
45
- for (const s of secrets) {
46
- table.push([s.name, s.createdAt ?? "-", s.updatedAt ?? "-"]);
47
- }
48
- terminal.log(table.toString());
22
+ }, "Failed to list secrets");
23
+ const secrets = response?.data?.data ?? [];
24
+ if (outputJsonOrContinue(secrets, options.json))
25
+ return;
26
+ if (secrets.length === 0) {
27
+ terminal.info(`No secrets found for environment "${env}".`);
28
+ return;
49
29
  }
50
- catch (error) {
51
- terminal.error(error.message);
52
- terminal.exit(1);
30
+ terminal.info(`Secrets (environment: ${env})`);
31
+ terminal.blank();
32
+ const table = terminal.table({
33
+ head: ["Name", "Created", "Updated"],
34
+ });
35
+ for (const s of secrets) {
36
+ table.push([s.name, s.createdAt ?? "-", s.updatedAt ?? "-"]);
53
37
  }
54
- }
38
+ terminal.log(table.toString());
39
+ });
55
40
  /**
56
41
  * Set a secret (prompts for value if not provided)
57
42
  */
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>");
43
+ export const executeSecretsSet = withCommandErrorHandler(async (options) => {
44
+ if (!SECRET_NAME_REGEX.test(options.name)) {
45
+ terminal.error("Secret name must start with a letter or underscore and contain only letters, numbers, and underscores.");
46
+ terminal.exit(1);
47
+ }
48
+ let value = options.value;
49
+ if (value === undefined) {
50
+ value = await promptSecret("Enter secret value:");
51
+ if (!value) {
52
+ terminal.error("Secret value cannot be empty.");
69
53
  terminal.exit(1);
70
54
  }
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
55
  }
91
- catch (error) {
92
- terminal.error(error.message);
93
- terminal.exit(1);
94
- }
95
- }
56
+ const sdk = await createSdkFromState();
57
+ const env = await resolveEnvironment(options.environment);
58
+ const response = await withSDKErrorHandling(async () => {
59
+ return sdk.putSecretsByName({
60
+ path: { name: options.name },
61
+ body: { value, environment: env },
62
+ });
63
+ }, "Failed to set secret");
64
+ const result = response?.data;
65
+ terminal.success(`Secret "${result.data.name}" saved.`);
66
+ });
96
67
  function promptSecret(promptText) {
97
68
  return new Promise((resolve) => {
98
69
  const rl = createInterface({
@@ -108,80 +79,49 @@ function promptSecret(promptText) {
108
79
  /**
109
80
  * Get secret metadata only
110
81
  */
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
- }
82
+ export const executeSecretsGet = withCommandErrorHandler(async (options) => {
83
+ const sdk = await createSdkFromState();
84
+ const env = await resolveEnvironment(options.environment);
85
+ const response = await withSDKErrorHandling(async () => {
86
+ return sdk.getSecretsByName({
87
+ path: { name: options.name },
88
+ query: { environment: env },
89
+ });
90
+ }, "Failed to get secret");
91
+ const secret = response?.data?.data;
92
+ if (outputJsonOrContinue(secret, options.json))
93
+ return;
94
+ terminal.info(`Secret: ${secret.name}`);
95
+ terminal.dim(`Created: ${secret.createdAt ?? "-"}`);
96
+ terminal.dim(`Updated: ${secret.updatedAt ?? "-"}`);
97
+ });
143
98
  /**
144
99
  * Delete a secret (with confirmation unless --force)
145
100
  */
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
- });
101
+ export const executeSecretsDelete = withCommandErrorHandler(async (options) => {
102
+ const sdk = await createSdkFromState();
103
+ const env = await resolveEnvironment(options.environment);
104
+ if (!options.force) {
105
+ const rl = createInterface({
106
+ input: process.stdin,
107
+ output: process.stdout,
108
+ });
109
+ const answer = await new Promise((resolve) => {
110
+ rl.question(`Delete secret "${options.name}" (environment: ${env})? [y/N] `, (a) => {
111
+ rl.close();
112
+ resolve(a.trim().toLowerCase());
168
113
  });
169
- if (answer !== "y" && answer !== "yes") {
170
- terminal.info("Aborted.");
171
- return;
172
- }
114
+ });
115
+ if (answer !== "y" && answer !== "yes") {
116
+ terminal.info("Aborted.");
117
+ return;
173
118
  }
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
119
  }
187
- }
120
+ await withSDKErrorHandling(async () => {
121
+ return sdk.deleteSecretsByName({
122
+ path: { name: options.name },
123
+ query: { environment: env },
124
+ });
125
+ }, "Failed to delete secret");
126
+ terminal.success(`Secret deleted.`);
127
+ });
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * Show hub connection status
3
3
  */
4
- export declare function executeStatus(): Promise<void>;
4
+ export declare const executeStatus: () => Promise<void>;
@@ -1,34 +1,21 @@
1
1
  import { loadState } from "../../core/state.js";
2
2
  import { getHubCredentials } from "../../core/credentials.js";
3
3
  import { terminal } from "../../utils/terminal.js";
4
+ import { withCommandErrorHandler } from "../../utils/command-wrapper.js";
4
5
  /**
5
6
  * Show hub connection status
6
7
  */
7
- export async function executeStatus() {
8
- try {
9
- const state = await loadState();
10
- if (!state.hub) {
11
- terminal.warn("No hub connection configured.");
12
- terminal.blank();
13
- terminal.dim("Connect with:");
14
- terminal.dim(" griffin hub connect --url <url> --token <token>");
15
- return;
16
- }
17
- // Read credentials from user-level credentials file
18
- const credentials = await getHubCredentials();
19
- terminal.info("Hub connection:");
20
- terminal.log(` URL: ${terminal.colors.cyan(state.hub.baseUrl)}`);
21
- if (credentials?.token) {
22
- terminal.log(` API Token: ${terminal.colors.dim(credentials.token.substring(0, 8) + "...")}`);
23
- terminal.log(` Updated: ${terminal.colors.dim(new Date(credentials.updatedAt).toLocaleString())}`);
24
- }
25
- else {
26
- terminal.log(` API Token: ${terminal.colors.dim("(not set)")}`);
27
- }
28
- terminal.blank();
8
+ export const executeStatus = withCommandErrorHandler(async () => {
9
+ const state = await loadState();
10
+ const credentials = await getHubCredentials();
11
+ terminal.info("Hub connection:");
12
+ terminal.log(` URL: ${terminal.colors.cyan(state.hub.baseUrl)}`);
13
+ if (credentials?.token) {
14
+ terminal.log(` API Token: ${terminal.colors.dim(credentials.token.substring(0, 8) + "...")}`);
15
+ terminal.log(` Updated: ${terminal.colors.dim(new Date(credentials.updatedAt).toLocaleString())}`);
29
16
  }
30
- catch (error) {
31
- terminal.error(error.message);
32
- terminal.exit(1);
17
+ else {
18
+ terminal.log(` API Token: ${terminal.colors.dim("(not set)")}`);
33
19
  }
34
- }
20
+ terminal.blank();
21
+ });
@@ -29,10 +29,10 @@ export async function executeInit(options) {
29
29
  terminal.dim(" griffin variables add API_KEY=your-key --env default");
30
30
  terminal.dim(" 2. Create test monitors (*.ts files in __griffin__/ directories)");
31
31
  terminal.dim(" 3. Run tests locally:");
32
- terminal.dim(" griffin local run");
32
+ terminal.dim(" griffin test");
33
33
  terminal.dim(" 4. Connect to hub (optional):");
34
- terminal.dim(" griffin hub connect --url <url> --token <token>");
34
+ terminal.dim(" griffin auth connect --url <url> --token <token>");
35
35
  terminal.dim(" 5. Deploy to hub:");
36
- terminal.dim(" griffin hub apply");
36
+ terminal.dim(" griffin apply");
37
37
  terminal.blank();
38
38
  }
@@ -1,4 +1,4 @@
1
1
  export interface RunLocalOptions {
2
- env?: string;
2
+ env: string;
3
3
  }
4
- export declare function executeRunLocal(options: RunLocalOptions): Promise<void>;
4
+ export declare const executeRunLocal: (options: RunLocalOptions) => Promise<void>;
@@ -3,54 +3,49 @@ 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";
6
- export async function executeRunLocal(options) {
7
- try {
8
- // Resolve environment
9
- const envName = await resolveEnvironment(options.env);
10
- terminal.info(`Running tests locally against ${terminal.colors.cyan(envName)} environment`);
11
- terminal.dim(`Variables will be loaded from variables.yaml for environment: ${envName}`);
12
- terminal.blank();
13
- const spinner = terminal.spinner("Discovering test files...").start();
14
- const testFiles = findTestFiles();
15
- if (testFiles.length === 0) {
16
- spinner.fail("No test files found");
17
- terminal.dim("Looking for .ts files in __griffin__ directories.");
18
- terminal.exit(1);
19
- }
20
- spinner.succeed(`Found ${terminal.colors.bold(testFiles.length.toString())} test file(s)`);
21
- testFiles.forEach((file) => terminal.dim(` - ${file}`));
22
- terminal.blank();
23
- const results = await Promise.all(testFiles.map(async (file) => {
24
- const fileName = basename(file);
25
- const testSpinner = terminal
26
- .spinner(`Running ${terminal.colors.cyan(fileName)}`)
27
- .start();
28
- const result = await runTest(file, envName);
29
- if (result.success) {
30
- testSpinner.succeed(`${terminal.colors.cyan(fileName)} passed`);
31
- }
32
- else {
33
- testSpinner.fail(`${terminal.colors.cyan(fileName)} failed`);
34
- }
35
- return result;
36
- }));
37
- // Print summary
38
- const successful = results.filter((r) => r.success).length;
39
- const failed = results.length - successful;
40
- terminal.blank();
41
- if (failed === 0) {
42
- terminal.success(`All tests passed (${terminal.colors.bold(successful.toString())} / ${results.length})`);
6
+ import { withCommandErrorHandler } from "../../utils/command-wrapper.js";
7
+ export const executeRunLocal = withCommandErrorHandler(async (options) => {
8
+ // Resolve environment
9
+ const envName = await resolveEnvironment(options.env);
10
+ terminal.info(`Running tests locally against ${terminal.colors.cyan(envName)} environment`);
11
+ terminal.dim(`Variables will be loaded from variables.yaml for environment: ${envName}`);
12
+ terminal.blank();
13
+ const spinner = terminal.spinner("Discovering test files...").start();
14
+ const testFiles = findTestFiles();
15
+ if (testFiles.length === 0) {
16
+ spinner.fail("No test files found");
17
+ terminal.dim("Looking for .ts files in __griffin__ directories.");
18
+ terminal.exit(1);
19
+ }
20
+ spinner.succeed(`Found ${terminal.colors.bold(testFiles.length.toString())} test file(s)`);
21
+ testFiles.forEach((file) => terminal.dim(` - ${file}`));
22
+ terminal.blank();
23
+ const results = await Promise.all(testFiles.map(async (file) => {
24
+ const fileName = basename(file);
25
+ const testSpinner = terminal
26
+ .spinner(`Running ${terminal.colors.cyan(fileName)}`)
27
+ .start();
28
+ const result = await runTest(file, envName);
29
+ if (result.success) {
30
+ testSpinner.succeed(`${terminal.colors.cyan(fileName)} passed`);
43
31
  }
44
32
  else {
45
- terminal.error(`${terminal.colors.bold(failed.toString())} test(s) failed, ${terminal.colors.bold(successful.toString())} passed`);
46
- terminal.exit(1);
33
+ testSpinner.fail(`${terminal.colors.cyan(fileName)} failed`);
47
34
  }
35
+ return result;
36
+ }));
37
+ // Print summary
38
+ const successful = results.filter((r) => r.success).length;
39
+ const failed = results.length - successful;
40
+ terminal.blank();
41
+ if (failed === 0) {
42
+ terminal.success(`All tests passed (${terminal.colors.bold(successful.toString())} / ${results.length})`);
48
43
  }
49
- catch (error) {
50
- terminal.error(error.message);
44
+ else {
45
+ terminal.error(`${terminal.colors.bold(failed.toString())} test(s) failed, ${terminal.colors.bold(successful.toString())} passed`);
51
46
  terminal.exit(1);
52
47
  }
53
- }
48
+ });
54
49
  async function runTest(file, envName) {
55
50
  try {
56
51
  const result = await runTestFile(file, envName);
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * Validate test monitor files without syncing
3
3
  */
4
- export declare function executeValidate(): Promise<void>;
4
+ export declare const executeValidate: () => Promise<void>;
@@ -1,47 +1,27 @@
1
1
  import { loadState } from "../core/state.js";
2
- import { discoverMonitors, formatDiscoveryErrors } from "../core/discovery.js";
3
2
  import { terminal } from "../utils/terminal.js";
3
+ import { withCommandErrorHandler } from "../utils/command-wrapper.js";
4
+ import { discoverLocalMonitors } from "../core/monitor-helpers.js";
4
5
  /**
5
6
  * Validate test monitor files without syncing
6
7
  */
7
- export async function executeValidate() {
8
- const spinner = terminal.spinner("Validating test monitors...").start();
9
- try {
10
- // Load state for discovery settings
11
- const state = await loadState();
12
- const discoveryPattern = state.discovery?.pattern || "**/__griffin__/*.{ts,js}";
13
- const discoveryIgnore = state.discovery?.ignore || [
14
- "node_modules/**",
15
- "dist/**",
16
- ];
17
- // Discover monitors
18
- const { monitors, errors } = await discoverMonitors(discoveryPattern, discoveryIgnore);
19
- // Report errors
20
- if (errors.length > 0) {
21
- spinner.fail(`Validation failed with ${errors.length} error(s)`);
22
- terminal.blank();
23
- terminal.error(formatDiscoveryErrors(errors));
24
- terminal.exit(1);
8
+ export const executeValidate = withCommandErrorHandler(async () => {
9
+ // Load state for discovery settings
10
+ const state = await loadState();
11
+ // Discover monitors
12
+ const { monitors } = await discoverLocalMonitors(state);
13
+ // Report success
14
+ terminal.blank();
15
+ for (const { monitor, filePath, exportName } of monitors) {
16
+ const shortPath = filePath.replace(process.cwd(), ".");
17
+ const exportInfo = exportName === "default" ? "" : terminal.colors.dim(` (${exportName})`);
18
+ terminal.log(` ${terminal.colors.green("●")} ${terminal.colors.cyan(monitor.name)}${exportInfo}`);
19
+ terminal.dim(` ${shortPath}`);
20
+ terminal.dim(` Nodes: ${monitor.nodes.length}, Edges: ${monitor.edges.length}`);
21
+ if (monitor.frequency) {
22
+ terminal.dim(` Schedule: Every ${monitor.frequency.every} ${monitor.frequency.unit}`);
25
23
  }
26
- // Report success
27
- spinner.succeed(`Found ${terminal.colors.bold(monitors.length.toString())} valid monitor(s)`);
28
24
  terminal.blank();
29
- for (const { monitor, filePath, exportName } of monitors) {
30
- const shortPath = filePath.replace(process.cwd(), ".");
31
- const exportInfo = exportName === "default" ? "" : terminal.colors.dim(` (${exportName})`);
32
- terminal.log(` ${terminal.colors.green("●")} ${terminal.colors.cyan(monitor.name)}${exportInfo}`);
33
- terminal.dim(` ${shortPath}`);
34
- terminal.dim(` Nodes: ${monitor.nodes.length}, Edges: ${monitor.edges.length}`);
35
- if (monitor.frequency) {
36
- terminal.dim(` Schedule: Every ${monitor.frequency.every} ${monitor.frequency.unit}`);
37
- }
38
- terminal.blank();
39
- }
40
- terminal.success("All monitors are valid");
41
- }
42
- catch (error) {
43
- spinner.fail("Validation failed");
44
- terminal.error(error.message);
45
- terminal.exit(1);
46
25
  }
47
- }
26
+ terminal.success("All monitors are valid");
27
+ });
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * List all variables for an environment
3
3
  */
4
- export declare function executeVariablesList(env: string): Promise<void>;
4
+ export declare const executeVariablesList: (env: string) => Promise<void>;
5
5
  /**
6
6
  * Add or update a variable for an environment
7
7
  */
8
- export declare function executeVariablesAdd(keyValue: string, env: string): Promise<void>;
8
+ export declare const executeVariablesAdd: (keyValue: string, env: string) => Promise<void>;
9
9
  /**
10
10
  * Remove a variable from an environment
11
11
  */
12
- export declare function executeVariablesRemove(key: string, env: string): Promise<void>;
12
+ export declare const executeVariablesRemove: (key: string, env: string) => Promise<void>;