@pikku/inspector 0.11.1 → 0.11.2

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 (68) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/dist/add/add-forge-credential.d.ts +8 -0
  3. package/dist/add/add-forge-credential.js +77 -0
  4. package/dist/add/add-forge-node.d.ts +7 -0
  5. package/dist/add/add-forge-node.js +77 -0
  6. package/dist/add/add-functions.js +102 -9
  7. package/dist/add/add-http-route.js +24 -1
  8. package/dist/add/add-rpc-invocations.d.ts +3 -0
  9. package/dist/add/add-rpc-invocations.js +51 -25
  10. package/dist/add/add-workflow-graph.d.ts +6 -0
  11. package/dist/add/add-workflow-graph.js +659 -0
  12. package/dist/add/add-workflow.js +118 -22
  13. package/dist/error-codes.d.ts +3 -1
  14. package/dist/error-codes.js +3 -1
  15. package/dist/index.d.ts +3 -0
  16. package/dist/index.js +2 -0
  17. package/dist/inspector.js +19 -3
  18. package/dist/types.d.ts +26 -0
  19. package/dist/utils/extract-function-name.js +7 -7
  20. package/dist/utils/get-property-value.d.ts +2 -1
  21. package/dist/utils/get-property-value.js +6 -2
  22. package/dist/utils/serialize-inspector-state.d.ts +24 -1
  23. package/dist/utils/serialize-inspector-state.js +24 -0
  24. package/dist/utils/workflow/dsl/deserialize-dsl-workflow.d.ts +24 -0
  25. package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +898 -0
  26. package/dist/{workflow/extract-simple-workflow.d.ts → utils/workflow/dsl/extract-dsl-workflow.d.ts} +4 -2
  27. package/dist/{workflow/extract-simple-workflow.js → utils/workflow/dsl/extract-dsl-workflow.js} +549 -68
  28. package/dist/utils/workflow/dsl/index.d.ts +7 -0
  29. package/dist/utils/workflow/dsl/index.js +7 -0
  30. package/dist/{workflow → utils/workflow/dsl}/patterns.d.ts +21 -0
  31. package/dist/{workflow → utils/workflow/dsl}/patterns.js +90 -10
  32. package/dist/{workflow → utils/workflow/dsl}/validation.d.ts +2 -0
  33. package/dist/{workflow → utils/workflow/dsl}/validation.js +25 -7
  34. package/dist/utils/workflow/graph/convert-dsl-to-graph.d.ts +13 -0
  35. package/dist/utils/workflow/graph/convert-dsl-to-graph.js +316 -0
  36. package/dist/utils/workflow/graph/index.d.ts +6 -0
  37. package/dist/utils/workflow/graph/index.js +6 -0
  38. package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +43 -0
  39. package/dist/utils/workflow/graph/serialize-workflow-graph.js +152 -0
  40. package/dist/utils/workflow/graph/workflow-graph.types.d.ts +229 -0
  41. package/dist/utils/workflow/graph/workflow-graph.types.js +38 -0
  42. package/dist/visit.js +6 -0
  43. package/package.json +14 -2
  44. package/src/add/add-forge-credential.ts +119 -0
  45. package/src/add/add-forge-node.ts +132 -0
  46. package/src/add/add-functions.ts +129 -15
  47. package/src/add/add-http-route.ts +25 -1
  48. package/src/add/add-rpc-invocations.ts +61 -31
  49. package/src/add/add-workflow-graph.ts +864 -0
  50. package/src/add/add-workflow.ts +112 -26
  51. package/src/error-codes.ts +3 -1
  52. package/src/index.ts +10 -0
  53. package/src/inspector.ts +20 -4
  54. package/src/types.ts +25 -1
  55. package/src/utils/extract-function-name.ts +7 -7
  56. package/src/utils/get-property-value.ts +9 -2
  57. package/src/utils/serialize-inspector-state.ts +39 -1
  58. package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +1180 -0
  59. package/src/{workflow/extract-simple-workflow.ts → utils/workflow/dsl/extract-dsl-workflow.ts} +654 -81
  60. package/src/utils/workflow/dsl/index.ts +11 -0
  61. package/src/{workflow → utils/workflow/dsl}/patterns.ts +108 -11
  62. package/src/{workflow → utils/workflow/dsl}/validation.ts +34 -7
  63. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +415 -0
  64. package/src/utils/workflow/graph/index.ts +6 -0
  65. package/src/utils/workflow/graph/serialize-workflow-graph.ts +223 -0
  66. package/src/utils/workflow/graph/workflow-graph.types.ts +280 -0
  67. package/src/visit.ts +6 -0
  68. package/tsconfig.tsbuildinfo +1 -1
@@ -3,7 +3,7 @@ import { AddWiring, InspectorState } from '../types.js'
3
3
  import { extractFunctionName } from '../utils/extract-function-name.js'
4
4
  import { extractFunctionNode } from '../utils/extract-function-node.js'
5
5
  import { ErrorCode } from '../error-codes.js'
6
- import { WorkflowStepMeta } from '@pikku/core/workflow'
6
+ import { WorkflowStepMeta, WorkflowContext } from '@pikku/core/workflow'
7
7
  import {
8
8
  extractStringLiteral,
9
9
  isStringLike,
@@ -11,8 +11,62 @@ import {
11
11
  extractDescription,
12
12
  extractDuration,
13
13
  } from '../utils/extract-node-value.js'
14
- import { extractSimpleWorkflow } from '../workflow/extract-simple-workflow.js'
15
14
  import { getCommonWireMetaData } from '../utils/get-property-value.js'
15
+ import { extractDSLWorkflow } from '../utils/workflow/dsl/extract-dsl-workflow.js'
16
+
17
+ /**
18
+ * Recursively check if any step has inline type (non-serializable)
19
+ */
20
+ function hasInlineSteps(steps: WorkflowStepMeta[]): boolean {
21
+ for (const step of steps) {
22
+ if (step.type === 'inline') {
23
+ return true
24
+ } else if (step.type === 'branch') {
25
+ for (const branch of step.branches) {
26
+ if (hasInlineSteps(branch.steps)) return true
27
+ }
28
+ if (step.elseSteps && hasInlineSteps(step.elseSteps)) return true
29
+ } else if (step.type === 'switch' && step.cases) {
30
+ for (const c of step.cases) {
31
+ if (c.steps && hasInlineSteps(c.steps)) return true
32
+ }
33
+ if (step.defaultSteps && hasInlineSteps(step.defaultSteps)) return true
34
+ } else if (step.type === 'fanout' && step.child) {
35
+ if (hasInlineSteps([step.child])) return true
36
+ } else if (step.type === 'parallel' && step.children) {
37
+ if (hasInlineSteps(step.children)) return true
38
+ }
39
+ }
40
+ return false
41
+ }
42
+
43
+ /**
44
+ * Recursively collect all RPC names from workflow steps
45
+ */
46
+ function collectInvokedRPCs(
47
+ steps: WorkflowStepMeta[],
48
+ rpcs: Set<string>
49
+ ): void {
50
+ for (const step of steps) {
51
+ if (step.type === 'rpc' && step.rpcName) {
52
+ rpcs.add(step.rpcName)
53
+ } else if (step.type === 'branch') {
54
+ for (const branch of step.branches) {
55
+ collectInvokedRPCs(branch.steps, rpcs)
56
+ }
57
+ if (step.elseSteps) collectInvokedRPCs(step.elseSteps, rpcs)
58
+ } else if (step.type === 'switch' && step.cases) {
59
+ for (const c of step.cases) {
60
+ if (c.steps) collectInvokedRPCs(c.steps, rpcs)
61
+ }
62
+ if (step.defaultSteps) collectInvokedRPCs(step.defaultSteps, rpcs)
63
+ } else if (step.type === 'fanout' && step.child) {
64
+ collectInvokedRPCs([step.child], rpcs)
65
+ } else if (step.type === 'parallel' && step.children) {
66
+ collectInvokedRPCs(step.children, rpcs)
67
+ }
68
+ }
69
+ }
16
70
 
17
71
  /**
18
72
  * Scan for workflow.do(), workflow.sleep(), and workflow.cancel() calls to extract workflow steps
@@ -87,13 +141,10 @@ function getWorkflowInvocations(
87
141
  }
88
142
  }
89
143
 
90
- // Don't recurse into nested functions - only look at top-level workflow calls
144
+ // Recurse into children, including arrow functions (for Promise.all callbacks)
145
+ // but skip function declarations (which would be separate functions)
91
146
  ts.forEachChild(node, (child) => {
92
- if (
93
- ts.isFunctionDeclaration(child) ||
94
- ts.isFunctionExpression(child) ||
95
- ts.isArrowFunction(child)
96
- ) {
147
+ if (ts.isFunctionDeclaration(child)) {
97
148
  return
98
149
  }
99
150
  getWorkflowInvocations(child, checker, state, workflowName, steps)
@@ -117,11 +168,11 @@ export const addWorkflow: AddWiring = (logger, node, checker, state) => {
117
168
  return
118
169
  }
119
170
 
120
- let wrapperType: 'simple' | 'regular' | null = null
171
+ let wrapperType: 'dsl' | 'regular' | null = null
121
172
  if (expression.text === 'pikkuWorkflowFunc') {
173
+ wrapperType = 'dsl'
174
+ } else if (expression.text === 'pikkuWorkflowComplexFunc') {
122
175
  wrapperType = 'regular'
123
- } else if (expression.text === 'pikkuSimpleWorkflowFunc') {
124
- wrapperType = 'simple'
125
176
  } else {
126
177
  return
127
178
  }
@@ -198,41 +249,76 @@ export const addWorkflow: AddWiring = (logger, node, checker, state) => {
198
249
  }
199
250
 
200
251
  let steps: WorkflowStepMeta[] = []
201
- let simple: boolean | undefined = undefined
252
+ let context: WorkflowContext | undefined = undefined
253
+ let dsl: boolean | undefined = undefined
202
254
 
203
- // Try simple workflow extraction first
255
+ // Try DSL workflow extraction first
204
256
  // Pass the whole CallExpression node so findWorkflowFunction can find the arrow function
205
- const result = extractSimpleWorkflow(node, checker)
257
+ const result = extractDSLWorkflow(node, checker)
206
258
 
207
259
  if (result.status === 'ok' && result.steps) {
208
- // Simple extraction succeeded
260
+ // Extraction succeeded
209
261
  steps = result.steps
210
- simple = true
262
+ context = result.context
263
+
264
+ // Check if workflow contains inline steps (non-serializable)
265
+ if (hasInlineSteps(steps)) {
266
+ if (wrapperType === 'dsl') {
267
+ // pikkuWorkflowFunc should not have inline steps
268
+ logger.critical(
269
+ ErrorCode.INVALID_DSL_WORKFLOW,
270
+ `Workflow '${workflowName}' uses pikkuWorkflowFunc but contains inline steps which are not allowed in DSL workflows. Use pikkuWorkflowComplexFunc instead.`
271
+ )
272
+ return
273
+ }
274
+ // pikkuWorkflowComplexFunc with inline steps is marked as non-dsl
275
+ dsl = false
276
+ } else {
277
+ // pikkuWorkflowComplexFunc is always non-dsl, pikkuWorkflowFunc is dsl
278
+ dsl = wrapperType === 'dsl'
279
+ }
280
+
281
+ // Collect all invoked RPCs from workflow steps
282
+ const rpcs = new Set<string>()
283
+ collectInvokedRPCs(steps, rpcs)
284
+ for (const rpc of rpcs) {
285
+ state.rpc.invokedFunctions.add(rpc)
286
+ }
211
287
  } else {
212
- // Simple extraction failed
213
- if (wrapperType === 'simple') {
214
- // For pikkuSimpleWorkflowFunc, this is a critical error
288
+ // DSL extraction failed
289
+ if (wrapperType === 'dsl') {
290
+ // For pikkuWorkflowFunc, this is a critical error
291
+ // But still track RPC invocations for function registration
292
+ getWorkflowInvocations(resolvedFunc, checker, state, workflowName, steps)
215
293
  logger.critical(
216
- ErrorCode.INVALID_SIMPLE_WORKFLOW,
217
- `Workflow '${workflowName}' uses pikkuSimpleWorkflowFunc but does not conform to simple workflow DSL:\n${result.reason || 'Unknown error'}`
294
+ ErrorCode.INVALID_DSL_WORKFLOW,
295
+ `Workflow '${workflowName}' uses pikkuWorkflowFunc but does not conform to DSL workflow rules:\n${result.reason || 'Unknown error'}`
218
296
  )
219
297
  return
220
298
  } else {
221
- // For pikkuWorkflowFunc, fall back to basic extraction
299
+ // For pikkuWorkflowComplexFunc, fall back to basic extraction
222
300
  logger.debug(
223
- `Workflow '${workflowName}' could not be extracted as simple workflow: ${result.reason || 'Unknown error'}. Falling back to basic extraction.`
301
+ `Workflow '${workflowName}' could not be extracted as DSL workflow: ${result.reason || 'Unknown error'}. Falling back to basic extraction.`
224
302
  )
225
- simple = false
303
+ dsl = false
226
304
  }
227
305
  }
228
306
 
229
- getWorkflowInvocations(resolvedFunc, checker, state, workflowName, steps)
307
+ /**
308
+ * For non-dsl workflows or pikkuWorkflowComplexFunc, run basic extraction
309
+ * to ensure all RPC invocations are tracked for function registration.
310
+ * This catches RPCs in Promise.all callbacks and other patterns DSL can't extract.
311
+ */
312
+ if (!dsl || wrapperType === 'regular') {
313
+ getWorkflowInvocations(resolvedFunc, checker, state, workflowName, steps)
314
+ }
230
315
 
231
316
  state.workflows.meta[workflowName] = {
232
317
  pikkuFuncName,
233
318
  workflowName,
234
319
  steps,
235
- simple,
320
+ context,
321
+ dsl,
236
322
  summary,
237
323
  description,
238
324
  errors,
@@ -11,6 +11,7 @@ export enum ErrorCode {
11
11
  // Validation errors
12
12
  MISSING_NAME = 'PKU111',
13
13
  MISSING_DESCRIPTION = 'PKU123',
14
+ INVALID_VALUE = 'PKU124',
14
15
  MISSING_URI = 'PKU220',
15
16
  MISSING_FUNC = 'PKU236',
16
17
  INVALID_TAGS_TYPE = 'PKU247',
@@ -21,7 +22,7 @@ export enum ErrorCode {
21
22
  CLI_CLIENTSIDE_RENDERER_HAS_SERVICES = 'PKU672',
22
23
  DYNAMIC_STEP_NAME = 'PKU529',
23
24
  WORKFLOW_ORCHESTRATOR_NOT_CONFIGURED = 'PKU600',
24
- INVALID_SIMPLE_WORKFLOW = 'PKU641',
25
+ INVALID_DSL_WORKFLOW = 'PKU641',
25
26
 
26
27
  // Configuration errors
27
28
  CONFIG_TYPE_NOT_FOUND = 'PKU426',
@@ -29,6 +30,7 @@ export enum ErrorCode {
29
30
  SCHEMA_NO_ROOT = 'PKU431',
30
31
  SCHEMA_GENERATION_ERROR = 'PKU456',
31
32
  SCHEMA_LOAD_ERROR = 'PKU488',
33
+ INLINE_ZOD_SCHEMA = 'PKU489',
32
34
 
33
35
  // Function errors
34
36
  FUNCTION_METADATA_NOT_FOUND = 'PKU559',
package/src/index.ts CHANGED
@@ -16,3 +16,13 @@ export type { SerializableInspectorState } from './utils/serialize-inspector-sta
16
16
  export { filterInspectorState } from './utils/filter-inspector-state.js'
17
17
  export { writeAllServiceMetadata } from './utils/write-service-metadata.js'
18
18
  export type { ServiceMetadata } from './utils/extract-service-metadata.js'
19
+ export {
20
+ convertAllDslToGraphs,
21
+ convertDslToGraph,
22
+ } from './utils/workflow/graph/convert-dsl-to-graph.js'
23
+ export {
24
+ deserializeDslWorkflow,
25
+ deserializeGraphWorkflow,
26
+ deserializeAllDslWorkflows,
27
+ } from './utils/workflow/dsl/deserialize-dsl-workflow.js'
28
+ export type { SerializedWorkflowGraph } from './utils/workflow/graph/workflow-graph.types.js'
package/src/inspector.ts CHANGED
@@ -29,6 +29,7 @@ export function getInitialInspectorState(rootDir: string): InspectorState {
29
29
  filesAndMethods: {},
30
30
  filesAndMethodsErrors: new Map(),
31
31
  typesLookup: new Map(),
32
+ zodLookup: new Map(),
32
33
  functions: {
33
34
  typesMap: new TypesMap(),
34
35
  meta: {},
@@ -64,6 +65,8 @@ export function getInitialInspectorState(rootDir: string): InspectorState {
64
65
  workflows: {
65
66
  meta: {},
66
67
  files: new Map(),
68
+ graphMeta: {},
69
+ graphFiles: new Map(),
67
70
  },
68
71
  rpc: {
69
72
  internalMeta: {},
@@ -71,6 +74,7 @@ export function getInitialInspectorState(rootDir: string): InspectorState {
71
74
  exposedMeta: {},
72
75
  exposedFiles: new Map(),
73
76
  invokedFunctions: new Set(),
77
+ usedExternalPackages: new Set(),
74
78
  },
75
79
  mcpEndpoints: {
76
80
  resourcesMeta: {},
@@ -85,6 +89,14 @@ export function getInitialInspectorState(rootDir: string): InspectorState {
85
89
  },
86
90
  files: new Set(),
87
91
  },
92
+ forgeNodes: {
93
+ meta: {},
94
+ files: new Set(),
95
+ },
96
+ forgeCredentials: {
97
+ meta: {},
98
+ files: new Set(),
99
+ },
88
100
  middleware: {
89
101
  meta: {},
90
102
  tagMiddleware: new Map(),
@@ -131,15 +143,19 @@ export const inspect = (
131
143
  `Got type checker in ${(performance.now() - startChecker).toFixed(2)}ms`
132
144
  )
133
145
 
146
+ // Use provided rootDir or infer from source files
147
+ const rootDir = options.rootDir || findCommonAncestor(routeFiles)
148
+
134
149
  const startSourceFiles = performance.now()
135
- const sourceFiles = program.getSourceFiles()
150
+ // Filter source files to only include files within the project rootDir
151
+ // This prevents picking up types from external packages (including workspace symlinks)
152
+ const sourceFiles = program
153
+ .getSourceFiles()
154
+ .filter((sf) => sf.fileName.startsWith(rootDir))
136
155
  logger.debug(
137
156
  `Got source files in ${(performance.now() - startSourceFiles).toFixed(2)}ms`
138
157
  )
139
158
 
140
- // Infer root directory from source files
141
- const rootDir = findCommonAncestor(routeFiles)
142
-
143
159
  const state = getInitialInspectorState(rootDir)
144
160
 
145
161
  // First sweep: add all functions
package/src/types.ts CHANGED
@@ -6,9 +6,11 @@ import { QueueWorkersMeta } from '@pikku/core/queue'
6
6
  import { WorkflowsMeta } from '@pikku/core/workflow'
7
7
  import { MCPResourceMeta, MCPToolMeta, MCPPromptMeta } from '@pikku/core/mcp'
8
8
  import { CLIMeta } from '@pikku/core/cli'
9
+ import { ForgeNodesMeta, ForgeCredentialsMeta } from '@pikku/core/forge-node'
9
10
  import { TypesMap } from './types-map.js'
10
11
  import { FunctionsMeta, FunctionServicesMeta } from '@pikku/core'
11
12
  import { ErrorCode } from './error-codes.js'
13
+ import type { SerializedWorkflowGraphs } from './utils/workflow/graph/workflow-graph.types.js'
12
14
 
13
15
  export type PathToNameAndType = Map<
14
16
  string,
@@ -56,6 +58,14 @@ export interface InspectorHTTPState {
56
58
  routePermissions: Map<string, PermissionGroupMeta>
57
59
  }
58
60
 
61
+ /**
62
+ * Zod schema reference for deferred conversion to JSON Schema at build time
63
+ */
64
+ export interface ZodSchemaRef {
65
+ variableName: string
66
+ sourceFile: string
67
+ }
68
+
59
69
  export interface InspectorFunctionState {
60
70
  typesMap: TypesMap
61
71
  meta: FunctionsMeta
@@ -116,6 +126,8 @@ export type InspectorFilters = {
116
126
 
117
127
  export type InspectorOptions = Partial<{
118
128
  setupOnly: boolean
129
+ /** Project root directory - used to filter out external package files */
130
+ rootDir: string
119
131
  types: Partial<{
120
132
  configFileType: string
121
133
  userSessionType: string
@@ -198,6 +210,7 @@ export interface InspectorState {
198
210
  filesAndMethods: InspectorFilesAndMethods
199
211
  filesAndMethodsErrors: Map<string, PathToNameAndType>
200
212
  typesLookup: Map<string, ts.Type[]> // Lookup for types by name (e.g., function input types, Config type)
213
+ zodLookup: Map<string, ZodSchemaRef> // Lookup for Zod schemas by name for deferred JSON Schema conversion
201
214
  http: InspectorHTTPState
202
215
  functions: InspectorFunctionState
203
216
  channels: InspectorChannelState
@@ -212,13 +225,16 @@ export interface InspectorState {
212
225
  workflows: {
213
226
  meta: WorkflowsMeta
214
227
  files: Map<string, { path: string; exportedName: string }>
228
+ graphMeta: SerializedWorkflowGraphs
229
+ graphFiles: Map<string, { path: string; exportedName: string }>
215
230
  }
216
231
  rpc: {
217
232
  internalMeta: Record<string, string>
218
233
  internalFiles: Map<string, { path: string; exportedName: string }>
219
234
  exposedMeta: Record<string, string>
220
235
  exposedFiles: Map<string, { path: string; exportedName: string }>
221
- invokedFunctions: Set<string> // Track functions called via rpc.invoke()
236
+ invokedFunctions: Set<string>
237
+ usedExternalPackages: Set<string>
222
238
  }
223
239
  mcpEndpoints: {
224
240
  resourcesMeta: MCPResourceMeta
@@ -230,6 +246,14 @@ export interface InspectorState {
230
246
  meta: CLIMeta
231
247
  files: Set<string>
232
248
  }
249
+ forgeNodes: {
250
+ meta: ForgeNodesMeta
251
+ files: Set<string>
252
+ }
253
+ forgeCredentials: {
254
+ meta: ForgeCredentialsMeta
255
+ files: Set<string>
256
+ }
233
257
  middleware: InspectorMiddlewareState
234
258
  permissions: InspectorPermissionState
235
259
  serviceAggregation: {
@@ -360,10 +360,10 @@ export function extractFunctionName(
360
360
  if (
361
361
  ts.isPropertyAssignment(prop) &&
362
362
  ts.isIdentifier(prop.name) &&
363
- prop.name.text === 'name' &&
363
+ prop.name.text === 'override' &&
364
364
  ts.isStringLiteral(prop.initializer)
365
365
  ) {
366
- // Priority 1: Object with name property
366
+ // Priority 1: Object with override property
367
367
  result.explicitName = prop.initializer.text
368
368
  break
369
369
  }
@@ -614,17 +614,17 @@ export function extractFunctionName(
614
614
  // instead of the variable declaration position
615
615
  originalCallExpr = decl.initializer
616
616
 
617
- // Check for object with 'name' property in first argument
617
+ // Check for object with 'override' property in first argument
618
618
  const firstArg = decl.initializer.arguments[0]
619
619
  if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
620
620
  for (const prop of firstArg.properties) {
621
621
  if (
622
622
  ts.isPropertyAssignment(prop) &&
623
623
  ts.isIdentifier(prop.name) &&
624
- prop.name.text === 'name' &&
624
+ prop.name.text === 'override' &&
625
625
  ts.isStringLiteral(prop.initializer)
626
626
  ) {
627
- // Priority 1: Object with name property
627
+ // Priority 1: Object with override property
628
628
  result.explicitName = prop.initializer.text
629
629
  break
630
630
  }
@@ -713,7 +713,7 @@ export function extractFunctionName(
713
713
  ) {
714
714
  result.propertyName = parent.name.text
715
715
  }
716
- // 3) Handle any remaining cases for pikkuFunc({ name: '…', func: … })
716
+ // 3) Handle any remaining cases for pikkuFunc({ override: '…', func: … })
717
717
  else if (ts.isCallExpression(originalCallExpr)) {
718
718
  const firstArg = originalCallExpr.arguments[0]
719
719
  if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
@@ -721,7 +721,7 @@ export function extractFunctionName(
721
721
  if (
722
722
  ts.isPropertyAssignment(prop) &&
723
723
  ts.isIdentifier(prop.name) &&
724
- prop.name.text === 'name' &&
724
+ prop.name.text === 'override' &&
725
725
  ts.isStringLiteral(prop.initializer) &&
726
726
  !result.explicitName // Only set if not already set
727
727
  ) {
@@ -57,7 +57,7 @@ export const getPropertyValue = (
57
57
  }
58
58
 
59
59
  /**
60
- * Extracts common wire metadata (tags, summary, description, errors) directly from an object
60
+ * Extracts common wire metadata (title, tags, summary, description, errors) directly from an object
61
61
  * @param obj - The TypeScript object literal expression to extract metadata from
62
62
  * @param wiringType - The type of wiring (e.g., 'HTTP route', 'Channel', 'Queue worker')
63
63
  * @param wiringName - The name/identifier of the wiring (e.g., route path, channel name)
@@ -70,12 +70,14 @@ export const getCommonWireMetaData = (
70
70
  wiringName: string | null,
71
71
  logger?: { critical: (code: ErrorCode, message: string) => void }
72
72
  ): {
73
+ title?: string
73
74
  tags?: string[]
74
75
  summary?: string
75
76
  description?: string
76
77
  errors?: string[]
77
78
  } => {
78
79
  const metadata: {
80
+ title?: string
79
81
  tags?: string[]
80
82
  summary?: string
81
83
  description?: string
@@ -86,7 +88,12 @@ export const getCommonWireMetaData = (
86
88
  if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
87
89
  const propName = prop.name.text
88
90
 
89
- if (propName === 'summary' && ts.isStringLiteral(prop.initializer)) {
91
+ if (propName === 'title' && ts.isStringLiteral(prop.initializer)) {
92
+ metadata.title = prop.initializer.text
93
+ } else if (
94
+ propName === 'summary' &&
95
+ ts.isStringLiteral(prop.initializer)
96
+ ) {
90
97
  metadata.summary = prop.initializer.text
91
98
  } else if (
92
99
  propName === 'description' &&
@@ -62,6 +62,7 @@ export interface SerializableInspectorState {
62
62
  >,
63
63
  ]
64
64
  >
65
+ zodLookup: Array<[string, { variableName: string; sourceFile: string }]>
65
66
  functions: {
66
67
  typesMap: {
67
68
  map: Array<[string, { originalName: string; path: string | null }]>
@@ -115,6 +116,8 @@ export interface SerializableInspectorState {
115
116
  workflows: {
116
117
  meta: InspectorState['workflows']['meta']
117
118
  files: Array<[string, { path: string; exportedName: string }]>
119
+ graphMeta: InspectorState['workflows']['graphMeta']
120
+ graphFiles: Array<[string, { path: string; exportedName: string }]>
118
121
  }
119
122
  rpc: {
120
123
  internalMeta: InspectorState['rpc']['internalMeta']
@@ -122,6 +125,7 @@ export interface SerializableInspectorState {
122
125
  exposedMeta: InspectorState['rpc']['exposedMeta']
123
126
  exposedFiles: Array<[string, { path: string; exportedName: string }]>
124
127
  invokedFunctions: string[]
128
+ usedExternalPackages: string[]
125
129
  }
126
130
  mcpEndpoints: {
127
131
  resourcesMeta: InspectorState['mcpEndpoints']['resourcesMeta']
@@ -133,6 +137,14 @@ export interface SerializableInspectorState {
133
137
  meta: InspectorState['cli']['meta']
134
138
  files: string[]
135
139
  }
140
+ forgeNodes: {
141
+ meta: InspectorState['forgeNodes']['meta']
142
+ files: string[]
143
+ }
144
+ forgeCredentials: {
145
+ meta: InspectorState['forgeCredentials']['meta']
146
+ files: string[]
147
+ }
136
148
  middleware: {
137
149
  meta: InspectorState['middleware']['meta']
138
150
  tagMiddleware: Array<
@@ -231,6 +243,7 @@ export function serializeInspectorState(
231
243
  filesAndMethodsErrors: Array.from(
232
244
  state.filesAndMethodsErrors.entries()
233
245
  ).map(([key, mapValue]) => [key, Array.from(mapValue.entries())] as const),
246
+ zodLookup: Array.from(state.zodLookup.entries()),
234
247
  functions: {
235
248
  typesMap: serializeTypesMap(state.functions.typesMap),
236
249
  meta: state.functions.meta,
@@ -258,6 +271,8 @@ export function serializeInspectorState(
258
271
  workflows: {
259
272
  meta: state.workflows.meta,
260
273
  files: Array.from(state.workflows.files.entries()),
274
+ graphMeta: state.workflows.graphMeta,
275
+ graphFiles: Array.from(state.workflows.graphFiles.entries()),
261
276
  },
262
277
  rpc: {
263
278
  internalMeta: state.rpc.internalMeta,
@@ -265,6 +280,7 @@ export function serializeInspectorState(
265
280
  exposedMeta: state.rpc.exposedMeta,
266
281
  exposedFiles: Array.from(state.rpc.exposedFiles.entries()),
267
282
  invokedFunctions: Array.from(state.rpc.invokedFunctions),
283
+ usedExternalPackages: Array.from(state.rpc.usedExternalPackages),
268
284
  },
269
285
  mcpEndpoints: {
270
286
  resourcesMeta: state.mcpEndpoints.resourcesMeta,
@@ -276,6 +292,14 @@ export function serializeInspectorState(
276
292
  meta: state.cli.meta,
277
293
  files: Array.from(state.cli.files),
278
294
  },
295
+ forgeNodes: {
296
+ meta: state.forgeNodes.meta,
297
+ files: Array.from(state.forgeNodes.files),
298
+ },
299
+ forgeCredentials: {
300
+ meta: state.forgeCredentials.meta,
301
+ files: Array.from(state.forgeCredentials.files),
302
+ },
279
303
  middleware: {
280
304
  meta: state.middleware.meta,
281
305
  tagMiddleware: Array.from(state.middleware.tagMiddleware.entries()),
@@ -302,7 +326,9 @@ export function serializeInspectorState(
302
326
  */
303
327
  export function deserializeInspectorState(
304
328
  data: SerializableInspectorState
305
- ): Omit<InspectorState, 'typesLookup'> {
329
+ ): Omit<InspectorState, 'typesLookup' | 'zodLookup'> & {
330
+ zodLookup: Map<string, { variableName: string; sourceFile: string }>
331
+ } {
306
332
  // Helper to deserialize TypesMap
307
333
  const deserializeTypesMap = (
308
334
  serialized: SerializableInspectorState['functions']['typesMap']
@@ -337,6 +363,7 @@ export function deserializeInspectorState(
337
363
  new Map(entries),
338
364
  ])
339
365
  ),
366
+ zodLookup: new Map(data.zodLookup || []),
340
367
  functions: {
341
368
  typesMap: deserializeTypesMap(data.functions.typesMap),
342
369
  meta: data.functions.meta,
@@ -364,6 +391,8 @@ export function deserializeInspectorState(
364
391
  workflows: {
365
392
  meta: data.workflows.meta,
366
393
  files: new Map(data.workflows.files),
394
+ graphMeta: data.workflows.graphMeta || {},
395
+ graphFiles: new Map(data.workflows.graphFiles || []),
367
396
  },
368
397
  rpc: {
369
398
  internalMeta: data.rpc.internalMeta,
@@ -371,6 +400,7 @@ export function deserializeInspectorState(
371
400
  exposedMeta: data.rpc.exposedMeta,
372
401
  exposedFiles: new Map(data.rpc.exposedFiles),
373
402
  invokedFunctions: new Set(data.rpc.invokedFunctions),
403
+ usedExternalPackages: new Set(data.rpc.usedExternalPackages || []),
374
404
  },
375
405
  mcpEndpoints: {
376
406
  resourcesMeta: data.mcpEndpoints.resourcesMeta,
@@ -382,6 +412,14 @@ export function deserializeInspectorState(
382
412
  meta: data.cli.meta,
383
413
  files: new Set(data.cli.files),
384
414
  },
415
+ forgeNodes: {
416
+ meta: data.forgeNodes?.meta || {},
417
+ files: new Set(data.forgeNodes?.files || []),
418
+ },
419
+ forgeCredentials: {
420
+ meta: data.forgeCredentials?.meta || {},
421
+ files: new Set(data.forgeCredentials?.files || []),
422
+ },
385
423
  middleware: {
386
424
  meta: data.middleware.meta,
387
425
  tagMiddleware: new Map(data.middleware.tagMiddleware),