@pikku/inspector 0.12.41 → 0.12.43
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/CHANGELOG.md +104 -0
- package/dist/add/add-addon-bans.js +1 -0
- package/dist/add/add-ai-agent.d.ts +3 -1
- package/dist/add/add-ai-agent.js +82 -0
- package/dist/add/add-credential.js +3 -1
- package/dist/add/add-functions.js +10 -9
- package/dist/add/add-gateway.js +3 -0
- package/dist/add/add-http-route.js +2 -2
- package/dist/add/add-mcp-prompt.js +0 -1
- package/dist/add/add-mcp-resource.js +0 -1
- package/dist/add/add-permission.d.ts +1 -1
- package/dist/add/add-permission.js +3 -177
- package/dist/add/add-queue-worker.js +3 -0
- package/dist/add/add-schedule.js +3 -0
- package/dist/add/add-scope.d.ts +2 -0
- package/dist/add/add-scope.js +146 -0
- package/dist/add/add-trigger.js +3 -0
- package/dist/add/add-wire-remote-addon.d.ts +10 -0
- package/dist/add/add-wire-remote-addon.js +66 -0
- package/dist/add/add-workflow-graph.js +32 -3
- package/dist/error-codes.d.ts +3 -0
- package/dist/error-codes.js +4 -0
- package/dist/inspector.js +19 -7
- package/dist/types.d.ts +19 -13
- package/dist/utils/custom-types-generator.js +2 -1
- package/dist/utils/ensure-function-metadata.d.ts +15 -0
- package/dist/utils/ensure-function-metadata.js +24 -0
- package/dist/utils/filter-inspector-state.js +9 -0
- package/dist/utils/get-property-value.d.ts +7 -2
- package/dist/utils/get-property-value.js +48 -1
- package/dist/utils/load-addon-functions-meta.js +81 -6
- package/dist/utils/nested-project-filter.d.ts +1 -0
- package/dist/utils/nested-project-filter.js +29 -0
- package/dist/utils/permissions.d.ts +1 -21
- package/dist/utils/permissions.js +17 -108
- package/dist/utils/post-process.d.ts +30 -1
- package/dist/utils/post-process.js +150 -72
- package/dist/utils/serialize-inspector-state.d.ts +13 -8
- package/dist/utils/serialize-inspector-state.js +12 -6
- package/dist/utils/serialize-permissions-groups-meta.d.ts +0 -2
- package/dist/utils/serialize-permissions-groups-meta.js +0 -26
- package/dist/utils/workflow/derive-workflow-plan.js +9 -3
- package/dist/utils/workflow/dsl/extract-dsl-workflow.js +56 -1
- package/dist/utils/workflow/dsl/patterns.d.ts +10 -0
- package/dist/utils/workflow/dsl/patterns.js +24 -8
- package/dist/utils/workflow/dsl/validation.js +3 -4
- package/dist/utils/workflow/graph/convert-dsl-to-graph.js +14 -0
- package/dist/utils/workflow/graph/finalize-workflows.d.ts +7 -0
- package/dist/utils/workflow/graph/finalize-workflows.js +11 -2
- package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +2 -0
- package/dist/utils/workflow/graph/serialize-workflow-graph.js +4 -0
- package/dist/utils/workflow/graph/workflow-graph.types.d.ts +6 -2
- package/dist/visit.js +4 -0
- package/package.json +3 -3
- package/src/add/add-addon-bans.ts +1 -0
- package/src/add/add-ai-agent.test.ts +87 -0
- package/src/add/add-ai-agent.ts +122 -0
- package/src/add/add-credential.ts +6 -0
- package/src/add/add-functions.ts +10 -12
- package/src/add/add-gateway.ts +11 -0
- package/src/add/add-http-route.ts +2 -8
- package/src/add/add-mcp-prompt.ts +0 -1
- package/src/add/add-mcp-resource.ts +0 -1
- package/src/add/add-permission.ts +4 -242
- package/src/add/add-queue-worker.ts +11 -0
- package/src/add/add-schedule.ts +11 -0
- package/src/add/add-scope.test.ts +346 -0
- package/src/add/add-scope.ts +225 -0
- package/src/add/add-trigger.ts +11 -0
- package/src/add/add-wire-remote-addon.ts +77 -0
- package/src/add/add-workflow-graph-input.test.ts +94 -0
- package/src/add/add-workflow-graph-notes.test.ts +74 -0
- package/src/add/add-workflow-graph.ts +35 -3
- package/src/add/inline-wiring-function.test.ts +104 -0
- package/src/add/validate-workflow-graph-addons.test.ts +102 -0
- package/src/error-codes.ts +5 -0
- package/src/inspector.ts +22 -6
- package/src/types.ts +16 -17
- package/src/utils/custom-types-generator.test.ts +26 -1
- package/src/utils/custom-types-generator.ts +2 -1
- package/src/utils/ensure-function-metadata.ts +42 -0
- package/src/utils/filter-inspector-state.test.ts +0 -2
- package/src/utils/filter-inspector-state.ts +9 -0
- package/src/utils/get-property-value.test.ts +141 -0
- package/src/utils/get-property-value.ts +62 -1
- package/src/utils/load-addon-functions-meta.test.ts +157 -0
- package/src/utils/load-addon-functions-meta.ts +94 -6
- package/src/utils/load-addon-scopes.test.ts +138 -0
- package/src/utils/nested-project-filter.test.ts +53 -0
- package/src/utils/nested-project-filter.ts +28 -0
- package/src/utils/permissions.test.ts +7 -232
- package/src/utils/permissions.ts +20 -160
- package/src/utils/post-process.test.ts +269 -4
- package/src/utils/post-process.ts +216 -95
- package/src/utils/serialize-inspector-state.ts +22 -25
- package/src/utils/serialize-permissions-groups-meta.ts +0 -28
- package/src/utils/workflow/derive-workflow-plan.test.ts +41 -3
- package/src/utils/workflow/derive-workflow-plan.ts +11 -4
- package/src/utils/workflow/dsl/extract-dsl-workflow.ts +66 -0
- package/src/utils/workflow/dsl/patterns.ts +34 -8
- package/src/utils/workflow/dsl/validation.ts +3 -4
- package/src/utils/workflow/graph/convert-dsl-to-graph.ts +15 -0
- package/src/utils/workflow/graph/finalize-workflows.test.ts +50 -0
- package/src/utils/workflow/graph/finalize-workflows.ts +13 -2
- package/src/utils/workflow/graph/serialize-workflow-graph.ts +6 -0
- package/src/utils/workflow/graph/workflow-graph.types.ts +8 -0
- package/src/visit.ts +4 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -63,9 +63,7 @@ function containsConditional(steps: WorkflowStepMeta[]): boolean {
|
|
|
63
63
|
* e.g. "awaiting_approval" → "Awaiting approval", "building-image" → "Building image"
|
|
64
64
|
*/
|
|
65
65
|
function reasonToLabel(reason: string): string {
|
|
66
|
-
return reason
|
|
67
|
-
.replace(/[-_]/g, ' ')
|
|
68
|
-
.replace(/^(.)/, (c) => c.toUpperCase())
|
|
66
|
+
return reason.replace(/[-_]/g, ' ').replace(/^(.)/, (c) => c.toUpperCase())
|
|
69
67
|
}
|
|
70
68
|
|
|
71
69
|
/** Flatten named steps (rpc/inline/sleep/parallel children) in source order. */
|
|
@@ -88,13 +86,22 @@ function collectNamedSteps(steps: WorkflowStepMeta[]): WorkflowPlannedStep[] {
|
|
|
88
86
|
displayName: reasonToLabel(step.reason),
|
|
89
87
|
})
|
|
90
88
|
break
|
|
89
|
+
case 'approval':
|
|
90
|
+
// Same as suspend, but namespaced `__workflow_approval:` to match the
|
|
91
|
+
// runtime's separate step key.
|
|
92
|
+
planned.push({
|
|
93
|
+
stepName: `__workflow_approval:${step.reason}`,
|
|
94
|
+
displayName: reasonToLabel(step.reason),
|
|
95
|
+
})
|
|
96
|
+
break
|
|
91
97
|
case 'parallel':
|
|
92
98
|
for (const child of step.children) {
|
|
93
99
|
planned.push({ stepName: child.stepName })
|
|
94
100
|
}
|
|
95
101
|
break
|
|
96
102
|
case 'branch':
|
|
97
|
-
for (const b of step.branches)
|
|
103
|
+
for (const b of step.branches)
|
|
104
|
+
planned.push(...collectNamedSteps(b.steps))
|
|
98
105
|
if (step.elseSteps) planned.push(...collectNamedSteps(step.elseSteps))
|
|
99
106
|
break
|
|
100
107
|
case 'switch':
|
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
FanoutStepMeta,
|
|
9
9
|
CancelStepMeta,
|
|
10
10
|
SuspendStepMeta,
|
|
11
|
+
ApprovalStepMeta,
|
|
11
12
|
SetStepMeta,
|
|
12
13
|
SwitchStepMeta,
|
|
13
14
|
SwitchCaseMeta,
|
|
@@ -25,6 +26,7 @@ import {
|
|
|
25
26
|
extractActorFromOptions,
|
|
26
27
|
isWorkflowSleepCall,
|
|
27
28
|
isWorkflowSuspendCall,
|
|
29
|
+
isWorkflowApprovalCall,
|
|
28
30
|
isThrowCancelException,
|
|
29
31
|
extractCancelReason,
|
|
30
32
|
isParallelFanout,
|
|
@@ -520,6 +522,22 @@ function extractExpressionStatement(
|
|
|
520
522
|
return extractSuspendStep(call, context)
|
|
521
523
|
}
|
|
522
524
|
|
|
525
|
+
if (isWorkflowApprovalCall(call, context.checker)) {
|
|
526
|
+
const step = extractApprovalStep(call, context, outputVar)
|
|
527
|
+
|
|
528
|
+
// Unlike suspend, an approval yields a value, so a downstream step can
|
|
529
|
+
// reference it — track the binding the same way workflow.do() does.
|
|
530
|
+
if (outputVar && step) {
|
|
531
|
+
const type = context.checker.getTypeAtLocation(expr)
|
|
532
|
+
context.outputVars.set(outputVar, { type, node: expr })
|
|
533
|
+
if (isArrayType(type, context.checker)) {
|
|
534
|
+
context.arrayVars.add(outputVar)
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
return step
|
|
539
|
+
}
|
|
540
|
+
|
|
523
541
|
// Check for parallel group or fanout
|
|
524
542
|
if (isParallelFanout(call)) {
|
|
525
543
|
return extractParallelFanout(call, context)
|
|
@@ -738,6 +756,54 @@ function extractSuspendStep(
|
|
|
738
756
|
}
|
|
739
757
|
}
|
|
740
758
|
|
|
759
|
+
/**
|
|
760
|
+
* Extract approval step from workflow.approval() call
|
|
761
|
+
*/
|
|
762
|
+
function extractApprovalStep(
|
|
763
|
+
call: ts.CallExpression,
|
|
764
|
+
context: ExtractionContext,
|
|
765
|
+
outputVar?: string
|
|
766
|
+
): ApprovalStepMeta | null {
|
|
767
|
+
const args = call.arguments
|
|
768
|
+
if (args.length < 1) return null
|
|
769
|
+
|
|
770
|
+
try {
|
|
771
|
+
const reason = extractStringLiteral(args[0], context.checker)
|
|
772
|
+
const step: ApprovalStepMeta = {
|
|
773
|
+
type: 'approval',
|
|
774
|
+
reason,
|
|
775
|
+
}
|
|
776
|
+
if (outputVar) {
|
|
777
|
+
step.outputVar = outputVar
|
|
778
|
+
}
|
|
779
|
+
// The `schema` option is a runtime value validated inside the workflow body,
|
|
780
|
+
// so it is deliberately not serialized here — only `expiry`, which the graph
|
|
781
|
+
// and planned-step ladder need in order to describe the gate.
|
|
782
|
+
const options = args[1]
|
|
783
|
+
if (options && ts.isObjectLiteralExpression(options)) {
|
|
784
|
+
for (const prop of options.properties) {
|
|
785
|
+
if (
|
|
786
|
+
ts.isPropertyAssignment(prop) &&
|
|
787
|
+
prop.name.getText() === 'expiry' &&
|
|
788
|
+
(ts.isStringLiteral(prop.initializer) ||
|
|
789
|
+
ts.isNumericLiteral(prop.initializer))
|
|
790
|
+
) {
|
|
791
|
+
step.expiry = ts.isNumericLiteral(prop.initializer)
|
|
792
|
+
? Number(prop.initializer.text)
|
|
793
|
+
: prop.initializer.text
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
return step
|
|
798
|
+
} catch (error) {
|
|
799
|
+
context.errors.push({
|
|
800
|
+
message: `Failed to extract approval step: ${error instanceof Error ? error.message : String(error)}`,
|
|
801
|
+
node: call,
|
|
802
|
+
})
|
|
803
|
+
return null
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
741
807
|
/**
|
|
742
808
|
* Extract cancel step from throw WorkflowCancelledException statement
|
|
743
809
|
*/
|
|
@@ -4,6 +4,18 @@ import * as ts from 'typescript'
|
|
|
4
4
|
* Pattern detection helpers for DSL workflow extraction
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* The wire object handed to a DSL func is named `workflow` in a workflow and
|
|
9
|
+
* `scenario` in a scenario — both carry the same `do`/`sleep`/`suspend` DSL, so
|
|
10
|
+
* step extraction accepts either identifier.
|
|
11
|
+
*/
|
|
12
|
+
export function isWorkflowWireIdentifier(expr: ts.Expression): boolean {
|
|
13
|
+
return (
|
|
14
|
+
ts.isIdentifier(expr) &&
|
|
15
|
+
(expr.text === 'workflow' || expr.text === 'scenario')
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
|
|
7
19
|
/**
|
|
8
20
|
* Check if a call expression is workflow.do() or workflow.expectEventually()
|
|
9
21
|
* (both are RPC steps; expectEventually is the polling variant used by scenarios)
|
|
@@ -20,8 +32,7 @@ export function isWorkflowDoCall(
|
|
|
20
32
|
return (
|
|
21
33
|
(propAccess.name.text === 'do' ||
|
|
22
34
|
propAccess.name.text === 'expectEventually') &&
|
|
23
|
-
|
|
24
|
-
propAccess.expression.text === 'workflow'
|
|
35
|
+
isWorkflowWireIdentifier(propAccess.expression)
|
|
25
36
|
)
|
|
26
37
|
}
|
|
27
38
|
|
|
@@ -34,8 +45,7 @@ export function isWorkflowExpectEventuallyCall(
|
|
|
34
45
|
return (
|
|
35
46
|
ts.isPropertyAccessExpression(node.expression) &&
|
|
36
47
|
node.expression.name.text === 'expectEventually' &&
|
|
37
|
-
|
|
38
|
-
node.expression.expression.text === 'workflow'
|
|
48
|
+
isWorkflowWireIdentifier(node.expression.expression)
|
|
39
49
|
)
|
|
40
50
|
}
|
|
41
51
|
|
|
@@ -83,8 +93,7 @@ export function isWorkflowSleepCall(
|
|
|
83
93
|
const propAccess = node.expression
|
|
84
94
|
return (
|
|
85
95
|
propAccess.name.text === 'sleep' &&
|
|
86
|
-
|
|
87
|
-
propAccess.expression.text === 'workflow'
|
|
96
|
+
isWorkflowWireIdentifier(propAccess.expression)
|
|
88
97
|
)
|
|
89
98
|
}
|
|
90
99
|
|
|
@@ -102,8 +111,25 @@ export function isWorkflowSuspendCall(
|
|
|
102
111
|
const propAccess = node.expression
|
|
103
112
|
return (
|
|
104
113
|
propAccess.name.text === 'suspend' &&
|
|
105
|
-
|
|
106
|
-
|
|
114
|
+
isWorkflowWireIdentifier(propAccess.expression)
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Check if a call expression is workflow.approval()
|
|
120
|
+
*/
|
|
121
|
+
export function isWorkflowApprovalCall(
|
|
122
|
+
node: ts.CallExpression,
|
|
123
|
+
_checker: ts.TypeChecker
|
|
124
|
+
): boolean {
|
|
125
|
+
if (!ts.isPropertyAccessExpression(node.expression)) {
|
|
126
|
+
return false
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const propAccess = node.expression
|
|
130
|
+
return (
|
|
131
|
+
propAccess.name.text === 'approval' &&
|
|
132
|
+
isWorkflowWireIdentifier(propAccess.expression)
|
|
107
133
|
)
|
|
108
134
|
}
|
|
109
135
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as ts from 'typescript'
|
|
2
|
+
import { isWorkflowWireIdentifier } from './patterns.js'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Validation rules for DSL workflows
|
|
@@ -78,8 +79,7 @@ export function validateNoDisallowedPatterns(
|
|
|
78
79
|
const propAccess = node.expression
|
|
79
80
|
if (
|
|
80
81
|
propAccess.name.text === 'do' &&
|
|
81
|
-
|
|
82
|
-
propAccess.expression.text === 'workflow'
|
|
82
|
+
isWorkflowWireIdentifier(propAccess.expression)
|
|
83
83
|
) {
|
|
84
84
|
const secondArg = node.arguments[1]
|
|
85
85
|
if (
|
|
@@ -154,8 +154,7 @@ export function validateAwaitedCalls(node: ts.Node): ValidationError[] {
|
|
|
154
154
|
const propAccess = node.expression
|
|
155
155
|
if (
|
|
156
156
|
(propAccess.name.text === 'do' || propAccess.name.text === 'sleep') &&
|
|
157
|
-
|
|
158
|
-
propAccess.expression.text === 'workflow'
|
|
157
|
+
isWorkflowWireIdentifier(propAccess.expression)
|
|
159
158
|
) {
|
|
160
159
|
if (!parentIsAwait && !insidePromiseAll) {
|
|
161
160
|
errors.push({
|
|
@@ -132,6 +132,21 @@ function convertStepToNode(
|
|
|
132
132
|
return [node]
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
case 'approval': {
|
|
136
|
+
// Unlike `suspend` — which has no output and so is deliberately absent
|
|
137
|
+
// from the graph — an approval yields a value a later step can reference,
|
|
138
|
+
// so it must be a real node for $ref resolution and graphHash to see it.
|
|
139
|
+
const node: FlowNode = {
|
|
140
|
+
nodeId,
|
|
141
|
+
flow: 'approval',
|
|
142
|
+
reason: step.reason,
|
|
143
|
+
outputVar: step.outputVar,
|
|
144
|
+
expiry: step.expiry,
|
|
145
|
+
next: nextNodeId,
|
|
146
|
+
}
|
|
147
|
+
return [node]
|
|
148
|
+
}
|
|
149
|
+
|
|
135
150
|
case 'inline': {
|
|
136
151
|
const node: FlowNode = {
|
|
137
152
|
nodeId,
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert'
|
|
3
|
+
import { computeGraphHash } from './finalize-workflows.js'
|
|
4
|
+
import type {
|
|
5
|
+
SerializedWorkflowGraph,
|
|
6
|
+
FunctionNode,
|
|
7
|
+
} from './workflow-graph.types.js'
|
|
8
|
+
|
|
9
|
+
function graph(
|
|
10
|
+
overrides: Partial<SerializedWorkflowGraph> = {},
|
|
11
|
+
nodeOverrides: Partial<FunctionNode> = {}
|
|
12
|
+
): SerializedWorkflowGraph {
|
|
13
|
+
const node: FunctionNode = {
|
|
14
|
+
nodeId: 'a',
|
|
15
|
+
rpcName: 'doThing',
|
|
16
|
+
input: { x: { $ref: 'trigger', path: 'x' } },
|
|
17
|
+
next: 'b',
|
|
18
|
+
...nodeOverrides,
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
name: 'wf',
|
|
22
|
+
pikkuFuncId: 'wf',
|
|
23
|
+
source: 'graph',
|
|
24
|
+
nodes: { a: node, b: { nodeId: 'b', rpcName: 'finish' } },
|
|
25
|
+
entryNodeIds: ['a'],
|
|
26
|
+
...overrides,
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
test('computeGraphHash: node-level notes do not change the hash', () => {
|
|
31
|
+
const without = computeGraphHash(graph())
|
|
32
|
+
const withNote = computeGraphHash(
|
|
33
|
+
graph({}, { notes: 'imported node "Do Thing"' })
|
|
34
|
+
)
|
|
35
|
+
assert.strictEqual(withNote, without)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('computeGraphHash: graph-level notes do not change the hash', () => {
|
|
39
|
+
const without = computeGraphHash(graph())
|
|
40
|
+
const withNotes = computeGraphHash(
|
|
41
|
+
graph({ notes: ['sticky note one', 'sticky note two'] })
|
|
42
|
+
)
|
|
43
|
+
assert.strictEqual(withNotes, without)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('computeGraphHash: a topology change DOES change the hash', () => {
|
|
47
|
+
const base = computeGraphHash(graph())
|
|
48
|
+
const changed = computeGraphHash(graph({}, { next: 'c' }))
|
|
49
|
+
assert.notStrictEqual(changed, base)
|
|
50
|
+
})
|
|
@@ -102,12 +102,23 @@ function computeStepHashes(
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Hash of graph topology only. `notes` (node- and graph-level) is deliberately
|
|
107
|
+
* excluded so editing documentation never changes the hash / marks the workflow
|
|
108
|
+
* as a new version.
|
|
109
|
+
*/
|
|
110
|
+
export function computeGraphHash(graph: SerializedWorkflowGraph): string {
|
|
111
|
+
const nodesForHash = Object.fromEntries(
|
|
112
|
+
Object.entries(graph.nodes).map(([nodeId, node]) => {
|
|
113
|
+
const { notes: _notes, ...rest } = node as { notes?: string }
|
|
114
|
+
return [nodeId, rest]
|
|
115
|
+
})
|
|
116
|
+
)
|
|
106
117
|
return hashString(
|
|
107
118
|
canonicalJSON({
|
|
108
119
|
source: graph.source,
|
|
109
120
|
context: graph.context,
|
|
110
|
-
nodes:
|
|
121
|
+
nodes: nodesForHash,
|
|
111
122
|
entryNodeIds: graph.entryNodeIds,
|
|
112
123
|
}),
|
|
113
124
|
12
|
|
@@ -89,12 +89,14 @@ export function serializeWorkflowGraph(
|
|
|
89
89
|
input?: (ref: any) => Record<string, unknown>
|
|
90
90
|
next?: string | string[] | Record<string, string | string[]>
|
|
91
91
|
onError?: string | string[]
|
|
92
|
+
notes?: string
|
|
92
93
|
}
|
|
93
94
|
>
|
|
94
95
|
},
|
|
95
96
|
options?: {
|
|
96
97
|
description?: string
|
|
97
98
|
tags?: string[]
|
|
99
|
+
notes?: string[]
|
|
98
100
|
}
|
|
99
101
|
): SerializedWorkflowGraph {
|
|
100
102
|
const nodes: Record<string, SerializedGraphNode> = {}
|
|
@@ -149,6 +151,9 @@ export function serializeWorkflowGraph(
|
|
|
149
151
|
next: serializeNext(node.next),
|
|
150
152
|
onError: node.onError,
|
|
151
153
|
}
|
|
154
|
+
if (node.notes !== undefined) {
|
|
155
|
+
funcNode.notes = node.notes
|
|
156
|
+
}
|
|
152
157
|
nodes[nodeId] = funcNode
|
|
153
158
|
|
|
154
159
|
// Entry nodes have no incoming edges
|
|
@@ -163,6 +168,7 @@ export function serializeWorkflowGraph(
|
|
|
163
168
|
source: 'graph' as const,
|
|
164
169
|
description: options?.description,
|
|
165
170
|
tags: options?.tags,
|
|
171
|
+
notes: options?.notes,
|
|
166
172
|
nodes,
|
|
167
173
|
entryNodeIds,
|
|
168
174
|
}
|
|
@@ -76,6 +76,9 @@ export type SerializedNext =
|
|
|
76
76
|
/** Default target if no conditions match */
|
|
77
77
|
default?: string | string[]
|
|
78
78
|
}
|
|
79
|
+
// Key-based branching: the running node picks a key via `graph.branch(key)`,
|
|
80
|
+
// routing to `next[key]`. Keys are branch/output identifiers, not predicates.
|
|
81
|
+
| Record<string, string | string[]>
|
|
79
82
|
|
|
80
83
|
/**
|
|
81
84
|
* Node execution options
|
|
@@ -94,6 +97,7 @@ export interface NodeOptions {
|
|
|
94
97
|
*/
|
|
95
98
|
export type FlowType =
|
|
96
99
|
| 'sleep'
|
|
100
|
+
| 'approval'
|
|
97
101
|
| 'branch'
|
|
98
102
|
| 'parallel'
|
|
99
103
|
| 'fanout'
|
|
@@ -128,6 +132,8 @@ interface BaseNode {
|
|
|
128
132
|
onError?: string | string[]
|
|
129
133
|
/** Execution options */
|
|
130
134
|
options?: NodeOptions
|
|
135
|
+
/** Free-text node documentation. Non-semantic — excluded from graphHash. */
|
|
136
|
+
notes?: string
|
|
131
137
|
}
|
|
132
138
|
|
|
133
139
|
/**
|
|
@@ -209,6 +215,8 @@ export interface SerializedWorkflowGraph {
|
|
|
209
215
|
context?: WorkflowContext
|
|
210
216
|
/** Serialized nodes */
|
|
211
217
|
nodes: Record<string, SerializedGraphNode>
|
|
218
|
+
/** Graph-level free-text notes (e.g. imported sticky notes). Non-semantic — excluded from graphHash. */
|
|
219
|
+
notes?: string[]
|
|
212
220
|
/** Entry node(s) - first nodes to execute */
|
|
213
221
|
entryNodeIds: string[]
|
|
214
222
|
/** Hash of graph topology (nodes, edges, input mappings) */
|
package/src/visit.ts
CHANGED
|
@@ -20,12 +20,14 @@ import { addChannel } from './add/add-channel.js'
|
|
|
20
20
|
import { addGateway } from './add/add-gateway.js'
|
|
21
21
|
import { addRPCInvocations } from './add/add-rpc-invocations.js'
|
|
22
22
|
import { addWireAddon } from './add/add-wire-addon.js'
|
|
23
|
+
import { addWireRemoteAddon } from './add/add-wire-remote-addon.js'
|
|
23
24
|
import { addMiddleware } from './add/add-middleware.js'
|
|
24
25
|
import { addPermission } from './add/add-permission.js'
|
|
25
26
|
import { addCLI, addCLIRenderers } from './add/add-cli.js'
|
|
26
27
|
import { addAuth } from './add/add-auth.js'
|
|
27
28
|
import { addSecret } from './add/add-secret.js'
|
|
28
29
|
import { addCredential } from './add/add-credential.js'
|
|
30
|
+
import { addScope } from './add/add-scope.js'
|
|
29
31
|
import { addVariable } from './add/add-variable.js'
|
|
30
32
|
import { addWorkflowGraph } from './add/add-workflow-graph.js'
|
|
31
33
|
import { addAIAgent } from './add/add-ai-agent.js'
|
|
@@ -96,6 +98,7 @@ export const visitSetup = (
|
|
|
96
98
|
|
|
97
99
|
addRPCInvocations(node, state, logger)
|
|
98
100
|
addWireAddon(node, state, logger)
|
|
101
|
+
addWireRemoteAddon(node, state, logger)
|
|
99
102
|
addMiddleware(logger, node, checker, state, options)
|
|
100
103
|
addPermission(logger, node, checker, state, options)
|
|
101
104
|
addApprovalDescription(logger, node, checker, state, options)
|
|
@@ -147,6 +150,7 @@ export const visitRoutes = (
|
|
|
147
150
|
addAuth(logger, node, checker, state, nextOptions)
|
|
148
151
|
addSecret(logger, node, checker, state, nextOptions)
|
|
149
152
|
addCredential(logger, node, checker, state, nextOptions)
|
|
153
|
+
addScope(logger, node, checker, state, nextOptions)
|
|
150
154
|
addVariable(logger, node, checker, state, nextOptions)
|
|
151
155
|
|
|
152
156
|
addHTTPRoute(logger, node, checker, state, nextOptions)
|