@griffin-app/griffin-cli 1.0.33 → 1.0.34

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
@@ -38,8 +38,12 @@ program
38
38
  program
39
39
  .command("validate")
40
40
  .description("Check monitor files for errors")
41
- .action(async () => {
42
- await executeValidate({ json: program.opts().json });
41
+ .option("--monitor <name>", "Validate only this monitor by name")
42
+ .action(async (options) => {
43
+ await executeValidate({
44
+ monitor: options.monitor,
45
+ json: program.opts().json,
46
+ });
43
47
  });
44
48
  program
45
49
  .command("status")
@@ -51,18 +55,25 @@ program
51
55
  .command("test")
52
56
  .description("Run monitors locally")
53
57
  .option("--env <name>", "Environment name", "default")
58
+ .option("--monitor <name>", "Run only this monitor by name")
54
59
  .action(async (options) => {
55
- await executeRunLocal({ env: options.env, json: program.opts().json });
60
+ await executeRunLocal({
61
+ env: options.env,
62
+ monitor: options.monitor,
63
+ json: program.opts().json,
64
+ });
56
65
  });
57
66
  program
58
67
  .command("plan")
59
68
  .description("Preview pending monitor changes")
60
69
  .option("--env <name>", "Environment name", "default")
70
+ .option("--monitor <name>", "Plan only this monitor by name")
61
71
  .option("--json", "Output in JSON format")
62
72
  .action(async (options) => {
63
73
  await executePlan({
64
74
  ...options,
65
75
  env: options.env,
76
+ monitor: options.monitor,
66
77
  json: program.opts().json ?? options.json,
67
78
  });
68
79
  });
@@ -70,6 +81,7 @@ program
70
81
  .command("apply")
71
82
  .description("Push monitor changes to the platform")
72
83
  .option("--env <name>", "Environment name", "default")
84
+ .option("--monitor <name>", "Apply only this monitor by name")
73
85
  .option("--auto-approve", "Skip confirmation prompt")
74
86
  .option("--dry-run", "Show what would be done without making changes")
75
87
  .option("--prune", "Delete monitors on hub that don't exist locally")
@@ -77,6 +89,7 @@ program
77
89
  await executeApply({
78
90
  ...options,
79
91
  env: options.env,
92
+ monitor: options.monitor,
80
93
  json: program.opts().json,
81
94
  });
82
95
  });
@@ -2,6 +2,7 @@ export interface ApplyOptions {
2
2
  autoApprove?: boolean;
3
3
  dryRun?: boolean;
4
4
  env: string;
5
+ monitor?: string;
5
6
  prune?: boolean;
6
7
  json?: boolean;
7
8
  }
@@ -3,7 +3,8 @@ import { computeDiff, formatDiff } from "../../core/diff.js";
3
3
  import { applyDiff, formatApplyResult } from "../../core/apply.js";
4
4
  import { createSdkAndState } from "../../core/sdk.js";
5
5
  import { createCommandHandler } from "../../utils/command-wrapper.js";
6
- import { discoverLocalMonitors, fetchRemoteMonitors, resolveLocalMonitors, } from "../../core/monitor-helpers.js";
6
+ import { discoverLocalMonitors, fetchRemoteMonitors, resolveLocalMonitors, monitorNotFoundData, } from "../../core/monitor-helpers.js";
7
+ import { OutputError } from "../../utils/output.js";
7
8
  /**
8
9
  * Apply changes to the hub
9
10
  */
@@ -15,9 +16,29 @@ export const executeApply = createCommandHandler("apply", async (options, output
15
16
  const { monitors } = await discoverLocalMonitors(state);
16
17
  const remoteMonitors = await fetchRemoteMonitors(sdk, state.projectId, envName);
17
18
  const resolvedMonitors = await resolveLocalMonitors(monitors, state.projectId, envName);
18
- const diff = computeDiff(resolvedMonitors, remoteMonitors, {
19
+ let diff = computeDiff(resolvedMonitors, remoteMonitors, {
19
20
  includeDeletions: options.prune || false,
20
21
  });
22
+ if (options.monitor) {
23
+ const name = options.monitor;
24
+ const filtered = diff.actions.filter((a) => a.monitor?.name === name || a.remoteMonitor?.name === name);
25
+ if (filtered.length === 0) {
26
+ const inPlan = diff.actions.map((a) => a.monitor?.name ?? a.remoteMonitor?.name ?? "").filter(Boolean);
27
+ const data = monitorNotFoundData(name, [...new Set(inPlan)]);
28
+ throw new OutputError("NOT_FOUND", `Monitor "${name}" not found in plan`, { available: data.available }, data.available.length
29
+ ? "Available: " + data.available.join(", ")
30
+ : undefined);
31
+ }
32
+ diff = {
33
+ actions: filtered,
34
+ summary: {
35
+ creates: filtered.filter((a) => a.type === "create").length,
36
+ updates: filtered.filter((a) => a.type === "update").length,
37
+ deletes: filtered.filter((a) => a.type === "delete").length,
38
+ noops: filtered.filter((a) => a.type === "noop").length,
39
+ },
40
+ };
41
+ }
21
42
  output.blank();
22
43
  output.log(formatDiff(diff));
23
44
  output.blank();
@@ -1,6 +1,7 @@
1
1
  export interface PlanOptions {
2
2
  json?: boolean;
3
3
  env: string;
4
+ monitor?: string;
4
5
  }
5
6
  /**
6
7
  * Show what changes would be applied
@@ -2,7 +2,8 @@ import { resolveEnvironment } from "../../core/state.js";
2
2
  import { createSdkAndState } from "../../core/sdk.js";
3
3
  import { computeDiff, formatDiff, formatDiffJson } from "../../core/diff.js";
4
4
  import { createCommandHandler } from "../../utils/command-wrapper.js";
5
- import { discoverLocalMonitors, fetchRemoteMonitors, resolveLocalMonitors, } from "../../core/monitor-helpers.js";
5
+ import { discoverLocalMonitors, fetchRemoteMonitors, resolveLocalMonitors, monitorNotFoundData, } from "../../core/monitor-helpers.js";
6
+ import { OutputError } from "../../utils/output.js";
6
7
  /**
7
8
  * Show what changes would be applied
8
9
  */
@@ -11,8 +12,23 @@ export const executePlan = createCommandHandler("plan", async (options, output)
11
12
  const envName = await resolveEnvironment(options.env);
12
13
  const { monitors } = await discoverLocalMonitors(state);
13
14
  const remoteMonitors = await fetchRemoteMonitors(sdk, state.projectId, envName);
14
- const resolvedMonitors = await resolveLocalMonitors(monitors, state.projectId, envName);
15
- const diff = computeDiff(resolvedMonitors, remoteMonitors, {
15
+ let resolvedMonitors = await resolveLocalMonitors(monitors, state.projectId, envName);
16
+ let remoteFiltered = remoteMonitors;
17
+ if (options.monitor) {
18
+ const name = options.monitor;
19
+ resolvedMonitors = resolvedMonitors.filter((m) => m.name === name);
20
+ remoteFiltered = remoteMonitors.filter((m) => m.name === name);
21
+ if (resolvedMonitors.length === 0 && remoteFiltered.length === 0) {
22
+ const localNames = monitors.map((m) => m.monitor.name);
23
+ const remoteNames = remoteMonitors.map((m) => m.name);
24
+ const available = [...new Set([...localNames, ...remoteNames])];
25
+ const data = monitorNotFoundData(name, available);
26
+ throw new OutputError("NOT_FOUND", `Monitor "${name}" not found locally or on hub`, { available: data.available }, data.available.length
27
+ ? "Available: " + data.available.join(", ")
28
+ : undefined);
29
+ }
30
+ }
31
+ const diff = computeDiff(resolvedMonitors, remoteFiltered, {
16
32
  includeDeletions: false,
17
33
  });
18
34
  output.setData({
@@ -1,5 +1,14 @@
1
+ import type { ExecutionResult } from "@griffin-app/griffin-executor";
2
+ export type TestResultPhase = "load" | "validate" | "resolve" | "execute" | "assert";
3
+ export interface TestResult {
4
+ success: boolean;
5
+ error?: string;
6
+ phase?: TestResultPhase;
7
+ executionResult?: ExecutionResult;
8
+ }
1
9
  export interface RunLocalOptions {
2
10
  env: string;
11
+ monitor?: string;
3
12
  json?: boolean;
4
13
  }
5
14
  export declare const executeRunLocal: (options: RunLocalOptions) => Promise<void>;
@@ -1,15 +1,53 @@
1
1
  import { findTestFiles } from "../../monitor-discovery.js";
2
- import { runTestFile } from "../../monitor-runner.js";
2
+ import { runTestFile, MonitorRunnerError } from "../../monitor-runner.js";
3
+ import { loadState } from "../../core/state.js";
3
4
  import { resolveEnvironment } from "../../core/state.js";
5
+ import { formatDiscoveryErrors } from "../../core/discovery.js";
6
+ import { discoverLocalMonitorsSilent, filterDiscoveredByName, monitorNotFoundData, } from "../../core/monitor-helpers.js";
7
+ import { OutputError } from "../../utils/output.js";
4
8
  import { basename } from "path";
5
9
  import { createCommandHandler } from "../../utils/command-wrapper.js";
10
+ function inferPhaseFromMessage(message) {
11
+ if (message.startsWith("Invalid monitor") || message.includes("valid TestMonitor"))
12
+ return "validate";
13
+ if (message.startsWith("Failed to load"))
14
+ return "load";
15
+ if (message.startsWith("Error executing monitor"))
16
+ return "execute";
17
+ return "execute";
18
+ }
6
19
  export const executeRunLocal = createCommandHandler("test", async (options, output) => {
7
20
  const envName = await resolveEnvironment(options.env);
8
21
  output.info(`Running tests locally against ${output.colors.cyan(envName)} environment`);
9
22
  output.dim(`Variables will be loaded from variables.yaml for environment: ${envName}`);
10
23
  output.blank();
11
24
  const spinner = output.spinner("Discovering test files...").start();
12
- const testFiles = findTestFiles();
25
+ let testFiles;
26
+ if (options.monitor) {
27
+ const state = await loadState();
28
+ const result = await discoverLocalMonitorsSilent(state);
29
+ if (result.errors.length > 0) {
30
+ spinner.fail("Discovery failed");
31
+ output.setData({
32
+ error: "DISCOVERY_FAILED",
33
+ message: formatDiscoveryErrors(result.errors),
34
+ });
35
+ output.error(formatDiscoveryErrors(result.errors));
36
+ output.exit(1);
37
+ }
38
+ const filtered = filterDiscoveredByName(result.monitors, options.monitor);
39
+ if (filtered.length === 0) {
40
+ spinner.fail(`Monitor "${options.monitor}" not found`);
41
+ const data = monitorNotFoundData(options.monitor, result.monitors.map((m) => m.monitor.name));
42
+ throw new OutputError("NOT_FOUND", data.message, { available: data.available }, data.available.length
43
+ ? "Available: " + data.available.join(", ")
44
+ : undefined);
45
+ }
46
+ testFiles = [...new Set(filtered.map((m) => m.filePath))];
47
+ }
48
+ else {
49
+ testFiles = findTestFiles();
50
+ }
13
51
  if (testFiles.length === 0) {
14
52
  spinner.fail("No test files found");
15
53
  output.setData({
@@ -22,9 +60,9 @@ export const executeRunLocal = createCommandHandler("test", async (options, outp
22
60
  output.exit(1);
23
61
  }
24
62
  spinner.succeed(`Found ${output.colors.bold(testFiles.length.toString())} test file(s)`);
25
- testFiles.forEach((file) => output.dim(` - ${file}`));
63
+ testFiles.forEach((file) => output.dim(` - ${basename(file)}`));
26
64
  output.blank();
27
- const results = await Promise.all(testFiles.map(async (file) => {
65
+ const rawResults = await Promise.all(testFiles.map(async (file) => {
28
66
  const fileName = basename(file);
29
67
  const testSpinner = output
30
68
  .spinner(`Running ${output.colors.cyan(fileName)}`)
@@ -35,32 +73,74 @@ export const executeRunLocal = createCommandHandler("test", async (options, outp
35
73
  }
36
74
  else {
37
75
  testSpinner.fail(`${output.colors.cyan(fileName)} failed`);
76
+ if (result.error) {
77
+ output.dim(` ${result.error}`);
78
+ }
79
+ if (result.executionResult?.errors &&
80
+ result.executionResult.errors.length > 0) {
81
+ for (const err of result.executionResult.errors) {
82
+ output.dim(` • ${err}`);
83
+ }
84
+ }
85
+ }
86
+ const row = {
87
+ file: fileName,
88
+ success: result.success,
89
+ };
90
+ if (!result.success) {
91
+ row.error = result.error;
92
+ row.phase = result.phase;
93
+ if (result.executionResult?.errors &&
94
+ result.executionResult.errors.length > 0) {
95
+ row.errors = result.executionResult.errors;
96
+ }
38
97
  }
39
- return { file: fileName, success: result.success };
98
+ return row;
40
99
  }));
41
- const passed = results.filter((r) => r.success).length;
42
- const failed = results.length - passed;
100
+ const passed = rawResults.filter((r) => r.success).length;
101
+ const failed = rawResults.length - passed;
43
102
  output.setData({
44
- results,
103
+ results: rawResults,
45
104
  passed,
46
105
  failed,
47
106
  });
48
107
  output.blank();
49
108
  if (failed === 0) {
50
- output.success(`All tests passed (${output.colors.bold(passed.toString())} / ${results.length})`);
109
+ output.success(`All tests passed (${output.colors.bold(passed.toString())} / ${rawResults.length})`);
51
110
  }
52
111
  else {
53
112
  output.error(`${output.colors.bold(failed.toString())} test(s) failed, ${output.colors.bold(passed.toString())} passed`);
113
+ const failedResults = rawResults.filter((r) => !r.success);
114
+ for (const r of failedResults) {
115
+ output.dim(` • ${r.file}: ${r.error ?? "unknown error"}`);
116
+ }
54
117
  output.exit(1);
55
118
  }
56
119
  });
57
120
  async function runTest(file, envName) {
58
121
  try {
59
122
  const result = await runTestFile(file, envName);
60
- return { success: result.success };
123
+ if (result.success) {
124
+ return { success: true };
125
+ }
126
+ const error = result.errors?.[0] ?? (result.errors?.length ? result.errors.join("; ") : undefined);
127
+ return {
128
+ success: false,
129
+ error: error ?? "Monitor run failed",
130
+ phase: "assert",
131
+ executionResult: result,
132
+ };
61
133
  }
62
134
  catch (error) {
63
- console.error(error.message || String(error));
64
- return { success: false };
135
+ const err = error;
136
+ const message = err?.message ?? String(error);
137
+ const phase = error instanceof MonitorRunnerError
138
+ ? error.phase
139
+ : inferPhaseFromMessage(message);
140
+ return {
141
+ success: false,
142
+ error: message,
143
+ phase,
144
+ };
65
145
  }
66
146
  }
@@ -1,4 +1,5 @@
1
1
  export interface ValidateOptions {
2
+ monitor?: string;
2
3
  json?: boolean;
3
4
  }
4
5
  /**
@@ -1,15 +1,26 @@
1
1
  import { loadState } from "../core/state.js";
2
2
  import { createCommandHandler } from "../utils/command-wrapper.js";
3
- import { discoverLocalMonitors } from "../core/monitor-helpers.js";
3
+ import { OutputError } from "../utils/output.js";
4
+ import { discoverLocalMonitors, filterDiscoveredByName, monitorNotFoundData, } from "../core/monitor-helpers.js";
4
5
  /**
5
6
  * Validate test monitor files without syncing
6
7
  */
7
- export const executeValidate = createCommandHandler("validate", async (_options, output) => {
8
+ export const executeValidate = createCommandHandler("validate", async (options, output) => {
8
9
  const state = await loadState();
9
10
  const { monitors } = await discoverLocalMonitors(state);
11
+ let toValidate = monitors;
12
+ if (options.monitor) {
13
+ toValidate = filterDiscoveredByName(monitors, options.monitor);
14
+ if (toValidate.length === 0) {
15
+ const data = monitorNotFoundData(options.monitor, monitors.map((m) => m.monitor.name));
16
+ throw new OutputError("NOT_FOUND", data.message, { available: data.available }, data.available.length
17
+ ? "Available: " + data.available.join(", ")
18
+ : undefined);
19
+ }
20
+ }
10
21
  output.setData({
11
22
  valid: true,
12
- monitors: monitors.map(({ monitor, filePath, exportName }) => ({
23
+ monitors: toValidate.map(({ monitor, filePath, exportName }) => ({
13
24
  name: monitor.name,
14
25
  filePath: filePath.replace(process.cwd(), "."),
15
26
  exportName,
@@ -21,7 +32,7 @@ export const executeValidate = createCommandHandler("validate", async (_options,
21
32
  })),
22
33
  });
23
34
  output.blank();
24
- for (const { monitor, filePath, exportName } of monitors) {
35
+ for (const { monitor, filePath, exportName } of toValidate) {
25
36
  const shortPath = filePath.replace(process.cwd(), ".");
26
37
  const exportInfo = exportName === "default" ? "" : output.colors.dim(` (${exportName})`);
27
38
  output.log(` ${output.colors.green("●")} ${output.colors.cyan(monitor.name)}${exportInfo}`);
@@ -84,7 +84,6 @@ async function applyUpdate(action, sdk, applied) {
84
84
  },
85
85
  body: resolvedMonitor,
86
86
  });
87
- console.log(JSON.stringify(result.data, null, 2));
88
87
  applied.push({
89
88
  type: "update",
90
89
  monitorName: action.remoteMonitor.name,
@@ -1,7 +1,19 @@
1
1
  import type { GriffinHubSdk, MonitorV1 } from "@griffin-app/griffin-hub-sdk";
2
2
  import type { StateFile } from "../schemas/state.js";
3
- import { type DiscoveryResult } from "./discovery.js";
3
+ import { type DiscoveredMonitor, type DiscoveryResult } from "./discovery.js";
4
4
  import { MonitorDSL } from "@griffin-app/griffin-core/types";
5
+ /**
6
+ * Get discovery pattern and ignore list from state (shared defaults).
7
+ */
8
+ export declare function getDiscoveryConfig(state?: StateFile): {
9
+ pattern: string;
10
+ ignore: string[];
11
+ };
12
+ /**
13
+ * Discover local monitors using state-configured patterns, without spinner or exit.
14
+ * Caller handles errors and UI. Use when you need to discover and then filter or handle errors yourself (e.g. test --monitor).
15
+ */
16
+ export declare function discoverLocalMonitorsSilent(state?: StateFile): Promise<DiscoveryResult>;
5
17
  /**
6
18
  * Discover local monitors using state-configured patterns.
7
19
  * Shows spinner, handles errors, and returns the discovery result.
@@ -18,3 +30,16 @@ export declare function fetchRemoteMonitors(sdk: GriffinHubSdk, projectId: strin
18
30
  export declare function resolveLocalMonitors(monitors: {
19
31
  monitor: MonitorDSL;
20
32
  }[], projectId: string, envName: string): Promise<Omit<MonitorV1, "id">[]>;
33
+ /**
34
+ * Filter discovered monitors by name (exact match).
35
+ */
36
+ export declare function filterDiscoveredByName(monitors: DiscoveredMonitor[], name: string): DiscoveredMonitor[];
37
+ /**
38
+ * Build NOT_FOUND error data for JSON output when a monitor name is missing.
39
+ * Use with output.setData() then output.error(), output.info("Available monitors:"), etc., and output.exit(1).
40
+ */
41
+ export declare function monitorNotFoundData(monitorName: string, availableNames: string[]): {
42
+ error: "NOT_FOUND";
43
+ message: string;
44
+ available: string[];
45
+ };
@@ -3,18 +3,31 @@ import { terminal } from "../utils/terminal.js";
3
3
  import { withSDKErrorHandling } from "../utils/sdk-error.js";
4
4
  import { loadVariables } from "./variables.js";
5
5
  import { resolveMonitor } from "../resolve.js";
6
+ /**
7
+ * Get discovery pattern and ignore list from state (shared defaults).
8
+ */
9
+ export function getDiscoveryConfig(state) {
10
+ return {
11
+ pattern: state?.discovery?.pattern ?? "**/__griffin__/*.{ts,js}",
12
+ ignore: state?.discovery?.ignore ?? ["node_modules/**", "dist/**"],
13
+ };
14
+ }
15
+ /**
16
+ * Discover local monitors using state-configured patterns, without spinner or exit.
17
+ * Caller handles errors and UI. Use when you need to discover and then filter or handle errors yourself (e.g. test --monitor).
18
+ */
19
+ export async function discoverLocalMonitorsSilent(state) {
20
+ const { pattern, ignore } = getDiscoveryConfig(state);
21
+ return discoverMonitors(pattern, ignore);
22
+ }
6
23
  /**
7
24
  * Discover local monitors using state-configured patterns.
8
25
  * Shows spinner, handles errors, and returns the discovery result.
9
26
  */
10
27
  export async function discoverLocalMonitors(state) {
11
- const discoveryPattern = state?.discovery?.pattern || "**/__griffin__/*.{ts,js}";
12
- const discoveryIgnore = state?.discovery?.ignore || [
13
- "node_modules/**",
14
- "dist/**",
15
- ];
28
+ const { pattern, ignore } = getDiscoveryConfig(state);
16
29
  const spinner = terminal.spinner("Discovering local monitors...").start();
17
- const result = await discoverMonitors(discoveryPattern, discoveryIgnore);
30
+ const result = await discoverMonitors(pattern, ignore);
18
31
  if (result.errors.length > 0) {
19
32
  spinner.fail("Discovery failed");
20
33
  terminal.error(formatDiscoveryErrors(result.errors));
@@ -46,3 +59,20 @@ export async function resolveLocalMonitors(monitors, projectId, envName) {
46
59
  const variables = await loadVariables(envName);
47
60
  return monitors.map((p) => resolveMonitor(p.monitor, projectId, envName, variables));
48
61
  }
62
+ /**
63
+ * Filter discovered monitors by name (exact match).
64
+ */
65
+ export function filterDiscoveredByName(monitors, name) {
66
+ return monitors.filter((m) => m.monitor.name === name);
67
+ }
68
+ /**
69
+ * Build NOT_FOUND error data for JSON output when a monitor name is missing.
70
+ * Use with output.setData() then output.error(), output.info("Available monitors:"), etc., and output.exit(1).
71
+ */
72
+ export function monitorNotFoundData(monitorName, availableNames) {
73
+ return {
74
+ error: "NOT_FOUND",
75
+ message: `Monitor "${monitorName}" not found`,
76
+ available: availableNames,
77
+ };
78
+ }
@@ -1,5 +1,10 @@
1
1
  import "tsx";
2
2
  import { ExecutionResult } from "@griffin-app/griffin-executor";
3
+ export type MonitorRunnerPhase = "load" | "validate" | "resolve" | "execute" | "assert";
4
+ export declare class MonitorRunnerError extends Error {
5
+ readonly phase: MonitorRunnerPhase;
6
+ constructor(phase: MonitorRunnerPhase, message: string, cause?: unknown);
7
+ }
3
8
  /**
4
9
  * Runs a TypeScript test file and executes the resulting JSON monitor.
5
10
  */
@@ -6,9 +6,20 @@ 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
- import { terminal } from "./utils/terminal.js";
10
9
  import { createEnvSecretsProvider, HubSecretProvider } from "./core/secrets.js";
11
10
  import { createSdkFromState } from "./core/sdk.js";
11
+ export class MonitorRunnerError extends Error {
12
+ phase;
13
+ constructor(phase, message, cause) {
14
+ super(message);
15
+ this.phase = phase;
16
+ this.name = "MonitorRunnerError";
17
+ if (cause !== undefined && cause instanceof Error && cause.cause === undefined) {
18
+ this.cause = cause;
19
+ }
20
+ Object.setPrototypeOf(this, MonitorRunnerError.prototype);
21
+ }
22
+ }
12
23
  function validateDsl(monitor) {
13
24
  const errors = Value.Errors(MonitorDSLSchema, monitor);
14
25
  if (errors.length > 0) {
@@ -16,16 +27,43 @@ function validateDsl(monitor) {
16
27
  }
17
28
  return monitor;
18
29
  }
30
+ function messageFromCause(cause) {
31
+ return cause instanceof Error ? cause.message : String(cause);
32
+ }
19
33
  /**
20
34
  * Runs a TypeScript test file and executes the resulting JSON monitor.
21
35
  */
22
36
  export async function runTestFile(filePath, envName) {
23
- const variables = await loadVariables(envName);
24
- const projectId = await getProjectId();
25
- const defaultExport = await import(filePath);
26
- const rawMonitor = validateDsl(defaultExport.default);
27
- terminal.dim(`Project ID: ${projectId}`);
28
- const resolvedMonitor = resolveMonitor(rawMonitor, projectId, envName, variables);
37
+ let variables;
38
+ let projectId;
39
+ try {
40
+ variables = await loadVariables(envName);
41
+ projectId = await getProjectId();
42
+ }
43
+ catch (cause) {
44
+ throw new MonitorRunnerError("resolve", messageFromCause(cause), cause);
45
+ }
46
+ let defaultExport;
47
+ try {
48
+ defaultExport = await import(filePath);
49
+ }
50
+ catch (cause) {
51
+ throw new MonitorRunnerError("load", messageFromCause(cause), cause);
52
+ }
53
+ let rawMonitor;
54
+ try {
55
+ rawMonitor = validateDsl(defaultExport.default);
56
+ }
57
+ catch (cause) {
58
+ throw new MonitorRunnerError("validate", messageFromCause(cause), cause);
59
+ }
60
+ let resolvedMonitor;
61
+ try {
62
+ resolvedMonitor = resolveMonitor(rawMonitor, projectId, envName, variables);
63
+ }
64
+ catch (cause) {
65
+ throw new MonitorRunnerError("resolve", messageFromCause(cause), cause);
66
+ }
29
67
  // Create secret provider: prefer hub (authenticated) for cloud secrets,
30
68
  // fall back to env provider for fully-local runs.
31
69
  const monitorV1 = { ...resolvedMonitor, id: randomUUID() };
@@ -46,6 +84,7 @@ export async function runTestFile(filePath, envName) {
46
84
  return result;
47
85
  }
48
86
  catch (error) {
49
- throw new Error(`Error executing monitor: ${error instanceof Error ? error.message : String(error)}`);
87
+ const message = error instanceof Error ? error.message : String(error);
88
+ throw new MonitorRunnerError("execute", `Error executing monitor: ${message}`, error);
50
89
  }
51
90
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@griffin-app/griffin-cli",
3
- "version": "1.0.33",
3
+ "version": "1.0.34",
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-core": "0.2.5",
28
- "@griffin-app/griffin-executor": "0.1.7",
29
- "@griffin-app/griffin-hub-sdk": "1.0.29",
27
+ "@griffin-app/griffin-core": "0.2.6",
28
+ "@griffin-app/griffin-executor": "0.1.8",
29
+ "@griffin-app/griffin-hub-sdk": "1.0.30",
30
30
  "better-auth": "^1.4.17",
31
31
  "cli-table3": "^0.6.5",
32
32
  "commander": "^12.1.0",