@pikku/inspector 0.12.1 → 0.12.3
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 +23 -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 +44 -4
- package/dist/add/add-cli.js +94 -18
- package/dist/add/add-file-with-factory.js +1 -0
- package/dist/add/add-functions.js +22 -3
- package/dist/add/add-gateway.d.ts +2 -0
- package/dist/add/add-gateway.js +62 -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-queue-worker.js +5 -0
- package/dist/add/add-schedule.js +5 -0
- package/dist/add/add-wire-addon.js +7 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/inspector.js +11 -0
- package/dist/types.d.ts +15 -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.js +26 -0
- package/dist/utils/resolve-function-meta.d.ts +11 -0
- package/dist/utils/resolve-function-meta.js +17 -0
- package/dist/utils/serialize-inspector-state.d.ts +6 -0
- package/dist/utils/serialize-inspector-state.js +12 -0
- package/dist/utils/serialize-mcp-json.js +13 -7
- package/dist/visit.js +4 -0
- package/package.json +3 -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 +47 -11
- package/src/add/add-cli.ts +140 -30
- package/src/add/add-file-with-factory.ts +1 -0
- package/src/add/add-functions.ts +28 -3
- package/src/add/add-gateway.ts +101 -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-queue-worker.ts +6 -0
- package/src/add/add-schedule.ts +6 -0
- package/src/add/add-wire-addon.ts +8 -0
- package/src/index.ts +1 -0
- package/src/inspector.ts +16 -0
- package/src/types.ts +16 -0
- package/src/utils/load-addon-functions-meta.ts +94 -0
- package/src/utils/post-process.ts +25 -0
- package/src/utils/resolve-function-meta.ts +25 -0
- package/src/utils/serialize-inspector-state.ts +18 -0
- package/src/utils/serialize-mcp-json.ts +12 -7
- package/src/visit.ts +4 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -32,6 +32,7 @@ export function addWireAddon(node, state, logger) {
|
|
|
32
32
|
let name;
|
|
33
33
|
let pkg;
|
|
34
34
|
let rpcEndpoint;
|
|
35
|
+
let mcp;
|
|
35
36
|
let secretOverrides;
|
|
36
37
|
let variableOverrides;
|
|
37
38
|
for (const prop of firstArg.properties) {
|
|
@@ -47,6 +48,11 @@ export function addWireAddon(node, state, logger) {
|
|
|
47
48
|
else if (key === 'rpcEndpoint' && ts.isStringLiteral(prop.initializer)) {
|
|
48
49
|
rpcEndpoint = prop.initializer.text;
|
|
49
50
|
}
|
|
51
|
+
else if (key === 'mcp' &&
|
|
52
|
+
(prop.initializer.kind === ts.SyntaxKind.TrueKeyword ||
|
|
53
|
+
prop.initializer.kind === ts.SyntaxKind.FalseKeyword)) {
|
|
54
|
+
mcp = prop.initializer.kind === ts.SyntaxKind.TrueKeyword;
|
|
55
|
+
}
|
|
50
56
|
else if (key === 'secretOverrides' &&
|
|
51
57
|
ts.isObjectLiteralExpression(prop.initializer)) {
|
|
52
58
|
secretOverrides = parseStringRecord(prop.initializer);
|
|
@@ -62,6 +68,7 @@ export function addWireAddon(node, state, logger) {
|
|
|
62
68
|
state.rpc.wireAddonDeclarations.set(name, {
|
|
63
69
|
package: pkg,
|
|
64
70
|
rpcEndpoint,
|
|
71
|
+
mcp,
|
|
65
72
|
secretOverrides,
|
|
66
73
|
variableOverrides,
|
|
67
74
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -13,4 +13,5 @@ export { serializeMCPJson } from './utils/serialize-mcp-json.js';
|
|
|
13
13
|
export type { OpenAPISpecInfo } from './utils/serialize-openapi-json.js';
|
|
14
14
|
export { deserializeDslWorkflow, deserializeGraphWorkflow, deserializeAllDslWorkflows, } from './utils/workflow/dsl/index.js';
|
|
15
15
|
export { getFilesAndMethods } from './utils/get-files-and-methods.js';
|
|
16
|
+
export { resolveFunctionMeta } from './utils/resolve-function-meta.js';
|
|
16
17
|
export type { SerializedWorkflowGraph, SerializedWorkflowGraphs, } from './utils/workflow/graph/index.js';
|
package/dist/index.js
CHANGED
|
@@ -7,3 +7,4 @@ export { createEmptyManifest, serializeManifest, } from './utils/contract-hashes
|
|
|
7
7
|
export { serializeMCPJson } from './utils/serialize-mcp-json.js';
|
|
8
8
|
export { deserializeDslWorkflow, deserializeGraphWorkflow, deserializeAllDslWorkflows, } from './utils/workflow/dsl/index.js';
|
|
9
9
|
export { getFilesAndMethods } from './utils/get-files-and-methods.js';
|
|
10
|
+
export { resolveFunctionMeta } from './utils/resolve-function-meta.js';
|
package/dist/inspector.js
CHANGED
|
@@ -11,6 +11,7 @@ import { resolveLatestVersions } from './utils/resolve-versions.js';
|
|
|
11
11
|
import { finalizeWorkflows } from './utils/workflow/graph/finalize-workflows.js';
|
|
12
12
|
import { finalizeWorkflowHelperTypes, finalizeWorkflowWires, } from './utils/workflow/graph/finalize-workflow-wires.js';
|
|
13
13
|
import { generateAllSchemas } from './utils/schema-generator.js';
|
|
14
|
+
import { loadAddonFunctionsMeta, loadAddonSchemas, } from './utils/load-addon-functions-meta.js';
|
|
14
15
|
import { computeContractHashes, extractContractsFromMeta, updateManifest, createEmptyManifest, validateContracts, } from './utils/contract-hashes.js';
|
|
15
16
|
/**
|
|
16
17
|
* Creates an initial/empty inspector state with all required properties initialized
|
|
@@ -37,6 +38,7 @@ export function getInitialInspectorState(rootDir) {
|
|
|
37
38
|
typesMap: new TypesMap(),
|
|
38
39
|
meta: {},
|
|
39
40
|
files: new Map(),
|
|
41
|
+
approvalDescriptions: {},
|
|
40
42
|
},
|
|
41
43
|
http: {
|
|
42
44
|
metaInputTypes: new Map(),
|
|
@@ -57,6 +59,10 @@ export function getInitialInspectorState(rootDir) {
|
|
|
57
59
|
files: new Set(),
|
|
58
60
|
meta: {},
|
|
59
61
|
},
|
|
62
|
+
gateways: {
|
|
63
|
+
meta: {},
|
|
64
|
+
files: new Set(),
|
|
65
|
+
},
|
|
60
66
|
triggers: {
|
|
61
67
|
meta: {},
|
|
62
68
|
sourceMeta: {},
|
|
@@ -167,6 +173,7 @@ export function getInitialInspectorState(rootDir) {
|
|
|
167
173
|
requiredSchemas: new Set(),
|
|
168
174
|
openAPISpec: null,
|
|
169
175
|
diagnostics: [],
|
|
176
|
+
addonFunctions: {},
|
|
170
177
|
};
|
|
171
178
|
}
|
|
172
179
|
export const inspect = async (logger, routeFiles, options = {}) => {
|
|
@@ -201,6 +208,8 @@ export const inspect = async (logger, routeFiles, options = {}) => {
|
|
|
201
208
|
ts.forEachChild(sourceFile, (child) => visitSetup(logger, checker, child, state, options));
|
|
202
209
|
}
|
|
203
210
|
logger.debug(`Visit setup phase completed in ${(performance.now() - startSetup).toFixed(2)}ms`);
|
|
211
|
+
// Load addon function metadata so wirings can reference addon functions
|
|
212
|
+
await loadAddonFunctionsMeta(logger, state);
|
|
204
213
|
if (!options.setupOnly) {
|
|
205
214
|
// Second sweep: add all transports
|
|
206
215
|
const startRoutes = performance.now();
|
|
@@ -214,6 +223,8 @@ export const inspect = async (logger, routeFiles, options = {}) => {
|
|
|
214
223
|
computeContractHashes(state.schemas, state.functions.typesMap, state.functions.meta);
|
|
215
224
|
computeRequiredSchemas(state, options);
|
|
216
225
|
}
|
|
226
|
+
// Re-load addon schemas (generateAllSchemas replaces state.schemas)
|
|
227
|
+
await loadAddonSchemas(logger, state);
|
|
217
228
|
state.manifest.initial = options.manifest ?? null;
|
|
218
229
|
const contracts = extractContractsFromMeta(state.functions.meta);
|
|
219
230
|
const baseManifest = state.manifest.initial ?? createEmptyManifest();
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type * as ts from 'typescript';
|
|
2
2
|
import type { ChannelsMeta } from '@pikku/core/channel';
|
|
3
|
+
import type { GatewaysMeta } from '@pikku/core/gateway';
|
|
3
4
|
import type { HTTPWiringsMeta } from '@pikku/core/http';
|
|
4
5
|
import type { ScheduledTasksMeta } from '@pikku/core/scheduler';
|
|
5
6
|
import type { TriggerMeta, TriggerSourceMeta } from '@pikku/core/trigger';
|
|
@@ -72,6 +73,7 @@ export interface InspectorFunctionState {
|
|
|
72
73
|
path: string;
|
|
73
74
|
exportedName: string;
|
|
74
75
|
}>;
|
|
76
|
+
approvalDescriptions: Record<string, InspectorApprovalDescriptionDefinition>;
|
|
75
77
|
}
|
|
76
78
|
export interface InspectorChannelState {
|
|
77
79
|
meta: ChannelsMeta;
|
|
@@ -107,6 +109,13 @@ export interface InspectorChannelMiddlewareState {
|
|
|
107
109
|
export interface InspectorAIMiddlewareState {
|
|
108
110
|
definitions: Record<string, InspectorMiddlewareDefinition>;
|
|
109
111
|
}
|
|
112
|
+
export interface InspectorApprovalDescriptionDefinition {
|
|
113
|
+
services: FunctionServicesMeta;
|
|
114
|
+
wires?: FunctionWiresMeta;
|
|
115
|
+
sourceFile: string;
|
|
116
|
+
position: number;
|
|
117
|
+
exportedName: string | null;
|
|
118
|
+
}
|
|
110
119
|
export interface InspectorPermissionDefinition {
|
|
111
120
|
services: FunctionServicesMeta;
|
|
112
121
|
wires?: FunctionWiresMeta;
|
|
@@ -262,6 +271,10 @@ export interface InspectorState {
|
|
|
262
271
|
http: InspectorHTTPState;
|
|
263
272
|
functions: InspectorFunctionState;
|
|
264
273
|
channels: InspectorChannelState;
|
|
274
|
+
gateways: {
|
|
275
|
+
meta: GatewaysMeta;
|
|
276
|
+
files: Set<string>;
|
|
277
|
+
};
|
|
265
278
|
triggers: {
|
|
266
279
|
meta: TriggerMeta;
|
|
267
280
|
sourceMeta: TriggerSourceMeta;
|
|
@@ -304,6 +317,7 @@ export interface InspectorState {
|
|
|
304
317
|
wireAddonDeclarations: Map<string, {
|
|
305
318
|
package: string;
|
|
306
319
|
rpcEndpoint?: string;
|
|
320
|
+
mcp?: boolean;
|
|
307
321
|
secretOverrides?: Record<string, string>;
|
|
308
322
|
variableOverrides?: Record<string, string>;
|
|
309
323
|
}>;
|
|
@@ -378,4 +392,5 @@ export interface InspectorState {
|
|
|
378
392
|
requiredSchemas: Set<string>;
|
|
379
393
|
openAPISpec: Record<string, any> | null;
|
|
380
394
|
diagnostics: InspectorDiagnostic[];
|
|
395
|
+
addonFunctions: Record<string, FunctionsMeta>;
|
|
381
396
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { InspectorState, InspectorLogger } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* After the setup sweep discovers wireAddon() declarations, load each addon
|
|
4
|
+
* package's function metadata so that wiring handlers (channels, HTTP routes,
|
|
5
|
+
* schedules, etc.) can look up addon function types during the routes sweep.
|
|
6
|
+
*/
|
|
7
|
+
export declare function loadAddonFunctionsMeta(logger: InspectorLogger, state: InspectorState): Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* Load addon schemas into state.schemas. Called after generateAllSchemas
|
|
10
|
+
* to ensure addon schemas aren't overwritten.
|
|
11
|
+
*/
|
|
12
|
+
export declare function loadAddonSchemas(logger: InspectorLogger, state: InspectorState): Promise<void>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { readFile, readdir } from 'fs/promises';
|
|
2
|
+
import { createRequire } from 'module';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
/**
|
|
5
|
+
* After the setup sweep discovers wireAddon() declarations, load each addon
|
|
6
|
+
* package's function metadata so that wiring handlers (channels, HTTP routes,
|
|
7
|
+
* schedules, etc.) can look up addon function types during the routes sweep.
|
|
8
|
+
*/
|
|
9
|
+
export async function loadAddonFunctionsMeta(logger, state) {
|
|
10
|
+
const { wireAddonDeclarations } = state.rpc;
|
|
11
|
+
if (wireAddonDeclarations.size === 0)
|
|
12
|
+
return;
|
|
13
|
+
const require = createRequire(join(state.rootDir, 'package.json'));
|
|
14
|
+
for (const [namespace, decl] of wireAddonDeclarations) {
|
|
15
|
+
try {
|
|
16
|
+
const metaPath = require.resolve(`${decl.package}/.pikku/function/pikku-functions-meta.gen.json`);
|
|
17
|
+
const raw = await readFile(metaPath, 'utf-8');
|
|
18
|
+
const meta = JSON.parse(raw);
|
|
19
|
+
state.addonFunctions[namespace] = meta;
|
|
20
|
+
logger.debug(`Loaded ${Object.keys(meta).length} addon functions for '${namespace}' from ${decl.package}`);
|
|
21
|
+
// If wireAddon has mcp: true, expose addon functions with mcp: true as MCP tools
|
|
22
|
+
if (decl.mcp) {
|
|
23
|
+
for (const [funcName, funcMeta] of Object.entries(meta)) {
|
|
24
|
+
if (funcMeta.mcp) {
|
|
25
|
+
const toolName = `${namespace}:${funcName}`;
|
|
26
|
+
state.mcpEndpoints.toolsMeta[toolName] = {
|
|
27
|
+
pikkuFuncId: `${namespace}:${funcName}`,
|
|
28
|
+
name: toolName,
|
|
29
|
+
description: funcMeta.description || funcMeta.title || funcName,
|
|
30
|
+
inputSchema: funcMeta.inputSchemaName ?? null,
|
|
31
|
+
outputSchema: funcMeta.outputSchemaName ?? null,
|
|
32
|
+
tags: funcMeta.tags,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
logger.warn(`Failed to load addon function metadata for '${namespace}' (${decl.package}): ${error.message}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Load addon schemas into state.schemas. Called after generateAllSchemas
|
|
45
|
+
* to ensure addon schemas aren't overwritten.
|
|
46
|
+
*/
|
|
47
|
+
export async function loadAddonSchemas(logger, state) {
|
|
48
|
+
const { wireAddonDeclarations } = state.rpc;
|
|
49
|
+
if (wireAddonDeclarations.size === 0)
|
|
50
|
+
return;
|
|
51
|
+
const require = createRequire(join(state.rootDir, 'package.json'));
|
|
52
|
+
for (const [namespace, decl] of wireAddonDeclarations) {
|
|
53
|
+
try {
|
|
54
|
+
const metaPath = require.resolve(`${decl.package}/.pikku/function/pikku-functions-meta.gen.json`);
|
|
55
|
+
const schemasDir = join(dirname(metaPath), '..', 'schemas', 'schemas');
|
|
56
|
+
try {
|
|
57
|
+
const schemaFiles = await readdir(schemasDir);
|
|
58
|
+
for (const file of schemaFiles) {
|
|
59
|
+
if (!file.endsWith('.schema.json'))
|
|
60
|
+
continue;
|
|
61
|
+
const schemaName = file.replace('.schema.json', '');
|
|
62
|
+
if (!state.schemas[schemaName]) {
|
|
63
|
+
const schemaRaw = await readFile(join(schemasDir, file), 'utf-8');
|
|
64
|
+
state.schemas[schemaName] = JSON.parse(schemaRaw);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// No schemas directory — that's fine
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
logger.warn(`Failed to load addon schemas for '${namespace}' (${decl.package}): ${error.message}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -217,6 +217,32 @@ export function computeResolvedIOTypes(state) {
|
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
219
|
state.resolvedIOTypes[pikkuFuncId] = { inputType, outputType };
|
|
220
|
+
if (meta.inputSchemaName && inputType !== 'null') {
|
|
221
|
+
meta.inputSchemaName = inputType;
|
|
222
|
+
}
|
|
223
|
+
if (meta.outputSchemaName && outputType !== 'null') {
|
|
224
|
+
meta.outputSchemaName = outputType;
|
|
225
|
+
}
|
|
226
|
+
if (meta.inputs) {
|
|
227
|
+
meta.inputs = meta.inputs.map((name) => {
|
|
228
|
+
try {
|
|
229
|
+
return functions.typesMap.getTypeMeta(name).uniqueName;
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
return name;
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
if (meta.outputs) {
|
|
237
|
+
meta.outputs = meta.outputs.map((name) => {
|
|
238
|
+
try {
|
|
239
|
+
return functions.typesMap.getTypeMeta(name).uniqueName;
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
return name;
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
220
246
|
}
|
|
221
247
|
}
|
|
222
248
|
const serializeGroupMap = (groupMap) => {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FunctionMeta, FunctionsMeta } from '@pikku/core';
|
|
2
|
+
/**
|
|
3
|
+
* Look up function metadata by pikkuFuncId, checking both local functions
|
|
4
|
+
* and addon functions. Addon functions use namespaced IDs like 'namespace:funcName'.
|
|
5
|
+
*/
|
|
6
|
+
export declare function resolveFunctionMeta(state: {
|
|
7
|
+
functions: {
|
|
8
|
+
meta: FunctionsMeta;
|
|
9
|
+
};
|
|
10
|
+
addonFunctions: Record<string, FunctionsMeta>;
|
|
11
|
+
}, pikkuFuncId: string): FunctionMeta | undefined;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Look up function metadata by pikkuFuncId, checking both local functions
|
|
3
|
+
* and addon functions. Addon functions use namespaced IDs like 'namespace:funcName'.
|
|
4
|
+
*/
|
|
5
|
+
export function resolveFunctionMeta(state, pikkuFuncId) {
|
|
6
|
+
// Check local functions first
|
|
7
|
+
const local = state.functions.meta[pikkuFuncId];
|
|
8
|
+
if (local)
|
|
9
|
+
return local;
|
|
10
|
+
// Check addon functions (namespaced like 'swaggerPetstore:addPet')
|
|
11
|
+
const colonIndex = pikkuFuncId.indexOf(':');
|
|
12
|
+
if (colonIndex === -1)
|
|
13
|
+
return undefined;
|
|
14
|
+
const namespace = pikkuFuncId.substring(0, colonIndex);
|
|
15
|
+
const funcName = pikkuFuncId.substring(colonIndex + 1);
|
|
16
|
+
return state.addonFunctions[namespace]?.[funcName];
|
|
17
|
+
}
|
|
@@ -100,6 +100,7 @@ export interface SerializableInspectorState {
|
|
|
100
100
|
path: string;
|
|
101
101
|
exportedName: string;
|
|
102
102
|
}]>;
|
|
103
|
+
approvalDescriptions: InspectorState['functions']['approvalDescriptions'];
|
|
103
104
|
};
|
|
104
105
|
http: {
|
|
105
106
|
metaInputTypes: Array<[
|
|
@@ -125,6 +126,10 @@ export interface SerializableInspectorState {
|
|
|
125
126
|
files: string[];
|
|
126
127
|
meta: InspectorState['channels']['meta'];
|
|
127
128
|
};
|
|
129
|
+
gateways: {
|
|
130
|
+
meta: InspectorState['gateways']['meta'];
|
|
131
|
+
files: string[];
|
|
132
|
+
};
|
|
128
133
|
triggers: {
|
|
129
134
|
meta: InspectorState['triggers']['meta'];
|
|
130
135
|
sourceMeta: InspectorState['triggers']['sourceMeta'];
|
|
@@ -249,6 +254,7 @@ export interface SerializableInspectorState {
|
|
|
249
254
|
requiredSchemas: string[];
|
|
250
255
|
openAPISpec: Record<string, any> | null;
|
|
251
256
|
diagnostics: InspectorDiagnostic[];
|
|
257
|
+
addonFunctions: InspectorState['addonFunctions'];
|
|
252
258
|
}
|
|
253
259
|
/**
|
|
254
260
|
* Serializes InspectorState to a JSON-compatible format
|
|
@@ -32,6 +32,7 @@ export function serializeInspectorState(state) {
|
|
|
32
32
|
typesMap: serializeTypesMap(state.functions.typesMap),
|
|
33
33
|
meta: state.functions.meta,
|
|
34
34
|
files: Array.from(state.functions.files.entries()),
|
|
35
|
+
approvalDescriptions: state.functions.approvalDescriptions,
|
|
35
36
|
},
|
|
36
37
|
http: {
|
|
37
38
|
metaInputTypes: Array.from(state.http.metaInputTypes.entries()),
|
|
@@ -44,6 +45,10 @@ export function serializeInspectorState(state) {
|
|
|
44
45
|
files: Array.from(state.channels.files),
|
|
45
46
|
meta: state.channels.meta,
|
|
46
47
|
},
|
|
48
|
+
gateways: {
|
|
49
|
+
meta: state.gateways.meta,
|
|
50
|
+
files: Array.from(state.gateways.files),
|
|
51
|
+
},
|
|
47
52
|
triggers: {
|
|
48
53
|
meta: state.triggers.meta,
|
|
49
54
|
sourceMeta: state.triggers.sourceMeta,
|
|
@@ -133,6 +138,7 @@ export function serializeInspectorState(state) {
|
|
|
133
138
|
requiredSchemas: Array.from(state.requiredSchemas),
|
|
134
139
|
openAPISpec: state.openAPISpec,
|
|
135
140
|
diagnostics: state.diagnostics,
|
|
141
|
+
addonFunctions: state.addonFunctions,
|
|
136
142
|
};
|
|
137
143
|
}
|
|
138
144
|
/**
|
|
@@ -169,6 +175,7 @@ export function deserializeInspectorState(data) {
|
|
|
169
175
|
typesMap: deserializeTypesMap(data.functions.typesMap),
|
|
170
176
|
meta: data.functions.meta,
|
|
171
177
|
files: new Map(data.functions.files),
|
|
178
|
+
approvalDescriptions: data.functions.approvalDescriptions || {},
|
|
172
179
|
},
|
|
173
180
|
http: {
|
|
174
181
|
metaInputTypes: new Map(data.http.metaInputTypes),
|
|
@@ -181,6 +188,10 @@ export function deserializeInspectorState(data) {
|
|
|
181
188
|
files: new Set(data.channels.files),
|
|
182
189
|
meta: data.channels.meta,
|
|
183
190
|
},
|
|
191
|
+
gateways: {
|
|
192
|
+
meta: data.gateways?.meta ?? {},
|
|
193
|
+
files: new Set(data.gateways?.files ?? []),
|
|
194
|
+
},
|
|
184
195
|
triggers: {
|
|
185
196
|
meta: data.triggers?.meta ?? {},
|
|
186
197
|
sourceMeta: data.triggers?.sourceMeta ?? {},
|
|
@@ -280,5 +291,6 @@ export function deserializeInspectorState(data) {
|
|
|
280
291
|
requiredSchemas: new Set(data.requiredSchemas || []),
|
|
281
292
|
openAPISpec: data.openAPISpec || null,
|
|
282
293
|
diagnostics: data.diagnostics || [],
|
|
294
|
+
addonFunctions: data.addonFunctions || {},
|
|
283
295
|
};
|
|
284
296
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { resolveFunctionMeta } from './resolve-function-meta.js';
|
|
1
2
|
export const serializeMCPJson = (logger, state) => {
|
|
2
3
|
const { mcpEndpoints, functions, schemas } = state;
|
|
3
|
-
const {
|
|
4
|
+
const { typesMap } = functions;
|
|
4
5
|
const { resourcesMeta, toolsMeta, promptsMeta } = mcpEndpoints;
|
|
5
6
|
const tools = [];
|
|
6
7
|
const resources = [];
|
|
@@ -19,9 +20,14 @@ export const serializeMCPJson = (logger, state) => {
|
|
|
19
20
|
].includes(typeName)) {
|
|
20
21
|
return undefined;
|
|
21
22
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
// Try local typesMap first, fall back to direct schema lookup (for addon types)
|
|
24
|
+
let uniqueName;
|
|
25
|
+
try {
|
|
26
|
+
uniqueName = typesMap.getUniqueName(typeName);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Type not in local typesMap — try direct schema lookup (addon schemas)
|
|
30
|
+
uniqueName = typeName;
|
|
25
31
|
}
|
|
26
32
|
const schema = schemas[uniqueName];
|
|
27
33
|
if (!schema) {
|
|
@@ -31,7 +37,7 @@ export const serializeMCPJson = (logger, state) => {
|
|
|
31
37
|
return schema;
|
|
32
38
|
};
|
|
33
39
|
for (const [name, endpointMeta] of Object.entries(resourcesMeta)) {
|
|
34
|
-
const functionMeta =
|
|
40
|
+
const functionMeta = resolveFunctionMeta(state, endpointMeta.pikkuFuncId);
|
|
35
41
|
if (!functionMeta) {
|
|
36
42
|
logger.warn(`Function ${endpointMeta.pikkuFuncId} not found in functionsMeta. Skipping resource ${name}.`);
|
|
37
43
|
continue;
|
|
@@ -50,7 +56,7 @@ export const serializeMCPJson = (logger, state) => {
|
|
|
50
56
|
});
|
|
51
57
|
}
|
|
52
58
|
for (const [name, endpointMeta] of Object.entries(toolsMeta)) {
|
|
53
|
-
const functionMeta =
|
|
59
|
+
const functionMeta = resolveFunctionMeta(state, endpointMeta.pikkuFuncId);
|
|
54
60
|
if (!functionMeta) {
|
|
55
61
|
logger.warn(`Function ${endpointMeta.pikkuFuncId} not found in functionsMeta. Skipping tool ${name}.`);
|
|
56
62
|
continue;
|
|
@@ -68,7 +74,7 @@ export const serializeMCPJson = (logger, state) => {
|
|
|
68
74
|
});
|
|
69
75
|
}
|
|
70
76
|
for (const [name, endpointMeta] of Object.entries(promptsMeta)) {
|
|
71
|
-
const functionMeta =
|
|
77
|
+
const functionMeta = resolveFunctionMeta(state, endpointMeta.pikkuFuncId);
|
|
72
78
|
if (!functionMeta) {
|
|
73
79
|
logger.warn(`Function ${endpointMeta.pikkuFuncId} not found in functionsMeta. Skipping prompt ${name}.`);
|
|
74
80
|
continue;
|
package/dist/visit.js
CHANGED
|
@@ -11,6 +11,7 @@ import { addMCPResource } from './add/add-mcp-resource.js';
|
|
|
11
11
|
import { addMCPPrompt } from './add/add-mcp-prompt.js';
|
|
12
12
|
import { addFunctions } from './add/add-functions.js';
|
|
13
13
|
import { addChannel } from './add/add-channel.js';
|
|
14
|
+
import { addGateway } from './add/add-gateway.js';
|
|
14
15
|
import { addRPCInvocations } from './add/add-rpc-invocations.js';
|
|
15
16
|
import { addWireAddon } from './add/add-wire-addon.js';
|
|
16
17
|
import { addMiddleware } from './add/add-middleware.js';
|
|
@@ -20,6 +21,7 @@ import { addSecret, addOAuth2Credential } from './add/add-secret.js';
|
|
|
20
21
|
import { addVariable } from './add/add-variable.js';
|
|
21
22
|
import { addWorkflowGraph } from './add/add-workflow-graph.js';
|
|
22
23
|
import { addAIAgent } from './add/add-ai-agent.js';
|
|
24
|
+
import { addApprovalDescription } from './add/add-approval-description.js';
|
|
23
25
|
export const visitSetup = (logger, checker, node, state, options) => {
|
|
24
26
|
addFileExtendsCoreType(node, checker, state.singletonServicesTypeImportMap, 'CoreSingletonServices', state);
|
|
25
27
|
addFileExtendsCoreType(node, checker, state.wireServicesTypeImportMap, 'CoreServices', state);
|
|
@@ -32,6 +34,7 @@ export const visitSetup = (logger, checker, node, state, options) => {
|
|
|
32
34
|
addWireAddon(node, state, logger);
|
|
33
35
|
addMiddleware(logger, node, checker, state, options);
|
|
34
36
|
addPermission(logger, node, checker, state, options);
|
|
37
|
+
addApprovalDescription(logger, node, checker, state, options);
|
|
35
38
|
addWorkflow(logger, node, checker, state, options);
|
|
36
39
|
ts.forEachChild(node, (child) => visitSetup(logger, checker, child, state, options));
|
|
37
40
|
};
|
|
@@ -46,6 +49,7 @@ export const visitRoutes = (logger, checker, node, state, options) => {
|
|
|
46
49
|
addTrigger(logger, node, checker, state, options);
|
|
47
50
|
addQueueWorker(logger, node, checker, state, options);
|
|
48
51
|
addChannel(logger, node, checker, state, options);
|
|
52
|
+
addGateway(logger, node, checker, state, options);
|
|
49
53
|
addCLI(logger, node, checker, state, options);
|
|
50
54
|
addCLIRenderers(logger, node, checker, state, options);
|
|
51
55
|
addMCPResource(logger, node, checker, state, options);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/inspector",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.3",
|
|
4
4
|
"author": "yasser.fadl@gmail.com",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"type": "module",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@openapi-contrib/json-schema-to-openapi-schema": "^4.3.1",
|
|
37
|
-
"@pikku/core": "^0.12.
|
|
37
|
+
"@pikku/core": "^0.12.3",
|
|
38
38
|
"path-to-regexp": "^8.3.0",
|
|
39
39
|
"ts-json-schema-generator": "^2.5.0",
|
|
40
40
|
"tsx": "^4.21.0",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"zod-to-ts": "^2.0.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@types/node": "^24.
|
|
46
|
+
"@types/node": "^24.11.0"
|
|
47
47
|
},
|
|
48
48
|
"engines": {
|
|
49
49
|
"node": ">=18"
|
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
|
+
}
|