@pikku/inspector 0.11.2 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/CHANGELOG.md +36 -1
  2. package/OPTIMIZATION-PLAN.md +195 -0
  3. package/dist/add/add-ai-agent.d.ts +2 -0
  4. package/dist/add/add-ai-agent.js +314 -0
  5. package/dist/add/add-channel.js +81 -61
  6. package/dist/add/add-cli.d.ts +1 -1
  7. package/dist/add/add-cli.js +42 -19
  8. package/dist/add/add-file-extends-core-type.d.ts +1 -1
  9. package/dist/add/add-file-with-config.d.ts +1 -1
  10. package/dist/add/add-file-with-factory.d.ts +1 -1
  11. package/dist/add/add-file-with-factory.js +2 -0
  12. package/dist/add/add-functions.d.ts +1 -1
  13. package/dist/add/add-functions.js +256 -82
  14. package/dist/add/add-http-route.d.ts +20 -10
  15. package/dist/add/add-http-route.js +156 -66
  16. package/dist/add/add-http-routes.d.ts +5 -0
  17. package/dist/add/add-http-routes.js +160 -0
  18. package/dist/add/add-keyed-wiring.d.ts +12 -0
  19. package/dist/add/add-keyed-wiring.js +97 -0
  20. package/dist/add/add-mcp-prompt.d.ts +1 -1
  21. package/dist/add/add-mcp-prompt.js +14 -9
  22. package/dist/add/add-mcp-resource.d.ts +1 -1
  23. package/dist/add/add-mcp-resource.js +14 -9
  24. package/dist/add/add-middleware.d.ts +1 -4
  25. package/dist/add/add-middleware.js +364 -79
  26. package/dist/add/add-permission.d.ts +1 -1
  27. package/dist/add/add-permission.js +152 -40
  28. package/dist/add/add-queue-worker.d.ts +1 -1
  29. package/dist/add/add-queue-worker.js +18 -12
  30. package/dist/add/add-rpc-invocations.d.ts +3 -3
  31. package/dist/add/add-rpc-invocations.js +24 -10
  32. package/dist/add/add-schedule.d.ts +1 -1
  33. package/dist/add/add-schedule.js +11 -5
  34. package/dist/add/add-secret.d.ts +3 -0
  35. package/dist/add/add-secret.js +82 -0
  36. package/dist/add/add-trigger.d.ts +2 -0
  37. package/dist/add/add-trigger.js +87 -0
  38. package/dist/add/add-variable.d.ts +1 -0
  39. package/dist/add/add-variable.js +8 -0
  40. package/dist/add/add-wire-addon.d.ts +7 -0
  41. package/dist/add/add-wire-addon.js +70 -0
  42. package/dist/add/add-workflow-graph.d.ts +3 -2
  43. package/dist/add/add-workflow-graph.js +143 -406
  44. package/dist/add/add-workflow.d.ts +1 -1
  45. package/dist/add/add-workflow.js +6 -4
  46. package/dist/error-codes.d.ts +15 -1
  47. package/dist/error-codes.js +20 -1
  48. package/dist/index.d.ts +9 -8
  49. package/dist/index.js +5 -4
  50. package/dist/inspector.d.ts +2 -2
  51. package/dist/inspector.js +95 -15
  52. package/dist/schema-generator.d.ts +1 -0
  53. package/dist/schema-generator.js +1 -0
  54. package/dist/types-map.js +10 -1
  55. package/dist/types.d.ts +180 -50
  56. package/dist/utils/compute-required-schemas.d.ts +4 -0
  57. package/dist/utils/compute-required-schemas.js +40 -0
  58. package/dist/utils/contract-hashes.d.ts +52 -0
  59. package/dist/utils/contract-hashes.js +269 -0
  60. package/dist/utils/custom-types-generator.d.ts +9 -0
  61. package/dist/utils/custom-types-generator.js +71 -0
  62. package/dist/utils/detect-schema-vendor.d.ts +22 -0
  63. package/dist/utils/detect-schema-vendor.js +76 -0
  64. package/dist/utils/does-type-extend-core-type.d.ts +1 -1
  65. package/dist/utils/ensure-function-metadata.d.ts +6 -3
  66. package/dist/utils/ensure-function-metadata.js +220 -6
  67. package/dist/utils/extract-function-name.d.ts +5 -16
  68. package/dist/utils/extract-function-name.js +86 -291
  69. package/dist/utils/extract-services.d.ts +2 -1
  70. package/dist/utils/extract-services.js +25 -1
  71. package/dist/utils/filter-inspector-state.d.ts +1 -1
  72. package/dist/utils/filter-inspector-state.js +107 -23
  73. package/dist/utils/filter-utils.d.ts +2 -2
  74. package/dist/utils/get-files-and-methods.d.ts +1 -1
  75. package/dist/utils/get-property-value.d.ts +6 -1
  76. package/dist/utils/get-property-value.js +28 -3
  77. package/dist/utils/hash.d.ts +2 -0
  78. package/dist/utils/hash.js +23 -0
  79. package/dist/utils/middleware.d.ts +9 -32
  80. package/dist/utils/middleware.js +80 -66
  81. package/dist/utils/permissions.d.ts +4 -4
  82. package/dist/utils/permissions.js +10 -10
  83. package/dist/utils/post-process.d.ts +11 -11
  84. package/dist/utils/post-process.js +247 -24
  85. package/dist/utils/resolve-addon-package.d.ts +16 -0
  86. package/dist/utils/resolve-addon-package.js +34 -0
  87. package/dist/utils/resolve-function-types.d.ts +6 -0
  88. package/dist/utils/resolve-function-types.js +29 -0
  89. package/dist/utils/resolve-identifier.d.ts +10 -0
  90. package/dist/utils/resolve-identifier.js +36 -0
  91. package/dist/utils/resolve-versions.d.ts +2 -0
  92. package/dist/utils/resolve-versions.js +78 -0
  93. package/dist/utils/schema-generator.d.ts +9 -0
  94. package/dist/utils/schema-generator.js +209 -0
  95. package/dist/utils/serialize-inspector-state.d.ts +70 -23
  96. package/dist/utils/serialize-inspector-state.js +98 -22
  97. package/dist/utils/serialize-mcp-json.d.ts +2 -0
  98. package/dist/utils/serialize-mcp-json.js +99 -0
  99. package/dist/utils/serialize-middleware-groups-meta.d.ts +12 -0
  100. package/dist/utils/serialize-middleware-groups-meta.js +28 -0
  101. package/dist/utils/serialize-openapi-json.d.ts +85 -0
  102. package/dist/utils/serialize-openapi-json.js +151 -0
  103. package/dist/utils/serialize-permissions-groups-meta.d.ts +6 -0
  104. package/dist/utils/serialize-permissions-groups-meta.js +31 -0
  105. package/dist/utils/validate-auth-sessionless.d.ts +3 -0
  106. package/dist/utils/validate-auth-sessionless.js +14 -0
  107. package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +34 -102
  108. package/dist/utils/workflow/dsl/extract-dsl-workflow.d.ts +1 -1
  109. package/dist/utils/workflow/dsl/extract-dsl-workflow.js +23 -4
  110. package/dist/utils/workflow/graph/convert-dsl-to-graph.js +12 -10
  111. package/dist/utils/workflow/graph/finalize-workflow-wires.d.ts +3 -0
  112. package/dist/utils/workflow/graph/finalize-workflow-wires.js +276 -0
  113. package/dist/utils/workflow/graph/finalize-workflows.d.ts +2 -0
  114. package/dist/utils/workflow/graph/finalize-workflows.js +75 -0
  115. package/dist/utils/workflow/graph/index.d.ts +2 -0
  116. package/dist/utils/workflow/graph/index.js +2 -0
  117. package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +0 -8
  118. package/dist/utils/workflow/graph/serialize-workflow-graph.js +1 -3
  119. package/dist/utils/workflow/graph/workflow-graph.types.d.ts +53 -79
  120. package/dist/utils/workflow/graph/workflow-graph.types.js +1 -1
  121. package/dist/visit.d.ts +1 -1
  122. package/dist/visit.js +13 -6
  123. package/package.json +14 -4
  124. package/src/add/add-ai-agent.ts +468 -0
  125. package/src/add/add-channel.ts +103 -79
  126. package/src/add/add-cli.ts +68 -24
  127. package/src/add/add-file-extends-core-type.ts +1 -1
  128. package/src/add/add-file-with-config.ts +1 -1
  129. package/src/add/add-file-with-factory.ts +3 -1
  130. package/src/add/add-functions.ts +349 -103
  131. package/src/add/add-http-route.ts +263 -89
  132. package/src/add/add-http-routes.ts +229 -0
  133. package/src/add/add-keyed-wiring.ts +151 -0
  134. package/src/add/add-mcp-prompt.ts +27 -16
  135. package/src/add/add-mcp-resource.ts +28 -16
  136. package/src/add/add-middleware.ts +482 -80
  137. package/src/add/add-permission.ts +199 -40
  138. package/src/add/add-queue-worker.ts +25 -20
  139. package/src/add/add-rpc-invocations.ts +28 -11
  140. package/src/add/add-schedule.ts +17 -12
  141. package/src/add/add-secret.ts +140 -0
  142. package/src/add/add-trigger.ts +154 -0
  143. package/src/add/add-variable.ts +9 -0
  144. package/src/add/add-wire-addon.ts +80 -0
  145. package/src/add/add-workflow-graph.ts +180 -522
  146. package/src/add/add-workflow.ts +7 -6
  147. package/src/error-codes.ts +25 -1
  148. package/src/index.ts +23 -13
  149. package/src/inspector.ts +139 -19
  150. package/src/schema-generator.ts +1 -0
  151. package/src/types-map.ts +12 -1
  152. package/src/types.ts +199 -69
  153. package/src/utils/compute-required-schemas.ts +48 -0
  154. package/src/utils/contract-hashes.test.ts +553 -0
  155. package/src/utils/contract-hashes.ts +386 -0
  156. package/src/utils/custom-types-generator.ts +88 -0
  157. package/src/utils/detect-schema-vendor.ts +90 -0
  158. package/src/utils/does-type-extend-core-type.ts +1 -1
  159. package/src/utils/ensure-function-metadata.ts +325 -8
  160. package/src/utils/extract-function-name.ts +101 -351
  161. package/src/utils/extract-services.ts +35 -2
  162. package/src/utils/filter-inspector-state.test.ts +37 -25
  163. package/src/utils/filter-inspector-state.ts +146 -32
  164. package/src/utils/filter-utils.test.ts +1 -1
  165. package/src/utils/filter-utils.ts +2 -2
  166. package/src/utils/get-files-and-methods.ts +1 -1
  167. package/src/utils/get-property-value.ts +42 -4
  168. package/src/utils/hash.ts +26 -0
  169. package/src/utils/middleware.test.ts +204 -0
  170. package/src/utils/middleware.ts +131 -69
  171. package/src/utils/permissions.test.ts +35 -12
  172. package/src/utils/permissions.ts +12 -12
  173. package/src/utils/post-process.ts +306 -44
  174. package/src/utils/resolve-addon-package.ts +49 -0
  175. package/src/utils/resolve-function-types.ts +42 -0
  176. package/src/utils/resolve-identifier.ts +46 -0
  177. package/src/utils/resolve-versions.test.ts +249 -0
  178. package/src/utils/resolve-versions.ts +105 -0
  179. package/src/utils/schema-generator.ts +329 -0
  180. package/src/utils/serialize-inspector-state.ts +184 -43
  181. package/src/utils/serialize-mcp-json.ts +145 -0
  182. package/src/utils/serialize-middleware-groups-meta.ts +33 -0
  183. package/src/utils/serialize-openapi-json.ts +277 -0
  184. package/src/utils/serialize-permissions-groups-meta.ts +35 -0
  185. package/src/utils/test-data/inspector-state.json +69 -66
  186. package/src/utils/validate-auth-sessionless.ts +29 -0
  187. package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +43 -119
  188. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +26 -6
  189. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +17 -10
  190. package/src/utils/workflow/graph/finalize-workflow-wires.ts +310 -0
  191. package/src/utils/workflow/graph/finalize-workflows.ts +100 -0
  192. package/src/utils/workflow/graph/index.ts +5 -0
  193. package/src/utils/workflow/graph/serialize-workflow-graph.ts +1 -8
  194. package/src/utils/workflow/graph/workflow-graph.types.ts +29 -78
  195. package/src/visit.ts +19 -7
  196. package/tsconfig.tsbuildinfo +1 -1
  197. package/dist/add/add-forge-credential.d.ts +0 -8
  198. package/dist/add/add-forge-credential.js +0 -77
  199. package/dist/add/add-forge-node.d.ts +0 -7
  200. package/dist/add/add-forge-node.js +0 -77
  201. package/dist/add/add-mcp-tool.d.ts +0 -2
  202. package/dist/add/add-mcp-tool.js +0 -81
  203. package/dist/utils/extract-service-metadata.d.ts +0 -19
  204. package/dist/utils/extract-service-metadata.js +0 -244
  205. package/dist/utils/write-service-metadata.d.ts +0 -13
  206. package/dist/utils/write-service-metadata.js +0 -37
  207. package/src/add/add-forge-credential.ts +0 -119
  208. package/src/add/add-forge-node.ts +0 -132
  209. package/src/add/add-mcp-tool.ts +0 -141
  210. package/src/utils/extract-service-metadata.ts +0 -353
  211. package/src/utils/write-service-metadata.ts +0 -51
@@ -1,4 +1,7 @@
1
+ import { parseVersionedId } from '@pikku/core';
1
2
  import { aggregateRequiredServices } from './post-process.js';
3
+ // Module-level Set to track warned groups across multiple filter calls
4
+ const globalWarnedGroups = new Set();
2
5
  /**
3
6
  * Match a value against a pattern with wildcard support
4
7
  * Supports "*" at the beginning, end, or both (e.g., "send*", "*Payment", "*process*")
@@ -27,7 +30,7 @@ function matchesWildcard(value, pattern) {
27
30
  /**
28
31
  * Check if metadata matches the given filters
29
32
  */
30
- function matchesFilters(filters, meta, logger) {
33
+ function matchesFilters(filters, meta, logger, warnedGroups) {
31
34
  // If no filters, allow everything
32
35
  if (Object.keys(filters).length === 0)
33
36
  return true;
@@ -66,9 +69,11 @@ function matchesFilters(filters, meta, logger) {
66
69
  return false;
67
70
  }
68
71
  }
69
- // Check name filter
72
+ // Check name filter (match against both full ID and base name for versioned functions)
70
73
  if (filters.names && filters.names.length > 0) {
71
- const nameMatches = filters.names.some((pattern) => matchesWildcard(meta.name, pattern));
74
+ const { baseName } = parseVersionedId(meta.name);
75
+ const nameMatches = filters.names.some((pattern) => matchesWildcard(meta.name, pattern) ||
76
+ (baseName !== meta.name && matchesWildcard(baseName, pattern)));
72
77
  if (!nameMatches) {
73
78
  logger.debug(`⒡ Filtered by name: ${meta.type}:${meta.name}`);
74
79
  return false;
@@ -81,6 +86,17 @@ function matchesFilters(filters, meta, logger) {
81
86
  logger.debug(`⒡ Filtered by HTTP route: ${meta.httpRoute}`);
82
87
  return false;
83
88
  }
89
+ // If route is part of a wireHTTPRoutes group, check if filter is at group level
90
+ if (meta.groupBasePath && warnedGroups) {
91
+ const groupMatches = filters.httpRoutes.some((pattern) => matchesWildcard(meta.groupBasePath, pattern) ||
92
+ matchesWildcard(meta.groupBasePath + '/*', pattern));
93
+ if (!groupMatches && !warnedGroups.has(meta.groupBasePath)) {
94
+ warnedGroups.add(meta.groupBasePath);
95
+ logger.warn(`Filtering within wireHTTPRoutes group is not yet supported. ` +
96
+ `Route '${meta.httpRoute}' is part of group '${meta.groupBasePath}'. ` +
97
+ `Use '--httpRoutes=${meta.groupBasePath}/*' to filter the entire group.`);
98
+ }
99
+ }
84
100
  }
85
101
  // Check HTTP method filter
86
102
  if (filters.httpMethods &&
@@ -143,6 +159,11 @@ export function filterInspectorState(state, filters, logger) {
143
159
  meta: JSON.parse(JSON.stringify(state.channels.meta)),
144
160
  files: new Set(), // Will be repopulated with filtered files
145
161
  },
162
+ triggers: {
163
+ ...state.triggers,
164
+ meta: JSON.parse(JSON.stringify(state.triggers?.meta ?? {})),
165
+ files: new Set(),
166
+ },
146
167
  scheduledTasks: {
147
168
  ...state.scheduledTasks,
148
169
  meta: JSON.parse(JSON.stringify(state.scheduledTasks.meta)),
@@ -158,7 +179,12 @@ export function filterInspectorState(state, filters, logger) {
158
179
  toolsMeta: JSON.parse(JSON.stringify(state.mcpEndpoints.toolsMeta)),
159
180
  resourcesMeta: JSON.parse(JSON.stringify(state.mcpEndpoints.resourcesMeta)),
160
181
  promptsMeta: JSON.parse(JSON.stringify(state.mcpEndpoints.promptsMeta)),
161
- files: new Set(), // Will be repopulated with filtered files
182
+ files: new Set(),
183
+ },
184
+ agents: {
185
+ ...state.agents,
186
+ agentsMeta: JSON.parse(JSON.stringify(state.agents?.agentsMeta ?? {})),
187
+ files: new Map(),
162
188
  },
163
189
  cli: {
164
190
  ...state.cli,
@@ -172,23 +198,24 @@ export function filterInspectorState(state, filters, logger) {
172
198
  for (const route of Object.keys(routes)) {
173
199
  const routeMeta = routes[route];
174
200
  // Get function file path for directory filtering
175
- const funcFile = filteredState.functions.files.get(routeMeta.pikkuFuncName);
201
+ const funcFile = filteredState.functions.files.get(routeMeta.pikkuFuncId);
176
202
  const filePath = funcFile?.path;
177
203
  const matches = matchesFilters(filters, {
178
204
  type: 'http',
179
- name: routeMeta.pikkuFuncName, // Use function name, not route
205
+ name: routeMeta.pikkuFuncId, // Use function name, not route
180
206
  tags: routeMeta.tags,
181
207
  filePath,
182
208
  httpRoute: routeMeta.route,
183
209
  httpMethod: routeMeta.method,
184
- }, logger);
210
+ groupBasePath: routeMeta.groupBasePath,
211
+ }, logger, globalWarnedGroups);
185
212
  if (!matches) {
186
213
  delete routes[route];
187
214
  }
188
215
  else {
189
216
  // Track used functions/middleware/permissions
190
- if (routeMeta.pikkuFuncName) {
191
- filteredState.serviceAggregation.usedFunctions.add(routeMeta.pikkuFuncName);
217
+ if (routeMeta.pikkuFuncId) {
218
+ filteredState.serviceAggregation.usedFunctions.add(routeMeta.pikkuFuncId);
192
219
  }
193
220
  extractWireNames(routeMeta.middleware).forEach((name) => filteredState.serviceAggregation.usedMiddleware.add(name));
194
221
  extractWireNames(routeMeta.permissions).forEach((name) => filteredState.serviceAggregation.usedPermissions.add(name));
@@ -212,8 +239,8 @@ export function filterInspectorState(state, filters, logger) {
212
239
  delete filteredState.channels.meta[name];
213
240
  }
214
241
  else {
215
- if (channelMeta.pikkuFuncName) {
216
- filteredState.serviceAggregation.usedFunctions.add(channelMeta.pikkuFuncName);
242
+ if (channelMeta.pikkuFuncId) {
243
+ filteredState.serviceAggregation.usedFunctions.add(channelMeta.pikkuFuncId);
217
244
  }
218
245
  extractWireNames(channelMeta.middleware).forEach((name) => filteredState.serviceAggregation.usedMiddleware.add(name));
219
246
  extractWireNames(channelMeta.permissions).forEach((name) => filteredState.serviceAggregation.usedPermissions.add(name));
@@ -223,6 +250,27 @@ export function filterInspectorState(state, filters, logger) {
223
250
  if (Object.keys(filteredState.channels.meta).length > 0) {
224
251
  filteredState.channels.files = new Set(state.channels.files);
225
252
  }
253
+ // Filter triggers
254
+ for (const name of Object.keys(filteredState.triggers.meta)) {
255
+ const triggerMeta = filteredState.triggers.meta[name];
256
+ const matches = matchesFilters(filters, {
257
+ type: 'trigger',
258
+ name,
259
+ tags: triggerMeta.tags,
260
+ }, logger);
261
+ if (!matches) {
262
+ delete filteredState.triggers.meta[name];
263
+ }
264
+ else {
265
+ if (triggerMeta.pikkuFuncId) {
266
+ filteredState.serviceAggregation.usedFunctions.add(triggerMeta.pikkuFuncId);
267
+ }
268
+ }
269
+ }
270
+ // Repopulate triggers.files if any triggers remain
271
+ if (Object.keys(filteredState.triggers.meta).length > 0) {
272
+ filteredState.triggers.files = new Set(state.triggers.files);
273
+ }
226
274
  // Filter scheduled tasks
227
275
  for (const name of Object.keys(filteredState.scheduledTasks.meta)) {
228
276
  const taskMeta = filteredState.scheduledTasks.meta[name];
@@ -235,8 +283,8 @@ export function filterInspectorState(state, filters, logger) {
235
283
  delete filteredState.scheduledTasks.meta[name];
236
284
  }
237
285
  else {
238
- if (taskMeta.pikkuFuncName) {
239
- filteredState.serviceAggregation.usedFunctions.add(taskMeta.pikkuFuncName);
286
+ if (taskMeta.pikkuFuncId) {
287
+ filteredState.serviceAggregation.usedFunctions.add(taskMeta.pikkuFuncId);
240
288
  }
241
289
  extractWireNames(taskMeta.middleware).forEach((name) => filteredState.serviceAggregation.usedMiddleware.add(name));
242
290
  }
@@ -257,8 +305,8 @@ export function filterInspectorState(state, filters, logger) {
257
305
  delete filteredState.queueWorkers.meta[name];
258
306
  }
259
307
  else {
260
- if (workerMeta.pikkuFuncName) {
261
- filteredState.serviceAggregation.usedFunctions.add(workerMeta.pikkuFuncName);
308
+ if (workerMeta.pikkuFuncId) {
309
+ filteredState.serviceAggregation.usedFunctions.add(workerMeta.pikkuFuncId);
262
310
  }
263
311
  extractWireNames(workerMeta.middleware).forEach((name) => filteredState.serviceAggregation.usedMiddleware.add(name));
264
312
  }
@@ -279,8 +327,8 @@ export function filterInspectorState(state, filters, logger) {
279
327
  delete filteredState.mcpEndpoints.toolsMeta[name];
280
328
  }
281
329
  else {
282
- if (toolMeta.pikkuFuncName) {
283
- filteredState.serviceAggregation.usedFunctions.add(toolMeta.pikkuFuncName);
330
+ if (toolMeta.pikkuFuncId) {
331
+ filteredState.serviceAggregation.usedFunctions.add(toolMeta.pikkuFuncId);
284
332
  }
285
333
  extractWireNames(toolMeta.middleware).forEach((name) => filteredState.serviceAggregation.usedMiddleware.add(name));
286
334
  extractWireNames(toolMeta.permissions).forEach((name) => filteredState.serviceAggregation.usedPermissions.add(name));
@@ -298,8 +346,8 @@ export function filterInspectorState(state, filters, logger) {
298
346
  delete filteredState.mcpEndpoints.resourcesMeta[name];
299
347
  }
300
348
  else {
301
- if (resourceMeta.pikkuFuncName) {
302
- filteredState.serviceAggregation.usedFunctions.add(resourceMeta.pikkuFuncName);
349
+ if (resourceMeta.pikkuFuncId) {
350
+ filteredState.serviceAggregation.usedFunctions.add(resourceMeta.pikkuFuncId);
303
351
  }
304
352
  extractWireNames(resourceMeta.middleware).forEach((name) => filteredState.serviceAggregation.usedMiddleware.add(name));
305
353
  extractWireNames(resourceMeta.permissions).forEach((name) => filteredState.serviceAggregation.usedPermissions.add(name));
@@ -317,8 +365,8 @@ export function filterInspectorState(state, filters, logger) {
317
365
  delete filteredState.mcpEndpoints.promptsMeta[name];
318
366
  }
319
367
  else {
320
- if (promptMeta.pikkuFuncName) {
321
- filteredState.serviceAggregation.usedFunctions.add(promptMeta.pikkuFuncName);
368
+ if (promptMeta.pikkuFuncId) {
369
+ filteredState.serviceAggregation.usedFunctions.add(promptMeta.pikkuFuncId);
322
370
  }
323
371
  extractWireNames(promptMeta.middleware).forEach((name) => filteredState.serviceAggregation.usedMiddleware.add(name));
324
372
  extractWireNames(promptMeta.permissions).forEach((name) => filteredState.serviceAggregation.usedPermissions.add(name));
@@ -331,6 +379,28 @@ export function filterInspectorState(state, filters, logger) {
331
379
  if (hasMcpEndpoints) {
332
380
  filteredState.mcpEndpoints.files = new Set(state.mcpEndpoints.files);
333
381
  }
382
+ // Filter AI agents
383
+ for (const name of Object.keys(filteredState.agents.agentsMeta)) {
384
+ const agentMeta = filteredState.agents.agentsMeta[name];
385
+ const matches = matchesFilters(filters, {
386
+ type: 'agent',
387
+ name,
388
+ tags: agentMeta.tags,
389
+ }, logger);
390
+ if (!matches) {
391
+ delete filteredState.agents.agentsMeta[name];
392
+ }
393
+ else {
394
+ if (agentMeta.pikkuFuncId) {
395
+ filteredState.serviceAggregation.usedFunctions.add(agentMeta.pikkuFuncId);
396
+ }
397
+ extractWireNames(agentMeta.middleware).forEach((name) => filteredState.serviceAggregation.usedMiddleware.add(name));
398
+ extractWireNames(agentMeta.permissions).forEach((name) => filteredState.serviceAggregation.usedPermissions.add(name));
399
+ }
400
+ }
401
+ if (Object.keys(filteredState.agents.agentsMeta).length > 0) {
402
+ filteredState.agents.files = new Map(state.agents.files);
403
+ }
334
404
  // Filter CLI programs (note: CLI filtering might be more complex with nested commands)
335
405
  const referencedRenderers = new Set();
336
406
  for (const programName of Object.keys(filteredState.cli.meta.programs)) {
@@ -347,8 +417,8 @@ export function filterInspectorState(state, filters, logger) {
347
417
  delete programMeta.commands[commandName];
348
418
  }
349
419
  else {
350
- if (commandMeta.pikkuFuncName) {
351
- filteredState.serviceAggregation.usedFunctions.add(commandMeta.pikkuFuncName);
420
+ if (commandMeta.pikkuFuncId) {
421
+ filteredState.serviceAggregation.usedFunctions.add(commandMeta.pikkuFuncId);
352
422
  }
353
423
  extractWireNames(commandMeta.middleware).forEach((name) => filteredState.serviceAggregation.usedMiddleware.add(name));
354
424
  // Track referenced renderers
@@ -374,6 +444,20 @@ export function filterInspectorState(state, filters, logger) {
374
444
  if (hasCliPrograms || hasCliRenderers) {
375
445
  filteredState.cli.files = new Set(state.cli.files);
376
446
  }
447
+ // Post-filter version expansion: include all versions of matched functions
448
+ const includedBaseNames = new Set();
449
+ for (const funcId of filteredState.serviceAggregation.usedFunctions) {
450
+ const { baseName } = parseVersionedId(funcId);
451
+ includedBaseNames.add(baseName);
452
+ }
453
+ if (includedBaseNames.size > 0) {
454
+ for (const funcId of Object.keys(state.functions.meta)) {
455
+ const { baseName } = parseVersionedId(funcId);
456
+ if (includedBaseNames.has(baseName)) {
457
+ filteredState.serviceAggregation.usedFunctions.add(funcId);
458
+ }
459
+ }
460
+ }
377
461
  // Recalculate requiredServices based on filtered functions/middleware/permissions
378
462
  // Need to cast to InspectorState temporarily for aggregateRequiredServices
379
463
  const stateForAggregation = filteredState;
@@ -1,5 +1,5 @@
1
- import { InspectorFilters, InspectorLogger } from '../types.js';
2
- import { PikkuWiringTypes } from '@pikku/core';
1
+ import type { InspectorFilters, InspectorLogger } from '../types.js';
2
+ import type { PikkuWiringTypes } from '@pikku/core';
3
3
  /**
4
4
  * Match a value against a pattern with wildcard support
5
5
  * Supports "*" at the beginning, end, or both (e.g., "send*", "*Payment", "*process*")
@@ -1,4 +1,4 @@
1
- import { PathToNameAndType, InspectorState, InspectorOptions } from '../types.js';
1
+ import type { PathToNameAndType, InspectorState, InspectorOptions } from '../types.js';
2
2
  interface Meta {
3
3
  file: string;
4
4
  variable: string;
@@ -1,6 +1,10 @@
1
1
  import * as ts from 'typescript';
2
2
  import { ErrorCode } from '../error-codes.js';
3
- export declare const getPropertyValue: (obj: ts.ObjectLiteralExpression, propertyName: string) => string | string[] | null | boolean;
3
+ /**
4
+ * Extracts an array of strings from an object property.
5
+ */
6
+ export declare const getArrayPropertyValue: (obj: ts.ObjectLiteralExpression, propertyName: string) => string[] | null;
7
+ export declare const getPropertyValue: (obj: ts.ObjectLiteralExpression, propertyName: string) => string | string[] | number | null | boolean;
4
8
  /**
5
9
  * Extracts common wire metadata (title, tags, summary, description, errors) directly from an object
6
10
  * @param obj - The TypeScript object literal expression to extract metadata from
@@ -12,6 +16,7 @@ export declare const getPropertyValue: (obj: ts.ObjectLiteralExpression, propert
12
16
  export declare const getCommonWireMetaData: (obj: ts.ObjectLiteralExpression, wiringType: string, wiringName: string | null, logger?: {
13
17
  critical: (code: ErrorCode, message: string) => void;
14
18
  }) => {
19
+ disabled?: true;
15
20
  title?: string;
16
21
  tags?: string[];
17
22
  summary?: string;
@@ -1,5 +1,22 @@
1
1
  import * as ts from 'typescript';
2
2
  import { ErrorCode } from '../error-codes.js';
3
+ /**
4
+ * Extracts an array of strings from an object property.
5
+ */
6
+ export const getArrayPropertyValue = (obj, propertyName) => {
7
+ const property = obj.properties.find((p) => ts.isPropertyAssignment(p) &&
8
+ ts.isIdentifier(p.name) &&
9
+ p.name.text === propertyName);
10
+ if (property && ts.isPropertyAssignment(property)) {
11
+ const initializer = property.initializer;
12
+ if (ts.isArrayLiteralExpression(initializer)) {
13
+ return initializer.elements
14
+ .filter(ts.isStringLiteral)
15
+ .map((element) => element.text);
16
+ }
17
+ }
18
+ return null;
19
+ };
3
20
  export const getPropertyValue = (obj, propertyName) => {
4
21
  const property = obj.properties.find((p) => ts.isPropertyAssignment(p) &&
5
22
  ts.isIdentifier(p.name) &&
@@ -26,12 +43,16 @@ export const getPropertyValue = (obj, propertyName) => {
26
43
  if (initializer.kind === ts.SyntaxKind.FalseKeyword) {
27
44
  return false;
28
45
  }
29
- // Handle string literals for other properties
46
+ if (ts.isNumericLiteral(initializer)) {
47
+ if (propertyName === 'name' || propertyName === 'schedule') {
48
+ return initializer.text;
49
+ }
50
+ return Number(initializer.text);
51
+ }
30
52
  if (ts.isStringLiteral(initializer) ||
31
53
  ts.isNoSubstitutionTemplateLiteral(initializer)) {
32
54
  return initializer.text;
33
55
  }
34
- // Handle other initializer types if necessary
35
56
  return initializer.getText();
36
57
  }
37
58
  return null;
@@ -49,7 +70,11 @@ export const getCommonWireMetaData = (obj, wiringType, wiringName, logger) => {
49
70
  obj.properties.forEach((prop) => {
50
71
  if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
51
72
  const propName = prop.name.text;
52
- if (propName === 'title' && ts.isStringLiteral(prop.initializer)) {
73
+ if (propName === 'disabled' &&
74
+ prop.initializer.kind === ts.SyntaxKind.TrueKeyword) {
75
+ metadata.disabled = true;
76
+ }
77
+ else if (propName === 'title' && ts.isStringLiteral(prop.initializer)) {
53
78
  metadata.title = prop.initializer.text;
54
79
  }
55
80
  else if (propName === 'summary' &&
@@ -0,0 +1,2 @@
1
+ export declare function canonicalJSON(obj: unknown): string;
2
+ export declare function hashString(input: string, length?: number): string;
@@ -0,0 +1,23 @@
1
+ import { createHash } from 'crypto';
2
+ export function canonicalJSON(obj) {
3
+ return JSON.stringify(sortDeep(obj));
4
+ }
5
+ function sortDeep(value) {
6
+ if (value === null || value === undefined) {
7
+ return value;
8
+ }
9
+ if (Array.isArray(value)) {
10
+ return value.map(sortDeep);
11
+ }
12
+ if (typeof value === 'object') {
13
+ const sorted = {};
14
+ for (const key of Object.keys(value).sort()) {
15
+ sorted[key] = sortDeep(value[key]);
16
+ }
17
+ return sorted;
18
+ }
19
+ return value;
20
+ }
21
+ export function hashString(input, length = 16) {
22
+ return createHash('sha256').update(input).digest('hex').slice(0, length);
23
+ }
@@ -1,39 +1,16 @@
1
1
  import * as ts from 'typescript';
2
- import { MiddlewareMetadata } from '@pikku/core';
3
- import { InspectorState } from '../types.js';
4
- /**
5
- * Extract middleware pikkuFuncNames from an array literal expression
6
- * Resolves each identifier to its pikkuFuncName using extractFunctionName
7
- * Also handles call expressions (like logCommandInfoAndTime({...}))
8
- */
2
+ import type { MiddlewareMetadata } from '@pikku/core';
3
+ import type { InspectorState } from '../types.js';
4
+ export interface MiddlewareRef {
5
+ definitionId: string;
6
+ isFactoryCall: boolean;
7
+ }
8
+ export declare function extractMiddlewareRefs(arrayNode: ts.Expression, checker: ts.TypeChecker, rootDir: string): MiddlewareRef[];
9
9
  export declare function extractMiddlewarePikkuNames(arrayNode: ts.Expression, checker: ts.TypeChecker, rootDir: string): string[];
10
- /**
11
- * Get middleware array from an object literal expression property
12
- * Returns the initializer node for the 'middleware' property if it exists
13
- */
14
10
  export declare function getMiddlewareNode(obj: ts.ObjectLiteralExpression): ts.Expression | undefined;
15
- /**
16
- * Check if a route matches a pattern with wildcards
17
- * Pattern can be exact match or use * as wildcard
18
- * e.g., '/api/*' matches '/api/users', '/api/posts/123', etc.
19
- */
20
11
  export declare function routeMatchesPattern(route: string, pattern: string): boolean;
21
- /**
22
- * Resolve middleware for an HTTP wiring based on:
23
- * 1. Global HTTP middleware (addd([...]))
24
- * 2. Route-specific HTTP middleware (addHTTPMiddleware('/pattern', [...]))
25
- * 3. Tag-based middleware (addMiddleware('tag', [...]))
26
- * 4. Explicit wiring middleware (wireHTTP({ middleware: [...] }))
27
- * Returns undefined if no middleware is found, otherwise returns array with at least one item
28
- */
29
12
  export declare function resolveHTTPMiddleware(state: InspectorState, route: string, tags: string[] | undefined, explicitMiddlewareNode: ts.Expression | undefined, checker: ts.TypeChecker): MiddlewareMetadata[] | undefined;
30
- /**
31
- * Convenience wrapper: Extract middleware node from object and resolve
32
- * Use this in add-* files for cleaner code
33
- */
34
13
  export declare function resolveMiddleware(state: InspectorState, obj: ts.ObjectLiteralExpression, tags: string[] | undefined, checker: ts.TypeChecker): MiddlewareMetadata[] | undefined;
35
- /**
36
- * Convenience wrapper for HTTP: Extract middleware and resolve with HTTP-specific logic
37
- * Use this in add-http-route.ts for cleaner code
38
- */
39
14
  export declare function resolveHTTPMiddlewareFromObject(state: InspectorState, route: string, obj: ts.ObjectLiteralExpression, tags: string[] | undefined, checker: ts.TypeChecker): MiddlewareMetadata[] | undefined;
15
+ export declare function resolveAIMiddleware(state: InspectorState, obj: ts.ObjectLiteralExpression, checker: ts.TypeChecker): MiddlewareMetadata[] | undefined;
16
+ export declare function resolveChannelMiddleware(state: InspectorState, obj: ts.ObjectLiteralExpression, tags: string[] | undefined, checker: ts.TypeChecker): MiddlewareMetadata[] | undefined;
@@ -1,80 +1,64 @@
1
1
  import * as ts from 'typescript';
2
2
  import { extractFunctionName } from './extract-function-name.js';
3
- /**
4
- * Extract middleware pikkuFuncNames from an array literal expression
5
- * Resolves each identifier to its pikkuFuncName using extractFunctionName
6
- * Also handles call expressions (like logCommandInfoAndTime({...}))
7
- */
8
- export function extractMiddlewarePikkuNames(arrayNode, checker, rootDir) {
3
+ export function extractMiddlewareRefs(arrayNode, checker, rootDir) {
9
4
  if (!ts.isArrayLiteralExpression(arrayNode)) {
10
5
  return [];
11
6
  }
12
- const names = [];
7
+ const refs = [];
13
8
  for (const element of arrayNode.elements) {
14
9
  if (ts.isIdentifier(element)) {
15
- // Resolve the identifier to its pikkuFuncName
16
- const { pikkuFuncName } = extractFunctionName(element, checker, rootDir);
17
- names.push(pikkuFuncName);
10
+ const { pikkuFuncId } = extractFunctionName(element, checker, rootDir);
11
+ refs.push({
12
+ definitionId: pikkuFuncId.startsWith('__temp_')
13
+ ? element.text
14
+ : pikkuFuncId,
15
+ isFactoryCall: false,
16
+ });
18
17
  }
19
18
  else if (ts.isCallExpression(element)) {
20
- // Handle call expressions like rateLimiter(10) or logCommandInfoAndTime({...})
21
- // Extract the function being called (e.g., 'rateLimiter' from 'rateLimiter(10)')
22
- const { pikkuFuncName } = extractFunctionName(element.expression, checker, rootDir);
23
- names.push(pikkuFuncName);
19
+ const { pikkuFuncId } = extractFunctionName(element.expression, checker, rootDir);
20
+ refs.push({
21
+ definitionId: pikkuFuncId.startsWith('__temp_') &&
22
+ ts.isIdentifier(element.expression)
23
+ ? element.expression.text
24
+ : pikkuFuncId,
25
+ isFactoryCall: true,
26
+ });
24
27
  }
25
28
  }
26
- return names;
29
+ return refs;
30
+ }
31
+ export function extractMiddlewarePikkuNames(arrayNode, checker, rootDir) {
32
+ return extractMiddlewareRefs(arrayNode, checker, rootDir).map((r) => r.definitionId);
27
33
  }
28
- /**
29
- * Get middleware array from an object literal expression property
30
- * Returns the initializer node for the 'middleware' property if it exists
31
- */
32
34
  export function getMiddlewareNode(obj) {
33
35
  const middlewareProp = obj.properties.find((p) => ts.isPropertyAssignment(p) &&
34
36
  ts.isIdentifier(p.name) &&
35
37
  p.name.text === 'middleware');
36
38
  return middlewareProp?.initializer;
37
39
  }
38
- /**
39
- * Check if a route matches a pattern with wildcards
40
- * Pattern can be exact match or use * as wildcard
41
- * e.g., '/api/*' matches '/api/users', '/api/posts/123', etc.
42
- */
43
40
  export function routeMatchesPattern(route, pattern) {
44
41
  if (route === pattern)
45
42
  return true;
46
- // Convert pattern to regex: replace * with .*
47
43
  const regexPattern = pattern
48
- .replace(/[.+?^${}()|[\]\\]/g, '\\$&') // Escape regex special chars except *
49
- .replace(/\*/g, '.*'); // Replace * with .*
44
+ .replace(/[.+?^${}()|[\]\\]/g, '\\$&')
45
+ .replace(/\*/g, '.*');
50
46
  const regex = new RegExp(`^${regexPattern}$`);
51
47
  return regex.test(route);
52
48
  }
53
- /**
54
- * Resolve middleware for an HTTP wiring based on:
55
- * 1. Global HTTP middleware (addd([...]))
56
- * 2. Route-specific HTTP middleware (addHTTPMiddleware('/pattern', [...]))
57
- * 3. Tag-based middleware (addMiddleware('tag', [...]))
58
- * 4. Explicit wiring middleware (wireHTTP({ middleware: [...] }))
59
- * Returns undefined if no middleware is found, otherwise returns array with at least one item
60
- */
61
49
  export function resolveHTTPMiddleware(state, route, tags, explicitMiddlewareNode, checker) {
62
50
  const resolved = [];
63
- // 1. HTTP route middleware groups (includes '*' for global)
64
51
  for (const [pattern, _groupMeta] of state.http.routeMiddleware.entries()) {
65
52
  if (routeMatchesPattern(route, pattern)) {
66
- // Just reference the group by route pattern
67
53
  resolved.push({
68
54
  type: 'http',
69
55
  route: pattern,
70
56
  });
71
57
  }
72
58
  }
73
- // 2. Tag-based middleware groups
74
59
  if (tags && tags.length > 0) {
75
60
  for (const tag of tags) {
76
61
  if (state.middleware.tagMiddleware.has(tag)) {
77
- // Just reference the group by tag
78
62
  resolved.push({
79
63
  type: 'tag',
80
64
  tag,
@@ -82,32 +66,24 @@ export function resolveHTTPMiddleware(state, route, tags, explicitMiddlewareNode
82
66
  }
83
67
  }
84
68
  }
85
- // 3. Explicit wire middleware (inline is OK here)
86
69
  if (explicitMiddlewareNode) {
87
70
  const middlewareNames = extractMiddlewarePikkuNames(explicitMiddlewareNode, checker, state.rootDir);
88
71
  for (const name of middlewareNames) {
89
- const meta = state.middleware.meta[name];
72
+ const def = state.middleware.definitions[name];
90
73
  resolved.push({
91
74
  type: 'wire',
92
75
  name,
93
- inline: meta?.exportedName === null,
76
+ inline: def?.exportedName === null,
94
77
  });
95
78
  }
96
79
  }
97
80
  return resolved.length > 0 ? resolved : undefined;
98
81
  }
99
- /**
100
- * Resolve tag-based and explicit middleware (common logic for wires and functions)
101
- * 1. Tag-based middleware (addMiddleware('tag', [...]))
102
- * 2. Explicit middleware (wireHTTP/pikkuFunc({ middleware: [...] }))
103
- */
104
82
  function resolveTagAndExplicitMiddleware(state, tags, explicitMiddlewareNode, checker) {
105
83
  const resolved = [];
106
- // 1. Tag-based middleware groups
107
84
  if (tags && tags.length > 0) {
108
85
  for (const tag of tags) {
109
86
  if (state.middleware.tagMiddleware.has(tag)) {
110
- // Just reference the group by tag
111
87
  resolved.push({
112
88
  type: 'tag',
113
89
  tag,
@@ -115,43 +91,81 @@ function resolveTagAndExplicitMiddleware(state, tags, explicitMiddlewareNode, ch
115
91
  }
116
92
  }
117
93
  }
118
- // 2. Explicit middleware (inline is OK here - used directly in wire/function)
119
94
  if (explicitMiddlewareNode) {
120
95
  const middlewareNames = extractMiddlewarePikkuNames(explicitMiddlewareNode, checker, state.rootDir);
121
96
  for (const name of middlewareNames) {
122
- const meta = state.middleware.meta[name];
97
+ const def = state.middleware.definitions[name];
123
98
  resolved.push({
124
99
  type: 'wire',
125
100
  name,
126
- inline: meta?.exportedName === null,
101
+ inline: def?.exportedName === null,
127
102
  });
128
103
  }
129
104
  }
130
105
  return resolved;
131
106
  }
132
- /**
133
- * Resolve middleware for a function based on:
134
- * 1. Tag-based middleware (addMiddleware('tag', [...]))
135
- * 2. Explicit function middleware (pikkuFunc({ middleware: [...] }))
136
- * Returns undefined if no middleware is found, otherwise returns array with at least one item
137
- */
138
107
  function resolveFunctionMiddlewareInternal(state, tags, explicitMiddlewareNode, checker) {
139
108
  const resolved = resolveTagAndExplicitMiddleware(state, tags, explicitMiddlewareNode, checker);
140
109
  return resolved.length > 0 ? resolved : undefined;
141
110
  }
142
- /**
143
- * Convenience wrapper: Extract middleware node from object and resolve
144
- * Use this in add-* files for cleaner code
145
- */
146
111
  export function resolveMiddleware(state, obj, tags, checker) {
147
112
  const explicitMiddlewareNode = getMiddlewareNode(obj);
148
113
  return resolveFunctionMiddlewareInternal(state, tags, explicitMiddlewareNode, checker);
149
114
  }
150
- /**
151
- * Convenience wrapper for HTTP: Extract middleware and resolve with HTTP-specific logic
152
- * Use this in add-http-route.ts for cleaner code
153
- */
154
115
  export function resolveHTTPMiddlewareFromObject(state, route, obj, tags, checker) {
155
116
  const explicitMiddlewareNode = getMiddlewareNode(obj);
156
117
  return resolveHTTPMiddleware(state, route, tags, explicitMiddlewareNode, checker);
157
118
  }
119
+ function getAIMiddlewareNode(obj) {
120
+ const prop = obj.properties.find((p) => ts.isPropertyAssignment(p) &&
121
+ ts.isIdentifier(p.name) &&
122
+ p.name.text === 'aiMiddleware');
123
+ return prop?.initializer;
124
+ }
125
+ export function resolveAIMiddleware(state, obj, checker) {
126
+ const explicitNode = getAIMiddlewareNode(obj);
127
+ if (!explicitNode)
128
+ return undefined;
129
+ const names = extractMiddlewarePikkuNames(explicitNode, checker, state.rootDir);
130
+ const resolved = names.map((name) => {
131
+ const def = state.aiMiddleware.definitions[name];
132
+ return {
133
+ type: 'wire',
134
+ name,
135
+ inline: def?.exportedName === null,
136
+ };
137
+ });
138
+ return resolved.length > 0 ? resolved : undefined;
139
+ }
140
+ function getChannelMiddlewareNode(obj) {
141
+ const prop = obj.properties.find((p) => ts.isPropertyAssignment(p) &&
142
+ ts.isIdentifier(p.name) &&
143
+ p.name.text === 'channelMiddleware');
144
+ return prop?.initializer;
145
+ }
146
+ export function resolveChannelMiddleware(state, obj, tags, checker) {
147
+ const resolved = [];
148
+ if (tags && tags.length > 0) {
149
+ for (const tag of tags) {
150
+ if (state.channelMiddleware.tagMiddleware.has(tag)) {
151
+ resolved.push({
152
+ type: 'tag',
153
+ tag,
154
+ });
155
+ }
156
+ }
157
+ }
158
+ const explicitNode = getChannelMiddlewareNode(obj);
159
+ if (explicitNode) {
160
+ const names = extractMiddlewarePikkuNames(explicitNode, checker, state.rootDir);
161
+ for (const name of names) {
162
+ const def = state.channelMiddleware.definitions[name];
163
+ resolved.push({
164
+ type: 'wire',
165
+ name,
166
+ inline: def?.exportedName === null,
167
+ });
168
+ }
169
+ }
170
+ return resolved.length > 0 ? resolved : undefined;
171
+ }