@griffin-app/griffin-cli 1.0.3 → 1.0.5

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 (51) hide show
  1. package/README.md +111 -161
  2. package/dist/cli.js +34 -15
  3. package/dist/commands/env.d.ts +1 -22
  4. package/dist/commands/env.js +22 -109
  5. package/dist/commands/generate-key.js +16 -10
  6. package/dist/commands/hub/apply.js +58 -34
  7. package/dist/commands/hub/connect.js +16 -9
  8. package/dist/commands/hub/login.d.ts +1 -0
  9. package/dist/commands/hub/login.js +79 -0
  10. package/dist/commands/hub/logout.d.ts +6 -0
  11. package/dist/commands/hub/logout.js +16 -0
  12. package/dist/commands/hub/plan.js +38 -21
  13. package/dist/commands/hub/run.js +93 -57
  14. package/dist/commands/hub/runs.js +75 -42
  15. package/dist/commands/hub/status.js +18 -13
  16. package/dist/commands/init.js +32 -24
  17. package/dist/commands/local/run.js +30 -16
  18. package/dist/commands/validate.js +18 -17
  19. package/dist/core/apply.d.ts +2 -2
  20. package/dist/core/apply.js +36 -38
  21. package/dist/core/apply.test.js +71 -27
  22. package/dist/core/credentials.d.ts +36 -0
  23. package/dist/core/credentials.js +98 -0
  24. package/dist/core/credentials.test.d.ts +1 -0
  25. package/dist/core/credentials.test.js +137 -0
  26. package/dist/core/diff.d.ts +7 -6
  27. package/dist/core/diff.js +2 -1
  28. package/dist/core/diff.test.js +44 -20
  29. package/dist/core/discovery.d.ts +2 -3
  30. package/dist/core/discovery.js +3 -10
  31. package/dist/core/plan-diff.d.ts +5 -6
  32. package/dist/core/plan-diff.js +6 -6
  33. package/dist/core/sdk.d.ts +5 -9
  34. package/dist/core/sdk.js +23 -15
  35. package/dist/core/variables.js +13 -0
  36. package/dist/index.d.ts +8 -3
  37. package/dist/index.js +6 -2
  38. package/dist/resolve.d.ts +3 -0
  39. package/dist/resolve.js +9 -0
  40. package/dist/schemas/credentials.d.ts +24 -0
  41. package/dist/schemas/credentials.js +23 -0
  42. package/dist/schemas/state.d.ts +5 -5
  43. package/dist/schemas/state.js +5 -7
  44. package/dist/test-runner.js +18 -22
  45. package/dist/utils/console.d.ts +5 -0
  46. package/dist/utils/console.js +5 -0
  47. package/dist/utils/sdk-error.d.ts +8 -0
  48. package/dist/utils/sdk-error.js +114 -0
  49. package/dist/utils/terminal.d.ts +100 -0
  50. package/dist/utils/terminal.js +148 -0
  51. package/package.json +9 -4
@@ -1,6 +1,5 @@
1
1
  import { describe, it, expect } from "vitest";
2
2
  import { computeDiff } from "./diff.js";
3
- import { FrequencyUnit } from "@griffin-app/griffin-ts/schema";
4
3
  // Helper to create a minimal test plan
5
4
  function createPlan(name, overrides) {
6
5
  return {
@@ -9,7 +8,20 @@ function createPlan(name, overrides) {
9
8
  project: "test-project",
10
9
  environment: "test",
11
10
  version: "1.0",
12
- frequency: { every: 5, unit: FrequencyUnit.MINUTE },
11
+ frequency: { every: 5, unit: "MINUTE" },
12
+ nodes: [],
13
+ edges: [],
14
+ ...overrides,
15
+ };
16
+ }
17
+ // Helper to create a resolved plan (without id)
18
+ function createResolvedPlan(name, overrides) {
19
+ return {
20
+ name,
21
+ project: "test-project",
22
+ environment: "test",
23
+ version: "1.0",
24
+ frequency: { every: 5, unit: "MINUTE" },
13
25
  nodes: [],
14
26
  edges: [],
15
27
  ...overrides,
@@ -18,9 +30,11 @@ function createPlan(name, overrides) {
18
30
  describe("computeDiff", () => {
19
31
  describe("CREATE actions", () => {
20
32
  it("should create action when plan exists locally but not remotely", () => {
21
- const local = [createPlan("health-check")];
33
+ const local = [createResolvedPlan("health-check")];
22
34
  const remote = [];
23
- const result = computeDiff(local, remote, { includeDeletions: false });
35
+ const result = computeDiff(local, remote, {
36
+ includeDeletions: false,
37
+ });
24
38
  expect(result.actions).toHaveLength(1);
25
39
  expect(result.actions[0].type).toBe("create");
26
40
  expect(result.actions[0].plan?.name).toBe("health-check");
@@ -33,16 +47,18 @@ describe("computeDiff", () => {
33
47
  describe("UPDATE actions", () => {
34
48
  it("should create update action when plan content differs", () => {
35
49
  const local = [
36
- createPlan("health-check", {
37
- frequency: { every: 10, unit: FrequencyUnit.MINUTE },
50
+ createResolvedPlan("health-check", {
51
+ frequency: { every: 10, unit: "MINUTE" },
38
52
  }),
39
53
  ];
40
54
  const remote = [
41
55
  createPlan("health-check", {
42
- frequency: { every: 5, unit: FrequencyUnit.MINUTE },
56
+ frequency: { every: 5, unit: "MINUTE" },
43
57
  }),
44
58
  ];
45
- const result = computeDiff(local, remote, { includeDeletions: false });
59
+ const result = computeDiff(local, remote, {
60
+ includeDeletions: false,
61
+ });
46
62
  expect(result.actions).toHaveLength(1);
47
63
  expect(result.actions[0].type).toBe("update");
48
64
  expect(result.actions[0].plan?.name).toBe("health-check");
@@ -54,10 +70,13 @@ describe("computeDiff", () => {
54
70
  });
55
71
  describe("NOOP actions", () => {
56
72
  it("should create noop action when plan content matches", () => {
57
- const plan = createPlan("health-check");
58
- const local = [plan];
59
- const remote = [{ ...plan }];
60
- const result = computeDiff(local, remote, { includeDeletions: false });
73
+ const resolvedPlan = createResolvedPlan("health-check");
74
+ const remotePlan = createPlan("health-check");
75
+ const local = [resolvedPlan];
76
+ const remote = [remotePlan];
77
+ const result = computeDiff(local, remote, {
78
+ includeDeletions: false,
79
+ });
61
80
  expect(result.actions).toHaveLength(1);
62
81
  expect(result.actions[0].type).toBe("noop");
63
82
  expect(result.summary.creates).toBe(0);
@@ -88,20 +107,22 @@ describe("computeDiff", () => {
88
107
  describe("Mixed scenarios", () => {
89
108
  it("should handle multiple plans with different actions", () => {
90
109
  const local = [
91
- createPlan("new-plan"),
92
- createPlan("updated-plan", {
93
- frequency: { every: 10, unit: FrequencyUnit.MINUTE },
110
+ createResolvedPlan("new-plan"),
111
+ createResolvedPlan("updated-plan", {
112
+ frequency: { every: 10, unit: "MINUTE" },
94
113
  }),
95
- createPlan("unchanged-plan"),
114
+ createResolvedPlan("unchanged-plan"),
96
115
  ];
97
116
  const remote = [
98
117
  createPlan("updated-plan", {
99
- frequency: { every: 5, unit: FrequencyUnit.MINUTE },
118
+ frequency: { every: 5, unit: "MINUTE" },
100
119
  }),
101
120
  createPlan("unchanged-plan"),
102
121
  createPlan("deleted-plan"),
103
122
  ];
104
- const result = computeDiff(local, remote, { includeDeletions: true });
123
+ const result = computeDiff(local, remote, {
124
+ includeDeletions: true,
125
+ });
105
126
  expect(result.actions).toHaveLength(4);
106
127
  expect(result.summary.creates).toBe(1);
107
128
  expect(result.summary.updates).toBe(1);
@@ -111,9 +132,12 @@ describe("computeDiff", () => {
111
132
  });
112
133
  describe("Matching by name", () => {
113
134
  it("should match plans by name, not by ID", () => {
114
- const local = [createPlan("health-check", { id: "local-id-123" })];
135
+ // Local plans don't have IDs after resolution
136
+ const local = [createResolvedPlan("health-check")];
115
137
  const remote = [createPlan("health-check", { id: "remote-id-456" })];
116
- const result = computeDiff(local, remote, { includeDeletions: false });
138
+ const result = computeDiff(local, remote, {
139
+ includeDeletions: false,
140
+ });
117
141
  // Should be NOOP because names match (IDs are ignored)
118
142
  expect(result.actions).toHaveLength(1);
119
143
  expect(result.actions[0].type).toBe("noop");
@@ -1,7 +1,6 @@
1
- import type { TestPlanV1 } from "@griffin-app/griffin-ts/types";
2
- export type RawTestPlan = Omit<TestPlanV1, "id" | "environment" | "project">;
1
+ import type { PlanDSL } from "@griffin-app/griffin-ts/types";
3
2
  export interface DiscoveredPlan {
4
- plan: RawTestPlan;
3
+ plan: PlanDSL;
5
4
  filePath: string;
6
5
  exportName: string;
7
6
  }
@@ -1,14 +1,8 @@
1
1
  import { glob } from "glob";
2
2
  import path from "node:path";
3
3
  import { pathToFileURL } from "node:url";
4
- import { TestPlanV1Schema } from "@griffin-app/griffin-ts/schema";
4
+ import { PlanDSLSchema } from "@griffin-app/griffin-ts/schema";
5
5
  import { Value } from "typebox/value";
6
- import { Type } from "typebox";
7
- const RawTestPlanSchema = Type.Omit(TestPlanV1Schema, [
8
- "id",
9
- "environment",
10
- "project",
11
- ]);
12
6
  /**
13
7
  * Discover and load test plan files from the filesystem
14
8
  */
@@ -21,7 +15,6 @@ export async function discoverPlans(pattern, ignore) {
21
15
  absolute: true,
22
16
  cwd: process.cwd(),
23
17
  });
24
- console.log(`Found ${files.length} test file(s)`);
25
18
  // Load each file
26
19
  for (const filePath of files) {
27
20
  try {
@@ -38,7 +31,7 @@ export async function discoverPlans(pattern, ignore) {
38
31
  return { plans, errors };
39
32
  }
40
33
  function isPlan(value) {
41
- return Value.Check(RawTestPlanSchema, value);
34
+ return Value.Check(PlanDSLSchema, value);
42
35
  }
43
36
  /**
44
37
  * Load plans from a single file
@@ -60,7 +53,7 @@ async function loadPlansFromFile(filePath) {
60
53
  });
61
54
  }
62
55
  else {
63
- const errors = Value.Errors(RawTestPlanSchema, module.default);
56
+ const errors = Value.Errors(PlanDSLSchema, module.default);
64
57
  throw new Error(`Default export is not a valid TestPlan. Got: ${JSON.stringify(errors, null, 2)}`);
65
58
  }
66
59
  }
@@ -1,6 +1,4 @@
1
- import type { RawTestPlan } from "./discovery.js";
2
- import type { TestPlanV1 } from "@griffin-app/griffin-ts/types";
3
- import { NodeType } from "@griffin-app/griffin-ts/schema";
1
+ import { PlanV1 } from "@griffin-app/griffin-hub-sdk";
4
2
  /**
5
3
  * Represents a change to a single field
6
4
  */
@@ -15,7 +13,7 @@ export interface FieldChange {
15
13
  export interface NodeChange {
16
14
  type: "add" | "remove" | "modify";
17
15
  nodeId: string;
18
- nodeType: NodeType;
16
+ nodeType: "ASSERTION" | "ENDPOINT" | "WAIT";
19
17
  summary: string;
20
18
  fieldChanges: FieldChange[];
21
19
  }
@@ -37,6 +35,7 @@ export interface PlanChanges {
37
35
  topLevel: FieldChange[];
38
36
  }
39
37
  /**
40
- * Compare two test plans and return granular changes
38
+ * Compare two test plans and return granular changes.
39
+ * Local plan should be resolved (variables replaced with actual values).
41
40
  */
42
- export declare function comparePlans(local: RawTestPlan, remote: TestPlanV1): PlanChanges;
41
+ export declare function comparePlans(local: PlanV1, remote: PlanV1): PlanChanges;
@@ -1,6 +1,8 @@
1
1
  import objectHash from "object-hash";
2
+ import { NodeType } from "@griffin-app/griffin-ts/schema";
2
3
  /**
3
- * Compare two test plans and return granular changes
4
+ * Compare two test plans and return granular changes.
5
+ * Local plan should be resolved (variables replaced with actual values).
4
6
  */
5
7
  export function comparePlans(local, remote) {
6
8
  const nodeChanges = compareNodes(local.nodes, remote.nodes);
@@ -82,8 +84,6 @@ function getNodeSummary(node) {
82
84
  return `wait ${node.duration_ms}ms`;
83
85
  case "ASSERTION":
84
86
  return `${node.assertions.length} assertion(s)`;
85
- default:
86
- return node.type;
87
87
  }
88
88
  }
89
89
  /**
@@ -116,13 +116,13 @@ function compareNodeFields(local, remote) {
116
116
  return changes;
117
117
  }
118
118
  switch (local.type) {
119
- case "ENDPOINT":
119
+ case NodeType.ENDPOINT:
120
120
  compareEndpointFields(local, remote, changes);
121
121
  break;
122
- case "WAIT":
122
+ case NodeType.WAIT:
123
123
  compareWaitFields(local, remote, changes);
124
124
  break;
125
- case "ASSERTION":
125
+ case NodeType.ASSERTION:
126
126
  compareAssertionFields(local, remote, changes);
127
127
  break;
128
128
  }
@@ -1,16 +1,12 @@
1
- import { PlanApi, RunsApi } from "@griffin-app/griffin-hub-sdk";
2
- import type { TestPlanV1 } from "@griffin-app/griffin-ts/types";
1
+ import { GriffinHubSdk } from "@griffin-app/griffin-hub-sdk";
3
2
  /**
4
3
  * Create configured SDK API instances
5
4
  */
6
- export declare function createSdkClients(config: {
5
+ export declare function createSdk(config: {
7
6
  baseUrl: string;
8
7
  apiToken?: string;
9
- }): {
10
- planApi: PlanApi;
11
- runsApi: RunsApi;
12
- };
8
+ }): GriffinHubSdk;
13
9
  /**
14
- * Inject projectId into a plan payload before sending to runner
10
+ * Create SDK with credentials from user-level credentials file
15
11
  */
16
- export declare function injectProjectId(plan: Omit<TestPlanV1, "project">, projectId: string): TestPlanV1;
12
+ export declare function createSdkWithCredentials(baseUrl: string): Promise<GriffinHubSdk>;
package/dist/core/sdk.js CHANGED
@@ -1,23 +1,31 @@
1
- import { PlanApi, RunsApi, Configuration } from "@griffin-app/griffin-hub-sdk";
1
+ import { GriffinHubSdk } from "@griffin-app/griffin-hub-sdk";
2
+ import { createClient } from "@griffin-app/griffin-hub-sdk/client";
3
+ import { getHubCredentials } from "./credentials.js";
2
4
  /**
3
5
  * Create configured SDK API instances
4
6
  */
5
- export function createSdkClients(config) {
6
- const configuration = new Configuration({
7
- basePath: config.baseUrl.replace(/\/$/, ""), // Remove trailing slash
8
- accessToken: config.apiToken,
7
+ export function createSdk(config) {
8
+ const client = createClient({
9
+ throwOnError: true,
10
+ baseURL: config.baseUrl,
9
11
  });
10
- return {
11
- planApi: new PlanApi(configuration),
12
- runsApi: new RunsApi(configuration),
13
- };
12
+ client.setConfig({
13
+ auth() {
14
+ return config.apiToken;
15
+ },
16
+ });
17
+ const sdk = new GriffinHubSdk({
18
+ client: client,
19
+ });
20
+ return sdk;
14
21
  }
15
22
  /**
16
- * Inject projectId into a plan payload before sending to runner
23
+ * Create SDK with credentials from user-level credentials file
17
24
  */
18
- export function injectProjectId(plan, projectId) {
19
- return {
20
- ...plan,
21
- project: projectId,
22
- };
25
+ export async function createSdkWithCredentials(baseUrl) {
26
+ const credentials = await getHubCredentials();
27
+ return createSdk({
28
+ baseUrl: baseUrl,
29
+ apiToken: credentials?.token,
30
+ });
23
31
  }
@@ -46,6 +46,16 @@ function isVariableRef(value) {
46
46
  return (typeof varData.key === "string" &&
47
47
  (varData.template === undefined || typeof varData.template === "string"));
48
48
  }
49
+ function isStringLiteral(value) {
50
+ if (typeof value !== "object" || value === null) {
51
+ return false;
52
+ }
53
+ const obj = value;
54
+ if (!("$literal" in obj) || typeof obj.$literal !== "string") {
55
+ return false;
56
+ }
57
+ return true;
58
+ }
49
59
  /**
50
60
  * Resolve a single variable reference to its string value.
51
61
  *
@@ -83,6 +93,9 @@ export function resolveVariablesInPlan(obj, variables) {
83
93
  if (isVariableRef(obj)) {
84
94
  return resolveVariable(obj, variables);
85
95
  }
96
+ if (isStringLiteral(obj)) {
97
+ return obj.$literal;
98
+ }
86
99
  // Recursively process arrays
87
100
  if (Array.isArray(obj)) {
88
101
  return obj.map((item) => resolveVariablesInPlan(item, variables));
package/dist/index.d.ts CHANGED
@@ -1,13 +1,16 @@
1
- export type { StateFile, RunnerConfig } from "./schemas/state.js";
1
+ export type { StateFile, HubConfig } from "./schemas/state.js";
2
+ export type { CredentialsFile, HubCredentials } from "./schemas/credentials.js";
2
3
  export type { DiscoveredPlan, DiscoveryResult, DiscoveryError, } from "./core/discovery.js";
3
4
  export type { DiffAction, DiffResult } from "./core/diff.js";
4
5
  export type { ApplyResult, ApplyAction, ApplyError } from "./core/apply.js";
5
- export { createEmptyState, StateFileSchema, RunnerConfigSchema, } from "./schemas/state.js";
6
+ export { createEmptyState, StateFileSchema, HubConfigSchema, } from "./schemas/state.js";
7
+ export { createEmptyCredentials, CredentialsFileSchema, HubCredentialsSchema, } from "./schemas/credentials.js";
6
8
  export { getStateDirPath, getStateFilePath, stateExists, loadState, saveState, initState, addEnvironment, removeEnvironment, setDefaultEnvironment, resolveEnvironment, getEnvironment, } from "./core/state.js";
9
+ export { getCredentialsDirPath, getCredentialsFilePath, credentialsExist, loadCredentials, saveCredentials, saveHubCredentials, getHubCredentials, removeHubCredentials, } from "./core/credentials.js";
7
10
  export { discoverPlans, formatDiscoveryErrors } from "./core/discovery.js";
8
11
  export { computeDiff, formatDiff, formatDiffJson } from "./core/diff.js";
9
12
  export { applyDiff, formatApplyResult } from "./core/apply.js";
10
- export { createSdkClients, injectProjectId } from "./core/sdk.js";
13
+ export { createSdk, createSdkWithCredentials } from "./core/sdk.js";
11
14
  export { detectProjectId } from "./core/project.js";
12
15
  export { executeInit } from "./commands/init.js";
13
16
  export { executeValidate } from "./commands/validate.js";
@@ -19,3 +22,5 @@ export { executeRuns } from "./commands/hub/runs.js";
19
22
  export { executePlan } from "./commands/hub/plan.js";
20
23
  export { executeApply } from "./commands/hub/apply.js";
21
24
  export { executeRun } from "./commands/hub/run.js";
25
+ export { executeLogin } from "./commands/hub/login.js";
26
+ export { executeLogout } from "./commands/hub/logout.js";
package/dist/index.js CHANGED
@@ -1,10 +1,12 @@
1
1
  // Export core functions
2
- export { createEmptyState, StateFileSchema, RunnerConfigSchema, } from "./schemas/state.js";
2
+ export { createEmptyState, StateFileSchema, HubConfigSchema, } from "./schemas/state.js";
3
+ export { createEmptyCredentials, CredentialsFileSchema, HubCredentialsSchema, } from "./schemas/credentials.js";
3
4
  export { getStateDirPath, getStateFilePath, stateExists, loadState, saveState, initState, addEnvironment, removeEnvironment, setDefaultEnvironment, resolveEnvironment, getEnvironment, } from "./core/state.js";
5
+ export { getCredentialsDirPath, getCredentialsFilePath, credentialsExist, loadCredentials, saveCredentials, saveHubCredentials, getHubCredentials, removeHubCredentials, } from "./core/credentials.js";
4
6
  export { discoverPlans, formatDiscoveryErrors } from "./core/discovery.js";
5
7
  export { computeDiff, formatDiff, formatDiffJson } from "./core/diff.js";
6
8
  export { applyDiff, formatApplyResult } from "./core/apply.js";
7
- export { createSdkClients, injectProjectId } from "./core/sdk.js";
9
+ export { createSdk, createSdkWithCredentials } from "./core/sdk.js";
8
10
  export { detectProjectId } from "./core/project.js";
9
11
  // Export command executors (for programmatic use)
10
12
  export { executeInit } from "./commands/init.js";
@@ -19,3 +21,5 @@ export { executeRuns } from "./commands/hub/runs.js";
19
21
  export { executePlan } from "./commands/hub/plan.js";
20
22
  export { executeApply } from "./commands/hub/apply.js";
21
23
  export { executeRun } from "./commands/hub/run.js";
24
+ export { executeLogin } from "./commands/hub/login.js";
25
+ export { executeLogout } from "./commands/hub/logout.js";
@@ -0,0 +1,3 @@
1
+ import { PlanV1 } from "@griffin-app/griffin-hub-sdk";
2
+ import { PlanDSL } from "@griffin-app/griffin-ts/types";
3
+ export declare function resolvePlan(plan: PlanDSL, projectId: string, envName: string, variables: Record<string, string>): Omit<PlanV1, "id">;
@@ -0,0 +1,9 @@
1
+ import { resolveVariablesInPlan } from "./core/variables.js";
2
+ export function resolvePlan(plan, projectId, envName, variables) {
3
+ const resolvedPlan = resolveVariablesInPlan(plan, variables);
4
+ return {
5
+ ...resolvedPlan,
6
+ project: projectId,
7
+ environment: envName,
8
+ };
9
+ }
@@ -0,0 +1,24 @@
1
+ import { Type, type Static } from "typebox";
2
+ /**
3
+ * Credentials file schema for storing user-level authentication
4
+ * Stored in ~/.griffin/credentials.json
5
+ *
6
+ * This file stores tokens and API keys scoped to the user across all projects.
7
+ */
8
+ export declare const HubCredentialsSchema: Type.TObject<{
9
+ token: Type.TString;
10
+ updatedAt: Type.TString;
11
+ }>;
12
+ export type HubCredentials = Static<typeof HubCredentialsSchema>;
13
+ export declare const CredentialsFileSchema: Type.TObject<{
14
+ version: Type.TLiteral<1>;
15
+ hub: Type.TOptional<Type.TObject<{
16
+ token: Type.TString;
17
+ updatedAt: Type.TString;
18
+ }>>;
19
+ }>;
20
+ export type CredentialsFile = Static<typeof CredentialsFileSchema>;
21
+ /**
22
+ * Create an empty credentials file
23
+ */
24
+ export declare function createEmptyCredentials(): CredentialsFile;
@@ -0,0 +1,23 @@
1
+ import { Type } from "typebox";
2
+ /**
3
+ * Credentials file schema for storing user-level authentication
4
+ * Stored in ~/.griffin/credentials.json
5
+ *
6
+ * This file stores tokens and API keys scoped to the user across all projects.
7
+ */
8
+ export const HubCredentialsSchema = Type.Object({
9
+ token: Type.String(),
10
+ updatedAt: Type.String(), // ISO timestamp
11
+ });
12
+ export const CredentialsFileSchema = Type.Object({
13
+ version: Type.Literal(1),
14
+ hub: Type.Optional(HubCredentialsSchema),
15
+ });
16
+ /**
17
+ * Create an empty credentials file
18
+ */
19
+ export function createEmptyCredentials() {
20
+ return {
21
+ version: 1,
22
+ };
23
+ }
@@ -8,11 +8,11 @@ import { Type, type Static } from "typebox";
8
8
  */
9
9
  export declare const EnvironmentConfigSchema: Type.TObject<{}>;
10
10
  export type EnvironmentConfig = Static<typeof EnvironmentConfigSchema>;
11
- export declare const RunnerConfigSchema: Type.TObject<{
11
+ export declare const HubConfigSchema: Type.TObject<{
12
12
  baseUrl: Type.TString;
13
- apiToken: Type.TOptional<Type.TString>;
13
+ clientId: Type.TString;
14
14
  }>;
15
- export type RunnerConfig = Static<typeof RunnerConfigSchema>;
15
+ export type HubConfig = Static<typeof HubConfigSchema>;
16
16
  export declare const DiscoveryConfigSchema: Type.TObject<{
17
17
  pattern: Type.TOptional<Type.TString>;
18
18
  ignore: Type.TOptional<Type.TArray<Type.TString>>;
@@ -23,9 +23,9 @@ export declare const StateFileSchema: Type.TObject<{
23
23
  projectId: Type.TString;
24
24
  environments: Type.TRecord<"^.*$", Type.TObject<{}>>;
25
25
  defaultEnvironment: Type.TOptional<Type.TString>;
26
- runner: Type.TOptional<Type.TObject<{
26
+ hub: Type.TOptional<Type.TObject<{
27
27
  baseUrl: Type.TString;
28
- apiToken: Type.TOptional<Type.TString>;
28
+ clientId: Type.TString;
29
29
  }>>;
30
30
  discovery: Type.TOptional<Type.TObject<{
31
31
  pattern: Type.TOptional<Type.TString>;
@@ -6,12 +6,10 @@ import { Type } from "typebox";
6
6
  * Note: The hub is now the source of truth for plans.
7
7
  * This file only stores configuration (project, environments, runner connection).
8
8
  */
9
- export const EnvironmentConfigSchema = Type.Object({
10
- // Empty for now - may be used for environment-specific config in the future
11
- });
12
- export const RunnerConfigSchema = Type.Object({
9
+ export const EnvironmentConfigSchema = Type.Object({});
10
+ export const HubConfigSchema = Type.Object({
13
11
  baseUrl: Type.String(),
14
- apiToken: Type.Optional(Type.String()),
12
+ clientId: Type.String(),
15
13
  });
16
14
  export const DiscoveryConfigSchema = Type.Object({
17
15
  pattern: Type.Optional(Type.String()),
@@ -24,8 +22,8 @@ export const StateFileSchema = Type.Object({
24
22
  // Environment configuration
25
23
  environments: Type.Record(Type.String(), EnvironmentConfigSchema),
26
24
  defaultEnvironment: Type.Optional(Type.String()),
27
- // Runner connection (for remote execution)
28
- runner: Type.Optional(RunnerConfigSchema),
25
+ // Hub connection (for remote execution)
26
+ hub: Type.Optional(HubConfigSchema),
29
27
  // Discovery settings
30
28
  discovery: Type.Optional(DiscoveryConfigSchema),
31
29
  });
@@ -1,16 +1,19 @@
1
1
  import "tsx";
2
2
  import { Value } from "typebox/value";
3
3
  import { executePlanV1, AxiosAdapter, EnvSecretProvider, SecretProviderRegistry, } from "@griffin-app/griffin-plan-executor";
4
- import { TestPlanV1Schema } from "@griffin-app/griffin-ts/schema";
5
- import { Type } from "typebox";
4
+ import { PlanDSLSchema } from "@griffin-app/griffin-ts/schema";
6
5
  import { randomUUID } from "crypto";
7
- import { loadVariables, resolveVariablesInPlan } from "./core/variables.js";
6
+ import { loadVariables } from "./core/variables.js";
8
7
  import { getProjectId } from "./core/state.js";
9
- const RawTestSchema = Type.Omit(TestPlanV1Schema, [
10
- "id",
11
- "environment",
12
- "project",
13
- ]);
8
+ import { resolvePlan } from "./resolve.js";
9
+ import { terminal } from "./utils/terminal.js";
10
+ function validateDsl(plan) {
11
+ const errors = Value.Errors(PlanDSLSchema, plan);
12
+ if (errors.length > 0) {
13
+ throw new Error(`Invalid plan: ${JSON.stringify([...errors], null, 2)}`);
14
+ }
15
+ return plan;
16
+ }
14
17
  /**
15
18
  * Runs a TypeScript test file and executes the resulting JSON plan.
16
19
  */
@@ -18,31 +21,24 @@ export async function runTestFile(filePath, envName) {
18
21
  const variables = await loadVariables(envName);
19
22
  const projectId = await getProjectId();
20
23
  const defaultExport = await import(filePath);
21
- const rawPlan = defaultExport.default;
22
- console.log(`Project ID: ${projectId}`);
23
- // Resolve all variable references in the plan
24
- const resolvedPlan = resolveVariablesInPlan(rawPlan, variables);
24
+ const rawPlan = validateDsl(defaultExport.default);
25
+ terminal.dim(`Project ID: ${projectId}`);
26
+ const resolvedPlan = resolvePlan(rawPlan, projectId, envName, variables);
25
27
  const secretRegistry = new SecretProviderRegistry();
26
28
  secretRegistry.register(new EnvSecretProvider());
27
29
  try {
28
- const parsedPlan = Value.Parse(RawTestSchema, resolvedPlan);
29
- const syntheticPlan = {
30
- ...parsedPlan,
30
+ const result = await executePlanV1({
31
+ ...resolvedPlan,
31
32
  id: randomUUID(),
32
- project: projectId,
33
- environment: envName,
34
- };
35
- const result = await executePlanV1(syntheticPlan, "default-org", {
33
+ }, "default-org", {
36
34
  mode: "local",
37
35
  httpClient: new AxiosAdapter(),
38
36
  secretRegistry: secretRegistry,
39
37
  });
40
- console.log(JSON.stringify(result, null, 2));
41
38
  return result;
42
39
  }
43
40
  catch (error) {
44
- const errors = Value.Errors(RawTestSchema, resolvedPlan);
45
- throw new Error(`Invalid plan: ${JSON.stringify([...errors], null, 2)}: ${error}`);
41
+ throw new Error(`Error executing plan: ${error instanceof Error ? error.message : String(error)}`);
46
42
  }
47
43
  }
48
44
  //function findWorkspaceRoot(): string {
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @deprecated Use terminal from "./terminal.js" instead
3
+ * This file is kept for backwards compatibility
4
+ */
5
+ export { terminal as console, colors } from "./terminal.js";
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @deprecated Use terminal from "./terminal.js" instead
3
+ * This file is kept for backwards compatibility
4
+ */
5
+ export { terminal as console, colors } from "./terminal.js";
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Handle SDK errors with user-friendly messaging
3
+ */
4
+ export declare function handleSDKError(error: unknown, context?: string): never;
5
+ /**
6
+ * Wrap an SDK call with error handling
7
+ */
8
+ export declare function withSDKErrorHandling<T>(fn: () => Promise<T>, context?: string): Promise<T>;