@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
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
|
-
import {
|
|
2
|
+
import { detectSchemaVendorOrError } from '../utils/detect-schema-vendor.js';
|
|
3
|
+
import { extractFunctionName, funcIdToTypeName, } from '../utils/extract-function-name.js';
|
|
3
4
|
import { extractFunctionNode } from '../utils/extract-function-node.js';
|
|
5
|
+
import { extractUsedWires } from '../utils/extract-services.js';
|
|
6
|
+
import { formatVersionedId } from '@pikku/core';
|
|
4
7
|
import { getPropertyValue, getCommonWireMetaData, } from '../utils/get-property-value.js';
|
|
5
8
|
import { resolveMiddleware } from '../utils/middleware.js';
|
|
6
9
|
import { resolvePermissions } from '../utils/permissions.js';
|
|
10
|
+
import { extractWireNames } from '../utils/post-process.js';
|
|
7
11
|
import { ErrorCode } from '../error-codes.js';
|
|
8
12
|
const isValidVariableName = (name) => {
|
|
9
13
|
const regex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
@@ -13,7 +17,8 @@ const nullifyTypes = (type) => {
|
|
|
13
17
|
if (type === 'void' ||
|
|
14
18
|
type === 'undefined' ||
|
|
15
19
|
type === 'unknown' ||
|
|
16
|
-
type === 'any'
|
|
20
|
+
type === 'any' ||
|
|
21
|
+
type === 'null') {
|
|
17
22
|
return null;
|
|
18
23
|
}
|
|
19
24
|
return type;
|
|
@@ -131,8 +136,8 @@ const getNamesAndTypes = (checker, typesMap, direction, funcName, type) => {
|
|
|
131
136
|
if (!type) {
|
|
132
137
|
return { names: [], types: [] };
|
|
133
138
|
}
|
|
134
|
-
// 1) Handle an explicit void (or undefined) type up front
|
|
135
|
-
if (type.flags & ts.TypeFlags.VoidLike) {
|
|
139
|
+
// 1) Handle an explicit void (or undefined or null) type up front
|
|
140
|
+
if (type.flags & (ts.TypeFlags.VoidLike | ts.TypeFlags.Null)) {
|
|
136
141
|
return {
|
|
137
142
|
names: [],
|
|
138
143
|
types: [],
|
|
@@ -147,7 +152,7 @@ const getNamesAndTypes = (checker, typesMap, direction, funcName, type) => {
|
|
|
147
152
|
const firstName = rawNames[0];
|
|
148
153
|
if (rawNames.length > 1 || (firstName && !isValidVariableName(firstName))) {
|
|
149
154
|
const aliasType = rawNames.join(' | ');
|
|
150
|
-
const aliasName =
|
|
155
|
+
const aliasName = funcIdToTypeName(funcName) + direction;
|
|
151
156
|
// record the alias in your TypesMap
|
|
152
157
|
const references = rawTypes
|
|
153
158
|
.map((t) => resolveTypeImports(t, typesMap, true, checker))
|
|
@@ -186,7 +191,6 @@ const isPrimitiveType = (type) => {
|
|
|
186
191
|
ts.TypeFlags.Void |
|
|
187
192
|
ts.TypeFlags.Undefined |
|
|
188
193
|
ts.TypeFlags.Null |
|
|
189
|
-
ts.TypeFlags.Any |
|
|
190
194
|
ts.TypeFlags.Unknown |
|
|
191
195
|
ts.TypeFlags.VoidLike;
|
|
192
196
|
return (type.flags & primitiveFlags) !== 0;
|
|
@@ -213,7 +217,7 @@ function unwrapPromise(checker, type) {
|
|
|
213
217
|
* Inspect pikkuFunc calls, extract input/output and first-arg destructuring,
|
|
214
218
|
* then push into state.functions.meta.
|
|
215
219
|
*/
|
|
216
|
-
export const addFunctions = (logger, node, checker, state) => {
|
|
220
|
+
export const addFunctions = (logger, node, checker, state, options) => {
|
|
217
221
|
if (!ts.isCallExpression(node))
|
|
218
222
|
return;
|
|
219
223
|
const { expression, arguments: args, typeArguments } = node;
|
|
@@ -232,80 +236,139 @@ export const addFunctions = (logger, node, checker, state) => {
|
|
|
232
236
|
}
|
|
233
237
|
if (args.length === 0)
|
|
234
238
|
return;
|
|
235
|
-
|
|
239
|
+
let { pikkuFuncId, name, explicitName, exportedName } = extractFunctionName(node, checker, state.rootDir);
|
|
240
|
+
if (!pikkuFuncId || pikkuFuncId.startsWith('__temp_')) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
236
243
|
let title;
|
|
237
244
|
let tags;
|
|
238
245
|
let summary;
|
|
239
246
|
let description;
|
|
240
247
|
let errors;
|
|
241
248
|
let expose;
|
|
242
|
-
let
|
|
249
|
+
let remote;
|
|
250
|
+
let mcp;
|
|
251
|
+
let requiresApproval;
|
|
252
|
+
let version;
|
|
243
253
|
let objectNode;
|
|
254
|
+
let nodeDisplayName = null;
|
|
255
|
+
let nodeCategory = null;
|
|
256
|
+
let nodeType = null;
|
|
257
|
+
let nodeErrorOutput = null;
|
|
244
258
|
// Extract the function node using shared utility
|
|
245
259
|
const firstArg = args[0];
|
|
246
260
|
const { funcNode: handlerNode, resolvedFunc, isDirectFunction, } = extractFunctionNode(firstArg, checker);
|
|
247
|
-
// Variables to hold
|
|
248
|
-
let
|
|
249
|
-
let
|
|
250
|
-
// Helper to resolve schema identifier to its actual source file
|
|
251
|
-
|
|
261
|
+
// Variables to hold schema references if provided
|
|
262
|
+
let inputSchemaRef = null;
|
|
263
|
+
let outputSchemaRef = null;
|
|
264
|
+
// Helper to resolve schema identifier to its actual source file and detect vendor.
|
|
265
|
+
// Logs a fatal error and returns null if vendor cannot be determined.
|
|
266
|
+
const resolveSchemaRef = (identifier, context) => {
|
|
252
267
|
const symbol = checker.getSymbolAtLocation(identifier);
|
|
253
268
|
if (!symbol)
|
|
254
269
|
return null;
|
|
255
270
|
const decl = symbol.valueDeclaration || symbol.declarations?.[0];
|
|
256
271
|
if (!decl)
|
|
257
272
|
return null;
|
|
273
|
+
let sourceFile;
|
|
258
274
|
// If it's an import specifier, resolve the aliased symbol to get the actual source
|
|
259
275
|
if (ts.isImportSpecifier(decl)) {
|
|
260
276
|
const aliasedSymbol = checker.getAliasedSymbol(symbol);
|
|
261
277
|
if (aliasedSymbol) {
|
|
262
278
|
const aliasedDecl = aliasedSymbol.valueDeclaration || aliasedSymbol.declarations?.[0];
|
|
263
279
|
if (aliasedDecl) {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
280
|
+
sourceFile = aliasedDecl.getSourceFile().fileName;
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
return null;
|
|
268
284
|
}
|
|
269
285
|
}
|
|
286
|
+
else {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
sourceFile = decl.getSourceFile().fileName;
|
|
270
292
|
}
|
|
271
|
-
|
|
293
|
+
const vendor = detectSchemaVendorOrError(identifier, checker, logger, context, sourceFile);
|
|
294
|
+
if (!vendor)
|
|
295
|
+
return null;
|
|
272
296
|
return {
|
|
273
297
|
variableName: identifier.text,
|
|
274
|
-
sourceFile
|
|
298
|
+
sourceFile,
|
|
299
|
+
vendor,
|
|
275
300
|
};
|
|
276
301
|
};
|
|
277
302
|
// Extract config properties if using object form
|
|
278
303
|
if (ts.isObjectLiteralExpression(firstArg)) {
|
|
279
304
|
objectNode = firstArg;
|
|
280
305
|
const metadata = getCommonWireMetaData(firstArg, 'Function', name, logger);
|
|
306
|
+
if (metadata.disabled)
|
|
307
|
+
return;
|
|
281
308
|
title = metadata.title;
|
|
282
309
|
tags = metadata.tags;
|
|
283
310
|
summary = metadata.summary;
|
|
284
311
|
description = metadata.description;
|
|
285
312
|
errors = metadata.errors;
|
|
286
313
|
expose = getPropertyValue(firstArg, 'expose');
|
|
287
|
-
|
|
288
|
-
|
|
314
|
+
remote = getPropertyValue(firstArg, 'remote');
|
|
315
|
+
mcp = getPropertyValue(firstArg, 'mcp');
|
|
316
|
+
requiresApproval = getPropertyValue(firstArg, 'requiresApproval');
|
|
317
|
+
const versionRaw = getPropertyValue(firstArg, 'version');
|
|
318
|
+
if (versionRaw !== null && versionRaw !== undefined) {
|
|
319
|
+
const parsed = Number(versionRaw);
|
|
320
|
+
if (Number.isInteger(parsed) && parsed >= 1) {
|
|
321
|
+
version = parsed;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
// Extract node config from nested object
|
|
325
|
+
for (const prop of firstArg.properties) {
|
|
326
|
+
if (ts.isPropertyAssignment(prop) &&
|
|
327
|
+
ts.isIdentifier(prop.name) &&
|
|
328
|
+
prop.name.text === 'node' &&
|
|
329
|
+
ts.isObjectLiteralExpression(prop.initializer)) {
|
|
330
|
+
const nodeObj = prop.initializer;
|
|
331
|
+
nodeDisplayName = getPropertyValue(nodeObj, 'displayName');
|
|
332
|
+
nodeCategory = getPropertyValue(nodeObj, 'category');
|
|
333
|
+
nodeType = getPropertyValue(nodeObj, 'type');
|
|
334
|
+
nodeErrorOutput = getPropertyValue(nodeObj, 'errorOutput');
|
|
335
|
+
if (!nodeDisplayName) {
|
|
336
|
+
logger.critical(ErrorCode.MISSING_NAME, `Function '${name}' node config is missing the required 'displayName' property.`);
|
|
337
|
+
}
|
|
338
|
+
if (!nodeCategory) {
|
|
339
|
+
logger.critical(ErrorCode.MISSING_NAME, `Function '${name}' node config is missing the required 'category' property.`);
|
|
340
|
+
}
|
|
341
|
+
if (!nodeType) {
|
|
342
|
+
logger.critical(ErrorCode.MISSING_NAME, `Function '${name}' node config is missing the required 'type' property.`);
|
|
343
|
+
}
|
|
344
|
+
else if (!['trigger', 'action', 'end'].includes(nodeType)) {
|
|
345
|
+
logger.critical(ErrorCode.INVALID_VALUE, `Function '${name}' node config has invalid type '${nodeType}'. Must be 'trigger', 'action', or 'end'.`);
|
|
346
|
+
}
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
// Extract schema variable names from input/output properties
|
|
289
351
|
for (const prop of firstArg.properties) {
|
|
290
352
|
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
|
|
291
353
|
const propName = prop.name.text;
|
|
292
354
|
if (propName === 'input' || propName === 'output') {
|
|
293
355
|
if (ts.isIdentifier(prop.initializer)) {
|
|
294
|
-
// Good - it's a variable reference, resolve its actual source file
|
|
295
|
-
const
|
|
356
|
+
// Good - it's a variable reference, resolve its actual source file and vendor
|
|
357
|
+
const context = `Function '${name}' ${propName}`;
|
|
358
|
+
const ref = resolveSchemaRef(prop.initializer, context);
|
|
296
359
|
if (ref) {
|
|
297
360
|
if (propName === 'input') {
|
|
298
|
-
|
|
361
|
+
inputSchemaRef = ref;
|
|
299
362
|
}
|
|
300
363
|
else {
|
|
301
|
-
|
|
364
|
+
outputSchemaRef = ref;
|
|
302
365
|
}
|
|
303
366
|
}
|
|
304
367
|
}
|
|
305
368
|
else if (ts.isCallExpression(prop.initializer)) {
|
|
306
369
|
// Bad - it's an inline expression
|
|
307
|
-
const schemaName = `${
|
|
308
|
-
logger.critical(ErrorCode.
|
|
370
|
+
const schemaName = `${funcIdToTypeName(name)}${propName.charAt(0).toUpperCase() + propName.slice(1)}`;
|
|
371
|
+
logger.critical(ErrorCode.INLINE_SCHEMA, `Inline schemas are not supported for '${propName}' in '${name}'.\n` +
|
|
309
372
|
` Extract to an exported variable:\n` +
|
|
310
373
|
` export const ${schemaName} = ${prop.initializer.getText()}\n` +
|
|
311
374
|
` Then use: ${propName}: ${schemaName}`);
|
|
@@ -314,6 +377,12 @@ export const addFunctions = (logger, node, checker, state) => {
|
|
|
314
377
|
}
|
|
315
378
|
}
|
|
316
379
|
}
|
|
380
|
+
if (version !== undefined) {
|
|
381
|
+
const baseName = explicitName || exportedName || pikkuFuncId;
|
|
382
|
+
pikkuFuncId = formatVersionedId(baseName, version);
|
|
383
|
+
}
|
|
384
|
+
const isMCPToolFunc = expression.text === 'pikkuMCPToolFunc';
|
|
385
|
+
const mcpEnabled = mcp || isMCPToolFunc;
|
|
317
386
|
// Pick the handler: use resolvedFunc when it exists and is a function, otherwise fall back to handlerNode
|
|
318
387
|
const handler = resolvedFunc &&
|
|
319
388
|
(ts.isArrowFunction(resolvedFunc) || ts.isFunctionExpression(resolvedFunc))
|
|
@@ -321,10 +390,11 @@ export const addFunctions = (logger, node, checker, state) => {
|
|
|
321
390
|
: handlerNode;
|
|
322
391
|
// Validate that we got a valid function
|
|
323
392
|
if (!ts.isArrowFunction(handler) && !ts.isFunctionExpression(handler)) {
|
|
324
|
-
logger.error(`• No valid 'func' property found for ${
|
|
393
|
+
logger.error(`• No valid 'func' property found for ${pikkuFuncId}.`);
|
|
325
394
|
// Create stub metadata to prevent "function not found" errors in wirings
|
|
326
|
-
state.functions.meta[
|
|
327
|
-
|
|
395
|
+
state.functions.meta[pikkuFuncId] = {
|
|
396
|
+
pikkuFuncId,
|
|
397
|
+
functionType: 'user',
|
|
328
398
|
name,
|
|
329
399
|
services: { optimized: false, services: [] },
|
|
330
400
|
inputSchemaName: null,
|
|
@@ -353,37 +423,24 @@ export const addFunctions = (logger, node, checker, state) => {
|
|
|
353
423
|
}
|
|
354
424
|
}
|
|
355
425
|
}
|
|
356
|
-
else if (ts.isIdentifier(firstParam.name)
|
|
426
|
+
else if (ts.isIdentifier(firstParam.name) &&
|
|
427
|
+
!firstParam.name.text.startsWith('_')) {
|
|
357
428
|
services.optimized = false;
|
|
358
429
|
}
|
|
359
430
|
}
|
|
360
|
-
|
|
361
|
-
const usedWires = [];
|
|
362
|
-
const thirdParam = handler.parameters[2];
|
|
363
|
-
if (thirdParam && ts.isObjectBindingPattern(thirdParam.name)) {
|
|
364
|
-
for (const elem of thirdParam.name.elements) {
|
|
365
|
-
const propertyName = elem.propertyName && ts.isIdentifier(elem.propertyName)
|
|
366
|
-
? elem.propertyName.text
|
|
367
|
-
: ts.isIdentifier(elem.name)
|
|
368
|
-
? elem.name.text
|
|
369
|
-
: undefined;
|
|
370
|
-
if (propertyName) {
|
|
371
|
-
usedWires.push(propertyName);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
}
|
|
431
|
+
const wires = extractUsedWires(handler, 2);
|
|
375
432
|
// --- Generics → ts.Type[], unwrapped from Promise ---
|
|
376
433
|
const genericTypes = (typeArguments ?? [])
|
|
377
434
|
.map((tn) => checker.getTypeFromTypeNode(tn))
|
|
378
435
|
.map((t) => unwrapPromise(checker, t));
|
|
379
|
-
const capitalizedName =
|
|
436
|
+
const capitalizedName = funcIdToTypeName(name);
|
|
380
437
|
// --- Input Extraction ---
|
|
381
438
|
let inputNames = [];
|
|
382
439
|
let inputTypes = [];
|
|
383
|
-
if (
|
|
440
|
+
if (inputSchemaRef) {
|
|
384
441
|
const schemaName = `${capitalizedName}Input`;
|
|
385
442
|
inputNames = [schemaName];
|
|
386
|
-
state.
|
|
443
|
+
state.schemaLookup.set(schemaName, inputSchemaRef);
|
|
387
444
|
state.functions.typesMap.addCustomType(schemaName, 'unknown', []);
|
|
388
445
|
}
|
|
389
446
|
else if (genericTypes.length >= 1 && genericTypes[0]) {
|
|
@@ -397,17 +454,17 @@ export const addFunctions = (logger, node, checker, state) => {
|
|
|
397
454
|
const secondParam = handler.parameters[1];
|
|
398
455
|
if (secondParam) {
|
|
399
456
|
const paramType = checker.getTypeAtLocation(secondParam);
|
|
400
|
-
const result = getNamesAndTypes(checker, state.functions.typesMap, 'Input',
|
|
457
|
+
const result = getNamesAndTypes(checker, state.functions.typesMap, 'Input', pikkuFuncId, paramType);
|
|
401
458
|
inputNames = result.names;
|
|
402
459
|
inputTypes = result.types;
|
|
403
460
|
}
|
|
404
461
|
}
|
|
405
462
|
// --- Output Extraction ---
|
|
406
463
|
let outputNames = [];
|
|
407
|
-
if (
|
|
464
|
+
if (outputSchemaRef) {
|
|
408
465
|
const schemaName = `${capitalizedName}Output`;
|
|
409
466
|
outputNames = [schemaName];
|
|
410
|
-
state.
|
|
467
|
+
state.schemaLookup.set(schemaName, outputSchemaRef);
|
|
411
468
|
state.functions.typesMap.addCustomType(schemaName, 'unknown', []);
|
|
412
469
|
}
|
|
413
470
|
else if (genericTypes.length >= 2) {
|
|
@@ -418,35 +475,99 @@ export const addFunctions = (logger, node, checker, state) => {
|
|
|
418
475
|
if (sig) {
|
|
419
476
|
const rawRet = checker.getReturnTypeOfSignature(sig);
|
|
420
477
|
const unwrapped = unwrapPromise(checker, rawRet);
|
|
421
|
-
outputNames = getNamesAndTypes(checker, state.functions.typesMap, 'Output',
|
|
478
|
+
outputNames = getNamesAndTypes(checker, state.functions.typesMap, 'Output', pikkuFuncId, unwrapped).names;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
const mcpOutputTypes = {
|
|
482
|
+
pikkuMCPResourceFunc: 'MCPResourceResponse',
|
|
483
|
+
pikkuMCPToolFunc: 'MCPToolResponse',
|
|
484
|
+
pikkuMCPPromptFunc: 'MCPPromptResponse',
|
|
485
|
+
};
|
|
486
|
+
const mcpOutputType = mcpOutputTypes[expression.text];
|
|
487
|
+
if (mcpOutputType && outputNames[0] !== mcpOutputType) {
|
|
488
|
+
let resolved = false;
|
|
489
|
+
const rawSymbol = checker.getSymbolAtLocation(expression);
|
|
490
|
+
const funcSymbol = rawSymbol && rawSymbol.flags & ts.SymbolFlags.Alias
|
|
491
|
+
? checker.getAliasedSymbol(rawSymbol)
|
|
492
|
+
: rawSymbol;
|
|
493
|
+
const funcDecls = funcSymbol?.getDeclarations() || [];
|
|
494
|
+
for (const funcDecl of funcDecls) {
|
|
495
|
+
if (resolved)
|
|
496
|
+
break;
|
|
497
|
+
const mcpTypeSymbol = checker.resolveName(mcpOutputType, funcDecl, ts.SymbolFlags.Type, false);
|
|
498
|
+
if (mcpTypeSymbol) {
|
|
499
|
+
const aliased = mcpTypeSymbol.flags & ts.SymbolFlags.Alias
|
|
500
|
+
? checker.getAliasedSymbol(mcpTypeSymbol)
|
|
501
|
+
: mcpTypeSymbol;
|
|
502
|
+
const typeDecl = aliased?.getDeclarations()?.[0];
|
|
503
|
+
if (typeDecl) {
|
|
504
|
+
const path = typeDecl.getSourceFile().fileName;
|
|
505
|
+
if (!state.functions.typesMap.exists(mcpOutputType, path)) {
|
|
506
|
+
state.functions.typesMap.addType(mcpOutputType, path);
|
|
507
|
+
}
|
|
508
|
+
resolved = true;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
if (!resolved) {
|
|
513
|
+
state.functions.typesMap.addCustomType(mcpOutputType, mcpOutputType, []);
|
|
422
514
|
}
|
|
515
|
+
outputNames = [mcpOutputType];
|
|
423
516
|
}
|
|
424
517
|
if (inputNames.length > 1) {
|
|
425
518
|
logger.warn('More than one input type detected, only the first one will be used as a schema.');
|
|
426
519
|
}
|
|
427
520
|
// Store the input type for later use
|
|
428
521
|
if (inputTypes.length > 0) {
|
|
429
|
-
state.typesLookup.set(
|
|
522
|
+
state.typesLookup.set(pikkuFuncId, inputTypes);
|
|
430
523
|
}
|
|
431
524
|
// --- resolve middleware ---
|
|
432
|
-
|
|
525
|
+
let middleware = objectNode
|
|
433
526
|
? resolveMiddleware(state, objectNode, tags, checker)
|
|
434
527
|
: undefined;
|
|
435
528
|
// --- resolve permissions ---
|
|
436
|
-
|
|
529
|
+
let permissions = objectNode
|
|
437
530
|
? resolvePermissions(state, objectNode, tags, checker)
|
|
438
531
|
: undefined;
|
|
439
|
-
|
|
440
|
-
|
|
532
|
+
if (options.tags?.length) {
|
|
533
|
+
tags = [...new Set([...(tags || []), ...options.tags])];
|
|
534
|
+
const tagEntries = options.tags.map((tag) => ({
|
|
535
|
+
type: 'tag',
|
|
536
|
+
tag,
|
|
537
|
+
}));
|
|
538
|
+
const existingMiddlewareTags = new Set((middleware || [])
|
|
539
|
+
.filter((m) => m.type === 'tag')
|
|
540
|
+
.map((m) => m.tag));
|
|
541
|
+
const newMiddleware = tagEntries.filter((e) => !existingMiddlewareTags.has(e.tag));
|
|
542
|
+
if (newMiddleware.length > 0) {
|
|
543
|
+
middleware = [...(middleware || []), ...newMiddleware];
|
|
544
|
+
}
|
|
545
|
+
const existingPermissionTags = new Set((permissions || [])
|
|
546
|
+
.filter((p) => p.type === 'tag')
|
|
547
|
+
.map((p) => p.tag));
|
|
548
|
+
const newPermissions = tagEntries.filter((e) => !existingPermissionTags.has(e.tag));
|
|
549
|
+
if (newPermissions.length > 0) {
|
|
550
|
+
permissions = [...(permissions || []), ...newPermissions];
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
const sessionless = expression.text !== 'pikkuFunc';
|
|
554
|
+
state.functions.meta[pikkuFuncId] = {
|
|
555
|
+
pikkuFuncId,
|
|
556
|
+
functionType: 'user',
|
|
557
|
+
funcWrapper: expression.text,
|
|
558
|
+
sessionless,
|
|
441
559
|
name,
|
|
442
560
|
services,
|
|
443
|
-
|
|
561
|
+
wires: wires.wires.length > 0 || !wires.optimized ? wires : undefined,
|
|
444
562
|
inputSchemaName: inputNames[0] ?? null,
|
|
445
563
|
outputSchemaName: outputNames[0] ?? null,
|
|
446
564
|
inputs: inputNames.filter((n) => n !== 'void') ?? null,
|
|
447
565
|
outputs: outputNames.filter((n) => n !== 'void') ?? null,
|
|
448
566
|
expose: expose || undefined,
|
|
449
|
-
|
|
567
|
+
remote: remote || undefined,
|
|
568
|
+
mcp: mcpEnabled || undefined,
|
|
569
|
+
requiresApproval: requiresApproval || undefined,
|
|
570
|
+
version,
|
|
450
571
|
title,
|
|
451
572
|
tags: tags || undefined,
|
|
452
573
|
summary,
|
|
@@ -456,42 +577,96 @@ export const addFunctions = (logger, node, checker, state) => {
|
|
|
456
577
|
permissions,
|
|
457
578
|
isDirectFunction,
|
|
458
579
|
};
|
|
580
|
+
// Populate node metadata if node config is present
|
|
581
|
+
if (nodeDisplayName && nodeCategory && nodeType) {
|
|
582
|
+
state.nodes.files.add(node.getSourceFile().fileName);
|
|
583
|
+
state.nodes.meta[pikkuFuncId] = {
|
|
584
|
+
name: pikkuFuncId,
|
|
585
|
+
displayName: nodeDisplayName,
|
|
586
|
+
category: nodeCategory,
|
|
587
|
+
type: nodeType,
|
|
588
|
+
rpc: pikkuFuncId,
|
|
589
|
+
description,
|
|
590
|
+
errorOutput: nodeErrorOutput ?? false,
|
|
591
|
+
inputSchemaName: inputNames[0] ?? null,
|
|
592
|
+
outputSchemaName: outputNames[0] ?? null,
|
|
593
|
+
tags,
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
if (mcpEnabled) {
|
|
597
|
+
if (!description) {
|
|
598
|
+
logger.critical(ErrorCode.MISSING_DESCRIPTION, `MCP tool '${name}' is missing a description.`);
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
state.mcpEndpoints.files.add(node.getSourceFile().fileName);
|
|
602
|
+
state.mcpEndpoints.toolsMeta[name] = {
|
|
603
|
+
pikkuFuncId,
|
|
604
|
+
name,
|
|
605
|
+
title: title || undefined,
|
|
606
|
+
description,
|
|
607
|
+
summary,
|
|
608
|
+
errors,
|
|
609
|
+
tags,
|
|
610
|
+
inputSchema: inputNames[0] ?? null,
|
|
611
|
+
outputSchema: outputNames[0] ?? null,
|
|
612
|
+
middleware,
|
|
613
|
+
permissions,
|
|
614
|
+
};
|
|
615
|
+
state.serviceAggregation.usedFunctions.add(pikkuFuncId);
|
|
616
|
+
extractWireNames(middleware).forEach((n) => state.serviceAggregation.usedMiddleware.add(n));
|
|
617
|
+
extractWireNames(permissions).forEach((n) => state.serviceAggregation.usedPermissions.add(n));
|
|
618
|
+
}
|
|
619
|
+
// Workflow functions don't get registered as RPC functions,
|
|
620
|
+
// they are their own type handled by add-workflow
|
|
621
|
+
if (expression.text.includes('Workflow')) {
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
// Trigger and channel connect/disconnect functions are not callable via RPC
|
|
625
|
+
const nonRPCPatterns = [
|
|
626
|
+
/Trigger/i,
|
|
627
|
+
/ChannelConnection/i,
|
|
628
|
+
/ChannelDisconnection/i,
|
|
629
|
+
];
|
|
630
|
+
if (nonRPCPatterns.some((pattern) => pattern.test(expression.text))) {
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
459
633
|
// Store function file location for wiring generation
|
|
460
634
|
if (exportedName) {
|
|
461
|
-
state.functions.files.set(
|
|
635
|
+
state.functions.files.set(pikkuFuncId, {
|
|
462
636
|
path: node.getSourceFile().fileName,
|
|
463
637
|
exportedName,
|
|
464
638
|
});
|
|
465
639
|
}
|
|
466
|
-
// Workflow functions don't get registered as RPC functions,
|
|
467
|
-
// they are their own type handled by add-workdflow
|
|
468
|
-
if (expression.text.includes('Workflow')) {
|
|
469
|
-
return;
|
|
470
|
-
}
|
|
471
640
|
if (exportedName || explicitName) {
|
|
472
641
|
if (!exportedName) {
|
|
473
642
|
logger.error(`• Function with explicit name '${name}' is not exported, this is not allowed.`);
|
|
474
643
|
return;
|
|
475
644
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
state.rpc.invokedFunctions.add(pikkuFuncName);
|
|
645
|
+
if (remote) {
|
|
646
|
+
state.rpc.invokedFunctions.add(pikkuFuncId);
|
|
479
647
|
}
|
|
480
648
|
if (expose) {
|
|
481
|
-
state.rpc.exposedMeta[name] =
|
|
649
|
+
state.rpc.exposedMeta[name] = pikkuFuncId;
|
|
482
650
|
state.rpc.exposedFiles.set(name, {
|
|
483
651
|
path: node.getSourceFile().fileName,
|
|
484
652
|
exportedName,
|
|
485
653
|
});
|
|
486
654
|
// Track exposed RPC function for service aggregation
|
|
487
|
-
state.serviceAggregation.usedFunctions.add(
|
|
655
|
+
state.serviceAggregation.usedFunctions.add(pikkuFuncId);
|
|
488
656
|
}
|
|
489
657
|
// We add it to internal meta to allow autocomplete for everything
|
|
490
|
-
state.rpc.internalMeta[name] =
|
|
658
|
+
state.rpc.internalMeta[name] = pikkuFuncId;
|
|
659
|
+
if (version !== undefined) {
|
|
660
|
+
state.rpc.internalMeta[pikkuFuncId] = pikkuFuncId;
|
|
661
|
+
state.rpc.invokedFunctions.add(pikkuFuncId);
|
|
662
|
+
}
|
|
491
663
|
// But we only import the actual function if it's actually invoked to keep
|
|
492
664
|
// bundle size down
|
|
493
|
-
if (state.rpc.invokedFunctions.has(
|
|
494
|
-
|
|
665
|
+
if (state.rpc.invokedFunctions.has(pikkuFuncId) ||
|
|
666
|
+
expose ||
|
|
667
|
+
remote ||
|
|
668
|
+
mcpEnabled) {
|
|
669
|
+
state.rpc.internalFiles.set(pikkuFuncId, {
|
|
495
670
|
path: node.getSourceFile().fileName,
|
|
496
671
|
exportedName,
|
|
497
672
|
});
|
|
@@ -1,15 +1,24 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
import { AddWiring, InspectorState } from '../types.js';
|
|
3
|
+
import type { InspectorLogger } from '../types.js';
|
|
2
4
|
/**
|
|
3
|
-
*
|
|
4
|
-
* query and params. Returns undefined (we only mutate metaTypes).
|
|
5
|
+
* Parameters for registering an HTTP route
|
|
5
6
|
*/
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
export interface RegisterHTTPRouteParams {
|
|
8
|
+
obj: ts.ObjectLiteralExpression;
|
|
9
|
+
state: InspectorState;
|
|
10
|
+
checker: ts.TypeChecker;
|
|
11
|
+
logger: InspectorLogger;
|
|
12
|
+
sourceFile: ts.SourceFile;
|
|
13
|
+
basePath?: string;
|
|
14
|
+
inheritedTags?: string[];
|
|
15
|
+
}
|
|
11
16
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
17
|
+
* Shared function to register an HTTP route in the inspector state.
|
|
18
|
+
* Used by both wireHTTP and wireHTTPRoutes.
|
|
19
|
+
*/
|
|
20
|
+
export declare function registerHTTPRoute({ obj, state, checker, logger, sourceFile, basePath, inheritedTags, }: RegisterHTTPRouteParams): void;
|
|
21
|
+
/**
|
|
22
|
+
* Process wireHTTP calls
|
|
14
23
|
*/
|
|
15
24
|
export declare const addHTTPRoute: AddWiring;
|