@pikku/inspector 0.11.2 → 0.12.0
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 +11 -1
- package/OPTIMIZATION-PLAN.md +195 -0
- package/dist/add/add-ai-agent.d.ts +2 -0
- package/dist/add/add-ai-agent.js +314 -0
- package/dist/add/add-channel.js +69 -61
- package/dist/add/add-cli.js +36 -18
- package/dist/add/add-file-with-factory.js +2 -0
- package/dist/add/add-functions.js +250 -75
- package/dist/add/add-http-route.d.ts +19 -10
- package/dist/add/add-http-route.js +152 -66
- package/dist/add/add-http-routes.d.ts +5 -0
- package/dist/add/add-http-routes.js +159 -0
- package/dist/add/add-keyed-wiring.d.ts +12 -0
- package/dist/add/add-keyed-wiring.js +97 -0
- package/dist/add/add-mcp-prompt.js +14 -9
- package/dist/add/add-mcp-resource.js +14 -9
- package/dist/add/add-middleware.d.ts +1 -4
- package/dist/add/add-middleware.js +364 -79
- package/dist/add/add-permission.d.ts +1 -1
- package/dist/add/add-permission.js +152 -40
- package/dist/add/add-queue-worker.js +18 -12
- package/dist/add/add-rpc-invocations.js +14 -0
- package/dist/add/add-schedule.js +11 -5
- package/dist/add/add-secret.d.ts +3 -0
- package/dist/add/add-secret.js +82 -0
- package/dist/add/add-trigger.d.ts +2 -0
- package/dist/add/add-trigger.js +87 -0
- package/dist/add/add-variable.d.ts +1 -0
- package/dist/add/add-variable.js +8 -0
- package/dist/add/add-workflow-graph.d.ts +3 -2
- package/dist/add/add-workflow-graph.js +143 -406
- package/dist/add/add-workflow.js +6 -4
- package/dist/error-codes.d.ts +14 -1
- package/dist/error-codes.js +19 -1
- package/dist/index.d.ts +9 -8
- package/dist/index.js +5 -4
- package/dist/inspector.d.ts +1 -1
- package/dist/inspector.js +91 -14
- package/dist/schema-generator.d.ts +1 -0
- package/dist/schema-generator.js +1 -0
- package/dist/types-map.js +10 -1
- package/dist/types.d.ts +163 -39
- package/dist/utils/compute-required-schemas.d.ts +4 -0
- package/dist/utils/compute-required-schemas.js +41 -0
- package/dist/utils/contract-hashes.d.ts +35 -0
- package/dist/utils/contract-hashes.js +202 -0
- package/dist/utils/custom-types-generator.d.ts +9 -0
- package/dist/utils/custom-types-generator.js +71 -0
- package/dist/utils/detect-schema-vendor.d.ts +22 -0
- package/dist/utils/detect-schema-vendor.js +76 -0
- package/dist/utils/ensure-function-metadata.d.ts +5 -2
- package/dist/utils/ensure-function-metadata.js +220 -6
- package/dist/utils/extract-function-name.d.ts +5 -16
- package/dist/utils/extract-function-name.js +86 -291
- package/dist/utils/extract-services.d.ts +2 -1
- package/dist/utils/extract-services.js +25 -1
- package/dist/utils/filter-inspector-state.js +107 -23
- package/dist/utils/get-property-value.d.ts +6 -1
- package/dist/utils/get-property-value.js +28 -3
- package/dist/utils/hash.d.ts +2 -0
- package/dist/utils/hash.js +23 -0
- package/dist/utils/middleware.d.ts +7 -30
- package/dist/utils/middleware.js +80 -66
- package/dist/utils/permissions.d.ts +2 -2
- package/dist/utils/permissions.js +10 -10
- package/dist/utils/post-process.d.ts +9 -10
- package/dist/utils/post-process.js +231 -24
- package/dist/utils/resolve-external-package.d.ts +12 -0
- package/dist/utils/resolve-external-package.js +34 -0
- package/dist/utils/resolve-function-types.d.ts +6 -0
- package/dist/utils/resolve-function-types.js +29 -0
- package/dist/utils/resolve-identifier.d.ts +10 -0
- package/dist/utils/resolve-identifier.js +36 -0
- package/dist/utils/resolve-versions.d.ts +2 -0
- package/dist/utils/resolve-versions.js +78 -0
- package/dist/utils/schema-generator.d.ts +9 -0
- package/dist/utils/schema-generator.js +209 -0
- package/dist/utils/serialize-inspector-state.d.ts +59 -22
- package/dist/utils/serialize-inspector-state.js +92 -20
- package/dist/utils/serialize-mcp-json.d.ts +2 -0
- package/dist/utils/serialize-mcp-json.js +99 -0
- package/dist/utils/serialize-middleware-groups-meta.d.ts +12 -0
- package/dist/utils/serialize-middleware-groups-meta.js +28 -0
- package/dist/utils/serialize-openapi-json.d.ts +85 -0
- package/dist/utils/serialize-openapi-json.js +151 -0
- package/dist/utils/serialize-permissions-groups-meta.d.ts +6 -0
- package/dist/utils/serialize-permissions-groups-meta.js +31 -0
- package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +34 -102
- package/dist/utils/workflow/dsl/extract-dsl-workflow.js +23 -4
- package/dist/utils/workflow/graph/convert-dsl-to-graph.js +12 -10
- package/dist/utils/workflow/graph/finalize-workflow-wires.d.ts +3 -0
- package/dist/utils/workflow/graph/finalize-workflow-wires.js +276 -0
- package/dist/utils/workflow/graph/finalize-workflows.d.ts +2 -0
- package/dist/utils/workflow/graph/finalize-workflows.js +75 -0
- package/dist/utils/workflow/graph/index.d.ts +2 -0
- package/dist/utils/workflow/graph/index.js +2 -0
- package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +0 -8
- package/dist/utils/workflow/graph/serialize-workflow-graph.js +1 -3
- package/dist/utils/workflow/graph/workflow-graph.types.d.ts +53 -79
- package/dist/utils/workflow/graph/workflow-graph.types.js +1 -1
- package/dist/visit.js +11 -6
- package/package.json +14 -4
- package/src/add/add-ai-agent.ts +468 -0
- package/src/add/add-channel.ts +82 -79
- package/src/add/add-cli.ts +49 -20
- package/src/add/add-file-with-factory.ts +2 -0
- package/src/add/add-functions.ts +330 -86
- package/src/add/add-http-route.ts +245 -88
- package/src/add/add-http-routes.ts +228 -0
- package/src/add/add-keyed-wiring.ts +151 -0
- package/src/add/add-mcp-prompt.ts +26 -15
- package/src/add/add-mcp-resource.ts +27 -15
- package/src/add/add-middleware.ts +482 -80
- package/src/add/add-permission.ts +199 -40
- package/src/add/add-queue-worker.ts +24 -19
- package/src/add/add-rpc-invocations.ts +17 -0
- package/src/add/add-schedule.ts +16 -11
- package/src/add/add-secret.ts +140 -0
- package/src/add/add-trigger.ts +154 -0
- package/src/add/add-variable.ts +9 -0
- package/src/add/add-workflow-graph.ts +180 -522
- package/src/add/add-workflow.ts +5 -4
- package/src/error-codes.ts +24 -1
- package/src/index.ts +22 -13
- package/src/inspector.ts +129 -17
- package/src/schema-generator.ts +1 -0
- package/src/types-map.ts +12 -1
- package/src/types.ts +175 -58
- package/src/utils/compute-required-schemas.ts +49 -0
- package/src/utils/contract-hashes.test.ts +528 -0
- package/src/utils/contract-hashes.ts +290 -0
- package/src/utils/custom-types-generator.ts +88 -0
- package/src/utils/detect-schema-vendor.ts +90 -0
- package/src/utils/ensure-function-metadata.ts +324 -7
- package/src/utils/extract-function-name.ts +101 -351
- package/src/utils/extract-services.ts +35 -2
- package/src/utils/filter-inspector-state.test.ts +34 -20
- package/src/utils/filter-inspector-state.ts +140 -31
- package/src/utils/get-property-value.ts +42 -4
- package/src/utils/hash.ts +26 -0
- package/src/utils/middleware.test.ts +204 -0
- package/src/utils/middleware.ts +129 -67
- package/src/utils/permissions.test.ts +35 -12
- package/src/utils/permissions.ts +10 -10
- package/src/utils/post-process.ts +283 -43
- package/src/utils/resolve-external-package.ts +42 -0
- package/src/utils/resolve-function-types.ts +42 -0
- package/src/utils/resolve-identifier.ts +46 -0
- package/src/utils/resolve-versions.test.ts +249 -0
- package/src/utils/resolve-versions.ts +105 -0
- package/src/utils/schema-generator.ts +329 -0
- package/src/utils/serialize-inspector-state.ts +163 -40
- package/src/utils/serialize-mcp-json.ts +145 -0
- package/src/utils/serialize-middleware-groups-meta.ts +33 -0
- package/src/utils/serialize-openapi-json.ts +277 -0
- package/src/utils/serialize-permissions-groups-meta.ts +35 -0
- package/src/utils/test-data/inspector-state.json +69 -66
- package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +43 -119
- package/src/utils/workflow/dsl/extract-dsl-workflow.ts +24 -4
- package/src/utils/workflow/graph/convert-dsl-to-graph.ts +17 -10
- package/src/utils/workflow/graph/finalize-workflow-wires.ts +310 -0
- package/src/utils/workflow/graph/finalize-workflows.ts +100 -0
- package/src/utils/workflow/graph/index.ts +5 -0
- package/src/utils/workflow/graph/serialize-workflow-graph.ts +1 -8
- package/src/utils/workflow/graph/workflow-graph.types.ts +29 -78
- package/src/visit.ts +12 -6
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/add/add-forge-credential.d.ts +0 -8
- package/dist/add/add-forge-credential.js +0 -77
- package/dist/add/add-forge-node.d.ts +0 -7
- package/dist/add/add-forge-node.js +0 -77
- package/dist/add/add-mcp-tool.d.ts +0 -2
- package/dist/add/add-mcp-tool.js +0 -81
- package/dist/utils/extract-service-metadata.d.ts +0 -19
- package/dist/utils/extract-service-metadata.js +0 -244
- package/dist/utils/write-service-metadata.d.ts +0 -13
- package/dist/utils/write-service-metadata.js +0 -37
- package/src/add/add-forge-credential.ts +0 -119
- package/src/add/add-forge-node.ts +0 -132
- package/src/add/add-mcp-tool.ts +0 -141
- package/src/utils/extract-service-metadata.ts +0 -353
- package/src/utils/write-service-metadata.ts +0 -51
|
@@ -134,6 +134,15 @@ function valueToCode(value: unknown, itemVar?: string): string {
|
|
|
134
134
|
if (isTemplateRef(value)) {
|
|
135
135
|
return templateRefToCode(value, itemVar)
|
|
136
136
|
}
|
|
137
|
+
if (Array.isArray(value)) {
|
|
138
|
+
const elements = value.map((v) => valueToCode(v, itemVar))
|
|
139
|
+
return `[${elements.join(', ')}]`
|
|
140
|
+
}
|
|
141
|
+
if (typeof value === 'object' && value !== null) {
|
|
142
|
+
const entries = Object.entries(value)
|
|
143
|
+
const props = entries.map(([k, v]) => `${k}: ${valueToCode(v, itemVar)}`)
|
|
144
|
+
return `{ ${props.join(', ')} }`
|
|
145
|
+
}
|
|
137
146
|
return JSON.stringify(value)
|
|
138
147
|
}
|
|
139
148
|
|
|
@@ -845,6 +854,35 @@ function templateRefToGraphCode(
|
|
|
845
854
|
return `template('${templateStr}', [${refs.join(', ')}])`
|
|
846
855
|
}
|
|
847
856
|
|
|
857
|
+
function valueToGraphCode(
|
|
858
|
+
value: unknown,
|
|
859
|
+
outputVarToNodeId: Map<string, string>,
|
|
860
|
+
refTracker: { hasRefs: boolean }
|
|
861
|
+
): string {
|
|
862
|
+
if (isDataRef(value)) {
|
|
863
|
+
refTracker.hasRefs = true
|
|
864
|
+
return dataRefToGraphRef(value, outputVarToNodeId)
|
|
865
|
+
}
|
|
866
|
+
if (isTemplateRef(value)) {
|
|
867
|
+
refTracker.hasRefs = true
|
|
868
|
+
return templateRefToGraphCode(value, outputVarToNodeId)
|
|
869
|
+
}
|
|
870
|
+
if (Array.isArray(value)) {
|
|
871
|
+
const elements = value.map((v) =>
|
|
872
|
+
valueToGraphCode(v, outputVarToNodeId, refTracker)
|
|
873
|
+
)
|
|
874
|
+
return `[${elements.join(', ')}]`
|
|
875
|
+
}
|
|
876
|
+
if (typeof value === 'object' && value !== null) {
|
|
877
|
+
const entries = Object.entries(value)
|
|
878
|
+
const props = entries.map(
|
|
879
|
+
([k, v]) => `${k}: ${valueToGraphCode(v, outputVarToNodeId, refTracker)}`
|
|
880
|
+
)
|
|
881
|
+
return `{ ${props.join(', ')} }`
|
|
882
|
+
}
|
|
883
|
+
return JSON.stringify(value)
|
|
884
|
+
}
|
|
885
|
+
|
|
848
886
|
/**
|
|
849
887
|
* Convert input object to graph input code using ref()
|
|
850
888
|
* @param input - The input mapping
|
|
@@ -860,87 +898,17 @@ function inputToGraphCode(
|
|
|
860
898
|
const entries = Object.entries(input)
|
|
861
899
|
if (entries.length === 0) return { hasRefs: false, code: '{}' }
|
|
862
900
|
|
|
863
|
-
|
|
901
|
+
const refTracker = { hasRefs: false }
|
|
864
902
|
const lines = entries.map(([key, value]) => {
|
|
865
|
-
|
|
866
|
-
hasRefs = true
|
|
867
|
-
return ` ${key}: ${dataRefToGraphRef(value, outputVarToNodeId)},`
|
|
868
|
-
}
|
|
869
|
-
if (isTemplateRef(value)) {
|
|
870
|
-
hasRefs = true
|
|
871
|
-
return ` ${key}: ${templateRefToGraphCode(value, outputVarToNodeId)},`
|
|
872
|
-
}
|
|
873
|
-
return ` ${key}: ${JSON.stringify(value)},`
|
|
903
|
+
return ` ${key}: ${valueToGraphCode(value, outputVarToNodeId, refTracker)},`
|
|
874
904
|
})
|
|
875
905
|
|
|
876
906
|
return {
|
|
877
|
-
hasRefs,
|
|
907
|
+
hasRefs: refTracker.hasRefs,
|
|
878
908
|
code: `{\n${lines.join('\n')}\n }`,
|
|
879
909
|
}
|
|
880
910
|
}
|
|
881
911
|
|
|
882
|
-
/**
|
|
883
|
-
* Serialize wires to code
|
|
884
|
-
*/
|
|
885
|
-
function wiresToCode(wires: SerializedWorkflowGraph['wires']): string {
|
|
886
|
-
if (!wires || Object.keys(wires).length === 0) return '{}'
|
|
887
|
-
|
|
888
|
-
const parts: string[] = []
|
|
889
|
-
|
|
890
|
-
if (wires.http && wires.http.length > 0) {
|
|
891
|
-
const httpItems = wires.http.map(
|
|
892
|
-
(h) =>
|
|
893
|
-
`{ route: '${h.route}', method: '${h.method}', startNode: '${h.startNode}' }`
|
|
894
|
-
)
|
|
895
|
-
parts.push(`http: [${httpItems.join(', ')}]`)
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
if (wires.channel && wires.channel.length > 0) {
|
|
899
|
-
const channelItems = wires.channel.map((c) => {
|
|
900
|
-
const channelParts: string[] = [`name: '${c.name}'`]
|
|
901
|
-
if (c.onConnect) channelParts.push(`onConnect: '${c.onConnect}'`)
|
|
902
|
-
if (c.onDisconnect) channelParts.push(`onDisconnect: '${c.onDisconnect}'`)
|
|
903
|
-
if (c.onMessage) channelParts.push(`onMessage: '${c.onMessage}'`)
|
|
904
|
-
return `{ ${channelParts.join(', ')} }`
|
|
905
|
-
})
|
|
906
|
-
parts.push(`channel: [${channelItems.join(', ')}]`)
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
if (wires.queue && wires.queue.length > 0) {
|
|
910
|
-
const queueItems = wires.queue.map(
|
|
911
|
-
(q) => `{ name: '${q.name}', startNode: '${q.startNode}' }`
|
|
912
|
-
)
|
|
913
|
-
parts.push(`queue: [${queueItems.join(', ')}]`)
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
if (wires.cli && wires.cli.length > 0) {
|
|
917
|
-
const cliItems = wires.cli.map(
|
|
918
|
-
(c) => `{ command: '${c.command}', startNode: '${c.startNode}' }`
|
|
919
|
-
)
|
|
920
|
-
parts.push(`cli: [${cliItems.join(', ')}]`)
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
if (wires.schedule && wires.schedule.length > 0) {
|
|
924
|
-
const scheduleItems = wires.schedule.map((s) => {
|
|
925
|
-
const scheduleParts: string[] = []
|
|
926
|
-
if (s.cron) scheduleParts.push(`cron: '${s.cron}'`)
|
|
927
|
-
if (s.interval) scheduleParts.push(`interval: '${s.interval}'`)
|
|
928
|
-
scheduleParts.push(`startNode: '${s.startNode}'`)
|
|
929
|
-
return `{ ${scheduleParts.join(', ')} }`
|
|
930
|
-
})
|
|
931
|
-
parts.push(`schedule: [${scheduleItems.join(', ')}]`)
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
if (wires.trigger && wires.trigger.length > 0) {
|
|
935
|
-
const triggerItems = wires.trigger.map(
|
|
936
|
-
(t) => `{ name: '${t.name}', startNode: '${t.startNode}' }`
|
|
937
|
-
)
|
|
938
|
-
parts.push(`trigger: [${triggerItems.join(', ')}]`)
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
return `{ ${parts.join(', ')} }`
|
|
942
|
-
}
|
|
943
|
-
|
|
944
912
|
/**
|
|
945
913
|
* Check if a node is a flow node (non-RPC control flow)
|
|
946
914
|
*/
|
|
@@ -995,9 +963,7 @@ export function deserializeGraphWorkflow(
|
|
|
995
963
|
const lines: string[] = []
|
|
996
964
|
|
|
997
965
|
// Import statement
|
|
998
|
-
lines.push(
|
|
999
|
-
`import { pikkuWorkflowGraph, wireWorkflow } from '${pikkuImportPath}'`
|
|
1000
|
-
)
|
|
966
|
+
lines.push(`import { pikkuWorkflowGraph } from '${pikkuImportPath}'`)
|
|
1001
967
|
lines.push('')
|
|
1002
968
|
|
|
1003
969
|
// Add description as comment if present
|
|
@@ -1082,28 +1048,7 @@ export function deserializeGraphWorkflow(
|
|
|
1082
1048
|
}
|
|
1083
1049
|
}
|
|
1084
1050
|
|
|
1085
|
-
//
|
|
1086
|
-
const rpcNodeIds = new Set(Object.keys(nodeRpcMap))
|
|
1087
|
-
const nodesWithIncomingEdges = new Set<string>()
|
|
1088
|
-
for (const [nodeId, node] of Object.entries(workflow.nodes)) {
|
|
1089
|
-
if (!rpcNodeIds.has(nodeId)) continue
|
|
1090
|
-
if ('next' in node && node.next) {
|
|
1091
|
-
const nextId = node.next as string
|
|
1092
|
-
// Follow through flow nodes to find the actual next RPC node
|
|
1093
|
-
const actualNextId = flowNodeIds.has(nextId)
|
|
1094
|
-
? findNextRpcNode(nextId, workflow.nodes, flowNodeIds)
|
|
1095
|
-
: nextId
|
|
1096
|
-
if (actualNextId && rpcNodeIds.has(actualNextId)) {
|
|
1097
|
-
nodesWithIncomingEdges.add(actualNextId)
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
// Entry node is the first RPC node with no incoming edges
|
|
1102
|
-
const entryNode = Object.keys(nodeRpcMap).find(
|
|
1103
|
-
(id) => !nodesWithIncomingEdges.has(id)
|
|
1104
|
-
)
|
|
1105
|
-
|
|
1106
|
-
// Generate the pikkuWorkflowGraph call
|
|
1051
|
+
// Generate the pikkuWorkflowGraph call (builds graph and registers with core)
|
|
1107
1052
|
lines.push(`export const ${workflow.name} = pikkuWorkflowGraph({`)
|
|
1108
1053
|
lines.push(` name: '${workflow.name}',`)
|
|
1109
1054
|
if (workflow.description) {
|
|
@@ -1125,13 +1070,6 @@ export function deserializeGraphWorkflow(
|
|
|
1125
1070
|
lines.push(` nodes: {},`)
|
|
1126
1071
|
}
|
|
1127
1072
|
|
|
1128
|
-
// Generate wires with api entry point
|
|
1129
|
-
if (entryNode) {
|
|
1130
|
-
lines.push(` wires: {`)
|
|
1131
|
-
lines.push(` api: '${entryNode}',`)
|
|
1132
|
-
lines.push(` },`)
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
1073
|
// Generate config (node configurations)
|
|
1136
1074
|
if (nodeConfigs.length > 0) {
|
|
1137
1075
|
lines.push(` config: {`)
|
|
@@ -1142,20 +1080,6 @@ export function deserializeGraphWorkflow(
|
|
|
1142
1080
|
lines.push(`})`)
|
|
1143
1081
|
lines.push('')
|
|
1144
1082
|
|
|
1145
|
-
// Always generate wireWorkflow to register the graph workflow
|
|
1146
|
-
// (needed for testing even without explicit wires)
|
|
1147
|
-
if (workflow.wires && Object.keys(workflow.wires).length > 0) {
|
|
1148
|
-
lines.push(`wireWorkflow({`)
|
|
1149
|
-
lines.push(` wires: ${wiresToCode(workflow.wires)},`)
|
|
1150
|
-
lines.push(` graph: ${workflow.name},`)
|
|
1151
|
-
lines.push(`})`)
|
|
1152
|
-
} else {
|
|
1153
|
-
lines.push(`wireWorkflow({`)
|
|
1154
|
-
lines.push(` graph: ${workflow.name},`)
|
|
1155
|
-
lines.push(`})`)
|
|
1156
|
-
}
|
|
1157
|
-
lines.push('')
|
|
1158
|
-
|
|
1159
1083
|
return lines.join('\n')
|
|
1160
1084
|
}
|
|
1161
1085
|
|
|
@@ -1426,6 +1426,26 @@ function extractInputSources(
|
|
|
1426
1426
|
return Object.keys(inputs).length > 0 ? inputs : undefined
|
|
1427
1427
|
}
|
|
1428
1428
|
|
|
1429
|
+
function inputSourceToInlineValue(source: InputSource): unknown {
|
|
1430
|
+
switch (source.from) {
|
|
1431
|
+
case 'literal':
|
|
1432
|
+
return source.value
|
|
1433
|
+
case 'input':
|
|
1434
|
+
return { $ref: 'trigger', path: source.path }
|
|
1435
|
+
case 'outputVar':
|
|
1436
|
+
return { $ref: source.name, path: source.path }
|
|
1437
|
+
case 'item':
|
|
1438
|
+
return { $ref: '$item', path: source.path }
|
|
1439
|
+
case 'template':
|
|
1440
|
+
return {
|
|
1441
|
+
$template: {
|
|
1442
|
+
parts: source.parts,
|
|
1443
|
+
expressions: source.expressions.map(inputSourceToInlineValue),
|
|
1444
|
+
},
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1429
1449
|
/**
|
|
1430
1450
|
* Extract a single input source
|
|
1431
1451
|
*/
|
|
@@ -1498,8 +1518,8 @@ function extractInputSource(
|
|
|
1498
1518
|
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
|
|
1499
1519
|
const propName = prop.name.text
|
|
1500
1520
|
const propSource = extractInputSource(prop.initializer, context)
|
|
1501
|
-
if (propSource
|
|
1502
|
-
obj[propName] = propSource
|
|
1521
|
+
if (propSource) {
|
|
1522
|
+
obj[propName] = inputSourceToInlineValue(propSource)
|
|
1503
1523
|
}
|
|
1504
1524
|
}
|
|
1505
1525
|
}
|
|
@@ -1511,8 +1531,8 @@ function extractInputSource(
|
|
|
1511
1531
|
const arr: unknown[] = []
|
|
1512
1532
|
for (const elem of node.elements) {
|
|
1513
1533
|
const elemSource = extractInputSource(elem, context)
|
|
1514
|
-
if (elemSource
|
|
1515
|
-
arr.push(elemSource
|
|
1534
|
+
if (elemSource) {
|
|
1535
|
+
arr.push(inputSourceToInlineValue(elemSource))
|
|
1516
1536
|
}
|
|
1517
1537
|
}
|
|
1518
1538
|
return { from: 'literal', value: arr }
|
|
@@ -10,6 +10,17 @@ import type {
|
|
|
10
10
|
DataRef,
|
|
11
11
|
} from './workflow-graph.types.js'
|
|
12
12
|
|
|
13
|
+
function makeNodeId(
|
|
14
|
+
step: WorkflowStepMeta,
|
|
15
|
+
index: number,
|
|
16
|
+
prefix: string
|
|
17
|
+
): string {
|
|
18
|
+
if ('stepName' in step && step.stepName) {
|
|
19
|
+
return step.stepName
|
|
20
|
+
}
|
|
21
|
+
return `${prefix}_${index}`
|
|
22
|
+
}
|
|
23
|
+
|
|
13
24
|
/**
|
|
14
25
|
* Check if a node is a terminal flow (no next step should follow)
|
|
15
26
|
*/
|
|
@@ -69,16 +80,17 @@ function convertStepToNode(
|
|
|
69
80
|
steps: WorkflowStepMeta[],
|
|
70
81
|
nodeIdPrefix: string = 'step'
|
|
71
82
|
): SerializedGraphNode[] {
|
|
72
|
-
const nodeId =
|
|
83
|
+
const nodeId = makeNodeId(step, index, nodeIdPrefix)
|
|
73
84
|
const nextNodeId =
|
|
74
|
-
index < steps.length - 1
|
|
85
|
+
index < steps.length - 1
|
|
86
|
+
? makeNodeId(steps[index + 1], index + 1, nodeIdPrefix)
|
|
87
|
+
: undefined
|
|
75
88
|
|
|
76
89
|
switch (step.type) {
|
|
77
90
|
case 'rpc': {
|
|
78
91
|
const node: FunctionNode = {
|
|
79
92
|
nodeId,
|
|
80
93
|
rpcName: step.rpcName,
|
|
81
|
-
stepName: step.stepName,
|
|
82
94
|
next: nextNodeId,
|
|
83
95
|
}
|
|
84
96
|
if (step.inputs) {
|
|
@@ -108,7 +120,6 @@ function convertStepToNode(
|
|
|
108
120
|
const node: FlowNode = {
|
|
109
121
|
nodeId,
|
|
110
122
|
flow: 'sleep',
|
|
111
|
-
stepName: step.stepName,
|
|
112
123
|
duration: step.duration,
|
|
113
124
|
next: nextNodeId,
|
|
114
125
|
}
|
|
@@ -119,7 +130,6 @@ function convertStepToNode(
|
|
|
119
130
|
const node: FlowNode = {
|
|
120
131
|
nodeId,
|
|
121
132
|
flow: 'inline',
|
|
122
|
-
stepName: step.stepName,
|
|
123
133
|
description: step.description,
|
|
124
134
|
next: nextNodeId,
|
|
125
135
|
}
|
|
@@ -274,7 +284,6 @@ function convertStepToNode(
|
|
|
274
284
|
const node: FlowNode = {
|
|
275
285
|
nodeId,
|
|
276
286
|
flow: 'fanout',
|
|
277
|
-
stepName: step.stepName,
|
|
278
287
|
sourceVar: step.sourceVar,
|
|
279
288
|
itemVar: step.itemVar,
|
|
280
289
|
mode: step.mode,
|
|
@@ -378,8 +387,7 @@ export function convertDslToGraph(
|
|
|
378
387
|
nodesRecord[node.nodeId] = node
|
|
379
388
|
}
|
|
380
389
|
|
|
381
|
-
|
|
382
|
-
const entryNodeIds = nodes.length > 0 ? ['step_0'] : []
|
|
390
|
+
const entryNodeIds = nodes.length > 0 ? [nodes[0].nodeId] : []
|
|
383
391
|
|
|
384
392
|
// Determine source type based on dsl flag:
|
|
385
393
|
// - dsl === true: pure DSL workflow, can be serialized
|
|
@@ -388,12 +396,11 @@ export function convertDslToGraph(
|
|
|
388
396
|
|
|
389
397
|
return {
|
|
390
398
|
name: workflowName,
|
|
391
|
-
|
|
399
|
+
pikkuFuncId: meta.pikkuFuncId,
|
|
392
400
|
source,
|
|
393
401
|
description: meta.description,
|
|
394
402
|
tags: meta.tags,
|
|
395
403
|
context: meta.context,
|
|
396
|
-
wires: {}, // DSL workflows don't have explicit wires in meta
|
|
397
404
|
nodes: nodesRecord,
|
|
398
405
|
entryNodeIds,
|
|
399
406
|
}
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import type { InspectorState } from '../../../types.js'
|
|
2
|
+
import type {
|
|
3
|
+
SerializedWorkflowGraph,
|
|
4
|
+
WorkflowWires,
|
|
5
|
+
} from './workflow-graph.types.js'
|
|
6
|
+
import type { CLICommandMeta } from '@pikku/core/cli'
|
|
7
|
+
|
|
8
|
+
function parseWorkflowFuncId(
|
|
9
|
+
pikkuFuncId: string
|
|
10
|
+
): { workflowName: string; startNode?: string } | null {
|
|
11
|
+
for (const prefix of ['workflowStart:', 'workflow:']) {
|
|
12
|
+
if (pikkuFuncId.startsWith(prefix)) {
|
|
13
|
+
return { workflowName: pikkuFuncId.slice(prefix.length) }
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (pikkuFuncId.startsWith('graphStart:')) {
|
|
17
|
+
const rest = pikkuFuncId.slice('graphStart:'.length)
|
|
18
|
+
const colonIdx = rest.indexOf(':')
|
|
19
|
+
if (colonIdx !== -1) {
|
|
20
|
+
return {
|
|
21
|
+
workflowName: rest.slice(0, colonIdx),
|
|
22
|
+
startNode: rest.slice(colonIdx + 1),
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function resolveStartNode(
|
|
30
|
+
parsed: { workflowName: string; startNode?: string },
|
|
31
|
+
graph: SerializedWorkflowGraph
|
|
32
|
+
): string {
|
|
33
|
+
return parsed.startNode ?? graph.entryNodeIds[0]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function getOrCreateWires(graph: SerializedWorkflowGraph): WorkflowWires {
|
|
37
|
+
if (!graph.wires) {
|
|
38
|
+
graph.wires = {}
|
|
39
|
+
}
|
|
40
|
+
return graph.wires
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function finalizeWorkflowHelperTypes(state: InspectorState): void {
|
|
44
|
+
const { functions, workflows } = state
|
|
45
|
+
const graphMeta = workflows.graphMeta
|
|
46
|
+
|
|
47
|
+
for (const meta of Object.values(functions.meta)) {
|
|
48
|
+
if (meta.functionType !== 'helper') continue
|
|
49
|
+
if (meta.pikkuFuncId.startsWith('workflowStatus:')) continue
|
|
50
|
+
|
|
51
|
+
const parsed = parseWorkflowFuncId(meta.pikkuFuncId)
|
|
52
|
+
if (!parsed) continue
|
|
53
|
+
|
|
54
|
+
const graph = graphMeta[parsed.workflowName]
|
|
55
|
+
if (!graph) continue
|
|
56
|
+
|
|
57
|
+
const startNodeId = resolveStartNode(parsed, graph)
|
|
58
|
+
const startNode = graph.nodes[startNodeId]
|
|
59
|
+
if (!startNode || !('rpcName' in startNode)) continue
|
|
60
|
+
|
|
61
|
+
const rpcMeta = functions.meta[startNode.rpcName as string]
|
|
62
|
+
if (!rpcMeta) continue
|
|
63
|
+
|
|
64
|
+
if (rpcMeta.inputSchemaName) {
|
|
65
|
+
meta.inputSchemaName = rpcMeta.inputSchemaName
|
|
66
|
+
}
|
|
67
|
+
if (rpcMeta.inputs && rpcMeta.inputs.length > 0) {
|
|
68
|
+
meta.inputs = rpcMeta.inputs
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function finalizeWorkflowWires(state: InspectorState): void {
|
|
74
|
+
const { workflows } = state
|
|
75
|
+
const graphMeta = workflows.graphMeta
|
|
76
|
+
|
|
77
|
+
scanHTTP(state, graphMeta)
|
|
78
|
+
scanScheduledTasks(state, graphMeta)
|
|
79
|
+
scanTriggers(state, graphMeta)
|
|
80
|
+
scanQueueWorkers(state, graphMeta)
|
|
81
|
+
scanChannels(state, graphMeta)
|
|
82
|
+
scanMCPEndpoints(state, graphMeta)
|
|
83
|
+
scanCLI(state, graphMeta)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function scanHTTP(
|
|
87
|
+
state: InspectorState,
|
|
88
|
+
graphMeta: Record<string, SerializedWorkflowGraph>
|
|
89
|
+
): void {
|
|
90
|
+
for (const [method, routes] of Object.entries(state.http.meta)) {
|
|
91
|
+
for (const [route, meta] of Object.entries(routes)) {
|
|
92
|
+
const parsed = parseWorkflowFuncId(meta.pikkuFuncId)
|
|
93
|
+
if (!parsed) continue
|
|
94
|
+
const graph = graphMeta[parsed.workflowName]
|
|
95
|
+
if (!graph) continue
|
|
96
|
+
const wires = getOrCreateWires(graph)
|
|
97
|
+
if (!wires.http) wires.http = []
|
|
98
|
+
wires.http.push({
|
|
99
|
+
route,
|
|
100
|
+
method,
|
|
101
|
+
startNode: resolveStartNode(parsed, graph),
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function scanScheduledTasks(
|
|
108
|
+
state: InspectorState,
|
|
109
|
+
graphMeta: Record<string, SerializedWorkflowGraph>
|
|
110
|
+
): void {
|
|
111
|
+
for (const meta of Object.values(state.scheduledTasks.meta)) {
|
|
112
|
+
const parsed = parseWorkflowFuncId(meta.pikkuFuncId)
|
|
113
|
+
if (!parsed) continue
|
|
114
|
+
const graph = graphMeta[parsed.workflowName]
|
|
115
|
+
if (!graph) continue
|
|
116
|
+
const wires = getOrCreateWires(graph)
|
|
117
|
+
if (!wires.schedule) wires.schedule = []
|
|
118
|
+
wires.schedule.push({
|
|
119
|
+
cron: meta.schedule,
|
|
120
|
+
startNode: resolveStartNode(parsed, graph),
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function scanTriggers(
|
|
126
|
+
state: InspectorState,
|
|
127
|
+
graphMeta: Record<string, SerializedWorkflowGraph>
|
|
128
|
+
): void {
|
|
129
|
+
for (const meta of Object.values(state.triggers.meta)) {
|
|
130
|
+
const parsed = parseWorkflowFuncId(meta.pikkuFuncId)
|
|
131
|
+
if (!parsed) continue
|
|
132
|
+
const graph = graphMeta[parsed.workflowName]
|
|
133
|
+
if (!graph) continue
|
|
134
|
+
const wires = getOrCreateWires(graph)
|
|
135
|
+
if (!wires.trigger) wires.trigger = []
|
|
136
|
+
wires.trigger.push({
|
|
137
|
+
name: meta.name,
|
|
138
|
+
startNode: resolveStartNode(parsed, graph),
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function scanQueueWorkers(
|
|
144
|
+
state: InspectorState,
|
|
145
|
+
graphMeta: Record<string, SerializedWorkflowGraph>
|
|
146
|
+
): void {
|
|
147
|
+
for (const meta of Object.values(state.queueWorkers.meta)) {
|
|
148
|
+
const parsed = parseWorkflowFuncId(meta.pikkuFuncId)
|
|
149
|
+
if (!parsed) continue
|
|
150
|
+
const graph = graphMeta[parsed.workflowName]
|
|
151
|
+
if (!graph) continue
|
|
152
|
+
const wires = getOrCreateWires(graph)
|
|
153
|
+
if (!wires.queue) wires.queue = []
|
|
154
|
+
wires.queue.push({
|
|
155
|
+
name: meta.name,
|
|
156
|
+
startNode: resolveStartNode(parsed, graph),
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function scanChannels(
|
|
162
|
+
state: InspectorState,
|
|
163
|
+
graphMeta: Record<string, SerializedWorkflowGraph>
|
|
164
|
+
): void {
|
|
165
|
+
for (const channelMeta of Object.values(state.channels.meta)) {
|
|
166
|
+
const wire: NonNullable<WorkflowWires['channel']>[number] = {
|
|
167
|
+
name: channelMeta.name,
|
|
168
|
+
route: channelMeta.route,
|
|
169
|
+
}
|
|
170
|
+
let targetWorkflow: SerializedWorkflowGraph | undefined
|
|
171
|
+
|
|
172
|
+
if (channelMeta.connect) {
|
|
173
|
+
const parsed = parseWorkflowFuncId(channelMeta.connect.pikkuFuncId)
|
|
174
|
+
if (parsed) {
|
|
175
|
+
const graph = graphMeta[parsed.workflowName]
|
|
176
|
+
if (graph) {
|
|
177
|
+
targetWorkflow = graph
|
|
178
|
+
wire.onConnect = resolveStartNode(parsed, graph)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (channelMeta.disconnect) {
|
|
184
|
+
const parsed = parseWorkflowFuncId(channelMeta.disconnect.pikkuFuncId)
|
|
185
|
+
if (parsed) {
|
|
186
|
+
const graph = graphMeta[parsed.workflowName]
|
|
187
|
+
if (graph) {
|
|
188
|
+
targetWorkflow = targetWorkflow ?? graph
|
|
189
|
+
wire.onDisconnect = resolveStartNode(parsed, graph)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (channelMeta.message) {
|
|
195
|
+
const parsed = parseWorkflowFuncId(channelMeta.message.pikkuFuncId)
|
|
196
|
+
if (parsed) {
|
|
197
|
+
const graph = graphMeta[parsed.workflowName]
|
|
198
|
+
if (graph) {
|
|
199
|
+
targetWorkflow = targetWorkflow ?? graph
|
|
200
|
+
wire.onMessage = resolveStartNode(parsed, graph)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
for (const [routingProp, routeMap] of Object.entries(
|
|
206
|
+
channelMeta.messageWirings
|
|
207
|
+
)) {
|
|
208
|
+
for (const [routeValue, messageMeta] of Object.entries(routeMap)) {
|
|
209
|
+
const parsed = parseWorkflowFuncId(messageMeta.pikkuFuncId)
|
|
210
|
+
if (!parsed) continue
|
|
211
|
+
const graph = graphMeta[parsed.workflowName]
|
|
212
|
+
if (!graph) continue
|
|
213
|
+
targetWorkflow = targetWorkflow ?? graph
|
|
214
|
+
if (!wire.onMessageRoute) wire.onMessageRoute = {}
|
|
215
|
+
wire.onMessageRoute[`${routingProp}:${routeValue}`] = resolveStartNode(
|
|
216
|
+
parsed,
|
|
217
|
+
graph
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (targetWorkflow) {
|
|
223
|
+
const wires = getOrCreateWires(targetWorkflow)
|
|
224
|
+
if (!wires.channel) wires.channel = []
|
|
225
|
+
wires.channel.push(wire)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function scanMCPEndpoints(
|
|
231
|
+
state: InspectorState,
|
|
232
|
+
graphMeta: Record<string, SerializedWorkflowGraph>
|
|
233
|
+
): void {
|
|
234
|
+
for (const meta of Object.values(state.mcpEndpoints.toolsMeta)) {
|
|
235
|
+
const parsed = parseWorkflowFuncId(meta.pikkuFuncId)
|
|
236
|
+
if (!parsed) continue
|
|
237
|
+
const graph = graphMeta[parsed.workflowName]
|
|
238
|
+
if (!graph) continue
|
|
239
|
+
const wires = getOrCreateWires(graph)
|
|
240
|
+
if (!wires.mcp) wires.mcp = {}
|
|
241
|
+
if (!wires.mcp.tool) wires.mcp.tool = []
|
|
242
|
+
wires.mcp.tool.push({
|
|
243
|
+
name: meta.name,
|
|
244
|
+
startNode: resolveStartNode(parsed, graph),
|
|
245
|
+
})
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
for (const meta of Object.values(state.mcpEndpoints.promptsMeta)) {
|
|
249
|
+
const parsed = parseWorkflowFuncId(meta.pikkuFuncId)
|
|
250
|
+
if (!parsed) continue
|
|
251
|
+
const graph = graphMeta[parsed.workflowName]
|
|
252
|
+
if (!graph) continue
|
|
253
|
+
const wires = getOrCreateWires(graph)
|
|
254
|
+
if (!wires.mcp) wires.mcp = {}
|
|
255
|
+
if (!wires.mcp.prompt) wires.mcp.prompt = []
|
|
256
|
+
wires.mcp.prompt.push({
|
|
257
|
+
name: meta.name,
|
|
258
|
+
startNode: resolveStartNode(parsed, graph),
|
|
259
|
+
})
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
for (const meta of Object.values(state.mcpEndpoints.resourcesMeta)) {
|
|
263
|
+
const parsed = parseWorkflowFuncId(meta.pikkuFuncId)
|
|
264
|
+
if (!parsed) continue
|
|
265
|
+
const graph = graphMeta[parsed.workflowName]
|
|
266
|
+
if (!graph) continue
|
|
267
|
+
const wires = getOrCreateWires(graph)
|
|
268
|
+
if (!wires.mcp) wires.mcp = {}
|
|
269
|
+
if (!wires.mcp.resource) wires.mcp.resource = []
|
|
270
|
+
wires.mcp.resource.push({
|
|
271
|
+
uri: meta.uri,
|
|
272
|
+
startNode: resolveStartNode(parsed, graph),
|
|
273
|
+
})
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function visitCLICommands(
|
|
278
|
+
commands: Record<string, CLICommandMeta>,
|
|
279
|
+
programName: string,
|
|
280
|
+
path: string[],
|
|
281
|
+
graphMeta: Record<string, SerializedWorkflowGraph>
|
|
282
|
+
): void {
|
|
283
|
+
for (const [name, command] of Object.entries(commands)) {
|
|
284
|
+
const currentPath = [...path, name]
|
|
285
|
+
const parsed = parseWorkflowFuncId(command.pikkuFuncId)
|
|
286
|
+
if (parsed) {
|
|
287
|
+
const graph = graphMeta[parsed.workflowName]
|
|
288
|
+
if (graph) {
|
|
289
|
+
const wires = getOrCreateWires(graph)
|
|
290
|
+
if (!wires.cli) wires.cli = []
|
|
291
|
+
wires.cli.push({
|
|
292
|
+
command: `${programName} ${currentPath.join(' ')}`,
|
|
293
|
+
startNode: resolveStartNode(parsed, graph),
|
|
294
|
+
})
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (command.subcommands) {
|
|
298
|
+
visitCLICommands(command.subcommands, programName, currentPath, graphMeta)
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function scanCLI(
|
|
304
|
+
state: InspectorState,
|
|
305
|
+
graphMeta: Record<string, SerializedWorkflowGraph>
|
|
306
|
+
): void {
|
|
307
|
+
for (const program of Object.values(state.cli.meta.programs)) {
|
|
308
|
+
visitCLICommands(program.commands, program.program, [], graphMeta)
|
|
309
|
+
}
|
|
310
|
+
}
|