@pikku/inspector 0.11.0 → 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.
- package/CHANGELOG.md +32 -2
- package/dist/add/add-channel.js +11 -10
- package/dist/add/add-file-with-factory.js +10 -10
- package/dist/add/add-forge-credential.d.ts +8 -0
- package/dist/add/add-forge-credential.js +77 -0
- package/dist/add/add-forge-node.d.ts +7 -0
- package/dist/add/add-forge-node.js +77 -0
- package/dist/add/add-functions.js +158 -51
- package/dist/add/add-http-route.js +28 -4
- package/dist/add/add-mcp-prompt.js +6 -5
- package/dist/add/add-mcp-resource.js +6 -5
- package/dist/add/add-mcp-tool.js +6 -5
- package/dist/add/add-middleware.js +1 -1
- package/dist/add/add-permission.js +1 -1
- package/dist/add/add-queue-worker.js +6 -5
- package/dist/add/add-rpc-invocations.d.ts +3 -0
- package/dist/add/add-rpc-invocations.js +51 -25
- package/dist/add/add-schedule.js +5 -4
- package/dist/add/add-workflow-graph.d.ts +6 -0
- package/dist/add/add-workflow-graph.js +659 -0
- package/dist/add/add-workflow.d.ts +1 -1
- package/dist/add/add-workflow.js +191 -69
- package/dist/error-codes.d.ts +3 -0
- package/dist/error-codes.js +3 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +3 -0
- package/dist/inspector.js +29 -9
- package/dist/types.d.ts +47 -8
- package/dist/utils/extract-function-name.js +7 -7
- package/dist/utils/extract-function-node.d.ts +10 -0
- package/dist/utils/extract-function-node.js +38 -0
- package/dist/utils/extract-node-value.d.ts +8 -0
- package/dist/utils/extract-node-value.js +24 -0
- package/dist/utils/extract-service-metadata.d.ts +19 -0
- package/dist/utils/extract-service-metadata.js +244 -0
- package/dist/utils/get-files-and-methods.d.ts +3 -3
- package/dist/utils/get-files-and-methods.js +3 -3
- package/dist/utils/get-property-value.d.ts +14 -6
- package/dist/utils/get-property-value.js +55 -43
- package/dist/utils/post-process.d.ts +9 -0
- package/dist/utils/post-process.js +30 -3
- package/dist/utils/serialize-inspector-state.d.ts +42 -6
- package/dist/utils/serialize-inspector-state.js +36 -10
- package/dist/utils/workflow/dsl/deserialize-dsl-workflow.d.ts +24 -0
- package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +898 -0
- package/dist/utils/workflow/dsl/extract-dsl-workflow.d.ts +17 -0
- package/dist/utils/workflow/dsl/extract-dsl-workflow.js +1284 -0
- package/dist/utils/workflow/dsl/index.d.ts +7 -0
- package/dist/utils/workflow/dsl/index.js +7 -0
- package/dist/utils/workflow/dsl/patterns.d.ts +60 -0
- package/dist/utils/workflow/dsl/patterns.js +218 -0
- package/dist/utils/workflow/dsl/validation.d.ts +30 -0
- package/dist/utils/workflow/dsl/validation.js +142 -0
- package/dist/utils/workflow/graph/convert-dsl-to-graph.d.ts +13 -0
- package/dist/utils/workflow/graph/convert-dsl-to-graph.js +316 -0
- package/dist/utils/workflow/graph/index.d.ts +6 -0
- package/dist/utils/workflow/graph/index.js +6 -0
- package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +43 -0
- package/dist/utils/workflow/graph/serialize-workflow-graph.js +152 -0
- package/dist/utils/workflow/graph/workflow-graph.types.d.ts +229 -0
- package/dist/utils/workflow/graph/workflow-graph.types.js +38 -0
- package/dist/utils/write-service-metadata.d.ts +13 -0
- package/dist/utils/write-service-metadata.js +37 -0
- package/dist/visit.js +8 -2
- package/package.json +16 -4
- package/src/add/add-channel.ts +37 -17
- package/src/add/add-file-with-factory.ts +10 -10
- package/src/add/add-forge-credential.ts +119 -0
- package/src/add/add-forge-node.ts +132 -0
- package/src/add/add-functions.ts +199 -69
- package/src/add/add-http-route.ts +34 -5
- package/src/add/add-mcp-prompt.ts +11 -7
- package/src/add/add-mcp-resource.ts +11 -7
- package/src/add/add-mcp-tool.ts +11 -7
- package/src/add/add-middleware.ts +1 -1
- package/src/add/add-permission.ts +1 -1
- package/src/add/add-queue-worker.ts +11 -12
- package/src/add/add-rpc-invocations.ts +61 -31
- package/src/add/add-schedule.ts +10 -5
- package/src/add/add-workflow-graph.ts +864 -0
- package/src/add/add-workflow.ts +212 -116
- package/src/error-codes.ts +3 -0
- package/src/index.ts +12 -0
- package/src/inspector.ts +36 -10
- package/src/types.ts +43 -9
- package/src/utils/extract-function-name.ts +7 -7
- package/src/utils/extract-function-node.ts +58 -0
- package/src/utils/extract-node-value.ts +31 -0
- package/src/utils/extract-service-metadata.ts +353 -0
- package/src/utils/filter-inspector-state.test.ts +3 -3
- package/src/utils/filter-utils.test.ts +45 -51
- package/src/utils/get-files-and-methods.ts +11 -11
- package/src/utils/get-property-value.ts +67 -53
- package/src/utils/permissions.test.ts +3 -3
- package/src/utils/post-process.ts +56 -3
- package/src/utils/serialize-inspector-state.ts +67 -19
- package/src/utils/test-data/inspector-state.json +9 -9
- package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +1180 -0
- package/src/utils/workflow/dsl/extract-dsl-workflow.ts +1608 -0
- package/src/utils/workflow/dsl/index.ts +11 -0
- package/src/utils/workflow/dsl/patterns.ts +279 -0
- package/src/utils/workflow/dsl/validation.ts +180 -0
- package/src/utils/workflow/graph/convert-dsl-to-graph.ts +415 -0
- package/src/utils/workflow/graph/index.ts +6 -0
- package/src/utils/workflow/graph/serialize-workflow-graph.ts +223 -0
- package/src/utils/workflow/graph/workflow-graph.types.ts +280 -0
- package/src/utils/write-service-metadata.ts +51 -0
- package/src/visit.ts +9 -3
- package/tsconfig.tsbuildinfo +1 -1
package/src/add/add-workflow.ts
CHANGED
|
@@ -1,29 +1,75 @@
|
|
|
1
1
|
import * as ts from 'typescript'
|
|
2
|
-
import {
|
|
3
|
-
getPropertyValue,
|
|
4
|
-
getPropertyTags,
|
|
5
|
-
} from '../utils/get-property-value.js'
|
|
6
|
-
import { PikkuDocs } from '@pikku/core'
|
|
7
2
|
import { AddWiring, InspectorState } from '../types.js'
|
|
8
3
|
import { extractFunctionName } from '../utils/extract-function-name.js'
|
|
9
|
-
import {
|
|
10
|
-
getPropertyAssignmentInitializer,
|
|
11
|
-
resolveFunctionDeclaration,
|
|
12
|
-
} from '../utils/type-utils.js'
|
|
13
|
-
import { resolveMiddleware } from '../utils/middleware.js'
|
|
14
|
-
import { extractWireNames } from '../utils/post-process.js'
|
|
4
|
+
import { extractFunctionNode } from '../utils/extract-function-node.js'
|
|
15
5
|
import { ErrorCode } from '../error-codes.js'
|
|
16
|
-
import { WorkflowStepMeta } from '@pikku/core/workflow'
|
|
6
|
+
import { WorkflowStepMeta, WorkflowContext } from '@pikku/core/workflow'
|
|
17
7
|
import {
|
|
18
8
|
extractStringLiteral,
|
|
19
|
-
extractNumberLiteral,
|
|
20
|
-
extractPropertyString,
|
|
21
9
|
isStringLike,
|
|
22
10
|
isFunctionLike,
|
|
11
|
+
extractDescription,
|
|
12
|
+
extractDuration,
|
|
23
13
|
} from '../utils/extract-node-value.js'
|
|
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
|
+
}
|
|
24
42
|
|
|
25
43
|
/**
|
|
26
|
-
*
|
|
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
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Scan for workflow.do(), workflow.sleep(), and workflow.cancel() calls to extract workflow steps
|
|
27
73
|
*/
|
|
28
74
|
function getWorkflowInvocations(
|
|
29
75
|
node: ts.Node,
|
|
@@ -37,7 +83,7 @@ function getWorkflowInvocations(
|
|
|
37
83
|
const { name } = node
|
|
38
84
|
|
|
39
85
|
// Check if this is accessing 'do' or 'sleep' property
|
|
40
|
-
if (name.text === 'do' || name.text === 'sleep') {
|
|
86
|
+
if (name.text === 'do' || name.text === 'sleep' || name.text === 'cancel') {
|
|
41
87
|
// Check if the parent is a call expression
|
|
42
88
|
const parent = node.parent
|
|
43
89
|
if (ts.isCallExpression(parent) && parent.expression === node) {
|
|
@@ -62,7 +108,6 @@ function getWorkflowInvocations(
|
|
|
62
108
|
type: 'rpc',
|
|
63
109
|
stepName,
|
|
64
110
|
rpcName,
|
|
65
|
-
description,
|
|
66
111
|
})
|
|
67
112
|
state.rpc.invokedFunctions.add(rpcName)
|
|
68
113
|
} else if (isFunctionLike(secondArg)) {
|
|
@@ -86,18 +131,20 @@ function getWorkflowInvocations(
|
|
|
86
131
|
stepName: stepName || '<dynamic>',
|
|
87
132
|
duration: duration || '<dynamic>',
|
|
88
133
|
})
|
|
134
|
+
} else if (name.text === 'cancel') {
|
|
135
|
+
// workflow.cancel(reason?)
|
|
136
|
+
steps.push({
|
|
137
|
+
type: 'cancel',
|
|
138
|
+
})
|
|
89
139
|
}
|
|
90
140
|
}
|
|
91
141
|
}
|
|
92
142
|
}
|
|
93
143
|
|
|
94
|
-
//
|
|
144
|
+
// Recurse into children, including arrow functions (for Promise.all callbacks)
|
|
145
|
+
// but skip function declarations (which would be separate functions)
|
|
95
146
|
ts.forEachChild(node, (child) => {
|
|
96
|
-
if (
|
|
97
|
-
ts.isFunctionDeclaration(child) ||
|
|
98
|
-
ts.isFunctionExpression(child) ||
|
|
99
|
-
ts.isArrowFunction(child)
|
|
100
|
-
) {
|
|
147
|
+
if (ts.isFunctionDeclaration(child)) {
|
|
101
148
|
return
|
|
102
149
|
}
|
|
103
150
|
getWorkflowInvocations(child, checker, state, workflowName, steps)
|
|
@@ -105,43 +152,10 @@ function getWorkflowInvocations(
|
|
|
105
152
|
}
|
|
106
153
|
|
|
107
154
|
/**
|
|
108
|
-
*
|
|
109
|
-
*/
|
|
110
|
-
function extractDescription(
|
|
111
|
-
optionsNode: ts.Node | undefined,
|
|
112
|
-
checker: ts.TypeChecker
|
|
113
|
-
): string | null {
|
|
114
|
-
if (!optionsNode || !ts.isObjectLiteralExpression(optionsNode)) {
|
|
115
|
-
return null
|
|
116
|
-
}
|
|
117
|
-
return extractPropertyString(optionsNode, 'description', checker)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Extract duration value (number or string)
|
|
122
|
-
*/
|
|
123
|
-
function extractDuration(
|
|
124
|
-
node: ts.Node,
|
|
125
|
-
checker: ts.TypeChecker
|
|
126
|
-
): string | number | null {
|
|
127
|
-
const numValue = extractNumberLiteral(node)
|
|
128
|
-
if (numValue !== null) {
|
|
129
|
-
return numValue
|
|
130
|
-
}
|
|
131
|
-
return extractStringLiteral(node, checker)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Inspector for wireWorkflow() calls
|
|
155
|
+
* Inspector for pikkuWorkflow() and pikkuSimpleWorkflow() calls
|
|
136
156
|
* Detects workflow registration and extracts metadata
|
|
137
157
|
*/
|
|
138
|
-
export const addWorkflow: AddWiring = (
|
|
139
|
-
logger,
|
|
140
|
-
node,
|
|
141
|
-
checker,
|
|
142
|
-
state,
|
|
143
|
-
options
|
|
144
|
-
) => {
|
|
158
|
+
export const addWorkflow: AddWiring = (logger, node, checker, state) => {
|
|
145
159
|
if (!ts.isCallExpression(node)) {
|
|
146
160
|
return
|
|
147
161
|
}
|
|
@@ -150,8 +164,16 @@ export const addWorkflow: AddWiring = (
|
|
|
150
164
|
const firstArg = args[0]
|
|
151
165
|
const expression = node.expression
|
|
152
166
|
|
|
153
|
-
|
|
154
|
-
|
|
167
|
+
if (!ts.isIdentifier(expression)) {
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
let wrapperType: 'dsl' | 'regular' | null = null
|
|
172
|
+
if (expression.text === 'pikkuWorkflowFunc') {
|
|
173
|
+
wrapperType = 'dsl'
|
|
174
|
+
} else if (expression.text === 'pikkuWorkflowComplexFunc') {
|
|
175
|
+
wrapperType = 'regular'
|
|
176
|
+
} else {
|
|
155
177
|
return
|
|
156
178
|
}
|
|
157
179
|
|
|
@@ -159,73 +181,147 @@ export const addWorkflow: AddWiring = (
|
|
|
159
181
|
return
|
|
160
182
|
}
|
|
161
183
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
'func',
|
|
176
|
-
true,
|
|
177
|
-
checker
|
|
184
|
+
// Extract workflow name and metadata using same logic as add-functions
|
|
185
|
+
const { pikkuFuncName, name, exportedName } = extractFunctionName(
|
|
186
|
+
node,
|
|
187
|
+
checker,
|
|
188
|
+
state.rootDir
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
const workflowName = exportedName || name
|
|
192
|
+
|
|
193
|
+
if (!workflowName) {
|
|
194
|
+
logger.critical(
|
|
195
|
+
ErrorCode.MISSING_NAME,
|
|
196
|
+
`Could not determine workflow name from export.`
|
|
178
197
|
)
|
|
198
|
+
return
|
|
199
|
+
}
|
|
179
200
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
ErrorCode.MISSING_NAME,
|
|
183
|
-
`Wasn't able to determine 'name' property for workflow wiring.`
|
|
184
|
-
)
|
|
185
|
-
return
|
|
186
|
-
}
|
|
201
|
+
// Extract the function node (either direct function or from config.func)
|
|
202
|
+
const { funcNode, resolvedFunc } = extractFunctionNode(firstArg, checker)
|
|
187
203
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
return
|
|
194
|
-
}
|
|
204
|
+
// Extract metadata if using object form
|
|
205
|
+
let tags: string[] | undefined
|
|
206
|
+
let summary: string | undefined
|
|
207
|
+
let description: string | undefined
|
|
208
|
+
let errors: string[] | undefined
|
|
195
209
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
210
|
+
if (ts.isObjectLiteralExpression(firstArg)) {
|
|
211
|
+
const metadata = getCommonWireMetaData(
|
|
212
|
+
firstArg,
|
|
213
|
+
'Workflow',
|
|
214
|
+
workflowName,
|
|
215
|
+
logger
|
|
216
|
+
)
|
|
217
|
+
tags = metadata.tags
|
|
218
|
+
summary = metadata.summary
|
|
219
|
+
description = metadata.description
|
|
220
|
+
errors = metadata.errors
|
|
221
|
+
}
|
|
201
222
|
|
|
202
|
-
|
|
203
|
-
|
|
223
|
+
// Validate that we got a valid function
|
|
224
|
+
if (
|
|
225
|
+
ts.isObjectLiteralExpression(firstArg) &&
|
|
226
|
+
(!funcNode || funcNode === firstArg)
|
|
227
|
+
) {
|
|
228
|
+
logger.critical(
|
|
229
|
+
ErrorCode.MISSING_FUNC,
|
|
230
|
+
`No valid 'func' property for workflow '${workflowName}'.`
|
|
231
|
+
)
|
|
232
|
+
return
|
|
233
|
+
}
|
|
204
234
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
235
|
+
if (!resolvedFunc) {
|
|
236
|
+
logger.critical(
|
|
237
|
+
ErrorCode.MISSING_FUNC,
|
|
238
|
+
`Could not resolve workflow function for '${workflowName}'.`
|
|
209
239
|
)
|
|
240
|
+
return
|
|
241
|
+
}
|
|
210
242
|
|
|
211
|
-
|
|
243
|
+
// Track workflow file for wiring generation
|
|
244
|
+
if (exportedName) {
|
|
245
|
+
state.workflows.files.set(pikkuFuncName, {
|
|
246
|
+
path: node.getSourceFile().fileName,
|
|
247
|
+
exportedName,
|
|
248
|
+
})
|
|
249
|
+
}
|
|
212
250
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
251
|
+
let steps: WorkflowStepMeta[] = []
|
|
252
|
+
let context: WorkflowContext | undefined = undefined
|
|
253
|
+
let dsl: boolean | undefined = undefined
|
|
254
|
+
|
|
255
|
+
// Try DSL workflow extraction first
|
|
256
|
+
// Pass the whole CallExpression node so findWorkflowFunction can find the arrow function
|
|
257
|
+
const result = extractDSLWorkflow(node, checker)
|
|
258
|
+
|
|
259
|
+
if (result.status === 'ok' && result.steps) {
|
|
260
|
+
// Extraction succeeded
|
|
261
|
+
steps = result.steps
|
|
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'
|
|
219
279
|
}
|
|
220
280
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
+
}
|
|
287
|
+
} else {
|
|
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)
|
|
293
|
+
logger.critical(
|
|
294
|
+
ErrorCode.INVALID_DSL_WORKFLOW,
|
|
295
|
+
`Workflow '${workflowName}' uses pikkuWorkflowFunc but does not conform to DSL workflow rules:\n${result.reason || 'Unknown error'}`
|
|
296
|
+
)
|
|
297
|
+
return
|
|
298
|
+
} else {
|
|
299
|
+
// For pikkuWorkflowComplexFunc, fall back to basic extraction
|
|
300
|
+
logger.debug(
|
|
301
|
+
`Workflow '${workflowName}' could not be extracted as DSL workflow: ${result.reason || 'Unknown error'}. Falling back to basic extraction.`
|
|
302
|
+
)
|
|
303
|
+
dsl = false
|
|
229
304
|
}
|
|
230
305
|
}
|
|
306
|
+
|
|
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
|
+
}
|
|
315
|
+
|
|
316
|
+
state.workflows.meta[workflowName] = {
|
|
317
|
+
pikkuFuncName,
|
|
318
|
+
workflowName,
|
|
319
|
+
steps,
|
|
320
|
+
context,
|
|
321
|
+
dsl,
|
|
322
|
+
summary,
|
|
323
|
+
description,
|
|
324
|
+
errors,
|
|
325
|
+
tags,
|
|
326
|
+
}
|
|
231
327
|
}
|
package/src/error-codes.ts
CHANGED
|
@@ -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,6 +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',
|
|
25
|
+
INVALID_DSL_WORKFLOW = 'PKU641',
|
|
24
26
|
|
|
25
27
|
// Configuration errors
|
|
26
28
|
CONFIG_TYPE_NOT_FOUND = 'PKU426',
|
|
@@ -28,6 +30,7 @@ export enum ErrorCode {
|
|
|
28
30
|
SCHEMA_NO_ROOT = 'PKU431',
|
|
29
31
|
SCHEMA_GENERATION_ERROR = 'PKU456',
|
|
30
32
|
SCHEMA_LOAD_ERROR = 'PKU488',
|
|
33
|
+
INLINE_ZOD_SCHEMA = 'PKU489',
|
|
31
34
|
|
|
32
35
|
// Function errors
|
|
33
36
|
FUNCTION_METADATA_NOT_FOUND = 'PKU559',
|
package/src/index.ts
CHANGED
|
@@ -14,3 +14,15 @@ export {
|
|
|
14
14
|
} from './utils/serialize-inspector-state.js'
|
|
15
15
|
export type { SerializableInspectorState } from './utils/serialize-inspector-state.js'
|
|
16
16
|
export { filterInspectorState } from './utils/filter-inspector-state.js'
|
|
17
|
+
export { writeAllServiceMetadata } from './utils/write-service-metadata.js'
|
|
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
|
@@ -5,7 +5,10 @@ import { TypesMap } from './types-map.js'
|
|
|
5
5
|
import { InspectorState, InspectorLogger, InspectorOptions } from './types.js'
|
|
6
6
|
import { getFilesAndMethods } from './utils/get-files-and-methods.js'
|
|
7
7
|
import { findCommonAncestor } from './utils/find-root-dir.js'
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
aggregateRequiredServices,
|
|
10
|
+
extractServiceInterfaceMetadata,
|
|
11
|
+
} from './utils/post-process.js'
|
|
9
12
|
|
|
10
13
|
/**
|
|
11
14
|
* Creates an initial/empty inspector state with all required properties initialized
|
|
@@ -16,16 +19,17 @@ export function getInitialInspectorState(rootDir: string): InspectorState {
|
|
|
16
19
|
return {
|
|
17
20
|
rootDir,
|
|
18
21
|
singletonServicesTypeImportMap: new Map(),
|
|
19
|
-
|
|
22
|
+
wireServicesTypeImportMap: new Map(),
|
|
20
23
|
userSessionTypeImportMap: new Map(),
|
|
21
24
|
configTypeImportMap: new Map(),
|
|
22
25
|
singletonServicesFactories: new Map(),
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
wireServicesFactories: new Map(),
|
|
27
|
+
wireServicesMeta: new Map(),
|
|
25
28
|
configFactories: new Map(),
|
|
26
29
|
filesAndMethods: {},
|
|
27
30
|
filesAndMethodsErrors: new Map(),
|
|
28
31
|
typesLookup: new Map(),
|
|
32
|
+
zodLookup: new Map(),
|
|
29
33
|
functions: {
|
|
30
34
|
typesMap: new TypesMap(),
|
|
31
35
|
meta: {},
|
|
@@ -60,7 +64,9 @@ export function getInitialInspectorState(rootDir: string): InspectorState {
|
|
|
60
64
|
},
|
|
61
65
|
workflows: {
|
|
62
66
|
meta: {},
|
|
63
|
-
files: new
|
|
67
|
+
files: new Map(),
|
|
68
|
+
graphMeta: {},
|
|
69
|
+
graphFiles: new Map(),
|
|
64
70
|
},
|
|
65
71
|
rpc: {
|
|
66
72
|
internalMeta: {},
|
|
@@ -68,6 +74,7 @@ export function getInitialInspectorState(rootDir: string): InspectorState {
|
|
|
68
74
|
exposedMeta: {},
|
|
69
75
|
exposedFiles: new Map(),
|
|
70
76
|
invokedFunctions: new Set(),
|
|
77
|
+
usedExternalPackages: new Set(),
|
|
71
78
|
},
|
|
72
79
|
mcpEndpoints: {
|
|
73
80
|
resourcesMeta: {},
|
|
@@ -82,6 +89,14 @@ export function getInitialInspectorState(rootDir: string): InspectorState {
|
|
|
82
89
|
},
|
|
83
90
|
files: new Set(),
|
|
84
91
|
},
|
|
92
|
+
forgeNodes: {
|
|
93
|
+
meta: {},
|
|
94
|
+
files: new Set(),
|
|
95
|
+
},
|
|
96
|
+
forgeCredentials: {
|
|
97
|
+
meta: {},
|
|
98
|
+
files: new Set(),
|
|
99
|
+
},
|
|
85
100
|
middleware: {
|
|
86
101
|
meta: {},
|
|
87
102
|
tagMiddleware: new Map(),
|
|
@@ -96,8 +111,9 @@ export function getInitialInspectorState(rootDir: string): InspectorState {
|
|
|
96
111
|
usedMiddleware: new Set(),
|
|
97
112
|
usedPermissions: new Set(),
|
|
98
113
|
allSingletonServices: [],
|
|
99
|
-
|
|
114
|
+
allWireServices: [],
|
|
100
115
|
},
|
|
116
|
+
serviceMetadata: [],
|
|
101
117
|
}
|
|
102
118
|
}
|
|
103
119
|
|
|
@@ -127,15 +143,19 @@ export const inspect = (
|
|
|
127
143
|
`Got type checker in ${(performance.now() - startChecker).toFixed(2)}ms`
|
|
128
144
|
)
|
|
129
145
|
|
|
146
|
+
// Use provided rootDir or infer from source files
|
|
147
|
+
const rootDir = options.rootDir || findCommonAncestor(routeFiles)
|
|
148
|
+
|
|
130
149
|
const startSourceFiles = performance.now()
|
|
131
|
-
|
|
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))
|
|
132
155
|
logger.debug(
|
|
133
156
|
`Got source files in ${(performance.now() - startSourceFiles).toFixed(2)}ms`
|
|
134
157
|
)
|
|
135
158
|
|
|
136
|
-
// Infer root directory from source files
|
|
137
|
-
const rootDir = findCommonAncestor(routeFiles)
|
|
138
|
-
|
|
139
159
|
const state = getInitialInspectorState(rootDir)
|
|
140
160
|
|
|
141
161
|
// First sweep: add all functions
|
|
@@ -177,6 +197,12 @@ export const inspect = (
|
|
|
177
197
|
logger.debug(
|
|
178
198
|
`Aggregate required services completed in ${(performance.now() - startAggregate).toFixed(2)}ms`
|
|
179
199
|
)
|
|
200
|
+
|
|
201
|
+
const startServiceMeta = performance.now()
|
|
202
|
+
extractServiceInterfaceMetadata(state, checker)
|
|
203
|
+
logger.debug(
|
|
204
|
+
`Extract service metadata completed in ${(performance.now() - startServiceMeta).toFixed(2)}ms`
|
|
205
|
+
)
|
|
180
206
|
}
|
|
181
207
|
|
|
182
208
|
return state
|
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,11 +126,13 @@ 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
|
|
122
134
|
singletonServicesFactoryType: string
|
|
123
|
-
|
|
135
|
+
wireServicesFactoryType: string
|
|
124
136
|
}>
|
|
125
137
|
}>
|
|
126
138
|
|
|
@@ -147,7 +159,7 @@ export interface InspectorFilesAndMethods {
|
|
|
147
159
|
type: string
|
|
148
160
|
typePath: string
|
|
149
161
|
}
|
|
150
|
-
|
|
162
|
+
wireServicesType?: {
|
|
151
163
|
file: string
|
|
152
164
|
variable: string
|
|
153
165
|
type: string
|
|
@@ -177,7 +189,7 @@ export interface InspectorFilesAndMethods {
|
|
|
177
189
|
type: string
|
|
178
190
|
typePath: string
|
|
179
191
|
}
|
|
180
|
-
|
|
192
|
+
wireServicesFactory?: {
|
|
181
193
|
file: string
|
|
182
194
|
variable: string
|
|
183
195
|
type: string
|
|
@@ -188,16 +200,17 @@ export interface InspectorFilesAndMethods {
|
|
|
188
200
|
export interface InspectorState {
|
|
189
201
|
rootDir: string // Root directory inferred from source files
|
|
190
202
|
singletonServicesTypeImportMap: PathToNameAndType
|
|
191
|
-
|
|
203
|
+
wireServicesTypeImportMap: PathToNameAndType
|
|
192
204
|
userSessionTypeImportMap: PathToNameAndType
|
|
193
205
|
configTypeImportMap: PathToNameAndType
|
|
194
206
|
singletonServicesFactories: PathToNameAndType
|
|
195
|
-
|
|
196
|
-
|
|
207
|
+
wireServicesFactories: PathToNameAndType
|
|
208
|
+
wireServicesMeta: Map<string, string[]> // variable name -> singleton services consumed
|
|
197
209
|
configFactories: PathToNameAndType
|
|
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
|
|
@@ -211,14 +224,17 @@ export interface InspectorState {
|
|
|
211
224
|
}
|
|
212
225
|
workflows: {
|
|
213
226
|
meta: WorkflowsMeta
|
|
214
|
-
files:
|
|
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>
|
|
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: {
|
|
@@ -238,6 +262,16 @@ export interface InspectorState {
|
|
|
238
262
|
usedMiddleware: Set<string> // Middleware names used by wired functions
|
|
239
263
|
usedPermissions: Set<string> // Permission names used by wired functions
|
|
240
264
|
allSingletonServices: string[] // All services available in SingletonServices type
|
|
241
|
-
|
|
265
|
+
allWireServices: string[] // All services available in Services type (excluding SingletonServices)
|
|
242
266
|
}
|
|
267
|
+
serviceMetadata: Array<{
|
|
268
|
+
name: string
|
|
269
|
+
summary: string
|
|
270
|
+
description: string
|
|
271
|
+
package: string
|
|
272
|
+
path: string
|
|
273
|
+
version: string
|
|
274
|
+
interface: string
|
|
275
|
+
expandedProperties: Record<string, string>
|
|
276
|
+
}>
|
|
243
277
|
}
|