@pikku/inspector 0.12.2 → 0.12.4
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 +46 -0
- package/dist/add/add-ai-agent.js +4 -0
- package/dist/add/add-approval-description.d.ts +5 -0
- package/dist/add/add-approval-description.js +52 -0
- package/dist/add/add-channel.js +42 -4
- package/dist/add/add-cli.js +73 -13
- package/dist/add/add-file-with-factory.js +1 -0
- package/dist/add/add-functions.js +22 -3
- package/dist/add/add-gateway.js +5 -0
- package/dist/add/add-http-route.js +5 -0
- package/dist/add/add-mcp-prompt.js +5 -0
- package/dist/add/add-mcp-resource.js +5 -0
- package/dist/add/add-middleware.js +6 -10
- package/dist/add/add-permission.js +10 -12
- package/dist/add/add-queue-worker.js +5 -0
- package/dist/add/add-schedule.js +5 -0
- package/dist/add/add-wire-addon.js +7 -0
- package/dist/add/add-workflow.js +7 -1
- package/dist/error-codes.d.ts +1 -0
- package/dist/error-codes.js +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/inspector.js +21 -7
- package/dist/types.d.ts +12 -0
- package/dist/utils/custom-types-generator.js +1 -0
- package/dist/utils/load-addon-functions-meta.d.ts +12 -0
- package/dist/utils/load-addon-functions-meta.js +76 -0
- package/dist/utils/post-process.d.ts +9 -0
- package/dist/utils/post-process.js +72 -0
- package/dist/utils/resolve-function-meta.d.ts +11 -0
- package/dist/utils/resolve-function-meta.js +17 -0
- package/dist/utils/schema-generator.js +26 -6
- package/dist/utils/serialize-inspector-state.d.ts +2 -0
- package/dist/utils/serialize-inspector-state.js +5 -0
- package/dist/utils/serialize-mcp-json.js +13 -7
- package/dist/utils/workflow/graph/convert-dsl-to-graph.js +1 -0
- package/dist/utils/workflow/graph/workflow-graph.types.d.ts +2 -0
- package/dist/visit.js +2 -0
- package/package.json +4 -3
- package/src/add/add-ai-agent.ts +6 -0
- package/src/add/add-approval-description.ts +76 -0
- package/src/add/add-channel.ts +44 -4
- package/src/add/add-cli.ts +108 -21
- package/src/add/add-file-with-factory.ts +1 -0
- package/src/add/add-functions.ts +28 -3
- package/src/add/add-gateway.ts +6 -0
- package/src/add/add-http-route.ts +6 -0
- package/src/add/add-mcp-prompt.ts +6 -0
- package/src/add/add-mcp-resource.ts +6 -0
- package/src/add/add-middleware.ts +6 -14
- package/src/add/add-permission.ts +10 -16
- package/src/add/add-queue-worker.ts +6 -0
- package/src/add/add-schedule.ts +6 -0
- package/src/add/add-wire-addon.ts +8 -0
- package/src/add/add-workflow.ts +11 -1
- package/src/error-codes.ts +3 -0
- package/src/index.ts +1 -0
- package/src/inspector.ts +33 -6
- package/src/types.ts +13 -0
- package/src/utils/custom-types-generator.ts +1 -0
- package/src/utils/load-addon-functions-meta.ts +94 -0
- package/src/utils/post-process.ts +84 -0
- package/src/utils/resolve-function-meta.ts +25 -0
- package/src/utils/schema-generator.ts +38 -10
- package/src/utils/serialize-inspector-state.ts +7 -0
- package/src/utils/serialize-mcp-json.ts +12 -7
- package/src/utils/workflow/graph/convert-dsl-to-graph.ts +1 -0
- package/src/utils/workflow/graph/workflow-graph.types.ts +2 -0
- package/src/visit.ts +2 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -141,6 +141,8 @@ export interface SerializedWorkflowGraph {
|
|
|
141
141
|
description?: string;
|
|
142
142
|
/** Tags for organization */
|
|
143
143
|
tags?: string[];
|
|
144
|
+
/** If true, workflow always executes inline without queues */
|
|
145
|
+
inline?: boolean;
|
|
144
146
|
/** Workflow context/state variables (from Zod schema) */
|
|
145
147
|
context?: WorkflowContext;
|
|
146
148
|
/** Serialized nodes */
|
package/dist/visit.js
CHANGED
|
@@ -21,6 +21,7 @@ import { addSecret, addOAuth2Credential } from './add/add-secret.js';
|
|
|
21
21
|
import { addVariable } from './add/add-variable.js';
|
|
22
22
|
import { addWorkflowGraph } from './add/add-workflow-graph.js';
|
|
23
23
|
import { addAIAgent } from './add/add-ai-agent.js';
|
|
24
|
+
import { addApprovalDescription } from './add/add-approval-description.js';
|
|
24
25
|
export const visitSetup = (logger, checker, node, state, options) => {
|
|
25
26
|
addFileExtendsCoreType(node, checker, state.singletonServicesTypeImportMap, 'CoreSingletonServices', state);
|
|
26
27
|
addFileExtendsCoreType(node, checker, state.wireServicesTypeImportMap, 'CoreServices', state);
|
|
@@ -33,6 +34,7 @@ export const visitSetup = (logger, checker, node, state, options) => {
|
|
|
33
34
|
addWireAddon(node, state, logger);
|
|
34
35
|
addMiddleware(logger, node, checker, state, options);
|
|
35
36
|
addPermission(logger, node, checker, state, options);
|
|
37
|
+
addApprovalDescription(logger, node, checker, state, options);
|
|
36
38
|
addWorkflow(logger, node, checker, state, options);
|
|
37
39
|
ts.forEachChild(node, (child) => visitSetup(logger, checker, child, state, options));
|
|
38
40
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/inspector",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.4",
|
|
4
4
|
"author": "yasser.fadl@gmail.com",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"type": "module",
|
|
@@ -30,11 +30,12 @@
|
|
|
30
30
|
"release": "yarn build && npm test",
|
|
31
31
|
"test": "bash run-tests.sh",
|
|
32
32
|
"test:watch": "bash run-tests.sh --watch",
|
|
33
|
-
"test:coverage": "bash run-tests.sh --coverage"
|
|
33
|
+
"test:coverage": "bash run-tests.sh --coverage",
|
|
34
|
+
"prepublishOnly": "yarn build"
|
|
34
35
|
},
|
|
35
36
|
"dependencies": {
|
|
36
37
|
"@openapi-contrib/json-schema-to-openapi-schema": "^4.3.1",
|
|
37
|
-
"@pikku/core": "^0.12.
|
|
38
|
+
"@pikku/core": "^0.12.9",
|
|
38
39
|
"path-to-regexp": "^8.3.0",
|
|
39
40
|
"ts-json-schema-generator": "^2.5.0",
|
|
40
41
|
"tsx": "^4.21.0",
|
package/src/add/add-ai-agent.ts
CHANGED
|
@@ -268,6 +268,9 @@ export const addAIAgent: AddWiring = (
|
|
|
268
268
|
| number
|
|
269
269
|
| null
|
|
270
270
|
const toolChoiceValue = getPropertyValue(obj, 'toolChoice') as string | null
|
|
271
|
+
const dynamicWorkflowsValue = getPropertyValue(obj, 'dynamicWorkflows') as
|
|
272
|
+
| string
|
|
273
|
+
| null
|
|
271
274
|
const toolsValue = resolveToolReferences(
|
|
272
275
|
obj,
|
|
273
276
|
checker,
|
|
@@ -455,6 +458,9 @@ export const addAIAgent: AddWiring = (
|
|
|
455
458
|
}),
|
|
456
459
|
...(toolsValue !== null && { tools: toolsValue }),
|
|
457
460
|
...(agentsValue !== null && { agents: agentsValue }),
|
|
461
|
+
...(dynamicWorkflowsValue !== null && {
|
|
462
|
+
dynamicWorkflows: dynamicWorkflowsValue as 'read' | 'always' | 'ask',
|
|
463
|
+
}),
|
|
458
464
|
tags,
|
|
459
465
|
inputSchema,
|
|
460
466
|
outputSchema,
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as ts from 'typescript'
|
|
2
|
+
import type { AddWiring } from '../types.js'
|
|
3
|
+
import { extractFunctionName } from '../utils/extract-function-name.js'
|
|
4
|
+
import {
|
|
5
|
+
extractServicesFromFunction,
|
|
6
|
+
extractUsedWires,
|
|
7
|
+
} from '../utils/extract-services.js'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Inspect pikkuApprovalDescription() calls and extract metadata
|
|
11
|
+
*/
|
|
12
|
+
export const addApprovalDescription: AddWiring = (
|
|
13
|
+
logger,
|
|
14
|
+
node,
|
|
15
|
+
checker,
|
|
16
|
+
state
|
|
17
|
+
) => {
|
|
18
|
+
if (!ts.isCallExpression(node)) return
|
|
19
|
+
|
|
20
|
+
const { expression, arguments: args } = node
|
|
21
|
+
|
|
22
|
+
if (!ts.isIdentifier(expression)) return
|
|
23
|
+
if (expression.text !== 'pikkuApprovalDescription') return
|
|
24
|
+
|
|
25
|
+
const arg = args[0]
|
|
26
|
+
if (!arg) return
|
|
27
|
+
|
|
28
|
+
let actualHandler: ts.ArrowFunction | ts.FunctionExpression
|
|
29
|
+
|
|
30
|
+
if (ts.isArrowFunction(arg) || ts.isFunctionExpression(arg)) {
|
|
31
|
+
actualHandler = arg
|
|
32
|
+
} else {
|
|
33
|
+
logger.error(`• Handler for pikkuApprovalDescription is not a function.`)
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const services = extractServicesFromFunction(actualHandler)
|
|
38
|
+
const wires = extractUsedWires(actualHandler, 1)
|
|
39
|
+
let { pikkuFuncId, exportedName } = extractFunctionName(
|
|
40
|
+
node,
|
|
41
|
+
checker,
|
|
42
|
+
state.rootDir
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
if (pikkuFuncId.startsWith('__temp_')) {
|
|
46
|
+
if (
|
|
47
|
+
ts.isVariableDeclaration(node.parent) &&
|
|
48
|
+
ts.isIdentifier(node.parent.name)
|
|
49
|
+
) {
|
|
50
|
+
pikkuFuncId = node.parent.name.text
|
|
51
|
+
} else if (
|
|
52
|
+
ts.isPropertyAssignment(node.parent) &&
|
|
53
|
+
ts.isIdentifier(node.parent.name)
|
|
54
|
+
) {
|
|
55
|
+
pikkuFuncId = node.parent.name.text
|
|
56
|
+
} else {
|
|
57
|
+
logger.error(
|
|
58
|
+
`• pikkuApprovalDescription() must be assigned to a variable or object property. ` +
|
|
59
|
+
`Extract it to a const: const myApproval = pikkuApprovalDescription(...)`
|
|
60
|
+
)
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
state.functions.approvalDescriptions[pikkuFuncId] = {
|
|
66
|
+
services,
|
|
67
|
+
wires: wires.wires.length > 0 || !wires.optimized ? wires : undefined,
|
|
68
|
+
sourceFile: node.getSourceFile().fileName,
|
|
69
|
+
position: node.getStart(),
|
|
70
|
+
exportedName,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
logger.debug(
|
|
74
|
+
`• Found approval description '${pikkuFuncId}' with services: ${services.services.join(', ')}`
|
|
75
|
+
)
|
|
76
|
+
}
|
package/src/add/add-channel.ts
CHANGED
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
} from '../utils/middleware.js'
|
|
19
19
|
import { extractWireNames } from '../utils/post-process.js'
|
|
20
20
|
import { resolveIdentifier } from '../utils/resolve-identifier.js'
|
|
21
|
+
import { resolveFunctionMeta } from '../utils/resolve-function-meta.js'
|
|
22
|
+
import { resolveAddonName } from '../utils/resolve-addon-package.js'
|
|
21
23
|
import { validateAuthSessionless } from '../utils/validate-auth-sessionless.js'
|
|
22
24
|
|
|
23
25
|
/**
|
|
@@ -92,6 +94,13 @@ function getHandlerNameFromExpression(
|
|
|
92
94
|
|
|
93
95
|
// Handle call expressions
|
|
94
96
|
if (ts.isCallExpression(expr)) {
|
|
97
|
+
// Handle addon('namespace:funcName') calls
|
|
98
|
+
if (ts.isIdentifier(expr.expression) && expr.expression.text === 'addon') {
|
|
99
|
+
const [firstArg] = expr.arguments
|
|
100
|
+
if (firstArg && ts.isStringLiteral(firstArg)) {
|
|
101
|
+
return firstArg.text
|
|
102
|
+
}
|
|
103
|
+
}
|
|
95
104
|
const { pikkuFuncId } = extractFunctionName(expr, checker, rootDir)
|
|
96
105
|
return pikkuFuncId
|
|
97
106
|
}
|
|
@@ -460,11 +469,11 @@ export function addMessagesRoutes(
|
|
|
460
469
|
continue
|
|
461
470
|
}
|
|
462
471
|
|
|
463
|
-
const fnMeta = state
|
|
472
|
+
const fnMeta = resolveFunctionMeta(state, handlerName)
|
|
464
473
|
if (!fnMeta) {
|
|
465
474
|
logger.critical(
|
|
466
475
|
ErrorCode.FUNCTION_METADATA_NOT_FOUND,
|
|
467
|
-
`No function metadata found for handler '${handlerName}'
|
|
476
|
+
`No function metadata found for channel handler '${handlerName}' on route '${routeKey}'. If this is an inline function, it must be exported for the inspector to discover it.`
|
|
468
477
|
)
|
|
469
478
|
continue
|
|
470
479
|
}
|
|
@@ -478,8 +487,17 @@ export function addMessagesRoutes(
|
|
|
478
487
|
? resolveMiddleware(state, init, routeTags, checker)
|
|
479
488
|
: undefined
|
|
480
489
|
|
|
490
|
+
// Resolve package name for addon functions (e.g. 'swaggerPetstore:addPet')
|
|
491
|
+
const colonIdx = handlerName.indexOf(':')
|
|
492
|
+
const addonNs =
|
|
493
|
+
colonIdx !== -1 ? handlerName.substring(0, colonIdx) : null
|
|
494
|
+
const packageName = addonNs
|
|
495
|
+
? state.rpc.wireAddonDeclarations.get(addonNs)?.package
|
|
496
|
+
: undefined
|
|
497
|
+
|
|
481
498
|
result[channelKey]![routeKey] = {
|
|
482
499
|
pikkuFuncId: handlerName,
|
|
500
|
+
packageName,
|
|
483
501
|
middleware: routeMiddleware,
|
|
484
502
|
}
|
|
485
503
|
}
|
|
@@ -564,8 +582,12 @@ export const addChannel: AddWiring = (
|
|
|
564
582
|
)
|
|
565
583
|
return
|
|
566
584
|
}
|
|
585
|
+
const msgPackageName = ts.isIdentifier(onMsgProp)
|
|
586
|
+
? resolveAddonName(onMsgProp, checker, state.rpc.wireAddonDeclarations)
|
|
587
|
+
: null
|
|
567
588
|
message = {
|
|
568
589
|
pikkuFuncId: msgFuncId,
|
|
590
|
+
...(msgPackageName && { packageName: msgPackageName }),
|
|
569
591
|
}
|
|
570
592
|
}
|
|
571
593
|
|
|
@@ -579,15 +601,20 @@ export const addChannel: AddWiring = (
|
|
|
579
601
|
// --- track used functions/middleware for service aggregation ---
|
|
580
602
|
// Track connect/disconnect/message handlers
|
|
581
603
|
let connectFuncId: string | undefined
|
|
604
|
+
let connectPackageName: string | null = null
|
|
582
605
|
if (connect) {
|
|
583
606
|
const extracted = extractFunctionName(connect, checker, state.rootDir)
|
|
584
607
|
connectFuncId = extracted.pikkuFuncId.startsWith('__temp_')
|
|
585
608
|
? makeContextBasedId('channel', name, 'connect')
|
|
586
609
|
: extracted.pikkuFuncId
|
|
610
|
+
connectPackageName = ts.isIdentifier(connect)
|
|
611
|
+
? resolveAddonName(connect, checker, state.rpc.wireAddonDeclarations)
|
|
612
|
+
: null
|
|
587
613
|
state.serviceAggregation.usedFunctions.add(connectFuncId)
|
|
588
614
|
}
|
|
589
615
|
|
|
590
616
|
let disconnectFuncId: string | undefined
|
|
617
|
+
let disconnectPackageName: string | null = null
|
|
591
618
|
if (disconnect) {
|
|
592
619
|
const extracted = extractFunctionName(
|
|
593
620
|
disconnect as any,
|
|
@@ -597,6 +624,9 @@ export const addChannel: AddWiring = (
|
|
|
597
624
|
disconnectFuncId = extracted.pikkuFuncId.startsWith('__temp_')
|
|
598
625
|
? makeContextBasedId('channel', name, 'disconnect')
|
|
599
626
|
: extracted.pikkuFuncId
|
|
627
|
+
disconnectPackageName = ts.isIdentifier(disconnect)
|
|
628
|
+
? resolveAddonName(disconnect, checker, state.rpc.wireAddonDeclarations)
|
|
629
|
+
: null
|
|
600
630
|
state.serviceAggregation.usedFunctions.add(disconnectFuncId)
|
|
601
631
|
}
|
|
602
632
|
|
|
@@ -634,8 +664,18 @@ export const addChannel: AddWiring = (
|
|
|
634
664
|
input: null,
|
|
635
665
|
params: params.length ? params : undefined,
|
|
636
666
|
query: query?.length ? query : undefined,
|
|
637
|
-
connect: connectFuncId
|
|
638
|
-
|
|
667
|
+
connect: connectFuncId
|
|
668
|
+
? {
|
|
669
|
+
pikkuFuncId: connectFuncId,
|
|
670
|
+
...(connectPackageName && { packageName: connectPackageName }),
|
|
671
|
+
}
|
|
672
|
+
: null,
|
|
673
|
+
disconnect: disconnectFuncId
|
|
674
|
+
? {
|
|
675
|
+
pikkuFuncId: disconnectFuncId,
|
|
676
|
+
...(disconnectPackageName && { packageName: disconnectPackageName }),
|
|
677
|
+
}
|
|
678
|
+
: null,
|
|
639
679
|
message,
|
|
640
680
|
messageWirings,
|
|
641
681
|
binary: binary === undefined ? undefined : binary,
|
package/src/add/add-cli.ts
CHANGED
|
@@ -12,9 +12,11 @@ import {
|
|
|
12
12
|
makeContextBasedId,
|
|
13
13
|
} from '../utils/extract-function-name.js'
|
|
14
14
|
import { resolveMiddleware } from '../utils/middleware.js'
|
|
15
|
+
import { resolveFunctionMeta } from '../utils/resolve-function-meta.js'
|
|
15
16
|
import { extractWireNames } from '../utils/post-process.js'
|
|
16
17
|
import { getPropertyValue } from '../utils/get-property-value.js'
|
|
17
18
|
import { resolveIdentifier } from '../utils/resolve-identifier.js'
|
|
19
|
+
import { resolveAddonName } from '../utils/resolve-addon-package.js'
|
|
18
20
|
import { validateAuthSessionless } from '../utils/validate-auth-sessionless.js'
|
|
19
21
|
|
|
20
22
|
// Track if we've warned about missing Config type to avoid duplicate warnings
|
|
@@ -344,15 +346,53 @@ function processCommand(
|
|
|
344
346
|
const propName = prop.name.text
|
|
345
347
|
|
|
346
348
|
if (propName === 'func') {
|
|
347
|
-
|
|
348
|
-
prop.initializer
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
)
|
|
352
|
-
|
|
353
|
-
|
|
349
|
+
if (
|
|
350
|
+
ts.isCallExpression(prop.initializer) &&
|
|
351
|
+
ts.isIdentifier(prop.initializer.expression) &&
|
|
352
|
+
prop.initializer.expression.text === 'addon'
|
|
353
|
+
) {
|
|
354
|
+
const [firstArg] = prop.initializer.arguments
|
|
355
|
+
if (!firstArg || !ts.isStringLiteral(firstArg)) {
|
|
356
|
+
throw new Error(
|
|
357
|
+
`addon() call requires a string literal argument in the form "namespace:funcName"`
|
|
358
|
+
)
|
|
359
|
+
}
|
|
360
|
+
pikkuFuncId = firstArg.text
|
|
361
|
+
const addonNamespace = pikkuFuncId.split(':')[0]
|
|
362
|
+
if (!addonNamespace || !pikkuFuncId.includes(':')) {
|
|
363
|
+
throw new Error(
|
|
364
|
+
`Malformed addon function ID "${pikkuFuncId}": expected "namespace:funcName" format`
|
|
365
|
+
)
|
|
366
|
+
}
|
|
367
|
+
if (!inspectorState.rpc.wireAddonDeclarations.has(addonNamespace)) {
|
|
368
|
+
throw new Error(
|
|
369
|
+
`Unknown addon namespace "${addonNamespace}" in "${pikkuFuncId}": no matching wireAddonDeclarations entry found`
|
|
370
|
+
)
|
|
371
|
+
}
|
|
372
|
+
meta.pikkuFuncId = pikkuFuncId
|
|
373
|
+
meta.packageName =
|
|
374
|
+
inspectorState.rpc.wireAddonDeclarations.get(addonNamespace)!.package
|
|
375
|
+
} else {
|
|
376
|
+
pikkuFuncId = extractFunctionName(
|
|
377
|
+
prop.initializer,
|
|
378
|
+
typeChecker,
|
|
379
|
+
inspectorState.rootDir
|
|
380
|
+
).pikkuFuncId
|
|
381
|
+
if (pikkuFuncId.startsWith('__temp_')) {
|
|
382
|
+
pikkuFuncId = makeContextBasedId('cli', programName, ...fullPath)
|
|
383
|
+
}
|
|
384
|
+
meta.pikkuFuncId = pikkuFuncId
|
|
385
|
+
const cliPackageName = ts.isIdentifier(prop.initializer)
|
|
386
|
+
? resolveAddonName(
|
|
387
|
+
prop.initializer,
|
|
388
|
+
typeChecker,
|
|
389
|
+
inspectorState.rpc.wireAddonDeclarations
|
|
390
|
+
)
|
|
391
|
+
: null
|
|
392
|
+
if (cliPackageName) {
|
|
393
|
+
meta.packageName = cliPackageName
|
|
394
|
+
}
|
|
354
395
|
}
|
|
355
|
-
meta.pikkuFuncId = pikkuFuncId
|
|
356
396
|
} else if (
|
|
357
397
|
propName === 'options' &&
|
|
358
398
|
ts.isObjectLiteralExpression(prop.initializer)
|
|
@@ -573,22 +613,31 @@ function processOptions(
|
|
|
573
613
|
}
|
|
574
614
|
|
|
575
615
|
// Extract enum values from the function input type if available
|
|
576
|
-
|
|
577
|
-
|
|
616
|
+
let derivedChoices: string[] | null = null
|
|
617
|
+
|
|
578
618
|
if (pikkuFuncId) {
|
|
579
|
-
|
|
580
|
-
|
|
619
|
+
// 1. Try TypeScript types first (most precise — handles unions, TS enums)
|
|
620
|
+
const inputTypes = inspectorState.typesLookup.get(pikkuFuncId)
|
|
621
|
+
if (inputTypes && inputTypes.length > 0) {
|
|
622
|
+
derivedChoices = extractEnumFromPropertyType(
|
|
623
|
+
inputTypes[0]!,
|
|
624
|
+
optionName,
|
|
625
|
+
typeChecker
|
|
626
|
+
)
|
|
627
|
+
}
|
|
581
628
|
|
|
582
|
-
|
|
629
|
+
// 2. Fallback: try JSON schema (works for addon functions)
|
|
630
|
+
if (!derivedChoices) {
|
|
631
|
+
derivedChoices = extractEnumFromJsonSchema(
|
|
632
|
+
inspectorState,
|
|
633
|
+
pikkuFuncId,
|
|
634
|
+
optionName
|
|
635
|
+
)
|
|
636
|
+
}
|
|
637
|
+
}
|
|
583
638
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
inputTypes[0]!,
|
|
587
|
-
optionName,
|
|
588
|
-
typeChecker
|
|
589
|
-
)
|
|
590
|
-
} else {
|
|
591
|
-
// Fallback: try to extract from Config type
|
|
639
|
+
// 3. Last resort: try Config type
|
|
640
|
+
if (!derivedChoices) {
|
|
592
641
|
derivedChoices = extractEnumFromConfigType(
|
|
593
642
|
logger,
|
|
594
643
|
optionName,
|
|
@@ -750,6 +799,44 @@ function extractEnumFromConfigType(
|
|
|
750
799
|
return extractEnumFromPropertyType(configType, propertyName, typeChecker)
|
|
751
800
|
}
|
|
752
801
|
|
|
802
|
+
/**
|
|
803
|
+
* Extracts enum values from the function's JSON schema.
|
|
804
|
+
* Works for addon functions whose schemas are generated from OpenAPI/Zod.
|
|
805
|
+
*/
|
|
806
|
+
function extractEnumFromJsonSchema(
|
|
807
|
+
inspectorState: InspectorState,
|
|
808
|
+
pikkuFuncId: string,
|
|
809
|
+
propertyName: string
|
|
810
|
+
): string[] | null {
|
|
811
|
+
const fnMeta = resolveFunctionMeta(inspectorState, pikkuFuncId)
|
|
812
|
+
if (!fnMeta?.inputSchemaName) return null
|
|
813
|
+
|
|
814
|
+
const schema = inspectorState.schemas[fnMeta.inputSchemaName] as any
|
|
815
|
+
if (!schema?.properties?.[propertyName]) return null
|
|
816
|
+
|
|
817
|
+
const prop = schema.properties[propertyName]
|
|
818
|
+
|
|
819
|
+
// Direct enum on property
|
|
820
|
+
if (prop.enum && Array.isArray(prop.enum)) {
|
|
821
|
+
const strings = prop.enum.filter((v: unknown) => typeof v === 'string')
|
|
822
|
+
if (strings.length > 0) return strings
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// Array with enum items (e.g. z.array(z.enum([...])))
|
|
826
|
+
if (
|
|
827
|
+
prop.type === 'array' &&
|
|
828
|
+
prop.items?.enum &&
|
|
829
|
+
Array.isArray(prop.items.enum)
|
|
830
|
+
) {
|
|
831
|
+
const strings = prop.items.enum.filter(
|
|
832
|
+
(v: unknown) => typeof v === 'string'
|
|
833
|
+
)
|
|
834
|
+
if (strings.length > 0) return strings
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
return null
|
|
838
|
+
}
|
|
839
|
+
|
|
753
840
|
/**
|
|
754
841
|
* Gets the property name from a property assignment
|
|
755
842
|
*/
|
|
@@ -9,6 +9,7 @@ const wrapperFunctionMap: Record<string, string> = {
|
|
|
9
9
|
pikkuServices: 'CreateSingletonServices',
|
|
10
10
|
pikkuAddonServices: 'CreateSingletonServices',
|
|
11
11
|
pikkuWireServices: 'CreateWireServices',
|
|
12
|
+
pikkuAddonWireServices: 'CreateWireServices',
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
export const addFileWithFactory = (
|
package/src/add/add-functions.ts
CHANGED
|
@@ -339,7 +339,8 @@ export const addFunctions: AddWiring = (
|
|
|
339
339
|
let remote: boolean | undefined
|
|
340
340
|
let mcp: boolean | undefined
|
|
341
341
|
let readonly_: boolean | undefined
|
|
342
|
-
let
|
|
342
|
+
let approvalRequired: boolean | undefined
|
|
343
|
+
let approvalDescription: string | undefined
|
|
343
344
|
let version: number | undefined
|
|
344
345
|
let objectNode: ts.ObjectLiteralExpression | undefined
|
|
345
346
|
let nodeDisplayName: string | null = null
|
|
@@ -421,10 +422,33 @@ export const addFunctions: AddWiring = (
|
|
|
421
422
|
remote = getPropertyValue(firstArg, 'remote') as boolean | undefined
|
|
422
423
|
mcp = getPropertyValue(firstArg, 'mcp') as boolean | undefined
|
|
423
424
|
readonly_ = getPropertyValue(firstArg, 'readonly') as boolean | undefined
|
|
424
|
-
|
|
425
|
+
approvalRequired = getPropertyValue(firstArg, 'approvalRequired') as
|
|
425
426
|
| boolean
|
|
426
427
|
| undefined
|
|
427
428
|
|
|
429
|
+
// Extract approvalDescription identifier reference
|
|
430
|
+
for (const prop of firstArg.properties) {
|
|
431
|
+
if (
|
|
432
|
+
ts.isPropertyAssignment(prop) &&
|
|
433
|
+
ts.isIdentifier(prop.name) &&
|
|
434
|
+
prop.name.text === 'approvalDescription' &&
|
|
435
|
+
ts.isIdentifier(prop.initializer)
|
|
436
|
+
) {
|
|
437
|
+
const { pikkuFuncId: descId } = extractFunctionName(
|
|
438
|
+
prop.initializer,
|
|
439
|
+
checker,
|
|
440
|
+
state.rootDir
|
|
441
|
+
)
|
|
442
|
+
if (descId && !descId.startsWith('__temp_')) {
|
|
443
|
+
approvalDescription = descId
|
|
444
|
+
} else {
|
|
445
|
+
// Try resolving the identifier directly
|
|
446
|
+
approvalDescription = prop.initializer.text
|
|
447
|
+
}
|
|
448
|
+
break
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
428
452
|
const versionRaw = getPropertyValue(firstArg, 'version')
|
|
429
453
|
if (versionRaw !== null && versionRaw !== undefined) {
|
|
430
454
|
const parsed = Number(versionRaw)
|
|
@@ -759,7 +783,8 @@ export const addFunctions: AddWiring = (
|
|
|
759
783
|
remote: remote || undefined,
|
|
760
784
|
mcp: mcpEnabled || undefined,
|
|
761
785
|
readonly: readonly_ || undefined,
|
|
762
|
-
|
|
786
|
+
approvalRequired: approvalRequired || undefined,
|
|
787
|
+
approvalDescription: approvalDescription || undefined,
|
|
763
788
|
version,
|
|
764
789
|
title,
|
|
765
790
|
tags: tags || undefined,
|
package/src/add/add-gateway.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import { getPropertyAssignmentInitializer } from '../utils/type-utils.js'
|
|
12
12
|
import { resolveMiddleware } from '../utils/middleware.js'
|
|
13
13
|
import { extractWireNames } from '../utils/post-process.js'
|
|
14
|
+
import { resolveAddonName } from '../utils/resolve-addon-package.js'
|
|
14
15
|
import type { GatewayTransportType } from '@pikku/core/gateway'
|
|
15
16
|
|
|
16
17
|
import { ErrorCode } from '../error-codes.js'
|
|
@@ -68,6 +69,10 @@ export const addGateway: AddWiring = (
|
|
|
68
69
|
pikkuFuncId = makeContextBasedId('gateway', nameValue)
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
const packageName = ts.isIdentifier(funcInitializer)
|
|
73
|
+
? resolveAddonName(funcInitializer, checker, state.rpc.wireAddonDeclarations)
|
|
74
|
+
: null
|
|
75
|
+
|
|
71
76
|
if (!nameValue || !typeValue) {
|
|
72
77
|
return
|
|
73
78
|
}
|
|
@@ -82,6 +87,7 @@ export const addGateway: AddWiring = (
|
|
|
82
87
|
state.gateways.files.add(node.getSourceFile().fileName)
|
|
83
88
|
state.gateways.meta[nameValue] = {
|
|
84
89
|
pikkuFuncId,
|
|
90
|
+
...(packageName && { packageName }),
|
|
85
91
|
name: nameValue,
|
|
86
92
|
type: typeValue,
|
|
87
93
|
route: routeValue,
|
|
@@ -21,6 +21,7 @@ import { ensureFunctionMetadata } from '../utils/ensure-function-metadata.js'
|
|
|
21
21
|
import { ErrorCode } from '../error-codes.js'
|
|
22
22
|
import { validateAuthSessionless } from '../utils/validate-auth-sessionless.js'
|
|
23
23
|
import { detectSchemaVendorOrError } from '../utils/detect-schema-vendor.js'
|
|
24
|
+
import { resolveAddonName } from '../utils/resolve-addon-package.js'
|
|
24
25
|
|
|
25
26
|
import type { InspectorLogger } from '../types.js'
|
|
26
27
|
|
|
@@ -203,6 +204,10 @@ export function registerHTTPRoute({
|
|
|
203
204
|
funcName = makeContextBasedId('http', method, fullRoute)
|
|
204
205
|
}
|
|
205
206
|
|
|
207
|
+
const packageName = ts.isIdentifier(funcInitializer)
|
|
208
|
+
? resolveAddonName(funcInitializer, checker, state.rpc.wireAddonDeclarations)
|
|
209
|
+
: null
|
|
210
|
+
|
|
206
211
|
ensureFunctionMetadata(
|
|
207
212
|
state,
|
|
208
213
|
funcName,
|
|
@@ -320,6 +325,7 @@ export function registerHTTPRoute({
|
|
|
320
325
|
state.http.files.add(sourceFile.fileName)
|
|
321
326
|
state.http.meta[method][fullRoute] = {
|
|
322
327
|
pikkuFuncId: funcName,
|
|
328
|
+
...(packageName && { packageName }),
|
|
323
329
|
route: fullRoute,
|
|
324
330
|
method: method as HTTPMethod,
|
|
325
331
|
params: params.length > 0 ? params : undefined,
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { getPropertyAssignmentInitializer } from '../utils/type-utils.js'
|
|
14
14
|
import { resolveMiddleware } from '../utils/middleware.js'
|
|
15
15
|
import { resolvePermissions } from '../utils/permissions.js'
|
|
16
|
+
import { resolveAddonName } from '../utils/resolve-addon-package.js'
|
|
16
17
|
import { ErrorCode } from '../error-codes.js'
|
|
17
18
|
|
|
18
19
|
export const addMCPPrompt: AddWiring = (
|
|
@@ -72,6 +73,10 @@ export const addMCPPrompt: AddWiring = (
|
|
|
72
73
|
pikkuFuncId = makeContextBasedId('mcp', 'prompt', nameValue)
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
const packageName = ts.isIdentifier(funcInitializer)
|
|
77
|
+
? resolveAddonName(funcInitializer, checker, state.rpc.wireAddonDeclarations)
|
|
78
|
+
: null
|
|
79
|
+
|
|
75
80
|
ensureFunctionMetadata(
|
|
76
81
|
state,
|
|
77
82
|
pikkuFuncId,
|
|
@@ -128,6 +133,7 @@ export const addMCPPrompt: AddWiring = (
|
|
|
128
133
|
|
|
129
134
|
state.mcpEndpoints.promptsMeta[nameValue] = {
|
|
130
135
|
pikkuFuncId,
|
|
136
|
+
...(packageName && { packageName }),
|
|
131
137
|
name: nameValue,
|
|
132
138
|
description,
|
|
133
139
|
summary,
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { getPropertyAssignmentInitializer } from '../utils/type-utils.js'
|
|
14
14
|
import { resolveMiddleware } from '../utils/middleware.js'
|
|
15
15
|
import { resolvePermissions } from '../utils/permissions.js'
|
|
16
|
+
import { resolveAddonName } from '../utils/resolve-addon-package.js'
|
|
16
17
|
import { ErrorCode } from '../error-codes.js'
|
|
17
18
|
|
|
18
19
|
export const addMCPResource: AddWiring = (
|
|
@@ -81,6 +82,10 @@ export const addMCPResource: AddWiring = (
|
|
|
81
82
|
pikkuFuncId = makeContextBasedId('mcp', 'resource', uriValue)
|
|
82
83
|
}
|
|
83
84
|
|
|
85
|
+
const packageName = ts.isIdentifier(funcInitializer)
|
|
86
|
+
? resolveAddonName(funcInitializer, checker, state.rpc.wireAddonDeclarations)
|
|
87
|
+
: null
|
|
88
|
+
|
|
84
89
|
ensureFunctionMetadata(
|
|
85
90
|
state,
|
|
86
91
|
pikkuFuncId,
|
|
@@ -145,6 +150,7 @@ export const addMCPResource: AddWiring = (
|
|
|
145
150
|
|
|
146
151
|
state.mcpEndpoints.resourcesMeta[uriValue] = {
|
|
147
152
|
pikkuFuncId,
|
|
153
|
+
...(packageName && { packageName }),
|
|
148
154
|
uri: uriValue,
|
|
149
155
|
title: titleValue,
|
|
150
156
|
description,
|
|
@@ -274,13 +274,10 @@ export const addMiddleware: AddWiring = (logger, node, checker, state) => {
|
|
|
274
274
|
state.rootDir
|
|
275
275
|
)
|
|
276
276
|
|
|
277
|
-
if (refs.length === 0) {
|
|
278
|
-
logger.warn(`• addMiddleware('${tag}', ...) has empty middleware array`)
|
|
279
|
-
return
|
|
280
|
-
}
|
|
281
|
-
|
|
282
277
|
const definitionIds = refs.map((r) => r.definitionId)
|
|
283
|
-
|
|
278
|
+
if (definitionIds.length > 0) {
|
|
279
|
+
renameTempDefinitions(state, definitionIds, 'tag', tag)
|
|
280
|
+
}
|
|
284
281
|
|
|
285
282
|
const sourceFile = node.getSourceFile().fileName
|
|
286
283
|
const instanceIds: string[] = []
|
|
@@ -384,15 +381,10 @@ export const addMiddleware: AddWiring = (logger, node, checker, state) => {
|
|
|
384
381
|
state.rootDir
|
|
385
382
|
)
|
|
386
383
|
|
|
387
|
-
if (refs.length === 0) {
|
|
388
|
-
logger.warn(
|
|
389
|
-
`• addHTTPMiddleware('${pattern}', ...) has empty middleware array`
|
|
390
|
-
)
|
|
391
|
-
return
|
|
392
|
-
}
|
|
393
|
-
|
|
394
384
|
const definitionIds = refs.map((r) => r.definitionId)
|
|
395
|
-
|
|
385
|
+
if (definitionIds.length > 0) {
|
|
386
|
+
renameTempDefinitions(state, definitionIds, 'http', pattern)
|
|
387
|
+
}
|
|
396
388
|
|
|
397
389
|
const sourceFile = node.getSourceFile().fileName
|
|
398
390
|
const instanceIds: string[] = []
|