@griffin-app/griffin-plan-executor 0.1.13 → 0.1.15
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/README.md +14 -14
- package/dist/events/adapters/in-memory.test.js +25 -23
- package/dist/events/adapters/in-memory.test.js.map +1 -1
- package/dist/events/adapters/kinesis.d.ts.map +1 -1
- package/dist/events/adapters/kinesis.js.map +1 -1
- package/dist/events/adapters/kinesis.test.js +22 -20
- package/dist/events/adapters/kinesis.test.js.map +1 -1
- package/dist/events/emitter.test.js +15 -15
- package/dist/events/emitter.test.js.map +1 -1
- package/dist/events/types.d.ts +12 -12
- package/dist/events/types.d.ts.map +1 -1
- package/dist/events/types.js +1 -1
- package/dist/executor.d.ts +2 -2
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +33 -43
- package/dist/executor.js.map +1 -1
- package/dist/executor.test.js +102 -102
- package/dist/executor.test.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/secrets/index.d.ts +4 -4
- package/dist/secrets/index.d.ts.map +1 -1
- package/dist/secrets/index.js +4 -4
- package/dist/secrets/index.js.map +1 -1
- package/dist/secrets/providers/aws.d.ts.map +1 -1
- package/dist/secrets/providers/aws.js +4 -5
- package/dist/secrets/providers/aws.js.map +1 -1
- package/dist/secrets/providers/env.js +1 -1
- package/dist/secrets/providers/env.js.map +1 -1
- package/dist/secrets/providers/vault.js +7 -7
- package/dist/secrets/providers/vault.js.map +1 -1
- package/dist/secrets/registry.d.ts +11 -33
- package/dist/secrets/registry.d.ts.map +1 -1
- package/dist/secrets/registry.js +65 -113
- package/dist/secrets/registry.js.map +1 -1
- package/dist/secrets/resolver.d.ts +12 -12
- package/dist/secrets/resolver.d.ts.map +1 -1
- package/dist/secrets/resolver.js +21 -21
- package/dist/secrets/resolver.js.map +1 -1
- package/dist/secrets/secrets.test.js +96 -120
- package/dist/secrets/secrets.test.js.map +1 -1
- package/dist/secrets/types.d.ts +2 -5
- package/dist/secrets/types.d.ts.map +1 -1
- package/dist/secrets/types.js +1 -4
- package/dist/secrets/types.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/package.json +4 -4
- package/src/events/adapters/README.md +7 -7
- package/src/events/adapters/in-memory.test.ts +27 -23
- package/src/events/adapters/kinesis.test.ts +23 -21
- package/src/events/adapters/kinesis.ts +6 -3
- package/src/events/emitter.test.ts +15 -15
- package/src/events/types.ts +13 -13
- package/src/executor.test.ts +103 -103
- package/src/executor.ts +40 -48
- package/src/index.ts +7 -7
- package/src/secrets/index.ts +5 -5
- package/src/secrets/providers/aws.ts +4 -5
- package/src/secrets/providers/env.ts +1 -1
- package/src/secrets/providers/vault.ts +7 -7
- package/src/secrets/registry.ts +75 -142
- package/src/secrets/resolver.ts +28 -26
- package/src/secrets/secrets.test.ts +124 -155
- package/src/secrets/types.ts +4 -13
- package/src/{test-plan-types.ts → test-monitor-types.ts} +1 -1
- package/src/types.ts +2 -2
package/src/executor.ts
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
UnaryPredicate,
|
|
5
5
|
Assertions,
|
|
6
6
|
Node,
|
|
7
|
-
|
|
7
|
+
MonitorV1,
|
|
8
8
|
Wait,
|
|
9
9
|
HttpRequest,
|
|
10
10
|
} from "@griffin-app/griffin-hub-sdk";
|
|
@@ -23,14 +23,14 @@ import { createStateGraph, graphStore, StateGraphRegistry } from "ts-edge";
|
|
|
23
23
|
import type { ExecutionEvent, BaseEvent } from "./events/index.js";
|
|
24
24
|
import { randomUUID } from "crypto";
|
|
25
25
|
import {
|
|
26
|
-
|
|
26
|
+
resolveSecretsInMonitor,
|
|
27
27
|
planHasSecrets,
|
|
28
28
|
SecretResolutionError,
|
|
29
29
|
} from "./secrets/index.js";
|
|
30
30
|
import { utcNow } from "./utils/dates.js";
|
|
31
31
|
import {
|
|
32
32
|
migrateToLatest,
|
|
33
|
-
|
|
33
|
+
CURRENT_MONITOR_VERSION,
|
|
34
34
|
isSupportedVersion,
|
|
35
35
|
} from "@griffin-app/griffin-ts";
|
|
36
36
|
|
|
@@ -41,7 +41,7 @@ interface NodeExecuteContext {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
|
-
* Execution context that tracks event emission state throughout a
|
|
44
|
+
* Execution context that tracks event emission state throughout a monitor execution.
|
|
45
45
|
* Maintains sequence counter and provides event creation helpers.
|
|
46
46
|
*/
|
|
47
47
|
class ExecutionContext {
|
|
@@ -49,7 +49,7 @@ class ExecutionContext {
|
|
|
49
49
|
|
|
50
50
|
constructor(
|
|
51
51
|
public readonly executionId: string,
|
|
52
|
-
public readonly
|
|
52
|
+
public readonly monitor: MonitorV1,
|
|
53
53
|
public readonly organizationId: string,
|
|
54
54
|
private readonly emitter?: ExecutionOptions["eventEmitter"],
|
|
55
55
|
) {}
|
|
@@ -62,7 +62,7 @@ class ExecutionContext {
|
|
|
62
62
|
eventId: randomUUID(),
|
|
63
63
|
seq: this.seq++,
|
|
64
64
|
timestamp: Date.now(),
|
|
65
|
-
|
|
65
|
+
monitorId: this.monitor.id,
|
|
66
66
|
executionId: this.executionId,
|
|
67
67
|
organizationId: this.organizationId,
|
|
68
68
|
};
|
|
@@ -121,7 +121,7 @@ interface ExecutionState {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
function buildNode(
|
|
124
|
-
|
|
124
|
+
monitor: MonitorV1,
|
|
125
125
|
node: Node,
|
|
126
126
|
options: ExecutionOptions,
|
|
127
127
|
): {
|
|
@@ -294,7 +294,7 @@ function buildNode(
|
|
|
294
294
|
}
|
|
295
295
|
|
|
296
296
|
function buildGraph(
|
|
297
|
-
|
|
297
|
+
monitor: MonitorV1,
|
|
298
298
|
options: ExecutionOptions,
|
|
299
299
|
executionContext: ExecutionContext,
|
|
300
300
|
): DynamicStateGraph {
|
|
@@ -317,23 +317,24 @@ function buildGraph(
|
|
|
317
317
|
}) as DynamicStateGraph;
|
|
318
318
|
|
|
319
319
|
// Add all nodes - cast back to DynamicStateGraph to maintain our dynamic type
|
|
320
|
-
const graphWithNodes =
|
|
321
|
-
(g, node) =>
|
|
320
|
+
const graphWithNodes = monitor.nodes.reduce<DynamicStateGraph>(
|
|
321
|
+
(g, node) =>
|
|
322
|
+
g.addNode(buildNode(monitor, node, options)) as DynamicStateGraph,
|
|
322
323
|
graph,
|
|
323
324
|
);
|
|
324
325
|
|
|
325
326
|
// Add all edges
|
|
326
327
|
// Cast the edge method to accept string arguments since ts-edge expects literal types
|
|
327
|
-
// but we have runtime strings from the
|
|
328
|
-
const graphWithEdges =
|
|
328
|
+
// but we have runtime strings from the monitor
|
|
329
|
+
const graphWithEdges = monitor.edges.reduce<DynamicStateGraph>((g, edge) => {
|
|
329
330
|
const addEdge = g.edge as (from: string, to: string) => DynamicStateGraph;
|
|
330
331
|
return addEdge(edge.from, edge.to);
|
|
331
332
|
}, graphWithNodes);
|
|
332
333
|
|
|
333
334
|
return graphWithEdges;
|
|
334
335
|
}
|
|
335
|
-
export async function
|
|
336
|
-
|
|
336
|
+
export async function executeMonitorV1(
|
|
337
|
+
monitor: MonitorV1,
|
|
337
338
|
organizationId: string,
|
|
338
339
|
options: ExecutionOptions,
|
|
339
340
|
): Promise<ExecutionResult> {
|
|
@@ -342,34 +343,22 @@ export async function executePlanV1(
|
|
|
342
343
|
// Generate or use provided executionId
|
|
343
344
|
const executionId = options.executionId || randomUUID();
|
|
344
345
|
|
|
345
|
-
// Migrate plan to latest version if needed
|
|
346
|
-
let migratedPlan = plan;
|
|
347
|
-
if (plan.version !== CURRENT_PLAN_VERSION) {
|
|
348
|
-
if (!isSupportedVersion(plan.version)) {
|
|
349
|
-
throw new Error(
|
|
350
|
-
`Unsupported plan version: ${plan.version}. Supported versions: ${CURRENT_PLAN_VERSION}`,
|
|
351
|
-
);
|
|
352
|
-
}
|
|
353
|
-
// Migrate to latest version
|
|
354
|
-
migratedPlan = migrateToLatest(plan as any) as PlanV1;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
346
|
// Create execution context for event emission
|
|
358
347
|
const executionContext = new ExecutionContext(
|
|
359
348
|
executionId,
|
|
360
|
-
|
|
349
|
+
monitor,
|
|
361
350
|
organizationId,
|
|
362
351
|
options.eventEmitter,
|
|
363
352
|
);
|
|
364
353
|
|
|
365
354
|
try {
|
|
366
|
-
// Resolve secrets if the
|
|
367
|
-
let
|
|
368
|
-
if (planHasSecrets(
|
|
355
|
+
// Resolve secrets if the monitor contains any
|
|
356
|
+
let resolvedMonitor = monitor;
|
|
357
|
+
if (planHasSecrets(monitor)) {
|
|
369
358
|
if (!options.secretRegistry) {
|
|
370
359
|
throw new SecretResolutionError(
|
|
371
|
-
"
|
|
372
|
-
{
|
|
360
|
+
"Monitor contains secret references but no secret registry was provided",
|
|
361
|
+
{ ref: "unknown" },
|
|
373
362
|
);
|
|
374
363
|
}
|
|
375
364
|
|
|
@@ -380,7 +369,10 @@ export async function executePlanV1(
|
|
|
380
369
|
});
|
|
381
370
|
|
|
382
371
|
try {
|
|
383
|
-
|
|
372
|
+
resolvedMonitor = await resolveSecretsInMonitor(
|
|
373
|
+
monitor,
|
|
374
|
+
options.secretRegistry,
|
|
375
|
+
);
|
|
384
376
|
|
|
385
377
|
executionContext.emit({
|
|
386
378
|
type: "NODE_END",
|
|
@@ -402,13 +394,13 @@ export async function executePlanV1(
|
|
|
402
394
|
}
|
|
403
395
|
}
|
|
404
396
|
|
|
405
|
-
// Emit
|
|
397
|
+
// Emit MONITOR_START event
|
|
406
398
|
executionContext.emit({
|
|
407
|
-
type: "
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
nodeCount:
|
|
411
|
-
edgeCount:
|
|
399
|
+
type: "MONITOR_START",
|
|
400
|
+
monitorName: resolvedMonitor.name,
|
|
401
|
+
monitorVersion: resolvedMonitor.version,
|
|
402
|
+
nodeCount: resolvedMonitor.nodes.length,
|
|
403
|
+
edgeCount: resolvedMonitor.edges.length,
|
|
412
404
|
});
|
|
413
405
|
|
|
414
406
|
// Call onStart callback if provided
|
|
@@ -422,7 +414,7 @@ export async function executePlanV1(
|
|
|
422
414
|
}
|
|
423
415
|
|
|
424
416
|
// Build execution graph (state-based)
|
|
425
|
-
const graph = buildGraph(
|
|
417
|
+
const graph = buildGraph(resolvedMonitor, options, executionContext);
|
|
426
418
|
|
|
427
419
|
// Compile and run the state graph
|
|
428
420
|
const app = graph.compile(START, END);
|
|
@@ -436,9 +428,9 @@ export async function executePlanV1(
|
|
|
436
428
|
const finalResults = graphResult.output?.results || [];
|
|
437
429
|
const finalErrors = graphResult.output?.errors || [errorMessage];
|
|
438
430
|
|
|
439
|
-
// Emit
|
|
431
|
+
// Emit MONITOR_END event
|
|
440
432
|
executionContext.emit({
|
|
441
|
-
type: "
|
|
433
|
+
type: "MONITOR_END",
|
|
442
434
|
success: false,
|
|
443
435
|
totalDuration_ms: Date.now() - startTime,
|
|
444
436
|
nodeResultCount: finalResults.length,
|
|
@@ -477,9 +469,9 @@ export async function executePlanV1(
|
|
|
477
469
|
const success = finalState.errors.length === 0;
|
|
478
470
|
const duration = Date.now() - startTime;
|
|
479
471
|
|
|
480
|
-
// Emit
|
|
472
|
+
// Emit MONITOR_END event
|
|
481
473
|
executionContext.emit({
|
|
482
|
-
type: "
|
|
474
|
+
type: "MONITOR_END",
|
|
483
475
|
success,
|
|
484
476
|
totalDuration_ms: duration,
|
|
485
477
|
nodeResultCount: finalState.results.length,
|
|
@@ -519,9 +511,9 @@ export async function executePlanV1(
|
|
|
519
511
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
520
512
|
const duration = Date.now() - startTime;
|
|
521
513
|
|
|
522
|
-
// Emit
|
|
514
|
+
// Emit MONITOR_END event
|
|
523
515
|
executionContext.emit({
|
|
524
|
-
type: "
|
|
516
|
+
type: "MONITOR_END",
|
|
525
517
|
success: false,
|
|
526
518
|
totalDuration_ms: duration,
|
|
527
519
|
nodeResultCount: 0,
|
|
@@ -572,12 +564,12 @@ async function executeHttpRequest(
|
|
|
572
564
|
const path = endpoint.path;
|
|
573
565
|
const url = `${baseUrl}${path}`;
|
|
574
566
|
|
|
575
|
-
// TODO: Add retry configuration from
|
|
567
|
+
// TODO: Add retry configuration from monitor (node-level or monitor-level)
|
|
576
568
|
// For now, we always attempt once (attempt: 1)
|
|
577
569
|
const attempt = 1;
|
|
578
570
|
|
|
579
571
|
// After secret resolution, headers are guaranteed to be plain strings
|
|
580
|
-
// Cast is safe because
|
|
572
|
+
// Cast is safe because resolveSecretsInMonitor substitutes all SecretRefs
|
|
581
573
|
const resolvedHeaders = endpoint.headers as
|
|
582
574
|
| Record<string, string>
|
|
583
575
|
| undefined;
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { executeMonitorV1 } from "./executor.js";
|
|
2
2
|
export type {
|
|
3
3
|
ExecutionOptions,
|
|
4
4
|
ExecutionResult,
|
|
@@ -10,12 +10,12 @@ export type {
|
|
|
10
10
|
StatusCallbacks,
|
|
11
11
|
} from "./types.js";
|
|
12
12
|
export type {
|
|
13
|
-
|
|
13
|
+
TestMonitor,
|
|
14
14
|
HttpRequest as HttpRequestNode,
|
|
15
15
|
WaitNode,
|
|
16
16
|
AssertionNode,
|
|
17
17
|
Edge,
|
|
18
|
-
} from "./test-
|
|
18
|
+
} from "./test-monitor-types.js";
|
|
19
19
|
export {
|
|
20
20
|
AxiosAdapter,
|
|
21
21
|
StubAdapter,
|
|
@@ -31,8 +31,8 @@ export {
|
|
|
31
31
|
export type {
|
|
32
32
|
ExecutionEvent,
|
|
33
33
|
BaseEvent,
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
MonitorStartEvent,
|
|
35
|
+
MonitorEndEvent,
|
|
36
36
|
NodeStartEvent,
|
|
37
37
|
NodeEndEvent,
|
|
38
38
|
HttpRequestEvent,
|
|
@@ -62,8 +62,8 @@ export {
|
|
|
62
62
|
// Registry
|
|
63
63
|
SecretProviderRegistry,
|
|
64
64
|
// Resolution utilities
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
resolveSecretsInMonitor,
|
|
66
|
+
collectSecretsFromMonitor,
|
|
67
67
|
planHasSecrets,
|
|
68
68
|
// Providers
|
|
69
69
|
EnvSecretProvider,
|
package/src/secrets/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Secret management for griffin
|
|
2
|
+
* Secret management for griffin monitor executor.
|
|
3
3
|
*
|
|
4
4
|
* This module provides:
|
|
5
5
|
* - SecretProvider interface for implementing custom providers
|
|
6
|
-
* - SecretProviderRegistry for
|
|
7
|
-
* - Secret resolution utilities for test
|
|
6
|
+
* - SecretProviderRegistry for the configured secret provider
|
|
7
|
+
* - Secret resolution utilities for test monitors
|
|
8
8
|
* - Built-in providers: env, aws, vault
|
|
9
9
|
*/
|
|
10
10
|
|
|
@@ -24,8 +24,8 @@ export { SecretProviderRegistry } from "./registry.js";
|
|
|
24
24
|
|
|
25
25
|
// Resolution utilities
|
|
26
26
|
export {
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
resolveSecretsInMonitor,
|
|
28
|
+
collectSecretsFromMonitor,
|
|
29
29
|
planHasSecrets,
|
|
30
30
|
} from "./resolver.js";
|
|
31
31
|
|
|
@@ -81,7 +81,7 @@ export class AwsSecretsManagerProvider implements SecretProvider {
|
|
|
81
81
|
if (!response.SecretString) {
|
|
82
82
|
throw new SecretResolutionError(
|
|
83
83
|
`Secret "${secretId}" does not contain a string value (binary secrets are not supported)`,
|
|
84
|
-
{
|
|
84
|
+
{ ref },
|
|
85
85
|
);
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -110,7 +110,6 @@ export class AwsSecretsManagerProvider implements SecretProvider {
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
throw new SecretResolutionError(message, {
|
|
113
|
-
provider: this.name,
|
|
114
113
|
ref,
|
|
115
114
|
cause: error,
|
|
116
115
|
});
|
|
@@ -135,7 +134,7 @@ export class AwsSecretsManagerProvider implements SecretProvider {
|
|
|
135
134
|
if (typeof parsed !== "object" || parsed === null) {
|
|
136
135
|
throw new SecretResolutionError(
|
|
137
136
|
`Secret "${ref}" is not a JSON object, cannot extract field "${field}"`,
|
|
138
|
-
{
|
|
137
|
+
{ ref },
|
|
139
138
|
);
|
|
140
139
|
}
|
|
141
140
|
|
|
@@ -144,7 +143,7 @@ export class AwsSecretsManagerProvider implements SecretProvider {
|
|
|
144
143
|
if (value === undefined) {
|
|
145
144
|
throw new SecretResolutionError(
|
|
146
145
|
`Field "${field}" not found in secret "${ref}"`,
|
|
147
|
-
{
|
|
146
|
+
{ ref },
|
|
148
147
|
);
|
|
149
148
|
}
|
|
150
149
|
|
|
@@ -159,7 +158,7 @@ export class AwsSecretsManagerProvider implements SecretProvider {
|
|
|
159
158
|
`Failed to parse secret "${ref}" as JSON for field extraction: ${
|
|
160
159
|
error instanceof Error ? error.message : String(error)
|
|
161
160
|
}`,
|
|
162
|
-
{
|
|
161
|
+
{ ref, cause: error },
|
|
163
162
|
);
|
|
164
163
|
}
|
|
165
164
|
}
|
|
@@ -116,21 +116,21 @@ export class VaultProvider implements SecretProvider {
|
|
|
116
116
|
if (response.status === 404) {
|
|
117
117
|
throw new SecretResolutionError(
|
|
118
118
|
`Secret "${secretPath}" not found in Vault`,
|
|
119
|
-
{
|
|
119
|
+
{ ref },
|
|
120
120
|
);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
if (response.status === 403) {
|
|
124
124
|
throw new SecretResolutionError(
|
|
125
125
|
`Access denied to secret "${secretPath}". Check Vault policies.`,
|
|
126
|
-
{
|
|
126
|
+
{ ref },
|
|
127
127
|
);
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
if (response.status !== 200) {
|
|
131
131
|
throw new SecretResolutionError(
|
|
132
132
|
`Vault returned status ${response.status} for secret "${secretPath}"`,
|
|
133
|
-
{
|
|
133
|
+
{ ref },
|
|
134
134
|
);
|
|
135
135
|
}
|
|
136
136
|
|
|
@@ -149,7 +149,7 @@ export class VaultProvider implements SecretProvider {
|
|
|
149
149
|
if (!kvData?.data) {
|
|
150
150
|
throw new SecretResolutionError(
|
|
151
151
|
`Invalid KV v2 response structure for secret "${secretPath}"`,
|
|
152
|
-
{
|
|
152
|
+
{ ref },
|
|
153
153
|
);
|
|
154
154
|
}
|
|
155
155
|
secretData = kvData.data;
|
|
@@ -158,7 +158,7 @@ export class VaultProvider implements SecretProvider {
|
|
|
158
158
|
if (!data?.data || typeof data.data !== "object") {
|
|
159
159
|
throw new SecretResolutionError(
|
|
160
160
|
`Invalid KV v1 response structure for secret "${secretPath}"`,
|
|
161
|
-
{
|
|
161
|
+
{ ref },
|
|
162
162
|
);
|
|
163
163
|
}
|
|
164
164
|
secretData = data.data as Record<string, unknown>;
|
|
@@ -180,7 +180,7 @@ export class VaultProvider implements SecretProvider {
|
|
|
180
180
|
`Failed to retrieve secret "${secretPath}" from Vault: ${
|
|
181
181
|
error instanceof Error ? error.message : String(error)
|
|
182
182
|
}`,
|
|
183
|
-
{
|
|
183
|
+
{ ref, cause: error },
|
|
184
184
|
);
|
|
185
185
|
}
|
|
186
186
|
}
|
|
@@ -204,7 +204,7 @@ export class VaultProvider implements SecretProvider {
|
|
|
204
204
|
if (value === undefined) {
|
|
205
205
|
throw new SecretResolutionError(
|
|
206
206
|
`Field "${field}" not found in secret "${ref}"`,
|
|
207
|
-
{
|
|
207
|
+
{ ref },
|
|
208
208
|
);
|
|
209
209
|
}
|
|
210
210
|
|