@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.
Files changed (182) hide show
  1. package/CHANGELOG.md +11 -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 +69 -61
  6. package/dist/add/add-cli.js +36 -18
  7. package/dist/add/add-file-with-factory.js +2 -0
  8. package/dist/add/add-functions.js +250 -75
  9. package/dist/add/add-http-route.d.ts +19 -10
  10. package/dist/add/add-http-route.js +152 -66
  11. package/dist/add/add-http-routes.d.ts +5 -0
  12. package/dist/add/add-http-routes.js +159 -0
  13. package/dist/add/add-keyed-wiring.d.ts +12 -0
  14. package/dist/add/add-keyed-wiring.js +97 -0
  15. package/dist/add/add-mcp-prompt.js +14 -9
  16. package/dist/add/add-mcp-resource.js +14 -9
  17. package/dist/add/add-middleware.d.ts +1 -4
  18. package/dist/add/add-middleware.js +364 -79
  19. package/dist/add/add-permission.d.ts +1 -1
  20. package/dist/add/add-permission.js +152 -40
  21. package/dist/add/add-queue-worker.js +18 -12
  22. package/dist/add/add-rpc-invocations.js +14 -0
  23. package/dist/add/add-schedule.js +11 -5
  24. package/dist/add/add-secret.d.ts +3 -0
  25. package/dist/add/add-secret.js +82 -0
  26. package/dist/add/add-trigger.d.ts +2 -0
  27. package/dist/add/add-trigger.js +87 -0
  28. package/dist/add/add-variable.d.ts +1 -0
  29. package/dist/add/add-variable.js +8 -0
  30. package/dist/add/add-workflow-graph.d.ts +3 -2
  31. package/dist/add/add-workflow-graph.js +143 -406
  32. package/dist/add/add-workflow.js +6 -4
  33. package/dist/error-codes.d.ts +14 -1
  34. package/dist/error-codes.js +19 -1
  35. package/dist/index.d.ts +9 -8
  36. package/dist/index.js +5 -4
  37. package/dist/inspector.d.ts +1 -1
  38. package/dist/inspector.js +91 -14
  39. package/dist/schema-generator.d.ts +1 -0
  40. package/dist/schema-generator.js +1 -0
  41. package/dist/types-map.js +10 -1
  42. package/dist/types.d.ts +163 -39
  43. package/dist/utils/compute-required-schemas.d.ts +4 -0
  44. package/dist/utils/compute-required-schemas.js +41 -0
  45. package/dist/utils/contract-hashes.d.ts +35 -0
  46. package/dist/utils/contract-hashes.js +202 -0
  47. package/dist/utils/custom-types-generator.d.ts +9 -0
  48. package/dist/utils/custom-types-generator.js +71 -0
  49. package/dist/utils/detect-schema-vendor.d.ts +22 -0
  50. package/dist/utils/detect-schema-vendor.js +76 -0
  51. package/dist/utils/ensure-function-metadata.d.ts +5 -2
  52. package/dist/utils/ensure-function-metadata.js +220 -6
  53. package/dist/utils/extract-function-name.d.ts +5 -16
  54. package/dist/utils/extract-function-name.js +86 -291
  55. package/dist/utils/extract-services.d.ts +2 -1
  56. package/dist/utils/extract-services.js +25 -1
  57. package/dist/utils/filter-inspector-state.js +107 -23
  58. package/dist/utils/get-property-value.d.ts +6 -1
  59. package/dist/utils/get-property-value.js +28 -3
  60. package/dist/utils/hash.d.ts +2 -0
  61. package/dist/utils/hash.js +23 -0
  62. package/dist/utils/middleware.d.ts +7 -30
  63. package/dist/utils/middleware.js +80 -66
  64. package/dist/utils/permissions.d.ts +2 -2
  65. package/dist/utils/permissions.js +10 -10
  66. package/dist/utils/post-process.d.ts +9 -10
  67. package/dist/utils/post-process.js +231 -24
  68. package/dist/utils/resolve-external-package.d.ts +12 -0
  69. package/dist/utils/resolve-external-package.js +34 -0
  70. package/dist/utils/resolve-function-types.d.ts +6 -0
  71. package/dist/utils/resolve-function-types.js +29 -0
  72. package/dist/utils/resolve-identifier.d.ts +10 -0
  73. package/dist/utils/resolve-identifier.js +36 -0
  74. package/dist/utils/resolve-versions.d.ts +2 -0
  75. package/dist/utils/resolve-versions.js +78 -0
  76. package/dist/utils/schema-generator.d.ts +9 -0
  77. package/dist/utils/schema-generator.js +209 -0
  78. package/dist/utils/serialize-inspector-state.d.ts +59 -22
  79. package/dist/utils/serialize-inspector-state.js +92 -20
  80. package/dist/utils/serialize-mcp-json.d.ts +2 -0
  81. package/dist/utils/serialize-mcp-json.js +99 -0
  82. package/dist/utils/serialize-middleware-groups-meta.d.ts +12 -0
  83. package/dist/utils/serialize-middleware-groups-meta.js +28 -0
  84. package/dist/utils/serialize-openapi-json.d.ts +85 -0
  85. package/dist/utils/serialize-openapi-json.js +151 -0
  86. package/dist/utils/serialize-permissions-groups-meta.d.ts +6 -0
  87. package/dist/utils/serialize-permissions-groups-meta.js +31 -0
  88. package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +34 -102
  89. package/dist/utils/workflow/dsl/extract-dsl-workflow.js +23 -4
  90. package/dist/utils/workflow/graph/convert-dsl-to-graph.js +12 -10
  91. package/dist/utils/workflow/graph/finalize-workflow-wires.d.ts +3 -0
  92. package/dist/utils/workflow/graph/finalize-workflow-wires.js +276 -0
  93. package/dist/utils/workflow/graph/finalize-workflows.d.ts +2 -0
  94. package/dist/utils/workflow/graph/finalize-workflows.js +75 -0
  95. package/dist/utils/workflow/graph/index.d.ts +2 -0
  96. package/dist/utils/workflow/graph/index.js +2 -0
  97. package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +0 -8
  98. package/dist/utils/workflow/graph/serialize-workflow-graph.js +1 -3
  99. package/dist/utils/workflow/graph/workflow-graph.types.d.ts +53 -79
  100. package/dist/utils/workflow/graph/workflow-graph.types.js +1 -1
  101. package/dist/visit.js +11 -6
  102. package/package.json +14 -4
  103. package/src/add/add-ai-agent.ts +468 -0
  104. package/src/add/add-channel.ts +82 -79
  105. package/src/add/add-cli.ts +49 -20
  106. package/src/add/add-file-with-factory.ts +2 -0
  107. package/src/add/add-functions.ts +330 -86
  108. package/src/add/add-http-route.ts +245 -88
  109. package/src/add/add-http-routes.ts +228 -0
  110. package/src/add/add-keyed-wiring.ts +151 -0
  111. package/src/add/add-mcp-prompt.ts +26 -15
  112. package/src/add/add-mcp-resource.ts +27 -15
  113. package/src/add/add-middleware.ts +482 -80
  114. package/src/add/add-permission.ts +199 -40
  115. package/src/add/add-queue-worker.ts +24 -19
  116. package/src/add/add-rpc-invocations.ts +17 -0
  117. package/src/add/add-schedule.ts +16 -11
  118. package/src/add/add-secret.ts +140 -0
  119. package/src/add/add-trigger.ts +154 -0
  120. package/src/add/add-variable.ts +9 -0
  121. package/src/add/add-workflow-graph.ts +180 -522
  122. package/src/add/add-workflow.ts +5 -4
  123. package/src/error-codes.ts +24 -1
  124. package/src/index.ts +22 -13
  125. package/src/inspector.ts +129 -17
  126. package/src/schema-generator.ts +1 -0
  127. package/src/types-map.ts +12 -1
  128. package/src/types.ts +175 -58
  129. package/src/utils/compute-required-schemas.ts +49 -0
  130. package/src/utils/contract-hashes.test.ts +528 -0
  131. package/src/utils/contract-hashes.ts +290 -0
  132. package/src/utils/custom-types-generator.ts +88 -0
  133. package/src/utils/detect-schema-vendor.ts +90 -0
  134. package/src/utils/ensure-function-metadata.ts +324 -7
  135. package/src/utils/extract-function-name.ts +101 -351
  136. package/src/utils/extract-services.ts +35 -2
  137. package/src/utils/filter-inspector-state.test.ts +34 -20
  138. package/src/utils/filter-inspector-state.ts +140 -31
  139. package/src/utils/get-property-value.ts +42 -4
  140. package/src/utils/hash.ts +26 -0
  141. package/src/utils/middleware.test.ts +204 -0
  142. package/src/utils/middleware.ts +129 -67
  143. package/src/utils/permissions.test.ts +35 -12
  144. package/src/utils/permissions.ts +10 -10
  145. package/src/utils/post-process.ts +283 -43
  146. package/src/utils/resolve-external-package.ts +42 -0
  147. package/src/utils/resolve-function-types.ts +42 -0
  148. package/src/utils/resolve-identifier.ts +46 -0
  149. package/src/utils/resolve-versions.test.ts +249 -0
  150. package/src/utils/resolve-versions.ts +105 -0
  151. package/src/utils/schema-generator.ts +329 -0
  152. package/src/utils/serialize-inspector-state.ts +163 -40
  153. package/src/utils/serialize-mcp-json.ts +145 -0
  154. package/src/utils/serialize-middleware-groups-meta.ts +33 -0
  155. package/src/utils/serialize-openapi-json.ts +277 -0
  156. package/src/utils/serialize-permissions-groups-meta.ts +35 -0
  157. package/src/utils/test-data/inspector-state.json +69 -66
  158. package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +43 -119
  159. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +24 -4
  160. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +17 -10
  161. package/src/utils/workflow/graph/finalize-workflow-wires.ts +310 -0
  162. package/src/utils/workflow/graph/finalize-workflows.ts +100 -0
  163. package/src/utils/workflow/graph/index.ts +5 -0
  164. package/src/utils/workflow/graph/serialize-workflow-graph.ts +1 -8
  165. package/src/utils/workflow/graph/workflow-graph.types.ts +29 -78
  166. package/src/visit.ts +12 -6
  167. package/tsconfig.tsbuildinfo +1 -1
  168. package/dist/add/add-forge-credential.d.ts +0 -8
  169. package/dist/add/add-forge-credential.js +0 -77
  170. package/dist/add/add-forge-node.d.ts +0 -7
  171. package/dist/add/add-forge-node.js +0 -77
  172. package/dist/add/add-mcp-tool.d.ts +0 -2
  173. package/dist/add/add-mcp-tool.js +0 -81
  174. package/dist/utils/extract-service-metadata.d.ts +0 -19
  175. package/dist/utils/extract-service-metadata.js +0 -244
  176. package/dist/utils/write-service-metadata.d.ts +0 -13
  177. package/dist/utils/write-service-metadata.js +0 -37
  178. package/src/add/add-forge-credential.ts +0 -119
  179. package/src/add/add-forge-node.ts +0 -132
  180. package/src/add/add-mcp-tool.ts +0 -141
  181. package/src/utils/extract-service-metadata.ts +0 -353
  182. package/src/utils/write-service-metadata.ts +0 -51
@@ -5,347 +5,112 @@ import { extractStringLiteral } from '../utils/extract-node-value.js'
5
5
  import type {
6
6
  SerializedWorkflowGraph,
7
7
  DataRef,
8
- WorkflowWiresConfig,
9
- HttpWire,
10
- ChannelWire,
11
- QueueWire,
12
- CliWire,
13
- McpWires,
14
- ScheduleWire,
15
- TriggerWire,
16
8
  } from '../utils/workflow/graph/workflow-graph.types.js'
17
9
 
18
- /**
19
- * Extract wire configuration from object literal
20
- */
21
- function extractWiresConfig(
22
- wiresNode: ts.ObjectLiteralExpression,
23
- checker: ts.TypeChecker
24
- ): WorkflowWiresConfig {
25
- const wires: WorkflowWiresConfig = {}
26
-
27
- for (const prop of wiresNode.properties) {
28
- if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name)) continue
29
-
30
- const propName = prop.name.text
31
-
32
- if (propName === 'http' && ts.isArrayLiteralExpression(prop.initializer)) {
33
- wires.http = []
34
- for (const elem of prop.initializer.elements) {
35
- if (ts.isObjectLiteralExpression(elem)) {
36
- const httpWire: Partial<HttpWire> = {}
37
- for (const httpProp of elem.properties) {
38
- if (
39
- !ts.isPropertyAssignment(httpProp) ||
40
- !ts.isIdentifier(httpProp.name)
41
- )
42
- continue
43
- const httpPropName = httpProp.name.text
44
- if (httpPropName === 'route') {
45
- httpWire.route = extractStringLiteral(
46
- httpProp.initializer,
47
- checker
48
- )
49
- } else if (httpPropName === 'method') {
50
- httpWire.method = extractStringLiteral(
51
- httpProp.initializer,
52
- checker
53
- ) as HttpWire['method']
54
- } else if (httpPropName === 'startNode') {
55
- httpWire.startNode = extractStringLiteral(
56
- httpProp.initializer,
57
- checker
58
- )
59
- }
60
- }
61
- if (httpWire.route && httpWire.method && httpWire.startNode) {
62
- wires.http.push(httpWire as HttpWire)
63
- }
64
- }
10
+ function extractAstValue(
11
+ expr: ts.Expression,
12
+ refParamName: string,
13
+ templateParamName: string | undefined
14
+ ): unknown {
15
+ if (ts.isStringLiteral(expr)) {
16
+ return expr.text
17
+ }
18
+ if (ts.isNumericLiteral(expr)) {
19
+ return Number(expr.text)
20
+ }
21
+ if (expr.kind === ts.SyntaxKind.TrueKeyword) {
22
+ return true
23
+ }
24
+ if (expr.kind === ts.SyntaxKind.FalseKeyword) {
25
+ return false
26
+ }
27
+ if (expr.kind === ts.SyntaxKind.NullKeyword) {
28
+ return null
29
+ }
30
+ if (ts.isCallExpression(expr)) {
31
+ const callee = expr.expression
32
+ if (ts.isIdentifier(callee)) {
33
+ if (callee.text === refParamName) {
34
+ const args = expr.arguments
35
+ const nodeId =
36
+ args[0] && ts.isStringLiteral(args[0]) ? args[0].text : 'unknown'
37
+ const path =
38
+ args[1] && ts.isStringLiteral(args[1]) ? args[1].text : undefined
39
+ return { $ref: nodeId, path } as DataRef
65
40
  }
66
- } else if (
67
- propName === 'channel' &&
68
- ts.isArrayLiteralExpression(prop.initializer)
69
- ) {
70
- wires.channel = []
71
- for (const elem of prop.initializer.elements) {
72
- if (ts.isObjectLiteralExpression(elem)) {
73
- const channelWire: Partial<ChannelWire> = {}
74
- for (const channelProp of elem.properties) {
75
- if (
76
- !ts.isPropertyAssignment(channelProp) ||
77
- !ts.isIdentifier(channelProp.name)
41
+ if (templateParamName && callee.text === templateParamName) {
42
+ const templateStr =
43
+ expr.arguments[0] && ts.isStringLiteral(expr.arguments[0])
44
+ ? expr.arguments[0].text
45
+ : ''
46
+ const refsArg = expr.arguments[1]
47
+ const refs: Array<{ $ref: string; path?: string }> = []
48
+ if (refsArg && ts.isArrayLiteralExpression(refsArg)) {
49
+ for (const el of refsArg.elements) {
50
+ const resolved = extractAstValue(
51
+ el,
52
+ refParamName,
53
+ templateParamName
78
54
  )
79
- continue
80
- const channelPropName = channelProp.name.text
81
- if (channelPropName === 'name') {
82
- channelWire.name = extractStringLiteral(
83
- channelProp.initializer,
84
- checker
85
- )
86
- } else if (channelPropName === 'onConnect') {
87
- channelWire.onConnect = extractStringLiteral(
88
- channelProp.initializer,
89
- checker
90
- )
91
- } else if (channelPropName === 'onDisconnect') {
92
- channelWire.onDisconnect = extractStringLiteral(
93
- channelProp.initializer,
94
- checker
95
- )
96
- } else if (channelPropName === 'onMessage') {
97
- channelWire.onMessage = extractStringLiteral(
98
- channelProp.initializer,
99
- checker
100
- )
101
- }
102
- }
103
- if (channelWire.name) {
104
- wires.channel.push(channelWire as ChannelWire)
105
- }
106
- }
107
- }
108
- } else if (
109
- propName === 'queue' &&
110
- ts.isArrayLiteralExpression(prop.initializer)
111
- ) {
112
- wires.queue = []
113
- for (const elem of prop.initializer.elements) {
114
- if (ts.isObjectLiteralExpression(elem)) {
115
- const queueWire: Partial<QueueWire> = {}
116
- for (const queueProp of elem.properties) {
117
55
  if (
118
- !ts.isPropertyAssignment(queueProp) ||
119
- !ts.isIdentifier(queueProp.name)
120
- )
121
- continue
122
- const queuePropName = queueProp.name.text
123
- if (queuePropName === 'name') {
124
- queueWire.name = extractStringLiteral(
125
- queueProp.initializer,
126
- checker
127
- )
128
- } else if (queuePropName === 'startNode') {
129
- queueWire.startNode = extractStringLiteral(
130
- queueProp.initializer,
131
- checker
132
- )
56
+ typeof resolved === 'object' &&
57
+ resolved !== null &&
58
+ '$ref' in resolved
59
+ ) {
60
+ refs.push(resolved as { $ref: string; path?: string })
133
61
  }
134
62
  }
135
- if (queueWire.name && queueWire.startNode) {
136
- wires.queue.push(queueWire as QueueWire)
137
- }
138
63
  }
139
- }
140
- } else if (
141
- propName === 'cli' &&
142
- ts.isArrayLiteralExpression(prop.initializer)
143
- ) {
144
- wires.cli = []
145
- for (const elem of prop.initializer.elements) {
146
- if (ts.isObjectLiteralExpression(elem)) {
147
- const cliWire: Partial<CliWire> = {}
148
- for (const cliProp of elem.properties) {
149
- if (
150
- !ts.isPropertyAssignment(cliProp) ||
151
- !ts.isIdentifier(cliProp.name)
152
- )
153
- continue
154
- const cliPropName = cliProp.name.text
155
- if (cliPropName === 'command') {
156
- cliWire.command = extractStringLiteral(
157
- cliProp.initializer,
158
- checker
159
- )
160
- } else if (cliPropName === 'startNode') {
161
- cliWire.startNode = extractStringLiteral(
162
- cliProp.initializer,
163
- checker
164
- )
165
- }
166
- }
167
- if (cliWire.command && cliWire.startNode) {
168
- wires.cli.push(cliWire as CliWire)
169
- }
170
- }
171
- }
172
- } else if (
173
- propName === 'mcp' &&
174
- ts.isObjectLiteralExpression(prop.initializer)
175
- ) {
176
- const mcpWires: McpWires = {}
177
- for (const mcpProp of prop.initializer.properties) {
178
- if (!ts.isPropertyAssignment(mcpProp) || !ts.isIdentifier(mcpProp.name))
179
- continue
180
- const mcpPropName = mcpProp.name.text
181
- if (
182
- mcpPropName === 'tool' &&
183
- ts.isArrayLiteralExpression(mcpProp.initializer)
184
- ) {
185
- mcpWires.tool = extractMcpToolWireArray(mcpProp.initializer, checker)
186
- } else if (
187
- mcpPropName === 'prompt' &&
188
- ts.isArrayLiteralExpression(mcpProp.initializer)
189
- ) {
190
- mcpWires.prompt = extractMcpToolWireArray(
191
- mcpProp.initializer,
192
- checker
193
- )
194
- } else if (
195
- mcpPropName === 'resource' &&
196
- ts.isArrayLiteralExpression(mcpProp.initializer)
197
- ) {
198
- mcpWires.resource = extractMcpResourceWireArray(
199
- mcpProp.initializer,
200
- checker
201
- )
202
- }
203
- }
204
- if (mcpWires.tool || mcpWires.prompt || mcpWires.resource) {
205
- wires.mcp = mcpWires
206
- }
207
- } else if (
208
- propName === 'schedule' &&
209
- ts.isArrayLiteralExpression(prop.initializer)
210
- ) {
211
- wires.schedule = []
212
- for (const elem of prop.initializer.elements) {
213
- if (ts.isObjectLiteralExpression(elem)) {
214
- const scheduleWire: Partial<ScheduleWire> = {}
215
- for (const scheduleProp of elem.properties) {
216
- if (
217
- !ts.isPropertyAssignment(scheduleProp) ||
218
- !ts.isIdentifier(scheduleProp.name)
219
- )
220
- continue
221
- const schedulePropName = scheduleProp.name.text
222
- if (schedulePropName === 'cron') {
223
- scheduleWire.cron = extractStringLiteral(
224
- scheduleProp.initializer,
225
- checker
226
- )
227
- } else if (schedulePropName === 'interval') {
228
- scheduleWire.interval = extractStringLiteral(
229
- scheduleProp.initializer,
230
- checker
231
- )
232
- } else if (schedulePropName === 'startNode') {
233
- scheduleWire.startNode = extractStringLiteral(
234
- scheduleProp.initializer,
235
- checker
236
- )
237
- }
238
- }
239
- if (
240
- (scheduleWire.cron || scheduleWire.interval) &&
241
- scheduleWire.startNode
242
- ) {
243
- wires.schedule.push(scheduleWire as ScheduleWire)
244
- }
245
- }
246
- }
247
- } else if (
248
- propName === 'trigger' &&
249
- ts.isArrayLiteralExpression(prop.initializer)
250
- ) {
251
- wires.trigger = []
252
- for (const elem of prop.initializer.elements) {
253
- if (ts.isObjectLiteralExpression(elem)) {
254
- const triggerWire: Partial<TriggerWire> = {}
255
- for (const triggerProp of elem.properties) {
256
- if (
257
- !ts.isPropertyAssignment(triggerProp) ||
258
- !ts.isIdentifier(triggerProp.name)
259
- )
260
- continue
261
- const triggerPropName = triggerProp.name.text
262
- if (triggerPropName === 'name') {
263
- triggerWire.name = extractStringLiteral(
264
- triggerProp.initializer,
265
- checker
266
- )
267
- } else if (triggerPropName === 'startNode') {
268
- triggerWire.startNode = extractStringLiteral(
269
- triggerProp.initializer,
270
- checker
271
- )
272
- }
273
- }
274
- if (triggerWire.name && triggerWire.startNode) {
275
- wires.trigger.push(triggerWire as TriggerWire)
276
- }
64
+ const parts: string[] = []
65
+ const expressions: Array<{ $ref: string; path?: string }> = []
66
+ const regex = /\$(\d+)/g
67
+ let lastIndex = 0
68
+ let match
69
+ while ((match = regex.exec(templateStr)) !== null) {
70
+ parts.push(templateStr.slice(lastIndex, match.index))
71
+ const refIndex = parseInt(match[1]!, 10)
72
+ expressions.push(refs[refIndex] ?? { $ref: 'unknown' })
73
+ lastIndex = regex.lastIndex
277
74
  }
75
+ parts.push(templateStr.slice(lastIndex))
76
+ return { $template: { parts, expressions } }
278
77
  }
279
78
  }
280
79
  }
281
-
282
- return wires
283
- }
284
-
285
- /**
286
- * Helper to extract MCP wire arrays for tool/prompt (name field)
287
- */
288
- function extractMcpToolWireArray(
289
- arrayNode: ts.ArrayLiteralExpression,
290
- checker: ts.TypeChecker
291
- ): Array<{ name: string; startNode: string }> {
292
- const result: Array<{ name: string; startNode: string }> = []
293
- for (const elem of arrayNode.elements) {
294
- if (ts.isObjectLiteralExpression(elem)) {
295
- let name: string | undefined
296
- let startNode: string | undefined
297
- for (const prop of elem.properties) {
298
- if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name))
299
- continue
300
- const propName = prop.name.text
301
- if (propName === 'name') {
302
- name = extractStringLiteral(prop.initializer, checker)
303
- } else if (propName === 'startNode') {
304
- startNode = extractStringLiteral(prop.initializer, checker)
305
- }
306
- }
307
- if (name && startNode) {
308
- result.push({ name, startNode })
309
- }
80
+ if (ts.isArrayLiteralExpression(expr)) {
81
+ return expr.elements.map((el) =>
82
+ extractAstValue(el, refParamName, templateParamName)
83
+ )
84
+ }
85
+ if (ts.isObjectLiteralExpression(expr)) {
86
+ const obj: Record<string, unknown> = {}
87
+ for (const prop of expr.properties) {
88
+ if (!ts.isPropertyAssignment(prop)) continue
89
+ const key = ts.isIdentifier(prop.name)
90
+ ? prop.name.text
91
+ : ts.isStringLiteral(prop.name)
92
+ ? prop.name.text
93
+ : null
94
+ if (!key) continue
95
+ obj[key] = extractAstValue(
96
+ prop.initializer,
97
+ refParamName,
98
+ templateParamName
99
+ )
310
100
  }
101
+ return obj
311
102
  }
312
- return result
313
- }
314
-
315
- /**
316
- * Helper to extract MCP wire arrays for resource (uri field)
317
- */
318
- function extractMcpResourceWireArray(
319
- arrayNode: ts.ArrayLiteralExpression,
320
- checker: ts.TypeChecker
321
- ): Array<{ uri: string; startNode: string }> {
322
- const result: Array<{ uri: string; startNode: string }> = []
323
- for (const elem of arrayNode.elements) {
324
- if (ts.isObjectLiteralExpression(elem)) {
325
- let uri: string | undefined
326
- let startNode: string | undefined
327
- for (const prop of elem.properties) {
328
- if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name))
329
- continue
330
- const propName = prop.name.text
331
- if (propName === 'uri') {
332
- uri = extractStringLiteral(prop.initializer, checker)
333
- } else if (propName === 'startNode') {
334
- startNode = extractStringLiteral(prop.initializer, checker)
335
- }
336
- }
337
- if (uri && startNode) {
338
- result.push({ uri, startNode })
339
- }
103
+ if (ts.isPrefixUnaryExpression(expr)) {
104
+ if (
105
+ expr.operator === ts.SyntaxKind.MinusToken &&
106
+ ts.isNumericLiteral(expr.operand)
107
+ ) {
108
+ return -Number(expr.operand.text)
340
109
  }
341
110
  }
342
- return result
111
+ return undefined
343
112
  }
344
113
 
345
- /**
346
- * Extract input mapping from an arrow function
347
- * Parses: (ref) => ({ key: ref('nodeId', 'path'), key2: 'literal' })
348
- */
349
114
  function extractInputMapping(
350
115
  node: ts.Node,
351
116
  _checker: ts.TypeChecker
@@ -381,6 +146,11 @@ function extractInputMapping(
381
146
  ? node.parameters[0].name.text
382
147
  : 'ref'
383
148
 
149
+ const templateParamName =
150
+ node.parameters.length > 1 && ts.isIdentifier(node.parameters[1].name)
151
+ ? node.parameters[1].name.text
152
+ : undefined
153
+
384
154
  const input: Record<string, unknown | DataRef> = {}
385
155
 
386
156
  for (const prop of bodyObj.properties) {
@@ -394,36 +164,13 @@ function extractInputMapping(
394
164
 
395
165
  if (!key) continue
396
166
 
397
- if (ts.isCallExpression(prop.initializer)) {
398
- const callExpr = prop.initializer.expression
399
- if (ts.isIdentifier(callExpr) && callExpr.text === refParamName) {
400
- const args = prop.initializer.arguments
401
- const nodeIdArg = args[0]
402
- const pathArg = args[1]
403
-
404
- const nodeId =
405
- nodeIdArg && ts.isStringLiteral(nodeIdArg)
406
- ? nodeIdArg.text
407
- : 'unknown'
408
- const path =
409
- pathArg && ts.isStringLiteral(pathArg) ? pathArg.text : undefined
410
-
411
- input[key] = { $ref: nodeId, path } as DataRef
412
- continue
413
- }
414
- }
415
-
416
- if (ts.isStringLiteral(prop.initializer)) {
417
- input[key] = prop.initializer.text
418
- } else if (ts.isNumericLiteral(prop.initializer)) {
419
- input[key] = Number(prop.initializer.text)
420
- } else if (
421
- prop.initializer.kind === ts.SyntaxKind.TrueKeyword ||
422
- prop.initializer.kind === ts.SyntaxKind.FalseKeyword
423
- ) {
424
- input[key] = prop.initializer.kind === ts.SyntaxKind.TrueKeyword
425
- } else if (prop.initializer.kind === ts.SyntaxKind.NullKeyword) {
426
- input[key] = null
167
+ const value = extractAstValue(
168
+ prop.initializer,
169
+ refParamName,
170
+ templateParamName
171
+ )
172
+ if (value !== undefined) {
173
+ input[key] = value
427
174
  }
428
175
  }
429
176
 
@@ -474,43 +221,6 @@ function extractNextConfig(
474
221
  return undefined
475
222
  }
476
223
 
477
- /**
478
- * Extract definition object from wireWorkflow call
479
- */
480
- function extractDefinitionObject(
481
- firstArg: ts.Node
482
- ): ts.ObjectLiteralExpression | undefined {
483
- if (ts.isObjectLiteralExpression(firstArg)) {
484
- return firstArg
485
- }
486
-
487
- if (ts.isArrowFunction(firstArg)) {
488
- const body = firstArg.body
489
-
490
- if (ts.isObjectLiteralExpression(body)) {
491
- return body
492
- }
493
-
494
- if (ts.isParenthesizedExpression(body)) {
495
- if (ts.isObjectLiteralExpression(body.expression)) {
496
- return body.expression
497
- }
498
- }
499
-
500
- if (ts.isBlock(body)) {
501
- for (const stmt of body.statements) {
502
- if (ts.isReturnStatement(stmt) && stmt.expression) {
503
- if (ts.isObjectLiteralExpression(stmt.expression)) {
504
- return stmt.expression
505
- }
506
- }
507
- }
508
- }
509
- }
510
-
511
- return undefined
512
- }
513
-
514
224
  /**
515
225
  * Compute entry node IDs from graph nodes
516
226
  */
@@ -544,98 +254,59 @@ interface PikkuWorkflowGraphExtract {
544
254
  name?: string
545
255
  description?: string
546
256
  tags?: string[]
547
- wires?: WorkflowWiresConfig
548
- nodesNode?: ts.ObjectLiteralExpression // The nodes: { entry: 'rpc1', ... } object
549
- configNode?: ts.ObjectLiteralExpression // The config: { entry: { next: ... }, ... } object
257
+ disabled?: true
258
+ nodesNode?: ts.ObjectLiteralExpression
259
+ configNode?: ts.ObjectLiteralExpression
550
260
  exportedName?: string
551
261
  }
552
262
 
553
263
  /**
554
- * Extract pikkuWorkflowGraph config from a variable reference or call expression
555
- * New format: pikkuWorkflowGraph({ nodes: {...}, wires: {...}, config: {...} })
264
+ * Extract pikkuWorkflowGraph config from an object literal argument
556
265
  */
557
- function extractPikkuWorkflowGraphConfig(
558
- node: ts.Node,
266
+ function extractWorkflowGraphConfig(
267
+ configArg: ts.ObjectLiteralExpression,
559
268
  checker: ts.TypeChecker
560
269
  ): PikkuWorkflowGraphExtract | undefined {
561
- // If it's an identifier, resolve to the declaration
562
- if (ts.isIdentifier(node)) {
563
- const symbol = checker.getSymbolAtLocation(node)
564
- if (symbol) {
565
- const declarations = symbol.getDeclarations()
566
- if (declarations && declarations.length > 0) {
567
- const decl = declarations[0]
568
- if (ts.isVariableDeclaration(decl) && decl.initializer) {
569
- const result = extractPikkuWorkflowGraphConfig(
570
- decl.initializer,
571
- checker
572
- )
573
- if (result) {
574
- // Use the variable name as exportedName
575
- result.exportedName = ts.isIdentifier(decl.name)
576
- ? decl.name.text
577
- : undefined
578
- }
579
- return result
580
- }
581
- }
582
- }
583
- return undefined
584
- }
585
-
586
- // If it's a call expression to pikkuWorkflowGraph
587
- if (ts.isCallExpression(node)) {
588
- const expr = node.expression
589
- if (ts.isIdentifier(expr) && expr.text === 'pikkuWorkflowGraph') {
590
- const configArg = node.arguments[0]
591
- if (configArg && ts.isObjectLiteralExpression(configArg)) {
592
- let name: string | undefined
593
- let description: string | undefined
594
- let tags: string[] | undefined
595
- let wires: WorkflowWiresConfig | undefined
596
- let nodesNode: ts.ObjectLiteralExpression | undefined
597
- let configNode: ts.ObjectLiteralExpression | undefined
598
-
599
- for (const prop of configArg.properties) {
600
- if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name))
601
- continue
602
-
603
- const propName = prop.name.text
604
- if (propName === 'name') {
605
- name = extractStringLiteral(prop.initializer, checker)
606
- } else if (propName === 'description') {
607
- description = extractStringLiteral(prop.initializer, checker)
608
- } else if (
609
- propName === 'tags' &&
610
- ts.isArrayLiteralExpression(prop.initializer)
611
- ) {
612
- tags = prop.initializer.elements
613
- .filter(ts.isStringLiteral)
614
- .map((el) => (el as ts.StringLiteral).text)
615
- } else if (
616
- propName === 'wires' &&
617
- ts.isObjectLiteralExpression(prop.initializer)
618
- ) {
619
- wires = extractWiresConfig(prop.initializer, checker)
620
- } else if (
621
- propName === 'nodes' &&
622
- ts.isObjectLiteralExpression(prop.initializer)
623
- ) {
624
- nodesNode = prop.initializer
625
- } else if (
626
- propName === 'config' &&
627
- ts.isObjectLiteralExpression(prop.initializer)
628
- ) {
629
- configNode = prop.initializer
630
- }
631
- }
270
+ let name: string | undefined
271
+ let description: string | undefined
272
+ let tags: string[] | undefined
273
+ let disabled: true | undefined
274
+ let nodesNode: ts.ObjectLiteralExpression | undefined
275
+ let configNode: ts.ObjectLiteralExpression | undefined
276
+
277
+ for (const prop of configArg.properties) {
278
+ if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name)) continue
632
279
 
633
- return { name, description, tags, wires, nodesNode, configNode }
280
+ const propName = prop.name.text
281
+ if (propName === 'name') {
282
+ name = extractStringLiteral(prop.initializer, checker)
283
+ } else if (propName === 'description') {
284
+ description = extractStringLiteral(prop.initializer, checker)
285
+ } else if (propName === 'disabled') {
286
+ if (prop.initializer.kind === ts.SyntaxKind.TrueKeyword) {
287
+ disabled = true
634
288
  }
289
+ } else if (
290
+ propName === 'tags' &&
291
+ ts.isArrayLiteralExpression(prop.initializer)
292
+ ) {
293
+ tags = prop.initializer.elements
294
+ .filter(ts.isStringLiteral)
295
+ .map((el) => (el as ts.StringLiteral).text)
296
+ } else if (
297
+ propName === 'nodes' &&
298
+ ts.isObjectLiteralExpression(prop.initializer)
299
+ ) {
300
+ nodesNode = prop.initializer
301
+ } else if (
302
+ propName === 'config' &&
303
+ ts.isObjectLiteralExpression(prop.initializer)
304
+ ) {
305
+ configNode = prop.initializer
635
306
  }
636
307
  }
637
308
 
638
- return undefined
309
+ return { name, description, tags, disabled, nodesNode, configNode }
639
310
  }
640
311
 
641
312
  /**
@@ -671,6 +342,10 @@ function extractGraphFromNewFormat(
671
342
  if (rpcName) {
672
343
  nodeRpcMap[nodeId] = rpcName
673
344
  state.rpc.invokedFunctions.add(rpcName)
345
+ const funcFile = state.functions.files.get(rpcName)
346
+ if (funcFile && !state.rpc.internalFiles.has(rpcName)) {
347
+ state.rpc.internalFiles.set(rpcName, funcFile)
348
+ }
674
349
  }
675
350
  }
676
351
 
@@ -748,8 +423,9 @@ function extractNodeConfigFromObject(
748
423
  }
749
424
 
750
425
  /**
751
- * Inspector for wireWorkflow() calls with graph definitions
752
- * Detects: wireWorkflow({ wires: {...}, graph: pikkuWorkflowGraphResult })
426
+ * Inspector for pikkuWorkflowGraph() calls
427
+ * Detects: pikkuWorkflowGraph({ nodes: {...}, config: {...} })
428
+ * or: export const x = pikkuWorkflowGraph({...}) where the call is found via variable declaration
753
429
  */
754
430
  export const addWorkflowGraph: AddWiring = (logger, node, checker, state) => {
755
431
  if (!ts.isCallExpression(node)) {
@@ -757,7 +433,10 @@ export const addWorkflowGraph: AddWiring = (logger, node, checker, state) => {
757
433
  }
758
434
 
759
435
  const expression = node.expression
760
- if (!ts.isIdentifier(expression) || expression.text !== 'wireWorkflow') {
436
+ if (
437
+ !ts.isIdentifier(expression) ||
438
+ expression.text !== 'pikkuWorkflowGraph'
439
+ ) {
761
440
  return
762
441
  }
763
442
 
@@ -765,67 +444,52 @@ export const addWorkflowGraph: AddWiring = (logger, node, checker, state) => {
765
444
  const firstArg = args[0]
766
445
 
767
446
  if (!firstArg) {
768
- logger.critical(ErrorCode.MISSING_FUNC, 'wireWorkflow requires an argument')
769
- return
770
- }
771
-
772
- const definitionObj = extractDefinitionObject(firstArg)
773
-
774
- if (!definitionObj) {
775
447
  logger.critical(
776
448
  ErrorCode.MISSING_FUNC,
777
- 'wireWorkflow requires an object argument'
449
+ 'pikkuWorkflowGraph requires an argument'
778
450
  )
779
451
  return
780
452
  }
781
453
 
782
- // Check if this is a graph workflow (has 'graph' property)
783
- let graphPropNode: ts.Node | undefined
784
- let enabled = true // Default to true
785
-
786
- for (const prop of definitionObj.properties) {
787
- if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name)) continue
788
-
789
- const propName = prop.name.text
790
- if (propName === 'graph') {
791
- graphPropNode = prop.initializer
792
- } else if (propName === 'enabled') {
793
- // Check if enabled is explicitly set to false
794
- if (prop.initializer.kind === ts.SyntaxKind.FalseKeyword) {
795
- enabled = false
796
- }
797
- }
798
- // Note: We no longer extract wires from wireWorkflow - they come from pikkuWorkflowGraph
799
- }
800
-
801
- // If no graph property, this is a DSL workflow - skip (handled by add-workflow.ts)
802
- if (!graphPropNode) {
454
+ if (!ts.isObjectLiteralExpression(firstArg)) {
455
+ logger.critical(
456
+ ErrorCode.MISSING_FUNC,
457
+ 'pikkuWorkflowGraph requires an object argument'
458
+ )
803
459
  return
804
460
  }
805
461
 
806
- // Extract config from the pikkuWorkflowGraph result
807
- const graphConfig = extractPikkuWorkflowGraphConfig(graphPropNode, checker)
462
+ const graphConfig = extractWorkflowGraphConfig(firstArg, checker)
808
463
 
809
464
  if (!graphConfig) {
810
465
  logger.critical(
811
466
  ErrorCode.MISSING_NAME,
812
- 'wireWorkflow with graph requires a pikkuWorkflowGraph'
467
+ 'pikkuWorkflowGraph: failed to extract config'
813
468
  )
814
469
  return
815
470
  }
816
471
 
817
- // Use explicit name or fall back to exported variable name
472
+ // Resolve exportedName from variable declaration if this is `export const x = pikkuWorkflowGraph({...})`
473
+ const parent = node.parent
474
+ if (
475
+ !graphConfig.exportedName &&
476
+ parent &&
477
+ ts.isVariableDeclaration(parent) &&
478
+ ts.isIdentifier(parent.name)
479
+ ) {
480
+ graphConfig.exportedName = parent.name.text
481
+ }
482
+
818
483
  const workflowName = graphConfig.name || graphConfig.exportedName
819
484
 
820
485
  if (!workflowName) {
821
486
  logger.critical(
822
487
  ErrorCode.MISSING_NAME,
823
- 'wireWorkflow with graph requires a pikkuWorkflowGraph with a name property or exported variable name'
488
+ 'pikkuWorkflowGraph requires a name property or exported variable name'
824
489
  )
825
490
  return
826
491
  }
827
492
 
828
- // Extract graph nodes from the new format (nodes + config properties)
829
493
  let graphNodes: Record<string, any> = {}
830
494
  if (graphConfig.nodesNode) {
831
495
  graphNodes = extractGraphFromNewFormat(
@@ -838,27 +502,21 @@ export const addWorkflowGraph: AddWiring = (logger, node, checker, state) => {
838
502
 
839
503
  const entryNodeIds = computeEntryNodeIds(graphNodes)
840
504
 
841
- // Use wires from pikkuWorkflowGraph (not from wireWorkflow)
842
- const wires = graphConfig.wires || {}
843
-
844
505
  const serialized: SerializedWorkflowGraph = {
845
506
  name: workflowName,
846
- pikkuFuncName: workflowName,
507
+ pikkuFuncId: workflowName,
847
508
  source: 'graph',
848
509
  description: graphConfig.description,
849
510
  tags: graphConfig.tags,
850
- wires,
851
511
  nodes: graphNodes,
852
512
  entryNodeIds,
853
513
  }
854
514
 
855
- // Only add if enabled (or not explicitly disabled)
856
- if (enabled) {
857
- state.workflows.graphMeta[workflowName] = serialized
858
- // Store file path and exported name for import generation
859
- state.workflows.graphFiles.set(workflowName, {
860
- path: node.getSourceFile().fileName,
861
- exportedName: graphConfig.exportedName || workflowName,
862
- })
863
- }
515
+ if (graphConfig.disabled) return
516
+
517
+ state.workflows.graphMeta[workflowName] = serialized
518
+ state.workflows.graphFiles.set(workflowName, {
519
+ path: node.getSourceFile().fileName,
520
+ exportedName: graphConfig.exportedName || workflowName,
521
+ })
864
522
  }