@pikku/inspector 0.11.2 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -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 +81 -61
- package/dist/add/add-cli.d.ts +1 -1
- package/dist/add/add-cli.js +42 -19
- package/dist/add/add-file-extends-core-type.d.ts +1 -1
- package/dist/add/add-file-with-config.d.ts +1 -1
- package/dist/add/add-file-with-factory.d.ts +1 -1
- package/dist/add/add-file-with-factory.js +2 -0
- package/dist/add/add-functions.d.ts +1 -1
- package/dist/add/add-functions.js +256 -82
- package/dist/add/add-http-route.d.ts +20 -10
- package/dist/add/add-http-route.js +156 -66
- package/dist/add/add-http-routes.d.ts +5 -0
- package/dist/add/add-http-routes.js +160 -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.d.ts +1 -1
- package/dist/add/add-mcp-prompt.js +14 -9
- package/dist/add/add-mcp-resource.d.ts +1 -1
- 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.d.ts +1 -1
- package/dist/add/add-queue-worker.js +18 -12
- package/dist/add/add-rpc-invocations.d.ts +3 -3
- package/dist/add/add-rpc-invocations.js +24 -10
- package/dist/add/add-schedule.d.ts +1 -1
- 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-wire-addon.d.ts +7 -0
- package/dist/add/add-wire-addon.js +70 -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.d.ts +1 -1
- package/dist/add/add-workflow.js +6 -4
- package/dist/error-codes.d.ts +15 -1
- package/dist/error-codes.js +20 -1
- package/dist/index.d.ts +9 -8
- package/dist/index.js +5 -4
- package/dist/inspector.d.ts +2 -2
- package/dist/inspector.js +95 -15
- 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 +180 -50
- package/dist/utils/compute-required-schemas.d.ts +4 -0
- package/dist/utils/compute-required-schemas.js +40 -0
- package/dist/utils/contract-hashes.d.ts +52 -0
- package/dist/utils/contract-hashes.js +269 -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/does-type-extend-core-type.d.ts +1 -1
- package/dist/utils/ensure-function-metadata.d.ts +6 -3
- 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.d.ts +1 -1
- package/dist/utils/filter-inspector-state.js +107 -23
- package/dist/utils/filter-utils.d.ts +2 -2
- package/dist/utils/get-files-and-methods.d.ts +1 -1
- 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 +9 -32
- package/dist/utils/middleware.js +80 -66
- package/dist/utils/permissions.d.ts +4 -4
- package/dist/utils/permissions.js +10 -10
- package/dist/utils/post-process.d.ts +11 -11
- package/dist/utils/post-process.js +247 -24
- package/dist/utils/resolve-addon-package.d.ts +16 -0
- package/dist/utils/resolve-addon-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 +70 -23
- package/dist/utils/serialize-inspector-state.js +98 -22
- 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/validate-auth-sessionless.d.ts +3 -0
- package/dist/utils/validate-auth-sessionless.js +14 -0
- package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +34 -102
- package/dist/utils/workflow/dsl/extract-dsl-workflow.d.ts +1 -1
- 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.d.ts +1 -1
- package/dist/visit.js +13 -6
- package/package.json +14 -4
- package/src/add/add-ai-agent.ts +468 -0
- package/src/add/add-channel.ts +103 -79
- package/src/add/add-cli.ts +68 -24
- package/src/add/add-file-extends-core-type.ts +1 -1
- package/src/add/add-file-with-config.ts +1 -1
- package/src/add/add-file-with-factory.ts +3 -1
- package/src/add/add-functions.ts +349 -103
- package/src/add/add-http-route.ts +263 -89
- package/src/add/add-http-routes.ts +229 -0
- package/src/add/add-keyed-wiring.ts +151 -0
- package/src/add/add-mcp-prompt.ts +27 -16
- package/src/add/add-mcp-resource.ts +28 -16
- package/src/add/add-middleware.ts +482 -80
- package/src/add/add-permission.ts +199 -40
- package/src/add/add-queue-worker.ts +25 -20
- package/src/add/add-rpc-invocations.ts +28 -11
- package/src/add/add-schedule.ts +17 -12
- 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-wire-addon.ts +80 -0
- package/src/add/add-workflow-graph.ts +180 -522
- package/src/add/add-workflow.ts +7 -6
- package/src/error-codes.ts +25 -1
- package/src/index.ts +23 -13
- package/src/inspector.ts +139 -19
- package/src/schema-generator.ts +1 -0
- package/src/types-map.ts +12 -1
- package/src/types.ts +199 -69
- package/src/utils/compute-required-schemas.ts +48 -0
- package/src/utils/contract-hashes.test.ts +553 -0
- package/src/utils/contract-hashes.ts +386 -0
- package/src/utils/custom-types-generator.ts +88 -0
- package/src/utils/detect-schema-vendor.ts +90 -0
- package/src/utils/does-type-extend-core-type.ts +1 -1
- package/src/utils/ensure-function-metadata.ts +325 -8
- 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 +37 -25
- package/src/utils/filter-inspector-state.ts +146 -32
- package/src/utils/filter-utils.test.ts +1 -1
- package/src/utils/filter-utils.ts +2 -2
- package/src/utils/get-files-and-methods.ts +1 -1
- 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 +131 -69
- package/src/utils/permissions.test.ts +35 -12
- package/src/utils/permissions.ts +12 -12
- package/src/utils/post-process.ts +306 -44
- package/src/utils/resolve-addon-package.ts +49 -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 +184 -43
- 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/validate-auth-sessionless.ts +29 -0
- package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +43 -119
- package/src/utils/workflow/dsl/extract-dsl-workflow.ts +26 -6
- 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 +19 -7
- 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
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as ts from 'typescript'
|
|
2
|
-
import { PermissionMetadata } from '@pikku/core'
|
|
2
|
+
import type { PermissionMetadata } from '@pikku/core'
|
|
3
3
|
import { extractFunctionName } from './extract-function-name.js'
|
|
4
|
-
import { InspectorState } from '../types.js'
|
|
4
|
+
import type { 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,18 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import type {
|
|
2
|
+
InspectorState,
|
|
3
|
+
InspectorLogger,
|
|
4
|
+
InspectorOptions,
|
|
5
|
+
InspectorModelConfig,
|
|
6
|
+
MiddlewareGroupMeta,
|
|
7
|
+
InspectorDiagnostic,
|
|
8
|
+
} from '../types.js'
|
|
9
|
+
import type {
|
|
4
10
|
FunctionServicesMeta,
|
|
5
11
|
MiddlewareMetadata,
|
|
6
12
|
PermissionMetadata,
|
|
7
13
|
} from '@pikku/core'
|
|
8
14
|
import { extractTypeKeys } from './type-utils.js'
|
|
9
|
-
import {
|
|
10
|
-
extractAllServiceMetadata,
|
|
11
|
-
ServiceMetadata,
|
|
12
|
-
} from './extract-service-metadata.js'
|
|
15
|
+
import { ErrorCode } from '../error-codes.js'
|
|
13
16
|
|
|
14
17
|
/**
|
|
15
18
|
* Helper to extract wire-level middleware/permission names from metadata.
|
|
@@ -133,7 +136,7 @@ export function aggregateRequiredServices(
|
|
|
133
136
|
|
|
134
137
|
// 2. Services from used middleware (individual + groups)
|
|
135
138
|
usedMiddleware.forEach((middlewareName) => {
|
|
136
|
-
const middlewareMeta = state.middleware.
|
|
139
|
+
const middlewareMeta = state.middleware.definitions[middlewareName]
|
|
137
140
|
if (middlewareMeta?.services) {
|
|
138
141
|
addServices(middlewareMeta.services)
|
|
139
142
|
}
|
|
@@ -141,7 +144,7 @@ export function aggregateRequiredServices(
|
|
|
141
144
|
|
|
142
145
|
// 3. Services from used permissions (individual + groups)
|
|
143
146
|
usedPermissions.forEach((permissionName) => {
|
|
144
|
-
const permissionMeta = state.permissions.
|
|
147
|
+
const permissionMeta = state.permissions.definitions[permissionName]
|
|
145
148
|
if (permissionMeta?.services) {
|
|
146
149
|
addServices(permissionMeta.services)
|
|
147
150
|
}
|
|
@@ -220,50 +223,309 @@ export function aggregateRequiredServices(
|
|
|
220
223
|
}
|
|
221
224
|
}
|
|
222
225
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
226
|
+
export function validateSecretOverrides(
|
|
227
|
+
logger: InspectorLogger,
|
|
228
|
+
state: InspectorState | Omit<InspectorState, 'typesLookup'>
|
|
229
|
+
): void {
|
|
230
|
+
const { wireAddonDeclarations } = state.rpc
|
|
231
|
+
if (!wireAddonDeclarations || wireAddonDeclarations.size === 0) return
|
|
232
|
+
|
|
233
|
+
const secretNames = new Set(state.secrets.definitions.map((d) => d.name))
|
|
234
|
+
|
|
235
|
+
for (const [namespace, addonDecl] of wireAddonDeclarations.entries()) {
|
|
236
|
+
if (!addonDecl.secretOverrides) continue
|
|
237
|
+
|
|
238
|
+
for (const secretKey of Object.keys(addonDecl.secretOverrides)) {
|
|
239
|
+
if (!secretNames.has(secretKey)) {
|
|
240
|
+
const availableSecrets = Array.from(secretNames)
|
|
241
|
+
logger.critical(
|
|
242
|
+
ErrorCode.INVALID_VALUE,
|
|
243
|
+
`Secret override '${secretKey}' in addon '${namespace}' (${addonDecl.package}) does not exist. Available secrets: ${availableSecrets.join(', ') || 'none'}`
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function validateVariableOverrides(
|
|
251
|
+
logger: InspectorLogger,
|
|
252
|
+
state: InspectorState | Omit<InspectorState, 'typesLookup'>
|
|
253
|
+
): void {
|
|
254
|
+
const { wireAddonDeclarations } = state.rpc
|
|
255
|
+
if (!wireAddonDeclarations || wireAddonDeclarations.size === 0) return
|
|
256
|
+
|
|
257
|
+
const variableNames = new Set(state.variables.definitions.map((d) => d.name))
|
|
258
|
+
|
|
259
|
+
for (const [namespace, addonDecl] of wireAddonDeclarations.entries()) {
|
|
260
|
+
if (!addonDecl.variableOverrides) continue
|
|
261
|
+
|
|
262
|
+
for (const variableKey of Object.keys(addonDecl.variableOverrides)) {
|
|
263
|
+
if (!variableNames.has(variableKey)) {
|
|
264
|
+
const availableVariables = Array.from(variableNames)
|
|
265
|
+
logger.critical(
|
|
266
|
+
ErrorCode.INVALID_VALUE,
|
|
267
|
+
`Variable override '${variableKey}' in addon '${namespace}' (${addonDecl.package}) does not exist. Available variables: ${availableVariables.join(', ') || 'none'}`
|
|
268
|
+
)
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export function computeResolvedIOTypes(state: InspectorState): void {
|
|
275
|
+
const { functions } = state
|
|
276
|
+
for (const [pikkuFuncId, meta] of Object.entries(functions.meta)) {
|
|
277
|
+
const input = meta.inputs?.[0]
|
|
278
|
+
const output = meta.outputs?.[0]
|
|
279
|
+
|
|
280
|
+
let inputType = 'null'
|
|
281
|
+
if (input) {
|
|
282
|
+
try {
|
|
283
|
+
inputType = functions.typesMap.getTypeMeta(input).uniqueName
|
|
284
|
+
} catch {
|
|
285
|
+
inputType = input
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
let outputType = 'null'
|
|
290
|
+
if (output) {
|
|
291
|
+
try {
|
|
292
|
+
outputType = functions.typesMap.getTypeMeta(output).uniqueName
|
|
293
|
+
} catch {
|
|
294
|
+
outputType = output
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
state.resolvedIOTypes[pikkuFuncId] = { inputType, outputType }
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const serializeGroupMap = (
|
|
303
|
+
groupMap: Map<string, MiddlewareGroupMeta>
|
|
304
|
+
): Record<string, MiddlewareGroupMeta> => {
|
|
305
|
+
const result: Record<string, MiddlewareGroupMeta> = {}
|
|
306
|
+
for (const [key, meta] of groupMap.entries()) {
|
|
307
|
+
result[key] = {
|
|
308
|
+
exportName: meta.exportName,
|
|
309
|
+
sourceFile: meta.sourceFile,
|
|
310
|
+
position: meta.position,
|
|
311
|
+
services: meta.services,
|
|
312
|
+
count: meta.count,
|
|
313
|
+
instanceIds: meta.instanceIds,
|
|
314
|
+
isFactory: meta.isFactory,
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return result
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function computeMiddlewareGroupsMeta(state: InspectorState): void {
|
|
321
|
+
state.middlewareGroupsMeta = {
|
|
322
|
+
definitions: state.middleware.definitions,
|
|
323
|
+
instances: state.middleware.instances,
|
|
324
|
+
httpGroups: serializeGroupMap(state.http.routeMiddleware),
|
|
325
|
+
tagGroups: serializeGroupMap(state.middleware.tagMiddleware),
|
|
326
|
+
channelMiddleware: {
|
|
327
|
+
definitions: state.channelMiddleware.definitions,
|
|
328
|
+
instances: state.channelMiddleware.instances,
|
|
329
|
+
tagGroups: serializeGroupMap(state.channelMiddleware.tagMiddleware),
|
|
330
|
+
},
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export function computePermissionsGroupsMeta(state: InspectorState): void {
|
|
335
|
+
const httpGroups: Record<string, any> = {}
|
|
336
|
+
for (const [pattern, meta] of state.http.routePermissions.entries()) {
|
|
337
|
+
httpGroups[pattern] = {
|
|
338
|
+
exportName: meta.exportName,
|
|
339
|
+
sourceFile: meta.sourceFile,
|
|
340
|
+
position: meta.position,
|
|
341
|
+
services: meta.services,
|
|
342
|
+
count: meta.count,
|
|
343
|
+
instanceIds: meta.instanceIds,
|
|
344
|
+
isFactory: meta.isFactory,
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const tagGroups: Record<string, any> = {}
|
|
349
|
+
for (const [tag, meta] of state.permissions.tagPermissions.entries()) {
|
|
350
|
+
tagGroups[tag] = {
|
|
351
|
+
exportName: meta.exportName,
|
|
352
|
+
sourceFile: meta.sourceFile,
|
|
353
|
+
position: meta.position,
|
|
354
|
+
services: meta.services,
|
|
355
|
+
count: meta.count,
|
|
356
|
+
instanceIds: meta.instanceIds,
|
|
357
|
+
isFactory: meta.isFactory,
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
state.permissionsGroupsMeta = {
|
|
362
|
+
definitions: state.permissions.definitions,
|
|
363
|
+
httpGroups,
|
|
364
|
+
tagGroups,
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const PRIMITIVE_TYPES = new Set([
|
|
369
|
+
'boolean',
|
|
370
|
+
'string',
|
|
371
|
+
'number',
|
|
372
|
+
'null',
|
|
373
|
+
'undefined',
|
|
374
|
+
'void',
|
|
375
|
+
'any',
|
|
376
|
+
'unknown',
|
|
377
|
+
'never',
|
|
378
|
+
])
|
|
379
|
+
|
|
380
|
+
export function computeRequiredSchemas(
|
|
381
|
+
state: InspectorState,
|
|
382
|
+
options: InspectorOptions
|
|
383
|
+
): void {
|
|
384
|
+
const { functions, schemaLookup } = state
|
|
385
|
+
const schemasFromTypes = options.schemaConfig?.schemasFromTypes
|
|
386
|
+
|
|
387
|
+
state.requiredSchemas = new Set<string>([
|
|
388
|
+
...Object.values(functions.meta)
|
|
389
|
+
.flatMap(({ inputs, outputs }) => {
|
|
390
|
+
const types: (string | undefined)[] = []
|
|
391
|
+
if (inputs?.[0]) {
|
|
392
|
+
try {
|
|
393
|
+
types.push(functions.typesMap.getUniqueName(inputs[0]))
|
|
394
|
+
} catch {
|
|
395
|
+
types.push(inputs[0])
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (outputs?.[0]) {
|
|
399
|
+
try {
|
|
400
|
+
types.push(functions.typesMap.getUniqueName(outputs[0]))
|
|
401
|
+
} catch {
|
|
402
|
+
types.push(outputs[0])
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return types
|
|
406
|
+
})
|
|
407
|
+
.filter((s): s is string => !!s && !PRIMITIVE_TYPES.has(s)),
|
|
408
|
+
...functions.typesMap.customTypes.keys(),
|
|
409
|
+
...(schemasFromTypes || []),
|
|
410
|
+
...Array.from(schemaLookup.keys()),
|
|
411
|
+
])
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export function validateAgentModels(
|
|
415
|
+
logger: InspectorLogger,
|
|
231
416
|
state: InspectorState | Omit<InspectorState, 'typesLookup'>,
|
|
232
|
-
|
|
417
|
+
modelConfig?: InspectorModelConfig
|
|
233
418
|
): void {
|
|
234
|
-
|
|
235
|
-
|
|
419
|
+
const aliases = modelConfig?.models ?? {}
|
|
420
|
+
|
|
421
|
+
for (const [, meta] of Object.entries(state.agents.agentsMeta)) {
|
|
422
|
+
const model = meta.model
|
|
423
|
+
if (!model) {
|
|
424
|
+
logger.critical(
|
|
425
|
+
ErrorCode.MISSING_MODEL,
|
|
426
|
+
`AI agent '${meta.name}' is missing the 'model' property.`
|
|
427
|
+
)
|
|
428
|
+
continue
|
|
429
|
+
}
|
|
430
|
+
if (model.includes('/')) continue
|
|
431
|
+
if (!aliases[model]) {
|
|
432
|
+
const available = Object.keys(aliases)
|
|
433
|
+
logger.critical(
|
|
434
|
+
ErrorCode.INVALID_MODEL,
|
|
435
|
+
`AI agent '${meta.name}' uses model alias '${model}' which is not defined in pikku.config.json models. ` +
|
|
436
|
+
`Available aliases: ${available.join(', ') || 'none'}`
|
|
437
|
+
)
|
|
438
|
+
}
|
|
236
439
|
}
|
|
440
|
+
}
|
|
237
441
|
|
|
238
|
-
|
|
442
|
+
export function validateAgentOverrides(
|
|
443
|
+
logger: InspectorLogger,
|
|
444
|
+
state: InspectorState | Omit<InspectorState, 'typesLookup'>,
|
|
445
|
+
modelConfig?: InspectorModelConfig
|
|
446
|
+
): void {
|
|
447
|
+
const overrides = modelConfig?.agentOverrides ?? {}
|
|
448
|
+
const aliases = modelConfig?.models ?? {}
|
|
449
|
+
const agentNames = new Set(
|
|
450
|
+
Object.values(state.agents.agentsMeta).map((m) => m.name)
|
|
451
|
+
)
|
|
239
452
|
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
453
|
+
for (const [agentName, override] of Object.entries(overrides)) {
|
|
454
|
+
if (!agentNames.has(agentName)) {
|
|
455
|
+
logger.warn(`agentOverrides references unknown agent '${agentName}'`)
|
|
456
|
+
}
|
|
457
|
+
if (
|
|
458
|
+
override.model &&
|
|
459
|
+
!override.model.includes('/') &&
|
|
460
|
+
!aliases[override.model]
|
|
461
|
+
) {
|
|
462
|
+
logger.critical(
|
|
463
|
+
ErrorCode.INVALID_MODEL,
|
|
464
|
+
`agentOverrides['${agentName}'].model uses alias '${override.model}' which is not defined in models.`
|
|
465
|
+
)
|
|
466
|
+
}
|
|
248
467
|
}
|
|
468
|
+
}
|
|
249
469
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const wireServicesMeta = extractAllServiceMetadata(
|
|
253
|
-
servicesTypes[0],
|
|
254
|
-
checker,
|
|
255
|
-
state.rootDir
|
|
256
|
-
)
|
|
470
|
+
export function computeDiagnostics(state: InspectorState): void {
|
|
471
|
+
const diagnostics: InspectorDiagnostic[] = []
|
|
257
472
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
473
|
+
for (const [id, meta] of Object.entries(state.functions.meta)) {
|
|
474
|
+
if (meta.services && !meta.services.optimized) {
|
|
475
|
+
diagnostics.push({
|
|
476
|
+
code: ErrorCode.SERVICES_NOT_DESTRUCTURED,
|
|
477
|
+
message: `Function '${id}' does not destructure its services parameter, preventing tree-shaking optimization.`,
|
|
478
|
+
sourceFile: meta.pikkuFuncId,
|
|
479
|
+
position: 0,
|
|
480
|
+
})
|
|
481
|
+
}
|
|
482
|
+
if (meta.wires && !meta.wires.optimized) {
|
|
483
|
+
diagnostics.push({
|
|
484
|
+
code: ErrorCode.WIRES_NOT_DESTRUCTURED,
|
|
485
|
+
message: `Function '${id}' does not destructure its wires parameter, preventing tree-shaking optimization.`,
|
|
486
|
+
sourceFile: meta.pikkuFuncId,
|
|
487
|
+
position: 0,
|
|
488
|
+
})
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
for (const [id, def] of Object.entries(state.middleware.definitions)) {
|
|
493
|
+
if (def.services && !def.services.optimized) {
|
|
494
|
+
diagnostics.push({
|
|
495
|
+
code: ErrorCode.SERVICES_NOT_DESTRUCTURED,
|
|
496
|
+
message: `Middleware '${id}' does not destructure its services parameter, preventing tree-shaking optimization.`,
|
|
497
|
+
sourceFile: def.sourceFile,
|
|
498
|
+
position: def.position,
|
|
499
|
+
})
|
|
500
|
+
}
|
|
501
|
+
if (def.wires && !def.wires.optimized) {
|
|
502
|
+
diagnostics.push({
|
|
503
|
+
code: ErrorCode.WIRES_NOT_DESTRUCTURED,
|
|
504
|
+
message: `Middleware '${id}' does not destructure its wires parameter, preventing tree-shaking optimization.`,
|
|
505
|
+
sourceFile: def.sourceFile,
|
|
506
|
+
position: def.position,
|
|
507
|
+
})
|
|
508
|
+
}
|
|
509
|
+
}
|
|
264
510
|
|
|
265
|
-
|
|
511
|
+
for (const [id, def] of Object.entries(state.permissions.definitions)) {
|
|
512
|
+
if (def.services && !def.services.optimized) {
|
|
513
|
+
diagnostics.push({
|
|
514
|
+
code: ErrorCode.SERVICES_NOT_DESTRUCTURED,
|
|
515
|
+
message: `Permission '${id}' does not destructure its services parameter, preventing tree-shaking optimization.`,
|
|
516
|
+
sourceFile: def.sourceFile,
|
|
517
|
+
position: def.position,
|
|
518
|
+
})
|
|
519
|
+
}
|
|
520
|
+
if (def.wires && !def.wires.optimized) {
|
|
521
|
+
diagnostics.push({
|
|
522
|
+
code: ErrorCode.WIRES_NOT_DESTRUCTURED,
|
|
523
|
+
message: `Permission '${id}' does not destructure its wires parameter, preventing tree-shaking optimization.`,
|
|
524
|
+
sourceFile: def.sourceFile,
|
|
525
|
+
position: def.position,
|
|
526
|
+
})
|
|
527
|
+
}
|
|
266
528
|
}
|
|
267
529
|
|
|
268
|
-
state.
|
|
530
|
+
state.diagnostics = diagnostics
|
|
269
531
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as ts from 'typescript'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the addon package name from an imported identifier.
|
|
5
|
+
* Checks if the identifier's import module specifier matches any
|
|
6
|
+
* configured addon package.
|
|
7
|
+
*
|
|
8
|
+
* This is a general utility — any wire handler that processes a `func`
|
|
9
|
+
* property can use it to detect when the function comes from an
|
|
10
|
+
* addon package.
|
|
11
|
+
*/
|
|
12
|
+
export const resolveAddonName = (
|
|
13
|
+
identifier: ts.Identifier,
|
|
14
|
+
checker: ts.TypeChecker,
|
|
15
|
+
wireAddonDeclarations?: Map<
|
|
16
|
+
string,
|
|
17
|
+
{
|
|
18
|
+
package: string
|
|
19
|
+
rpcEndpoint?: string
|
|
20
|
+
secretOverrides?: Record<string, string>
|
|
21
|
+
variableOverrides?: Record<string, string>
|
|
22
|
+
}
|
|
23
|
+
>
|
|
24
|
+
): string | null => {
|
|
25
|
+
if (!wireAddonDeclarations || wireAddonDeclarations.size === 0) {
|
|
26
|
+
return null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const sym = checker.getSymbolAtLocation(identifier)
|
|
30
|
+
if (!sym) return null
|
|
31
|
+
|
|
32
|
+
const decl = sym.declarations?.[0]
|
|
33
|
+
if (!decl || !ts.isImportSpecifier(decl)) return null
|
|
34
|
+
|
|
35
|
+
// ImportSpecifier -> NamedImports -> ImportClause -> ImportDeclaration
|
|
36
|
+
const importDecl = decl.parent?.parent?.parent
|
|
37
|
+
if (!importDecl || !ts.isImportDeclaration(importDecl)) return null
|
|
38
|
+
if (!ts.isStringLiteral(importDecl.moduleSpecifier)) return null
|
|
39
|
+
|
|
40
|
+
const moduleSpecifier = importDecl.moduleSpecifier.text
|
|
41
|
+
|
|
42
|
+
for (const addonDecl of wireAddonDeclarations.values()) {
|
|
43
|
+
if (addonDecl.package === moduleSpecifier) {
|
|
44
|
+
return addonDecl.package
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return null
|
|
49
|
+
}
|
|
@@ -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
|
+
}
|