@pikku/inspector 0.11.2 → 0.12.1

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 (211) hide show
  1. package/CHANGELOG.md +36 -1
  2. package/OPTIMIZATION-PLAN.md +195 -0
  3. package/dist/add/add-ai-agent.d.ts +2 -0
  4. package/dist/add/add-ai-agent.js +314 -0
  5. package/dist/add/add-channel.js +81 -61
  6. package/dist/add/add-cli.d.ts +1 -1
  7. package/dist/add/add-cli.js +42 -19
  8. package/dist/add/add-file-extends-core-type.d.ts +1 -1
  9. package/dist/add/add-file-with-config.d.ts +1 -1
  10. package/dist/add/add-file-with-factory.d.ts +1 -1
  11. package/dist/add/add-file-with-factory.js +2 -0
  12. package/dist/add/add-functions.d.ts +1 -1
  13. package/dist/add/add-functions.js +256 -82
  14. package/dist/add/add-http-route.d.ts +20 -10
  15. package/dist/add/add-http-route.js +156 -66
  16. package/dist/add/add-http-routes.d.ts +5 -0
  17. package/dist/add/add-http-routes.js +160 -0
  18. package/dist/add/add-keyed-wiring.d.ts +12 -0
  19. package/dist/add/add-keyed-wiring.js +97 -0
  20. package/dist/add/add-mcp-prompt.d.ts +1 -1
  21. package/dist/add/add-mcp-prompt.js +14 -9
  22. package/dist/add/add-mcp-resource.d.ts +1 -1
  23. package/dist/add/add-mcp-resource.js +14 -9
  24. package/dist/add/add-middleware.d.ts +1 -4
  25. package/dist/add/add-middleware.js +364 -79
  26. package/dist/add/add-permission.d.ts +1 -1
  27. package/dist/add/add-permission.js +152 -40
  28. package/dist/add/add-queue-worker.d.ts +1 -1
  29. package/dist/add/add-queue-worker.js +18 -12
  30. package/dist/add/add-rpc-invocations.d.ts +3 -3
  31. package/dist/add/add-rpc-invocations.js +24 -10
  32. package/dist/add/add-schedule.d.ts +1 -1
  33. package/dist/add/add-schedule.js +11 -5
  34. package/dist/add/add-secret.d.ts +3 -0
  35. package/dist/add/add-secret.js +82 -0
  36. package/dist/add/add-trigger.d.ts +2 -0
  37. package/dist/add/add-trigger.js +87 -0
  38. package/dist/add/add-variable.d.ts +1 -0
  39. package/dist/add/add-variable.js +8 -0
  40. package/dist/add/add-wire-addon.d.ts +7 -0
  41. package/dist/add/add-wire-addon.js +70 -0
  42. package/dist/add/add-workflow-graph.d.ts +3 -2
  43. package/dist/add/add-workflow-graph.js +143 -406
  44. package/dist/add/add-workflow.d.ts +1 -1
  45. package/dist/add/add-workflow.js +6 -4
  46. package/dist/error-codes.d.ts +15 -1
  47. package/dist/error-codes.js +20 -1
  48. package/dist/index.d.ts +9 -8
  49. package/dist/index.js +5 -4
  50. package/dist/inspector.d.ts +2 -2
  51. package/dist/inspector.js +95 -15
  52. package/dist/schema-generator.d.ts +1 -0
  53. package/dist/schema-generator.js +1 -0
  54. package/dist/types-map.js +10 -1
  55. package/dist/types.d.ts +180 -50
  56. package/dist/utils/compute-required-schemas.d.ts +4 -0
  57. package/dist/utils/compute-required-schemas.js +40 -0
  58. package/dist/utils/contract-hashes.d.ts +52 -0
  59. package/dist/utils/contract-hashes.js +269 -0
  60. package/dist/utils/custom-types-generator.d.ts +9 -0
  61. package/dist/utils/custom-types-generator.js +71 -0
  62. package/dist/utils/detect-schema-vendor.d.ts +22 -0
  63. package/dist/utils/detect-schema-vendor.js +76 -0
  64. package/dist/utils/does-type-extend-core-type.d.ts +1 -1
  65. package/dist/utils/ensure-function-metadata.d.ts +6 -3
  66. package/dist/utils/ensure-function-metadata.js +220 -6
  67. package/dist/utils/extract-function-name.d.ts +5 -16
  68. package/dist/utils/extract-function-name.js +86 -291
  69. package/dist/utils/extract-services.d.ts +2 -1
  70. package/dist/utils/extract-services.js +25 -1
  71. package/dist/utils/filter-inspector-state.d.ts +1 -1
  72. package/dist/utils/filter-inspector-state.js +107 -23
  73. package/dist/utils/filter-utils.d.ts +2 -2
  74. package/dist/utils/get-files-and-methods.d.ts +1 -1
  75. package/dist/utils/get-property-value.d.ts +6 -1
  76. package/dist/utils/get-property-value.js +28 -3
  77. package/dist/utils/hash.d.ts +2 -0
  78. package/dist/utils/hash.js +23 -0
  79. package/dist/utils/middleware.d.ts +9 -32
  80. package/dist/utils/middleware.js +80 -66
  81. package/dist/utils/permissions.d.ts +4 -4
  82. package/dist/utils/permissions.js +10 -10
  83. package/dist/utils/post-process.d.ts +11 -11
  84. package/dist/utils/post-process.js +247 -24
  85. package/dist/utils/resolve-addon-package.d.ts +16 -0
  86. package/dist/utils/resolve-addon-package.js +34 -0
  87. package/dist/utils/resolve-function-types.d.ts +6 -0
  88. package/dist/utils/resolve-function-types.js +29 -0
  89. package/dist/utils/resolve-identifier.d.ts +10 -0
  90. package/dist/utils/resolve-identifier.js +36 -0
  91. package/dist/utils/resolve-versions.d.ts +2 -0
  92. package/dist/utils/resolve-versions.js +78 -0
  93. package/dist/utils/schema-generator.d.ts +9 -0
  94. package/dist/utils/schema-generator.js +209 -0
  95. package/dist/utils/serialize-inspector-state.d.ts +70 -23
  96. package/dist/utils/serialize-inspector-state.js +98 -22
  97. package/dist/utils/serialize-mcp-json.d.ts +2 -0
  98. package/dist/utils/serialize-mcp-json.js +99 -0
  99. package/dist/utils/serialize-middleware-groups-meta.d.ts +12 -0
  100. package/dist/utils/serialize-middleware-groups-meta.js +28 -0
  101. package/dist/utils/serialize-openapi-json.d.ts +85 -0
  102. package/dist/utils/serialize-openapi-json.js +151 -0
  103. package/dist/utils/serialize-permissions-groups-meta.d.ts +6 -0
  104. package/dist/utils/serialize-permissions-groups-meta.js +31 -0
  105. package/dist/utils/validate-auth-sessionless.d.ts +3 -0
  106. package/dist/utils/validate-auth-sessionless.js +14 -0
  107. package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +34 -102
  108. package/dist/utils/workflow/dsl/extract-dsl-workflow.d.ts +1 -1
  109. package/dist/utils/workflow/dsl/extract-dsl-workflow.js +23 -4
  110. package/dist/utils/workflow/graph/convert-dsl-to-graph.js +12 -10
  111. package/dist/utils/workflow/graph/finalize-workflow-wires.d.ts +3 -0
  112. package/dist/utils/workflow/graph/finalize-workflow-wires.js +276 -0
  113. package/dist/utils/workflow/graph/finalize-workflows.d.ts +2 -0
  114. package/dist/utils/workflow/graph/finalize-workflows.js +75 -0
  115. package/dist/utils/workflow/graph/index.d.ts +2 -0
  116. package/dist/utils/workflow/graph/index.js +2 -0
  117. package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +0 -8
  118. package/dist/utils/workflow/graph/serialize-workflow-graph.js +1 -3
  119. package/dist/utils/workflow/graph/workflow-graph.types.d.ts +53 -79
  120. package/dist/utils/workflow/graph/workflow-graph.types.js +1 -1
  121. package/dist/visit.d.ts +1 -1
  122. package/dist/visit.js +13 -6
  123. package/package.json +14 -4
  124. package/src/add/add-ai-agent.ts +468 -0
  125. package/src/add/add-channel.ts +103 -79
  126. package/src/add/add-cli.ts +68 -24
  127. package/src/add/add-file-extends-core-type.ts +1 -1
  128. package/src/add/add-file-with-config.ts +1 -1
  129. package/src/add/add-file-with-factory.ts +3 -1
  130. package/src/add/add-functions.ts +349 -103
  131. package/src/add/add-http-route.ts +263 -89
  132. package/src/add/add-http-routes.ts +229 -0
  133. package/src/add/add-keyed-wiring.ts +151 -0
  134. package/src/add/add-mcp-prompt.ts +27 -16
  135. package/src/add/add-mcp-resource.ts +28 -16
  136. package/src/add/add-middleware.ts +482 -80
  137. package/src/add/add-permission.ts +199 -40
  138. package/src/add/add-queue-worker.ts +25 -20
  139. package/src/add/add-rpc-invocations.ts +28 -11
  140. package/src/add/add-schedule.ts +17 -12
  141. package/src/add/add-secret.ts +140 -0
  142. package/src/add/add-trigger.ts +154 -0
  143. package/src/add/add-variable.ts +9 -0
  144. package/src/add/add-wire-addon.ts +80 -0
  145. package/src/add/add-workflow-graph.ts +180 -522
  146. package/src/add/add-workflow.ts +7 -6
  147. package/src/error-codes.ts +25 -1
  148. package/src/index.ts +23 -13
  149. package/src/inspector.ts +139 -19
  150. package/src/schema-generator.ts +1 -0
  151. package/src/types-map.ts +12 -1
  152. package/src/types.ts +199 -69
  153. package/src/utils/compute-required-schemas.ts +48 -0
  154. package/src/utils/contract-hashes.test.ts +553 -0
  155. package/src/utils/contract-hashes.ts +386 -0
  156. package/src/utils/custom-types-generator.ts +88 -0
  157. package/src/utils/detect-schema-vendor.ts +90 -0
  158. package/src/utils/does-type-extend-core-type.ts +1 -1
  159. package/src/utils/ensure-function-metadata.ts +325 -8
  160. package/src/utils/extract-function-name.ts +101 -351
  161. package/src/utils/extract-services.ts +35 -2
  162. package/src/utils/filter-inspector-state.test.ts +37 -25
  163. package/src/utils/filter-inspector-state.ts +146 -32
  164. package/src/utils/filter-utils.test.ts +1 -1
  165. package/src/utils/filter-utils.ts +2 -2
  166. package/src/utils/get-files-and-methods.ts +1 -1
  167. package/src/utils/get-property-value.ts +42 -4
  168. package/src/utils/hash.ts +26 -0
  169. package/src/utils/middleware.test.ts +204 -0
  170. package/src/utils/middleware.ts +131 -69
  171. package/src/utils/permissions.test.ts +35 -12
  172. package/src/utils/permissions.ts +12 -12
  173. package/src/utils/post-process.ts +306 -44
  174. package/src/utils/resolve-addon-package.ts +49 -0
  175. package/src/utils/resolve-function-types.ts +42 -0
  176. package/src/utils/resolve-identifier.ts +46 -0
  177. package/src/utils/resolve-versions.test.ts +249 -0
  178. package/src/utils/resolve-versions.ts +105 -0
  179. package/src/utils/schema-generator.ts +329 -0
  180. package/src/utils/serialize-inspector-state.ts +184 -43
  181. package/src/utils/serialize-mcp-json.ts +145 -0
  182. package/src/utils/serialize-middleware-groups-meta.ts +33 -0
  183. package/src/utils/serialize-openapi-json.ts +277 -0
  184. package/src/utils/serialize-permissions-groups-meta.ts +35 -0
  185. package/src/utils/test-data/inspector-state.json +69 -66
  186. package/src/utils/validate-auth-sessionless.ts +29 -0
  187. package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +43 -119
  188. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +26 -6
  189. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +17 -10
  190. package/src/utils/workflow/graph/finalize-workflow-wires.ts +310 -0
  191. package/src/utils/workflow/graph/finalize-workflows.ts +100 -0
  192. package/src/utils/workflow/graph/index.ts +5 -0
  193. package/src/utils/workflow/graph/serialize-workflow-graph.ts +1 -8
  194. package/src/utils/workflow/graph/workflow-graph.types.ts +29 -78
  195. package/src/visit.ts +19 -7
  196. package/tsconfig.tsbuildinfo +1 -1
  197. package/dist/add/add-forge-credential.d.ts +0 -8
  198. package/dist/add/add-forge-credential.js +0 -77
  199. package/dist/add/add-forge-node.d.ts +0 -7
  200. package/dist/add/add-forge-node.js +0 -77
  201. package/dist/add/add-mcp-tool.d.ts +0 -2
  202. package/dist/add/add-mcp-tool.js +0 -81
  203. package/dist/utils/extract-service-metadata.d.ts +0 -19
  204. package/dist/utils/extract-service-metadata.js +0 -244
  205. package/dist/utils/write-service-metadata.d.ts +0 -13
  206. package/dist/utils/write-service-metadata.js +0 -37
  207. package/src/add/add-forge-credential.ts +0 -119
  208. package/src/add/add-forge-node.ts +0 -132
  209. package/src/add/add-mcp-tool.ts +0 -141
  210. package/src/utils/extract-service-metadata.ts +0 -353
  211. package/src/utils/write-service-metadata.ts +0 -51
@@ -1,3 +1,9 @@
1
+ function makeNodeId(step, index, prefix) {
2
+ if ('stepName' in step && step.stepName) {
3
+ return step.stepName;
4
+ }
5
+ return `${prefix}_${index}`;
6
+ }
1
7
  /**
2
8
  * Check if a node is a terminal flow (no next step should follow)
3
9
  */
@@ -41,14 +47,15 @@ function convertInputSource(source) {
41
47
  * Convert a single DSL step to graph node(s)
42
48
  */
43
49
  function convertStepToNode(step, index, steps, nodeIdPrefix = 'step') {
44
- const nodeId = `${nodeIdPrefix}_${index}`;
45
- const nextNodeId = index < steps.length - 1 ? `${nodeIdPrefix}_${index + 1}` : undefined;
50
+ const nodeId = makeNodeId(step, index, nodeIdPrefix);
51
+ const nextNodeId = index < steps.length - 1
52
+ ? makeNodeId(steps[index + 1], index + 1, nodeIdPrefix)
53
+ : undefined;
46
54
  switch (step.type) {
47
55
  case 'rpc': {
48
56
  const node = {
49
57
  nodeId,
50
58
  rpcName: step.rpcName,
51
- stepName: step.stepName,
52
59
  next: nextNodeId,
53
60
  };
54
61
  if (step.inputs) {
@@ -78,7 +85,6 @@ function convertStepToNode(step, index, steps, nodeIdPrefix = 'step') {
78
85
  const node = {
79
86
  nodeId,
80
87
  flow: 'sleep',
81
- stepName: step.stepName,
82
88
  duration: step.duration,
83
89
  next: nextNodeId,
84
90
  };
@@ -88,7 +94,6 @@ function convertStepToNode(step, index, steps, nodeIdPrefix = 'step') {
88
94
  const node = {
89
95
  nodeId,
90
96
  flow: 'inline',
91
- stepName: step.stepName,
92
97
  description: step.description,
93
98
  next: nextNodeId,
94
99
  };
@@ -201,7 +206,6 @@ function convertStepToNode(step, index, steps, nodeIdPrefix = 'step') {
201
206
  const node = {
202
207
  nodeId,
203
208
  flow: 'fanout',
204
- stepName: step.stepName,
205
209
  sourceVar: step.sourceVar,
206
210
  itemVar: step.itemVar,
207
211
  mode: step.mode,
@@ -286,20 +290,18 @@ export function convertDslToGraph(workflowName, meta) {
286
290
  for (const node of nodes) {
287
291
  nodesRecord[node.nodeId] = node;
288
292
  }
289
- // Find entry nodes (step_0 is always entry for sequential workflows)
290
- const entryNodeIds = nodes.length > 0 ? ['step_0'] : [];
293
+ const entryNodeIds = nodes.length > 0 ? [nodes[0].nodeId] : [];
291
294
  // Determine source type based on dsl flag:
292
295
  // - dsl === true: pure DSL workflow, can be serialized
293
296
  // - dsl === false: complex workflow with inline steps, not serializable
294
297
  const source = meta.dsl === false ? 'complex' : 'dsl';
295
298
  return {
296
299
  name: workflowName,
297
- pikkuFuncName: meta.pikkuFuncName,
300
+ pikkuFuncId: meta.pikkuFuncId,
298
301
  source,
299
302
  description: meta.description,
300
303
  tags: meta.tags,
301
304
  context: meta.context,
302
- wires: {}, // DSL workflows don't have explicit wires in meta
303
305
  nodes: nodesRecord,
304
306
  entryNodeIds,
305
307
  };
@@ -0,0 +1,3 @@
1
+ import type { InspectorState } from '../../../types.js';
2
+ export declare function finalizeWorkflowHelperTypes(state: InspectorState): void;
3
+ export declare function finalizeWorkflowWires(state: InspectorState): void;
@@ -0,0 +1,276 @@
1
+ function parseWorkflowFuncId(pikkuFuncId) {
2
+ for (const prefix of ['workflowStart:', 'workflow:']) {
3
+ if (pikkuFuncId.startsWith(prefix)) {
4
+ return { workflowName: pikkuFuncId.slice(prefix.length) };
5
+ }
6
+ }
7
+ if (pikkuFuncId.startsWith('graphStart:')) {
8
+ const rest = pikkuFuncId.slice('graphStart:'.length);
9
+ const colonIdx = rest.indexOf(':');
10
+ if (colonIdx !== -1) {
11
+ return {
12
+ workflowName: rest.slice(0, colonIdx),
13
+ startNode: rest.slice(colonIdx + 1),
14
+ };
15
+ }
16
+ }
17
+ return null;
18
+ }
19
+ function resolveStartNode(parsed, graph) {
20
+ return parsed.startNode ?? graph.entryNodeIds[0];
21
+ }
22
+ function getOrCreateWires(graph) {
23
+ if (!graph.wires) {
24
+ graph.wires = {};
25
+ }
26
+ return graph.wires;
27
+ }
28
+ export function finalizeWorkflowHelperTypes(state) {
29
+ const { functions, workflows } = state;
30
+ const graphMeta = workflows.graphMeta;
31
+ for (const meta of Object.values(functions.meta)) {
32
+ if (meta.functionType !== 'helper')
33
+ continue;
34
+ if (meta.pikkuFuncId.startsWith('workflowStatus:'))
35
+ continue;
36
+ const parsed = parseWorkflowFuncId(meta.pikkuFuncId);
37
+ if (!parsed)
38
+ continue;
39
+ const graph = graphMeta[parsed.workflowName];
40
+ if (!graph)
41
+ continue;
42
+ const startNodeId = resolveStartNode(parsed, graph);
43
+ const startNode = graph.nodes[startNodeId];
44
+ if (!startNode || !('rpcName' in startNode))
45
+ continue;
46
+ const rpcMeta = functions.meta[startNode.rpcName];
47
+ if (!rpcMeta)
48
+ continue;
49
+ if (rpcMeta.inputSchemaName) {
50
+ meta.inputSchemaName = rpcMeta.inputSchemaName;
51
+ }
52
+ if (rpcMeta.inputs && rpcMeta.inputs.length > 0) {
53
+ meta.inputs = rpcMeta.inputs;
54
+ }
55
+ }
56
+ }
57
+ export function finalizeWorkflowWires(state) {
58
+ const { workflows } = state;
59
+ const graphMeta = workflows.graphMeta;
60
+ scanHTTP(state, graphMeta);
61
+ scanScheduledTasks(state, graphMeta);
62
+ scanTriggers(state, graphMeta);
63
+ scanQueueWorkers(state, graphMeta);
64
+ scanChannels(state, graphMeta);
65
+ scanMCPEndpoints(state, graphMeta);
66
+ scanCLI(state, graphMeta);
67
+ }
68
+ function scanHTTP(state, graphMeta) {
69
+ for (const [method, routes] of Object.entries(state.http.meta)) {
70
+ for (const [route, meta] of Object.entries(routes)) {
71
+ const parsed = parseWorkflowFuncId(meta.pikkuFuncId);
72
+ if (!parsed)
73
+ continue;
74
+ const graph = graphMeta[parsed.workflowName];
75
+ if (!graph)
76
+ continue;
77
+ const wires = getOrCreateWires(graph);
78
+ if (!wires.http)
79
+ wires.http = [];
80
+ wires.http.push({
81
+ route,
82
+ method,
83
+ startNode: resolveStartNode(parsed, graph),
84
+ });
85
+ }
86
+ }
87
+ }
88
+ function scanScheduledTasks(state, graphMeta) {
89
+ for (const meta of Object.values(state.scheduledTasks.meta)) {
90
+ const parsed = parseWorkflowFuncId(meta.pikkuFuncId);
91
+ if (!parsed)
92
+ continue;
93
+ const graph = graphMeta[parsed.workflowName];
94
+ if (!graph)
95
+ continue;
96
+ const wires = getOrCreateWires(graph);
97
+ if (!wires.schedule)
98
+ wires.schedule = [];
99
+ wires.schedule.push({
100
+ cron: meta.schedule,
101
+ startNode: resolveStartNode(parsed, graph),
102
+ });
103
+ }
104
+ }
105
+ function scanTriggers(state, graphMeta) {
106
+ for (const meta of Object.values(state.triggers.meta)) {
107
+ const parsed = parseWorkflowFuncId(meta.pikkuFuncId);
108
+ if (!parsed)
109
+ continue;
110
+ const graph = graphMeta[parsed.workflowName];
111
+ if (!graph)
112
+ continue;
113
+ const wires = getOrCreateWires(graph);
114
+ if (!wires.trigger)
115
+ wires.trigger = [];
116
+ wires.trigger.push({
117
+ name: meta.name,
118
+ startNode: resolveStartNode(parsed, graph),
119
+ });
120
+ }
121
+ }
122
+ function scanQueueWorkers(state, graphMeta) {
123
+ for (const meta of Object.values(state.queueWorkers.meta)) {
124
+ const parsed = parseWorkflowFuncId(meta.pikkuFuncId);
125
+ if (!parsed)
126
+ continue;
127
+ const graph = graphMeta[parsed.workflowName];
128
+ if (!graph)
129
+ continue;
130
+ const wires = getOrCreateWires(graph);
131
+ if (!wires.queue)
132
+ wires.queue = [];
133
+ wires.queue.push({
134
+ name: meta.name,
135
+ startNode: resolveStartNode(parsed, graph),
136
+ });
137
+ }
138
+ }
139
+ function scanChannels(state, graphMeta) {
140
+ for (const channelMeta of Object.values(state.channels.meta)) {
141
+ const wire = {
142
+ name: channelMeta.name,
143
+ route: channelMeta.route,
144
+ };
145
+ let targetWorkflow;
146
+ if (channelMeta.connect) {
147
+ const parsed = parseWorkflowFuncId(channelMeta.connect.pikkuFuncId);
148
+ if (parsed) {
149
+ const graph = graphMeta[parsed.workflowName];
150
+ if (graph) {
151
+ targetWorkflow = graph;
152
+ wire.onConnect = resolveStartNode(parsed, graph);
153
+ }
154
+ }
155
+ }
156
+ if (channelMeta.disconnect) {
157
+ const parsed = parseWorkflowFuncId(channelMeta.disconnect.pikkuFuncId);
158
+ if (parsed) {
159
+ const graph = graphMeta[parsed.workflowName];
160
+ if (graph) {
161
+ targetWorkflow = targetWorkflow ?? graph;
162
+ wire.onDisconnect = resolveStartNode(parsed, graph);
163
+ }
164
+ }
165
+ }
166
+ if (channelMeta.message) {
167
+ const parsed = parseWorkflowFuncId(channelMeta.message.pikkuFuncId);
168
+ if (parsed) {
169
+ const graph = graphMeta[parsed.workflowName];
170
+ if (graph) {
171
+ targetWorkflow = targetWorkflow ?? graph;
172
+ wire.onMessage = resolveStartNode(parsed, graph);
173
+ }
174
+ }
175
+ }
176
+ for (const [routingProp, routeMap] of Object.entries(channelMeta.messageWirings)) {
177
+ for (const [routeValue, messageMeta] of Object.entries(routeMap)) {
178
+ const parsed = parseWorkflowFuncId(messageMeta.pikkuFuncId);
179
+ if (!parsed)
180
+ continue;
181
+ const graph = graphMeta[parsed.workflowName];
182
+ if (!graph)
183
+ continue;
184
+ targetWorkflow = targetWorkflow ?? graph;
185
+ if (!wire.onMessageRoute)
186
+ wire.onMessageRoute = {};
187
+ wire.onMessageRoute[`${routingProp}:${routeValue}`] = resolveStartNode(parsed, graph);
188
+ }
189
+ }
190
+ if (targetWorkflow) {
191
+ const wires = getOrCreateWires(targetWorkflow);
192
+ if (!wires.channel)
193
+ wires.channel = [];
194
+ wires.channel.push(wire);
195
+ }
196
+ }
197
+ }
198
+ function scanMCPEndpoints(state, graphMeta) {
199
+ for (const meta of Object.values(state.mcpEndpoints.toolsMeta)) {
200
+ const parsed = parseWorkflowFuncId(meta.pikkuFuncId);
201
+ if (!parsed)
202
+ continue;
203
+ const graph = graphMeta[parsed.workflowName];
204
+ if (!graph)
205
+ continue;
206
+ const wires = getOrCreateWires(graph);
207
+ if (!wires.mcp)
208
+ wires.mcp = {};
209
+ if (!wires.mcp.tool)
210
+ wires.mcp.tool = [];
211
+ wires.mcp.tool.push({
212
+ name: meta.name,
213
+ startNode: resolveStartNode(parsed, graph),
214
+ });
215
+ }
216
+ for (const meta of Object.values(state.mcpEndpoints.promptsMeta)) {
217
+ const parsed = parseWorkflowFuncId(meta.pikkuFuncId);
218
+ if (!parsed)
219
+ continue;
220
+ const graph = graphMeta[parsed.workflowName];
221
+ if (!graph)
222
+ continue;
223
+ const wires = getOrCreateWires(graph);
224
+ if (!wires.mcp)
225
+ wires.mcp = {};
226
+ if (!wires.mcp.prompt)
227
+ wires.mcp.prompt = [];
228
+ wires.mcp.prompt.push({
229
+ name: meta.name,
230
+ startNode: resolveStartNode(parsed, graph),
231
+ });
232
+ }
233
+ for (const meta of Object.values(state.mcpEndpoints.resourcesMeta)) {
234
+ const parsed = parseWorkflowFuncId(meta.pikkuFuncId);
235
+ if (!parsed)
236
+ continue;
237
+ const graph = graphMeta[parsed.workflowName];
238
+ if (!graph)
239
+ continue;
240
+ const wires = getOrCreateWires(graph);
241
+ if (!wires.mcp)
242
+ wires.mcp = {};
243
+ if (!wires.mcp.resource)
244
+ wires.mcp.resource = [];
245
+ wires.mcp.resource.push({
246
+ uri: meta.uri,
247
+ startNode: resolveStartNode(parsed, graph),
248
+ });
249
+ }
250
+ }
251
+ function visitCLICommands(commands, programName, path, graphMeta) {
252
+ for (const [name, command] of Object.entries(commands)) {
253
+ const currentPath = [...path, name];
254
+ const parsed = parseWorkflowFuncId(command.pikkuFuncId);
255
+ if (parsed) {
256
+ const graph = graphMeta[parsed.workflowName];
257
+ if (graph) {
258
+ const wires = getOrCreateWires(graph);
259
+ if (!wires.cli)
260
+ wires.cli = [];
261
+ wires.cli.push({
262
+ command: `${programName} ${currentPath.join(' ')}`,
263
+ startNode: resolveStartNode(parsed, graph),
264
+ });
265
+ }
266
+ }
267
+ if (command.subcommands) {
268
+ visitCLICommands(command.subcommands, programName, currentPath, graphMeta);
269
+ }
270
+ }
271
+ }
272
+ function scanCLI(state, graphMeta) {
273
+ for (const program of Object.values(state.cli.meta.programs)) {
274
+ visitCLICommands(program.commands, program.program, [], graphMeta);
275
+ }
276
+ }
@@ -0,0 +1,2 @@
1
+ import type { InspectorState } from '../../../types.js';
2
+ export declare function finalizeWorkflows(state: InspectorState): void;
@@ -0,0 +1,75 @@
1
+ import { isVersionedId, formatVersionedId, parseVersionedId } from '@pikku/core';
2
+ import { canonicalJSON, hashString } from '../../hash.js';
3
+ import { convertDslToGraph } from './convert-dsl-to-graph.js';
4
+ export function finalizeWorkflows(state) {
5
+ const { workflows, functions } = state;
6
+ const functionsMeta = functions.meta;
7
+ for (const [name, meta] of Object.entries(workflows.meta)) {
8
+ const graph = convertDslToGraph(name, meta);
9
+ stampVersionsOnGraph(graph, functionsMeta);
10
+ computeStepHashes(graph, functionsMeta);
11
+ graph.graphHash = computeGraphHash(graph);
12
+ workflows.graphMeta[name] = graph;
13
+ }
14
+ for (const graph of Object.values(workflows.graphMeta)) {
15
+ if (graph.graphHash) {
16
+ continue;
17
+ }
18
+ stampVersionsOnGraph(graph, functionsMeta);
19
+ computeStepHashes(graph, functionsMeta);
20
+ graph.graphHash = computeGraphHash(graph);
21
+ }
22
+ }
23
+ function stampVersionsOnGraph(graph, functionsMeta) {
24
+ for (const node of Object.values(graph.nodes)) {
25
+ if (!('rpcName' in node) || typeof node.rpcName !== 'string') {
26
+ continue;
27
+ }
28
+ if (isVersionedId(node.rpcName)) {
29
+ continue;
30
+ }
31
+ const meta = functionsMeta[node.rpcName];
32
+ if (meta?.version !== undefined) {
33
+ node.rpcName = formatVersionedId(node.rpcName, meta.version);
34
+ }
35
+ else {
36
+ const latestVersion = findLatestVersion(node.rpcName, functionsMeta);
37
+ if (latestVersion > 0) {
38
+ node.rpcName = formatVersionedId(node.rpcName, latestVersion);
39
+ }
40
+ }
41
+ }
42
+ }
43
+ function findLatestVersion(baseName, functionsMeta) {
44
+ let max = 0;
45
+ for (const id of Object.keys(functionsMeta)) {
46
+ const parsed = parseVersionedId(id);
47
+ if (parsed.baseName === baseName && parsed.version !== null) {
48
+ max = Math.max(max, parsed.version);
49
+ }
50
+ }
51
+ return max;
52
+ }
53
+ function computeStepHashes(graph, functionsMeta) {
54
+ for (const node of Object.values(graph.nodes)) {
55
+ if (!('rpcName' in node) || typeof node.rpcName !== 'string') {
56
+ continue;
57
+ }
58
+ const rpcName = node.rpcName;
59
+ let meta = functionsMeta[rpcName];
60
+ if (!meta) {
61
+ const { baseName } = parseVersionedId(rpcName);
62
+ meta = functionsMeta[baseName];
63
+ }
64
+ ;
65
+ node.stepHash = hashString(`${node.nodeId}:${meta?.contractHash ?? ''}`, 12);
66
+ }
67
+ }
68
+ function computeGraphHash(graph) {
69
+ return hashString(canonicalJSON({
70
+ source: graph.source,
71
+ context: graph.context,
72
+ nodes: graph.nodes,
73
+ entryNodeIds: graph.entryNodeIds,
74
+ }), 12);
75
+ }
@@ -4,3 +4,5 @@
4
4
  export * from './workflow-graph.types.js';
5
5
  export { serializeWorkflowGraph } from './serialize-workflow-graph.js';
6
6
  export { convertDslToGraph } from './convert-dsl-to-graph.js';
7
+ export { finalizeWorkflows } from './finalize-workflows.js';
8
+ export { finalizeWorkflowHelperTypes, finalizeWorkflowWires, } from './finalize-workflow-wires.js';
@@ -4,3 +4,5 @@
4
4
  export * from './workflow-graph.types.js';
5
5
  export { serializeWorkflowGraph } from './serialize-workflow-graph.js';
6
6
  export { convertDslToGraph } from './convert-dsl-to-graph.js';
7
+ export { finalizeWorkflows } from './finalize-workflows.js';
8
+ export { finalizeWorkflowHelperTypes, finalizeWorkflowWires, } from './finalize-workflow-wires.js';
@@ -7,13 +7,6 @@ import type { SerializedWorkflowGraph, DataRef, SerializedNext } from './workflo
7
7
  */
8
8
  export declare function serializeWorkflowGraph(definition: {
9
9
  name: string;
10
- wires: {
11
- http?: {
12
- route: string;
13
- method: string;
14
- };
15
- queue?: string;
16
- };
17
10
  graph: Record<string, {
18
11
  func: {
19
12
  name?: string;
@@ -32,7 +25,6 @@ export declare function serializeWorkflowGraph(definition: {
32
25
  */
33
26
  export declare function deserializeWorkflowGraph(serialized: SerializedWorkflowGraph): {
34
27
  name: string;
35
- wires: SerializedWorkflowGraph['wires'];
36
28
  graph: Record<string, {
37
29
  rpcName: string;
38
30
  input: Record<string, unknown | DataRef>;
@@ -116,11 +116,10 @@ export function serializeWorkflowGraph(definition, options) {
116
116
  }
117
117
  return {
118
118
  name: definition.name,
119
- pikkuFuncName: definition.name, // For graph workflows, pikkuFuncName is the workflow name
119
+ pikkuFuncId: definition.name, // For graph workflows, pikkuFuncId is the workflow name
120
120
  source: 'graph',
121
121
  description: options?.description,
122
122
  tags: options?.tags,
123
- wires: definition.wires,
124
123
  nodes,
125
124
  entryNodeIds,
126
125
  };
@@ -145,7 +144,6 @@ export function deserializeWorkflowGraph(serialized) {
145
144
  }
146
145
  return {
147
146
  name: serialized.name,
148
- wires: serialized.wires,
149
147
  graph,
150
148
  entryNodeIds: serialized.entryNodeIds,
151
149
  };
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Serialized types for workflow graphs
3
3
  * These are extracted by the inspector and stored as JSON
4
- * Can be created from code (wireWorkflow) or UI
4
+ * Can be created from code (pikkuWorkflowGraph) or UI
5
5
  */
6
6
  /**
7
7
  * Reference to data from another node or trigger
@@ -96,6 +96,8 @@ export interface FunctionNode extends BaseNode {
96
96
  input?: Record<string, unknown | DataRef>;
97
97
  /** Output variable name for storing result */
98
98
  outputVar?: string;
99
+ /** Hash of nodeId + RPC input/output schemas for version detection */
100
+ stepHash?: string;
99
101
  }
100
102
  /**
101
103
  * Flow node - control flow only, no RPC call
@@ -118,81 +120,6 @@ export declare const isFunctionNode: (node: SerializedGraphNode) => node is Func
118
120
  * Type guard for flow nodes
119
121
  */
120
122
  export declare const isFlowNode: (node: SerializedGraphNode) => node is FlowNode;
121
- /**
122
- * HTTP wire configuration with startNode
123
- */
124
- export interface HttpWire {
125
- route: string;
126
- method: 'get' | 'post' | 'put' | 'patch' | 'delete';
127
- startNode: string;
128
- }
129
- /**
130
- * Channel wire configuration
131
- */
132
- export interface ChannelWire {
133
- name: string;
134
- onConnect?: string;
135
- onDisconnect?: string;
136
- onMessage?: string;
137
- }
138
- /**
139
- * Queue wire configuration
140
- */
141
- export interface QueueWire {
142
- name: string;
143
- startNode: string;
144
- }
145
- /**
146
- * CLI wire configuration
147
- */
148
- export interface CliWire {
149
- command: string;
150
- startNode: string;
151
- }
152
- /**
153
- * MCP wire configurations
154
- */
155
- export interface McpWires {
156
- tool?: Array<{
157
- name: string;
158
- startNode: string;
159
- }>;
160
- prompt?: Array<{
161
- name: string;
162
- startNode: string;
163
- }>;
164
- resource?: Array<{
165
- uri: string;
166
- startNode: string;
167
- }>;
168
- }
169
- /**
170
- * Schedule wire configuration
171
- */
172
- export interface ScheduleWire {
173
- cron?: string;
174
- interval?: string;
175
- startNode: string;
176
- }
177
- /**
178
- * Trigger wire configuration
179
- */
180
- export interface TriggerWire {
181
- name: string;
182
- startNode: string;
183
- }
184
- /**
185
- * All wire configurations for workflows
186
- */
187
- export interface WorkflowWiresConfig {
188
- http?: HttpWire[];
189
- channel?: ChannelWire[];
190
- queue?: QueueWire[];
191
- cli?: CliWire[];
192
- mcp?: McpWires;
193
- schedule?: ScheduleWire[];
194
- trigger?: TriggerWire[];
195
- }
196
123
  /**
197
124
  * Workflow source type
198
125
  * - 'dsl': Pure DSL workflow (pikkuWorkflowFunc) - can be round-tripped to code
@@ -207,7 +134,7 @@ export interface SerializedWorkflowGraph {
207
134
  /** Workflow name */
208
135
  name: string;
209
136
  /** Pikku function name (for runtime registration) */
210
- pikkuFuncName: string;
137
+ pikkuFuncId: string;
211
138
  /** Source type: 'dsl' for pikkuWorkflowFunc, 'graph' for pikkuWorkflowGraph */
212
139
  source: WorkflowSourceType;
213
140
  /** Optional description */
@@ -216,12 +143,59 @@ export interface SerializedWorkflowGraph {
216
143
  tags?: string[];
217
144
  /** Workflow context/state variables (from Zod schema) */
218
145
  context?: WorkflowContext;
219
- /** Wires - how the workflow is triggered */
220
- wires: WorkflowWiresConfig;
221
146
  /** Serialized nodes */
222
147
  nodes: Record<string, SerializedGraphNode>;
223
148
  /** Entry node(s) - first nodes to execute */
224
149
  entryNodeIds: string[];
150
+ /** Hash of graph topology (nodes, edges, input mappings) */
151
+ graphHash?: string;
152
+ /** Wire entry points (HTTP, channel, queue, etc.) that trigger this workflow */
153
+ wires?: WorkflowWires;
154
+ }
155
+ export interface WorkflowWires {
156
+ http?: Array<{
157
+ route: string;
158
+ method: string;
159
+ startNode: string;
160
+ }>;
161
+ channel?: Array<{
162
+ name: string;
163
+ route: string;
164
+ onConnect?: string;
165
+ onMessage?: string;
166
+ onDisconnect?: string;
167
+ onMessageRoute?: Record<string, string>;
168
+ }>;
169
+ queue?: Array<{
170
+ name: string;
171
+ startNode: string;
172
+ }>;
173
+ cli?: Array<{
174
+ command: string;
175
+ startNode: string;
176
+ }>;
177
+ mcp?: {
178
+ tool?: Array<{
179
+ name: string;
180
+ startNode: string;
181
+ }>;
182
+ prompt?: Array<{
183
+ name: string;
184
+ startNode: string;
185
+ }>;
186
+ resource?: Array<{
187
+ uri: string;
188
+ startNode: string;
189
+ }>;
190
+ };
191
+ schedule?: Array<{
192
+ cron: string;
193
+ startNode: string;
194
+ }>;
195
+ trigger?: Array<{
196
+ name: string;
197
+ startNode: string;
198
+ }>;
225
199
  }
226
200
  /**
227
201
  * All workflow graphs (serialized)
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Serialized types for workflow graphs
3
3
  * These are extracted by the inspector and stored as JSON
4
- * Can be created from code (wireWorkflow) or UI
4
+ * Can be created from code (pikkuWorkflowGraph) or UI
5
5
  */
6
6
  /**
7
7
  * Check if value is a DataRef
package/dist/visit.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import * as ts from 'typescript';
2
- import { InspectorState, InspectorLogger, InspectorOptions } from './types.js';
2
+ import type { InspectorState, InspectorLogger, InspectorOptions } from './types.js';
3
3
  export declare const visitSetup: (logger: InspectorLogger, checker: ts.TypeChecker, node: ts.Node, state: InspectorState, options: InspectorOptions) => void;
4
4
  export declare const visitRoutes: (logger: InspectorLogger, checker: ts.TypeChecker, node: ts.Node, state: InspectorState, options: InspectorOptions) => void;