@pikku/inspector 0.11.2 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -1
- package/OPTIMIZATION-PLAN.md +195 -0
- package/dist/add/add-ai-agent.d.ts +2 -0
- package/dist/add/add-ai-agent.js +314 -0
- package/dist/add/add-channel.js +69 -61
- package/dist/add/add-cli.js +36 -18
- package/dist/add/add-file-with-factory.js +2 -0
- package/dist/add/add-functions.js +250 -75
- package/dist/add/add-http-route.d.ts +19 -10
- package/dist/add/add-http-route.js +152 -66
- package/dist/add/add-http-routes.d.ts +5 -0
- package/dist/add/add-http-routes.js +159 -0
- package/dist/add/add-keyed-wiring.d.ts +12 -0
- package/dist/add/add-keyed-wiring.js +97 -0
- package/dist/add/add-mcp-prompt.js +14 -9
- package/dist/add/add-mcp-resource.js +14 -9
- package/dist/add/add-middleware.d.ts +1 -4
- package/dist/add/add-middleware.js +364 -79
- package/dist/add/add-permission.d.ts +1 -1
- package/dist/add/add-permission.js +152 -40
- package/dist/add/add-queue-worker.js +18 -12
- package/dist/add/add-rpc-invocations.js +14 -0
- package/dist/add/add-schedule.js +11 -5
- package/dist/add/add-secret.d.ts +3 -0
- package/dist/add/add-secret.js +82 -0
- package/dist/add/add-trigger.d.ts +2 -0
- package/dist/add/add-trigger.js +87 -0
- package/dist/add/add-variable.d.ts +1 -0
- package/dist/add/add-variable.js +8 -0
- package/dist/add/add-workflow-graph.d.ts +3 -2
- package/dist/add/add-workflow-graph.js +143 -406
- package/dist/add/add-workflow.js +6 -4
- package/dist/error-codes.d.ts +14 -1
- package/dist/error-codes.js +19 -1
- package/dist/index.d.ts +9 -8
- package/dist/index.js +5 -4
- package/dist/inspector.d.ts +1 -1
- package/dist/inspector.js +91 -14
- package/dist/schema-generator.d.ts +1 -0
- package/dist/schema-generator.js +1 -0
- package/dist/types-map.js +10 -1
- package/dist/types.d.ts +163 -39
- package/dist/utils/compute-required-schemas.d.ts +4 -0
- package/dist/utils/compute-required-schemas.js +41 -0
- package/dist/utils/contract-hashes.d.ts +35 -0
- package/dist/utils/contract-hashes.js +202 -0
- package/dist/utils/custom-types-generator.d.ts +9 -0
- package/dist/utils/custom-types-generator.js +71 -0
- package/dist/utils/detect-schema-vendor.d.ts +22 -0
- package/dist/utils/detect-schema-vendor.js +76 -0
- package/dist/utils/ensure-function-metadata.d.ts +5 -2
- package/dist/utils/ensure-function-metadata.js +220 -6
- package/dist/utils/extract-function-name.d.ts +5 -16
- package/dist/utils/extract-function-name.js +86 -291
- package/dist/utils/extract-services.d.ts +2 -1
- package/dist/utils/extract-services.js +25 -1
- package/dist/utils/filter-inspector-state.js +107 -23
- package/dist/utils/get-property-value.d.ts +6 -1
- package/dist/utils/get-property-value.js +28 -3
- package/dist/utils/hash.d.ts +2 -0
- package/dist/utils/hash.js +23 -0
- package/dist/utils/middleware.d.ts +7 -30
- package/dist/utils/middleware.js +80 -66
- package/dist/utils/permissions.d.ts +2 -2
- package/dist/utils/permissions.js +10 -10
- package/dist/utils/post-process.d.ts +9 -10
- package/dist/utils/post-process.js +231 -24
- package/dist/utils/resolve-external-package.d.ts +12 -0
- package/dist/utils/resolve-external-package.js +34 -0
- package/dist/utils/resolve-function-types.d.ts +6 -0
- package/dist/utils/resolve-function-types.js +29 -0
- package/dist/utils/resolve-identifier.d.ts +10 -0
- package/dist/utils/resolve-identifier.js +36 -0
- package/dist/utils/resolve-versions.d.ts +2 -0
- package/dist/utils/resolve-versions.js +78 -0
- package/dist/utils/schema-generator.d.ts +9 -0
- package/dist/utils/schema-generator.js +209 -0
- package/dist/utils/serialize-inspector-state.d.ts +59 -22
- package/dist/utils/serialize-inspector-state.js +92 -20
- package/dist/utils/serialize-mcp-json.d.ts +2 -0
- package/dist/utils/serialize-mcp-json.js +99 -0
- package/dist/utils/serialize-middleware-groups-meta.d.ts +12 -0
- package/dist/utils/serialize-middleware-groups-meta.js +28 -0
- package/dist/utils/serialize-openapi-json.d.ts +85 -0
- package/dist/utils/serialize-openapi-json.js +151 -0
- package/dist/utils/serialize-permissions-groups-meta.d.ts +6 -0
- package/dist/utils/serialize-permissions-groups-meta.js +31 -0
- package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +34 -102
- package/dist/utils/workflow/dsl/extract-dsl-workflow.js +23 -4
- package/dist/utils/workflow/graph/convert-dsl-to-graph.js +12 -10
- package/dist/utils/workflow/graph/finalize-workflow-wires.d.ts +3 -0
- package/dist/utils/workflow/graph/finalize-workflow-wires.js +276 -0
- package/dist/utils/workflow/graph/finalize-workflows.d.ts +2 -0
- package/dist/utils/workflow/graph/finalize-workflows.js +75 -0
- package/dist/utils/workflow/graph/index.d.ts +2 -0
- package/dist/utils/workflow/graph/index.js +2 -0
- package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +0 -8
- package/dist/utils/workflow/graph/serialize-workflow-graph.js +1 -3
- package/dist/utils/workflow/graph/workflow-graph.types.d.ts +53 -79
- package/dist/utils/workflow/graph/workflow-graph.types.js +1 -1
- package/dist/visit.js +11 -6
- package/package.json +14 -4
- package/src/add/add-ai-agent.ts +468 -0
- package/src/add/add-channel.ts +82 -79
- package/src/add/add-cli.ts +49 -20
- package/src/add/add-file-with-factory.ts +2 -0
- package/src/add/add-functions.ts +330 -86
- package/src/add/add-http-route.ts +245 -88
- package/src/add/add-http-routes.ts +228 -0
- package/src/add/add-keyed-wiring.ts +151 -0
- package/src/add/add-mcp-prompt.ts +26 -15
- package/src/add/add-mcp-resource.ts +27 -15
- package/src/add/add-middleware.ts +482 -80
- package/src/add/add-permission.ts +199 -40
- package/src/add/add-queue-worker.ts +24 -19
- package/src/add/add-rpc-invocations.ts +17 -0
- package/src/add/add-schedule.ts +16 -11
- package/src/add/add-secret.ts +140 -0
- package/src/add/add-trigger.ts +154 -0
- package/src/add/add-variable.ts +9 -0
- package/src/add/add-workflow-graph.ts +180 -522
- package/src/add/add-workflow.ts +5 -4
- package/src/error-codes.ts +24 -1
- package/src/index.ts +22 -13
- package/src/inspector.ts +129 -17
- package/src/schema-generator.ts +1 -0
- package/src/types-map.ts +12 -1
- package/src/types.ts +175 -58
- package/src/utils/compute-required-schemas.ts +49 -0
- package/src/utils/contract-hashes.test.ts +528 -0
- package/src/utils/contract-hashes.ts +290 -0
- package/src/utils/custom-types-generator.ts +88 -0
- package/src/utils/detect-schema-vendor.ts +90 -0
- package/src/utils/ensure-function-metadata.ts +324 -7
- package/src/utils/extract-function-name.ts +101 -351
- package/src/utils/extract-services.ts +35 -2
- package/src/utils/filter-inspector-state.test.ts +34 -20
- package/src/utils/filter-inspector-state.ts +140 -31
- package/src/utils/get-property-value.ts +42 -4
- package/src/utils/hash.ts +26 -0
- package/src/utils/middleware.test.ts +204 -0
- package/src/utils/middleware.ts +129 -67
- package/src/utils/permissions.test.ts +35 -12
- package/src/utils/permissions.ts +10 -10
- package/src/utils/post-process.ts +283 -43
- package/src/utils/resolve-external-package.ts +42 -0
- package/src/utils/resolve-function-types.ts +42 -0
- package/src/utils/resolve-identifier.ts +46 -0
- package/src/utils/resolve-versions.test.ts +249 -0
- package/src/utils/resolve-versions.ts +105 -0
- package/src/utils/schema-generator.ts +329 -0
- package/src/utils/serialize-inspector-state.ts +163 -40
- package/src/utils/serialize-mcp-json.ts +145 -0
- package/src/utils/serialize-middleware-groups-meta.ts +33 -0
- package/src/utils/serialize-openapi-json.ts +277 -0
- package/src/utils/serialize-permissions-groups-meta.ts +35 -0
- package/src/utils/test-data/inspector-state.json +69 -66
- package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +43 -119
- package/src/utils/workflow/dsl/extract-dsl-workflow.ts +24 -4
- package/src/utils/workflow/graph/convert-dsl-to-graph.ts +17 -10
- package/src/utils/workflow/graph/finalize-workflow-wires.ts +310 -0
- package/src/utils/workflow/graph/finalize-workflows.ts +100 -0
- package/src/utils/workflow/graph/index.ts +5 -0
- package/src/utils/workflow/graph/serialize-workflow-graph.ts +1 -8
- package/src/utils/workflow/graph/workflow-graph.types.ts +29 -78
- package/src/visit.ts +12 -6
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/add/add-forge-credential.d.ts +0 -8
- package/dist/add/add-forge-credential.js +0 -77
- package/dist/add/add-forge-node.d.ts +0 -7
- package/dist/add/add-forge-node.js +0 -77
- package/dist/add/add-mcp-tool.d.ts +0 -2
- package/dist/add/add-mcp-tool.js +0 -81
- package/dist/utils/extract-service-metadata.d.ts +0 -19
- package/dist/utils/extract-service-metadata.js +0 -244
- package/dist/utils/write-service-metadata.d.ts +0 -13
- package/dist/utils/write-service-metadata.js +0 -37
- package/src/add/add-forge-credential.ts +0 -119
- package/src/add/add-forge-node.ts +0 -132
- package/src/add/add-mcp-tool.ts +0 -141
- package/src/utils/extract-service-metadata.ts +0 -353
- package/src/utils/write-service-metadata.ts +0 -51
package/src/utils/permissions.ts
CHANGED
|
@@ -4,8 +4,8 @@ import { extractFunctionName } from './extract-function-name.js'
|
|
|
4
4
|
import { InspectorState } from '../types.js'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Extract permission
|
|
8
|
-
* Resolves each identifier to its
|
|
7
|
+
* Extract permission pikkuFuncIds from an expression (array or object literal)
|
|
8
|
+
* Resolves each identifier to its pikkuFuncId using extractFunctionName
|
|
9
9
|
* Also handles call expressions (like rolePermission({...}))
|
|
10
10
|
*
|
|
11
11
|
* Supports both formats:
|
|
@@ -22,17 +22,17 @@ export function extractPermissionPikkuNames(
|
|
|
22
22
|
// Helper to extract from a single element
|
|
23
23
|
const extractFromElement = (element: ts.Expression) => {
|
|
24
24
|
if (ts.isIdentifier(element)) {
|
|
25
|
-
const {
|
|
26
|
-
names.push(
|
|
25
|
+
const { pikkuFuncId } = extractFunctionName(element, checker, rootDir)
|
|
26
|
+
names.push(pikkuFuncId)
|
|
27
27
|
} else if (ts.isCallExpression(element)) {
|
|
28
28
|
// Handle call expressions like hasEmailQuota(100) or rolePermission({...})
|
|
29
29
|
// Extract the function being called (e.g., 'hasEmailQuota' from 'hasEmailQuota(100)')
|
|
30
|
-
const {
|
|
30
|
+
const { pikkuFuncId } = extractFunctionName(
|
|
31
31
|
element.expression,
|
|
32
32
|
checker,
|
|
33
33
|
rootDir
|
|
34
34
|
)
|
|
35
|
-
names.push(
|
|
35
|
+
names.push(pikkuFuncId)
|
|
36
36
|
} else if (ts.isArrayLiteralExpression(element)) {
|
|
37
37
|
// Nested array (for Record values that are arrays)
|
|
38
38
|
for (const nestedElement of element.elements) {
|
|
@@ -141,11 +141,11 @@ export function resolveHTTPPermissions(
|
|
|
141
141
|
state.rootDir
|
|
142
142
|
)
|
|
143
143
|
for (const name of permissionNames) {
|
|
144
|
-
const
|
|
144
|
+
const def = state.permissions.definitions[name]
|
|
145
145
|
resolved.push({
|
|
146
146
|
type: 'wire',
|
|
147
147
|
name,
|
|
148
|
-
inline:
|
|
148
|
+
inline: def?.exportedName === null,
|
|
149
149
|
})
|
|
150
150
|
}
|
|
151
151
|
}
|
|
@@ -187,11 +187,11 @@ function resolveTagAndExplicitPermissions(
|
|
|
187
187
|
state.rootDir
|
|
188
188
|
)
|
|
189
189
|
for (const name of permissionNames) {
|
|
190
|
-
const
|
|
190
|
+
const def = state.permissions.definitions[name]
|
|
191
191
|
resolved.push({
|
|
192
192
|
type: 'wire',
|
|
193
193
|
name,
|
|
194
|
-
inline:
|
|
194
|
+
inline: def?.exportedName === null,
|
|
195
195
|
})
|
|
196
196
|
}
|
|
197
197
|
}
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
InspectorState,
|
|
3
|
+
InspectorLogger,
|
|
4
|
+
InspectorOptions,
|
|
5
|
+
InspectorModelConfig,
|
|
6
|
+
ExternalPackageConfig,
|
|
7
|
+
MiddlewareGroupMeta,
|
|
8
|
+
InspectorDiagnostic,
|
|
9
|
+
} from '../types.js'
|
|
3
10
|
import {
|
|
4
11
|
FunctionServicesMeta,
|
|
5
12
|
MiddlewareMetadata,
|
|
6
13
|
PermissionMetadata,
|
|
7
14
|
} from '@pikku/core'
|
|
8
15
|
import { extractTypeKeys } from './type-utils.js'
|
|
9
|
-
import {
|
|
10
|
-
extractAllServiceMetadata,
|
|
11
|
-
ServiceMetadata,
|
|
12
|
-
} from './extract-service-metadata.js'
|
|
16
|
+
import { ErrorCode } from '../error-codes.js'
|
|
13
17
|
|
|
14
18
|
/**
|
|
15
19
|
* Helper to extract wire-level middleware/permission names from metadata.
|
|
@@ -133,7 +137,7 @@ export function aggregateRequiredServices(
|
|
|
133
137
|
|
|
134
138
|
// 2. Services from used middleware (individual + groups)
|
|
135
139
|
usedMiddleware.forEach((middlewareName) => {
|
|
136
|
-
const middlewareMeta = state.middleware.
|
|
140
|
+
const middlewareMeta = state.middleware.definitions[middlewareName]
|
|
137
141
|
if (middlewareMeta?.services) {
|
|
138
142
|
addServices(middlewareMeta.services)
|
|
139
143
|
}
|
|
@@ -141,7 +145,7 @@ export function aggregateRequiredServices(
|
|
|
141
145
|
|
|
142
146
|
// 3. Services from used permissions (individual + groups)
|
|
143
147
|
usedPermissions.forEach((permissionName) => {
|
|
144
|
-
const permissionMeta = state.permissions.
|
|
148
|
+
const permissionMeta = state.permissions.definitions[permissionName]
|
|
145
149
|
if (permissionMeta?.services) {
|
|
146
150
|
addServices(permissionMeta.services)
|
|
147
151
|
}
|
|
@@ -220,50 +224,286 @@ export function aggregateRequiredServices(
|
|
|
220
224
|
}
|
|
221
225
|
}
|
|
222
226
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
* This extracts metadata for services in SingletonServices and Services types
|
|
226
|
-
* to generate documentation for AI consumption.
|
|
227
|
-
*
|
|
228
|
-
* Must be called after aggregateRequiredServices() to ensure types are loaded.
|
|
229
|
-
*/
|
|
230
|
-
export function extractServiceInterfaceMetadata(
|
|
227
|
+
export function validateSecretOverrides(
|
|
228
|
+
logger: InspectorLogger,
|
|
231
229
|
state: InspectorState | Omit<InspectorState, 'typesLookup'>,
|
|
232
|
-
|
|
230
|
+
externalPackages?: Record<string, ExternalPackageConfig>
|
|
233
231
|
): void {
|
|
234
|
-
if (!
|
|
235
|
-
|
|
232
|
+
if (!externalPackages) return
|
|
233
|
+
|
|
234
|
+
const secretNames = new Set(state.secrets.definitions.map((d) => d.name))
|
|
235
|
+
|
|
236
|
+
for (const [namespace, pkgConfig] of Object.entries(externalPackages)) {
|
|
237
|
+
if (!pkgConfig.secretOverrides) continue
|
|
238
|
+
|
|
239
|
+
for (const secretKey of Object.keys(pkgConfig.secretOverrides)) {
|
|
240
|
+
if (!secretNames.has(secretKey)) {
|
|
241
|
+
const availableSecrets = Array.from(secretNames)
|
|
242
|
+
logger.critical(
|
|
243
|
+
ErrorCode.INVALID_VALUE,
|
|
244
|
+
`Secret override '${secretKey}' in external package '${namespace}' (${pkgConfig.package}) does not exist. Available secrets: ${availableSecrets.join(', ') || 'none'}`
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
}
|
|
236
248
|
}
|
|
249
|
+
}
|
|
237
250
|
|
|
238
|
-
|
|
251
|
+
export function computeResolvedIOTypes(state: InspectorState): void {
|
|
252
|
+
const { functions } = state
|
|
253
|
+
for (const [pikkuFuncId, meta] of Object.entries(functions.meta)) {
|
|
254
|
+
const input = meta.inputs?.[0]
|
|
255
|
+
const output = meta.outputs?.[0]
|
|
239
256
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
257
|
+
let inputType = 'null'
|
|
258
|
+
if (input) {
|
|
259
|
+
try {
|
|
260
|
+
inputType = functions.typesMap.getTypeMeta(input).uniqueName
|
|
261
|
+
} catch {
|
|
262
|
+
inputType = input
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
let outputType = 'null'
|
|
267
|
+
if (output) {
|
|
268
|
+
try {
|
|
269
|
+
outputType = functions.typesMap.getTypeMeta(output).uniqueName
|
|
270
|
+
} catch {
|
|
271
|
+
outputType = output
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
state.resolvedIOTypes[pikkuFuncId] = { inputType, outputType }
|
|
248
276
|
}
|
|
277
|
+
}
|
|
249
278
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
279
|
+
const serializeGroupMap = (
|
|
280
|
+
groupMap: Map<string, MiddlewareGroupMeta>
|
|
281
|
+
): Record<string, MiddlewareGroupMeta> => {
|
|
282
|
+
const result: Record<string, MiddlewareGroupMeta> = {}
|
|
283
|
+
for (const [key, meta] of groupMap.entries()) {
|
|
284
|
+
result[key] = {
|
|
285
|
+
exportName: meta.exportName,
|
|
286
|
+
sourceFile: meta.sourceFile,
|
|
287
|
+
position: meta.position,
|
|
288
|
+
services: meta.services,
|
|
289
|
+
count: meta.count,
|
|
290
|
+
instanceIds: meta.instanceIds,
|
|
291
|
+
isFactory: meta.isFactory,
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return result
|
|
295
|
+
}
|
|
257
296
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
)
|
|
297
|
+
export function computeMiddlewareGroupsMeta(state: InspectorState): void {
|
|
298
|
+
state.middlewareGroupsMeta = {
|
|
299
|
+
definitions: state.middleware.definitions,
|
|
300
|
+
instances: state.middleware.instances,
|
|
301
|
+
httpGroups: serializeGroupMap(state.http.routeMiddleware),
|
|
302
|
+
tagGroups: serializeGroupMap(state.middleware.tagMiddleware),
|
|
303
|
+
channelMiddleware: {
|
|
304
|
+
definitions: state.channelMiddleware.definitions,
|
|
305
|
+
instances: state.channelMiddleware.instances,
|
|
306
|
+
tagGroups: serializeGroupMap(state.channelMiddleware.tagMiddleware),
|
|
307
|
+
},
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export function computePermissionsGroupsMeta(state: InspectorState): void {
|
|
312
|
+
const httpGroups: Record<string, any> = {}
|
|
313
|
+
for (const [pattern, meta] of state.http.routePermissions.entries()) {
|
|
314
|
+
httpGroups[pattern] = {
|
|
315
|
+
exportName: meta.exportName,
|
|
316
|
+
sourceFile: meta.sourceFile,
|
|
317
|
+
position: meta.position,
|
|
318
|
+
services: meta.services,
|
|
319
|
+
count: meta.count,
|
|
320
|
+
instanceIds: meta.instanceIds,
|
|
321
|
+
isFactory: meta.isFactory,
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const tagGroups: Record<string, any> = {}
|
|
326
|
+
for (const [tag, meta] of state.permissions.tagPermissions.entries()) {
|
|
327
|
+
tagGroups[tag] = {
|
|
328
|
+
exportName: meta.exportName,
|
|
329
|
+
sourceFile: meta.sourceFile,
|
|
330
|
+
position: meta.position,
|
|
331
|
+
services: meta.services,
|
|
332
|
+
count: meta.count,
|
|
333
|
+
instanceIds: meta.instanceIds,
|
|
334
|
+
isFactory: meta.isFactory,
|
|
335
|
+
}
|
|
336
|
+
}
|
|
264
337
|
|
|
265
|
-
|
|
338
|
+
state.permissionsGroupsMeta = {
|
|
339
|
+
definitions: state.permissions.definitions,
|
|
340
|
+
httpGroups,
|
|
341
|
+
tagGroups,
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const PRIMITIVE_TYPES = new Set([
|
|
346
|
+
'boolean',
|
|
347
|
+
'string',
|
|
348
|
+
'number',
|
|
349
|
+
'null',
|
|
350
|
+
'undefined',
|
|
351
|
+
'void',
|
|
352
|
+
'any',
|
|
353
|
+
'unknown',
|
|
354
|
+
'never',
|
|
355
|
+
])
|
|
356
|
+
|
|
357
|
+
export function computeRequiredSchemas(
|
|
358
|
+
state: InspectorState,
|
|
359
|
+
options: InspectorOptions
|
|
360
|
+
): void {
|
|
361
|
+
const { functions, schemaLookup } = state
|
|
362
|
+
const schemasFromTypes = options.schemaConfig?.schemasFromTypes
|
|
363
|
+
|
|
364
|
+
state.requiredSchemas = new Set<string>([
|
|
365
|
+
...Object.values(functions.meta)
|
|
366
|
+
.map(({ inputs, outputs }) => {
|
|
367
|
+
const types: (string | undefined)[] = []
|
|
368
|
+
if (inputs?.[0]) {
|
|
369
|
+
try {
|
|
370
|
+
types.push(functions.typesMap.getUniqueName(inputs[0]))
|
|
371
|
+
} catch {
|
|
372
|
+
types.push(inputs[0])
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (outputs?.[0]) {
|
|
376
|
+
try {
|
|
377
|
+
types.push(functions.typesMap.getUniqueName(outputs[0]))
|
|
378
|
+
} catch {
|
|
379
|
+
types.push(outputs[0])
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return types
|
|
383
|
+
})
|
|
384
|
+
.flat()
|
|
385
|
+
.filter((s): s is string => !!s && !PRIMITIVE_TYPES.has(s)),
|
|
386
|
+
...functions.typesMap.customTypes.keys(),
|
|
387
|
+
...(schemasFromTypes || []),
|
|
388
|
+
...Array.from(schemaLookup.keys()),
|
|
389
|
+
])
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
export function validateAgentModels(
|
|
393
|
+
logger: InspectorLogger,
|
|
394
|
+
state: InspectorState | Omit<InspectorState, 'typesLookup'>,
|
|
395
|
+
modelConfig?: InspectorModelConfig
|
|
396
|
+
): void {
|
|
397
|
+
const aliases = modelConfig?.models ?? {}
|
|
398
|
+
|
|
399
|
+
for (const [, meta] of Object.entries(state.agents.agentsMeta)) {
|
|
400
|
+
const model = meta.model
|
|
401
|
+
if (!model) {
|
|
402
|
+
logger.critical(
|
|
403
|
+
ErrorCode.MISSING_MODEL,
|
|
404
|
+
`AI agent '${meta.name}' is missing the 'model' property.`
|
|
405
|
+
)
|
|
406
|
+
continue
|
|
407
|
+
}
|
|
408
|
+
if (model.includes('/')) continue
|
|
409
|
+
if (!aliases[model]) {
|
|
410
|
+
const available = Object.keys(aliases)
|
|
411
|
+
logger.critical(
|
|
412
|
+
ErrorCode.INVALID_MODEL,
|
|
413
|
+
`AI agent '${meta.name}' uses model alias '${model}' which is not defined in pikku.config.json models. ` +
|
|
414
|
+
`Available aliases: ${available.join(', ') || 'none'}`
|
|
415
|
+
)
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
export function validateAgentOverrides(
|
|
421
|
+
logger: InspectorLogger,
|
|
422
|
+
state: InspectorState | Omit<InspectorState, 'typesLookup'>,
|
|
423
|
+
modelConfig?: InspectorModelConfig
|
|
424
|
+
): void {
|
|
425
|
+
const overrides = modelConfig?.agentOverrides ?? {}
|
|
426
|
+
const aliases = modelConfig?.models ?? {}
|
|
427
|
+
const agentNames = new Set(
|
|
428
|
+
Object.values(state.agents.agentsMeta).map((m) => m.name)
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
for (const [agentName, override] of Object.entries(overrides)) {
|
|
432
|
+
if (!agentNames.has(agentName)) {
|
|
433
|
+
logger.warn(`agentOverrides references unknown agent '${agentName}'`)
|
|
434
|
+
}
|
|
435
|
+
if (
|
|
436
|
+
override.model &&
|
|
437
|
+
!override.model.includes('/') &&
|
|
438
|
+
!aliases[override.model]
|
|
439
|
+
) {
|
|
440
|
+
logger.critical(
|
|
441
|
+
ErrorCode.INVALID_MODEL,
|
|
442
|
+
`agentOverrides['${agentName}'].model uses alias '${override.model}' which is not defined in models.`
|
|
443
|
+
)
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export function computeDiagnostics(state: InspectorState): void {
|
|
449
|
+
const diagnostics: InspectorDiagnostic[] = []
|
|
450
|
+
|
|
451
|
+
for (const [id, meta] of Object.entries(state.functions.meta)) {
|
|
452
|
+
if (meta.services && !meta.services.optimized) {
|
|
453
|
+
diagnostics.push({
|
|
454
|
+
code: ErrorCode.SERVICES_NOT_DESTRUCTURED,
|
|
455
|
+
message: `Function '${id}' does not destructure its services parameter, preventing tree-shaking optimization.`,
|
|
456
|
+
sourceFile: meta.pikkuFuncId,
|
|
457
|
+
position: 0,
|
|
458
|
+
})
|
|
459
|
+
}
|
|
460
|
+
if (meta.wires && !meta.wires.optimized) {
|
|
461
|
+
diagnostics.push({
|
|
462
|
+
code: ErrorCode.WIRES_NOT_DESTRUCTURED,
|
|
463
|
+
message: `Function '${id}' does not destructure its wires parameter, preventing tree-shaking optimization.`,
|
|
464
|
+
sourceFile: meta.pikkuFuncId,
|
|
465
|
+
position: 0,
|
|
466
|
+
})
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
for (const [id, def] of Object.entries(state.middleware.definitions)) {
|
|
471
|
+
if (def.services && !def.services.optimized) {
|
|
472
|
+
diagnostics.push({
|
|
473
|
+
code: ErrorCode.SERVICES_NOT_DESTRUCTURED,
|
|
474
|
+
message: `Middleware '${id}' does not destructure its services parameter, preventing tree-shaking optimization.`,
|
|
475
|
+
sourceFile: def.sourceFile,
|
|
476
|
+
position: def.position,
|
|
477
|
+
})
|
|
478
|
+
}
|
|
479
|
+
if (def.wires && !def.wires.optimized) {
|
|
480
|
+
diagnostics.push({
|
|
481
|
+
code: ErrorCode.WIRES_NOT_DESTRUCTURED,
|
|
482
|
+
message: `Middleware '${id}' does not destructure its wires parameter, preventing tree-shaking optimization.`,
|
|
483
|
+
sourceFile: def.sourceFile,
|
|
484
|
+
position: def.position,
|
|
485
|
+
})
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
for (const [id, def] of Object.entries(state.permissions.definitions)) {
|
|
490
|
+
if (def.services && !def.services.optimized) {
|
|
491
|
+
diagnostics.push({
|
|
492
|
+
code: ErrorCode.SERVICES_NOT_DESTRUCTURED,
|
|
493
|
+
message: `Permission '${id}' does not destructure its services parameter, preventing tree-shaking optimization.`,
|
|
494
|
+
sourceFile: def.sourceFile,
|
|
495
|
+
position: def.position,
|
|
496
|
+
})
|
|
497
|
+
}
|
|
498
|
+
if (def.wires && !def.wires.optimized) {
|
|
499
|
+
diagnostics.push({
|
|
500
|
+
code: ErrorCode.WIRES_NOT_DESTRUCTURED,
|
|
501
|
+
message: `Permission '${id}' does not destructure its wires parameter, preventing tree-shaking optimization.`,
|
|
502
|
+
sourceFile: def.sourceFile,
|
|
503
|
+
position: def.position,
|
|
504
|
+
})
|
|
505
|
+
}
|
|
266
506
|
}
|
|
267
507
|
|
|
268
|
-
state.
|
|
508
|
+
state.diagnostics = diagnostics
|
|
269
509
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as ts from 'typescript'
|
|
2
|
+
import { ExternalPackageConfig } from '../types.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the external package name from an imported identifier.
|
|
6
|
+
* Checks if the identifier's import module specifier matches any
|
|
7
|
+
* configured external package.
|
|
8
|
+
*
|
|
9
|
+
* This is a general utility — any wire handler that processes a `func`
|
|
10
|
+
* property can use it to detect when the function comes from an
|
|
11
|
+
* external package.
|
|
12
|
+
*/
|
|
13
|
+
export const resolveExternalPackageName = (
|
|
14
|
+
identifier: ts.Identifier,
|
|
15
|
+
checker: ts.TypeChecker,
|
|
16
|
+
externalPackages?: Record<string, ExternalPackageConfig>
|
|
17
|
+
): string | null => {
|
|
18
|
+
if (!externalPackages || Object.keys(externalPackages).length === 0) {
|
|
19
|
+
return null
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const sym = checker.getSymbolAtLocation(identifier)
|
|
23
|
+
if (!sym) return null
|
|
24
|
+
|
|
25
|
+
const decl = sym.declarations?.[0]
|
|
26
|
+
if (!decl || !ts.isImportSpecifier(decl)) return null
|
|
27
|
+
|
|
28
|
+
// ImportSpecifier -> NamedImports -> ImportClause -> ImportDeclaration
|
|
29
|
+
const importDecl = decl.parent?.parent?.parent
|
|
30
|
+
if (!importDecl || !ts.isImportDeclaration(importDecl)) return null
|
|
31
|
+
if (!ts.isStringLiteral(importDecl.moduleSpecifier)) return null
|
|
32
|
+
|
|
33
|
+
const moduleSpecifier = importDecl.moduleSpecifier.text
|
|
34
|
+
|
|
35
|
+
for (const config of Object.values(externalPackages)) {
|
|
36
|
+
if (config.package === moduleSpecifier) {
|
|
37
|
+
return config.package
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return null
|
|
42
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { TypesMap } from '../types-map.js'
|
|
2
|
+
import type { FunctionsMeta } from '@pikku/core'
|
|
3
|
+
|
|
4
|
+
export function resolveFunctionIOTypes(
|
|
5
|
+
pikkuFuncId: string,
|
|
6
|
+
functionsMeta: FunctionsMeta,
|
|
7
|
+
typesMap: TypesMap,
|
|
8
|
+
requiredTypes: Set<string>
|
|
9
|
+
): { inputType: string; outputType: string } {
|
|
10
|
+
const functionMeta = functionsMeta[pikkuFuncId]
|
|
11
|
+
if (!functionMeta) {
|
|
12
|
+
throw new Error(
|
|
13
|
+
`Function ${pikkuFuncId} not found in functionsMeta. Please check your configuration.`
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const input = functionMeta.inputs ? functionMeta.inputs[0] : undefined
|
|
18
|
+
const output = functionMeta.outputs ? functionMeta.outputs[0] : undefined
|
|
19
|
+
|
|
20
|
+
let inputType = 'null'
|
|
21
|
+
if (input) {
|
|
22
|
+
try {
|
|
23
|
+
inputType = typesMap.getTypeMeta(input).uniqueName
|
|
24
|
+
} catch {
|
|
25
|
+
inputType = input
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let outputType = 'null'
|
|
30
|
+
if (output) {
|
|
31
|
+
try {
|
|
32
|
+
outputType = typesMap.getTypeMeta(output).uniqueName
|
|
33
|
+
} catch {
|
|
34
|
+
outputType = output
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
requiredTypes.add(inputType)
|
|
39
|
+
requiredTypes.add(outputType)
|
|
40
|
+
|
|
41
|
+
return { inputType, outputType }
|
|
42
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as ts from 'typescript'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Resolve an identifier to its definition, optionally unwrapping
|
|
5
|
+
* known "define" wrapper functions (e.g. defineHTTPRoutes, defineCLICommands).
|
|
6
|
+
*
|
|
7
|
+
* When the identifier resolves to a variable whose initializer is a call
|
|
8
|
+
* to one of the `unwrapFunctionNames`, the first argument of that call
|
|
9
|
+
* is returned instead.
|
|
10
|
+
*/
|
|
11
|
+
export function resolveIdentifier(
|
|
12
|
+
node: ts.Identifier,
|
|
13
|
+
checker: ts.TypeChecker,
|
|
14
|
+
unwrapFunctionNames?: string[]
|
|
15
|
+
): ts.Node | undefined {
|
|
16
|
+
const symbol = checker.getSymbolAtLocation(node)
|
|
17
|
+
if (!symbol) return undefined
|
|
18
|
+
|
|
19
|
+
// Handle aliased symbols (imports)
|
|
20
|
+
let resolvedSymbol = symbol
|
|
21
|
+
if (resolvedSymbol.flags & ts.SymbolFlags.Alias) {
|
|
22
|
+
resolvedSymbol = checker.getAliasedSymbol(resolvedSymbol) ?? resolvedSymbol
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const decl =
|
|
26
|
+
resolvedSymbol.valueDeclaration || resolvedSymbol.declarations?.[0]
|
|
27
|
+
if (!decl) return undefined
|
|
28
|
+
|
|
29
|
+
// Follow to the actual value (handles imports, variable declarations)
|
|
30
|
+
if (ts.isVariableDeclaration(decl) && decl.initializer) {
|
|
31
|
+
// Check if it's a call to one of the unwrap functions
|
|
32
|
+
if (
|
|
33
|
+
ts.isCallExpression(decl.initializer) &&
|
|
34
|
+
unwrapFunctionNames &&
|
|
35
|
+
unwrapFunctionNames.length > 0
|
|
36
|
+
) {
|
|
37
|
+
const expr = decl.initializer.expression
|
|
38
|
+
if (ts.isIdentifier(expr) && unwrapFunctionNames.includes(expr.text)) {
|
|
39
|
+
return decl.initializer.arguments[0]
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return decl.initializer
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return undefined
|
|
46
|
+
}
|