@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,11 +1,9 @@
1
1
  import { test, describe } from 'node:test'
2
2
  import { strict as assert } from 'node:assert'
3
3
  import { filterInspectorState } from './filter-inspector-state.js'
4
- import { InspectorState, InspectorFilters } from '../types.js'
5
- import {
6
- deserializeInspectorState,
7
- SerializableInspectorState,
8
- } from './serialize-inspector-state.js'
4
+ import type { InspectorState, InspectorFilters } from '../types.js'
5
+ import type { SerializableInspectorState } from './serialize-inspector-state.js'
6
+ import { deserializeInspectorState } from './serialize-inspector-state.js'
9
7
  import { readFileSync } from 'node:fs'
10
8
  import { fileURLToPath } from 'node:url'
11
9
  import { dirname, join } from 'node:path'
@@ -46,7 +44,7 @@ function createMockInspectorState(): Omit<InspectorState, 'typesLookup'> {
46
44
  meta: {
47
45
  get: {
48
46
  '/api/users': {
49
- pikkuFuncName: 'getUsers',
47
+ pikkuFuncId: 'getUsers',
50
48
  route: '/api/users',
51
49
  method: 'GET',
52
50
  tags: ['api', 'public'],
@@ -54,7 +52,7 @@ function createMockInspectorState(): Omit<InspectorState, 'typesLookup'> {
54
52
  permissions: [],
55
53
  },
56
54
  '/admin/settings': {
57
- pikkuFuncName: 'getAdminSettings',
55
+ pikkuFuncId: 'getAdminSettings',
58
56
  route: '/admin/settings',
59
57
  method: 'GET',
60
58
  tags: ['admin'],
@@ -64,7 +62,7 @@ function createMockInspectorState(): Omit<InspectorState, 'typesLookup'> {
64
62
  },
65
63
  post: {
66
64
  '/api/users': {
67
- pikkuFuncName: 'createUser',
65
+ pikkuFuncId: 'createUser',
68
66
  route: '/api/users',
69
67
  method: 'POST',
70
68
  tags: ['api'],
@@ -152,13 +150,13 @@ function createMockInspectorState(): Omit<InspectorState, 'typesLookup'> {
152
150
  channels: {
153
151
  meta: {
154
152
  'chat-channel': {
155
- pikkuFuncName: 'handleChatMessage',
153
+ pikkuFuncId: 'handleChatMessage',
156
154
  tags: ['realtime', 'public'],
157
155
  middleware: [],
158
156
  permissions: [],
159
157
  },
160
158
  'admin-channel': {
161
- pikkuFuncName: 'handleAdminMessage',
159
+ pikkuFuncId: 'handleAdminMessage',
162
160
  tags: ['realtime', 'admin'],
163
161
  middleware: [{ type: 'wire', name: 'authMiddleware' }],
164
162
  permissions: [],
@@ -169,13 +167,13 @@ function createMockInspectorState(): Omit<InspectorState, 'typesLookup'> {
169
167
  scheduledTasks: {
170
168
  meta: {
171
169
  'daily-report': {
172
- pikkuFuncName: 'dailyReport',
170
+ pikkuFuncId: 'dailyReport',
173
171
  schedule: '0 0 * * *',
174
172
  tags: ['cron', 'reports'],
175
173
  middleware: [],
176
174
  },
177
175
  'hourly-cleanup': {
178
- pikkuFuncName: 'hourlyCleanup',
176
+ pikkuFuncId: 'hourlyCleanup',
179
177
  schedule: '0 * * * *',
180
178
  tags: ['cron', 'maintenance'],
181
179
  middleware: [],
@@ -186,20 +184,27 @@ function createMockInspectorState(): Omit<InspectorState, 'typesLookup'> {
186
184
  queueWorkers: {
187
185
  meta: {
188
186
  'email-worker': {
189
- pikkuFuncName: 'sendEmailWorker',
190
- queueName: 'email-queue',
187
+ pikkuFuncId: 'sendEmailWorker',
188
+ name: 'email-queue',
191
189
  tags: ['queue', 'email'],
192
190
  middleware: [],
193
191
  },
194
192
  'notification-worker': {
195
- pikkuFuncName: 'sendNotificationWorker',
196
- queueName: 'notification-queue',
193
+ pikkuFuncId: 'sendNotificationWorker',
194
+ name: 'notification-queue',
197
195
  tags: ['queue', 'notifications'],
198
196
  middleware: [],
199
197
  },
200
198
  },
201
199
  files: new Set(['/test/project/src/workers/email.ts']),
202
200
  },
201
+ workflows: {
202
+ meta: {},
203
+ files: new Map(),
204
+ graphMeta: {},
205
+ graphFiles: new Map(),
206
+ invokedWorkflows: new Set(),
207
+ },
203
208
  rpc: {
204
209
  internalMeta: {},
205
210
  internalFiles: new Map(),
@@ -212,7 +217,7 @@ function createMockInspectorState(): Omit<InspectorState, 'typesLookup'> {
212
217
  'search-tool': {
213
218
  name: 'search-tool',
214
219
  description: 'Search tool',
215
- pikkuFuncName: 'mcpSearchTool',
220
+ pikkuFuncId: 'mcpSearchTool',
216
221
  tags: ['mcp', 'search'],
217
222
  middleware: [],
218
223
  permissions: [],
@@ -220,7 +225,7 @@ function createMockInspectorState(): Omit<InspectorState, 'typesLookup'> {
220
225
  'analyze-tool': {
221
226
  name: 'analyze-tool',
222
227
  description: 'Analyze tool',
223
- pikkuFuncName: 'mcpAnalyzeTool',
228
+ pikkuFuncId: 'mcpAnalyzeTool',
224
229
  tags: ['mcp', 'analytics'],
225
230
  middleware: [],
226
231
  permissions: [],
@@ -231,7 +236,7 @@ function createMockInspectorState(): Omit<InspectorState, 'typesLookup'> {
231
236
  title: 'Docs Resource',
232
237
  description: 'Documentation resource',
233
238
  uri: 'docs://resource',
234
- pikkuFuncName: 'mcpDocsResource',
239
+ pikkuFuncId: 'mcpDocsResource',
235
240
  tags: ['mcp', 'docs'],
236
241
  middleware: [],
237
242
  permissions: [],
@@ -241,7 +246,7 @@ function createMockInspectorState(): Omit<InspectorState, 'typesLookup'> {
241
246
  'help-prompt': {
242
247
  name: 'help-prompt',
243
248
  description: 'Help prompt',
244
- pikkuFuncName: 'mcpHelpPrompt',
249
+ pikkuFuncId: 'mcpHelpPrompt',
245
250
  tags: ['mcp', 'help'],
246
251
  middleware: [],
247
252
  permissions: [],
@@ -255,14 +260,14 @@ function createMockInspectorState(): Omit<InspectorState, 'typesLookup'> {
255
260
  'my-cli': {
256
261
  commands: {
257
262
  build: {
258
- pikkuFuncName: 'cliCommand',
263
+ pikkuFuncId: 'cliCommand',
259
264
  tags: ['cli', 'build'],
260
265
  middleware: [],
261
266
  positionals: [],
262
267
  options: {},
263
268
  } as any,
264
269
  test: {
265
- pikkuFuncName: 'cliTestCommand',
270
+ pikkuFuncId: 'cliTestCommand',
266
271
  tags: ['cli', 'test'],
267
272
  middleware: [],
268
273
  positionals: [],
@@ -275,11 +280,18 @@ function createMockInspectorState(): Omit<InspectorState, 'typesLookup'> {
275
280
  files: new Set(['/test/project/src/cli/commands.ts']),
276
281
  },
277
282
  middleware: {
278
- meta: {},
283
+ definitions: {},
284
+ instances: {},
285
+ tagMiddleware: new Map(),
286
+ },
287
+ channelMiddleware: {
288
+ definitions: {},
289
+ instances: {},
279
290
  tagMiddleware: new Map(),
280
291
  },
281
292
  permissions: {
282
- meta: {},
293
+ definitions: {},
294
+ instances: {},
283
295
  tagPermissions: new Map(),
284
296
  },
285
297
  serviceAggregation: {
@@ -413,7 +425,7 @@ describe('filterInspectorState', () => {
413
425
 
414
426
  assert.equal(Object.keys(result.http.meta.get).length, 1)
415
427
  assert.ok(result.http.meta.get['/api/users'])
416
- assert.equal(result.http.meta.get['/api/users'].pikkuFuncName, 'getUsers')
428
+ assert.equal(result.http.meta.get['/api/users'].pikkuFuncId, 'getUsers')
417
429
  })
418
430
 
419
431
  test('should filter HTTP routes by name wildcard', () => {
@@ -1,7 +1,15 @@
1
- import { InspectorState, InspectorFilters, InspectorLogger } from '../types.js'
2
- import { PikkuWiringTypes } from '@pikku/core'
1
+ import type {
2
+ InspectorState,
3
+ InspectorFilters,
4
+ InspectorLogger,
5
+ } from '../types.js'
6
+ import type { PikkuWiringTypes } from '@pikku/core'
7
+ import { parseVersionedId } from '@pikku/core'
3
8
  import { aggregateRequiredServices } from './post-process.js'
4
9
 
10
+ // Module-level Set to track warned groups across multiple filter calls
11
+ const globalWarnedGroups = new Set<string>()
12
+
5
13
  /**
6
14
  * Match a value against a pattern with wildcard support
7
15
  * Supports "*" at the beginning, end, or both (e.g., "send*", "*Payment", "*process*")
@@ -39,8 +47,10 @@ function matchesFilters(
39
47
  filePath?: string
40
48
  httpRoute?: string
41
49
  httpMethod?: string
50
+ groupBasePath?: string
42
51
  },
43
- logger: InspectorLogger
52
+ logger: InspectorLogger,
53
+ warnedGroups?: Set<string>
44
54
  ): boolean {
45
55
  // If no filters, allow everything
46
56
  if (Object.keys(filters).length === 0) return true
@@ -87,10 +97,13 @@ function matchesFilters(
87
97
  }
88
98
  }
89
99
 
90
- // Check name filter
100
+ // Check name filter (match against both full ID and base name for versioned functions)
91
101
  if (filters.names && filters.names.length > 0) {
92
- const nameMatches = filters.names.some((pattern) =>
93
- matchesWildcard(meta.name, pattern)
102
+ const { baseName } = parseVersionedId(meta.name)
103
+ const nameMatches = filters.names.some(
104
+ (pattern) =>
105
+ matchesWildcard(meta.name, pattern) ||
106
+ (baseName !== meta.name && matchesWildcard(baseName, pattern))
94
107
  )
95
108
  if (!nameMatches) {
96
109
  logger.debug(`⒡ Filtered by name: ${meta.type}:${meta.name}`)
@@ -107,6 +120,23 @@ function matchesFilters(
107
120
  logger.debug(`⒡ Filtered by HTTP route: ${meta.httpRoute}`)
108
121
  return false
109
122
  }
123
+
124
+ // If route is part of a wireHTTPRoutes group, check if filter is at group level
125
+ if (meta.groupBasePath && warnedGroups) {
126
+ const groupMatches = filters.httpRoutes.some(
127
+ (pattern) =>
128
+ matchesWildcard(meta.groupBasePath!, pattern) ||
129
+ matchesWildcard(meta.groupBasePath! + '/*', pattern)
130
+ )
131
+ if (!groupMatches && !warnedGroups.has(meta.groupBasePath)) {
132
+ warnedGroups.add(meta.groupBasePath)
133
+ logger.warn(
134
+ `Filtering within wireHTTPRoutes group is not yet supported. ` +
135
+ `Route '${meta.httpRoute}' is part of group '${meta.groupBasePath}'. ` +
136
+ `Use '--httpRoutes=${meta.groupBasePath}/*' to filter the entire group.`
137
+ )
138
+ }
139
+ }
110
140
  }
111
141
 
112
142
  // Check HTTP method filter
@@ -181,6 +211,11 @@ export function filterInspectorState(
181
211
  meta: JSON.parse(JSON.stringify(state.channels.meta)),
182
212
  files: new Set<string>(), // Will be repopulated with filtered files
183
213
  },
214
+ triggers: {
215
+ ...state.triggers,
216
+ meta: JSON.parse(JSON.stringify(state.triggers?.meta ?? {})),
217
+ files: new Set<string>(),
218
+ },
184
219
  scheduledTasks: {
185
220
  ...state.scheduledTasks,
186
221
  meta: JSON.parse(JSON.stringify(state.scheduledTasks.meta)),
@@ -198,7 +233,12 @@ export function filterInspectorState(
198
233
  JSON.stringify(state.mcpEndpoints.resourcesMeta)
199
234
  ),
200
235
  promptsMeta: JSON.parse(JSON.stringify(state.mcpEndpoints.promptsMeta)),
201
- files: new Set<string>(), // Will be repopulated with filtered files
236
+ files: new Set<string>(),
237
+ },
238
+ agents: {
239
+ ...state.agents,
240
+ agentsMeta: JSON.parse(JSON.stringify(state.agents?.agentsMeta ?? {})),
241
+ files: new Map(),
202
242
  },
203
243
  cli: {
204
244
  ...state.cli,
@@ -214,31 +254,31 @@ export function filterInspectorState(
214
254
  const routeMeta = routes[route]
215
255
 
216
256
  // Get function file path for directory filtering
217
- const funcFile = filteredState.functions.files.get(
218
- routeMeta.pikkuFuncName
219
- )
257
+ const funcFile = filteredState.functions.files.get(routeMeta.pikkuFuncId)
220
258
  const filePath = funcFile?.path
221
259
 
222
260
  const matches = matchesFilters(
223
261
  filters,
224
262
  {
225
263
  type: 'http' as PikkuWiringTypes,
226
- name: routeMeta.pikkuFuncName, // Use function name, not route
264
+ name: routeMeta.pikkuFuncId, // Use function name, not route
227
265
  tags: routeMeta.tags,
228
266
  filePath,
229
267
  httpRoute: routeMeta.route,
230
268
  httpMethod: routeMeta.method,
269
+ groupBasePath: routeMeta.groupBasePath,
231
270
  },
232
- logger
271
+ logger,
272
+ globalWarnedGroups
233
273
  )
234
274
 
235
275
  if (!matches) {
236
276
  delete routes[route]
237
277
  } else {
238
278
  // Track used functions/middleware/permissions
239
- if (routeMeta.pikkuFuncName) {
279
+ if (routeMeta.pikkuFuncId) {
240
280
  filteredState.serviceAggregation.usedFunctions.add(
241
- routeMeta.pikkuFuncName
281
+ routeMeta.pikkuFuncId
242
282
  )
243
283
  }
244
284
  extractWireNames(routeMeta.middleware).forEach((name: string) =>
@@ -275,9 +315,9 @@ export function filterInspectorState(
275
315
  if (!matches) {
276
316
  delete filteredState.channels.meta[name]
277
317
  } else {
278
- if (channelMeta.pikkuFuncName) {
318
+ if (channelMeta.pikkuFuncId) {
279
319
  filteredState.serviceAggregation.usedFunctions.add(
280
- channelMeta.pikkuFuncName
320
+ channelMeta.pikkuFuncId
281
321
  )
282
322
  }
283
323
  extractWireNames(channelMeta.middleware).forEach((name: string) =>
@@ -294,6 +334,35 @@ export function filterInspectorState(
294
334
  filteredState.channels.files = new Set(state.channels.files)
295
335
  }
296
336
 
337
+ // Filter triggers
338
+ for (const name of Object.keys(filteredState.triggers.meta)) {
339
+ const triggerMeta = filteredState.triggers.meta[name]
340
+ const matches = matchesFilters(
341
+ filters,
342
+ {
343
+ type: 'trigger' as PikkuWiringTypes,
344
+ name,
345
+ tags: triggerMeta.tags,
346
+ },
347
+ logger
348
+ )
349
+
350
+ if (!matches) {
351
+ delete filteredState.triggers.meta[name]
352
+ } else {
353
+ if (triggerMeta.pikkuFuncId) {
354
+ filteredState.serviceAggregation.usedFunctions.add(
355
+ triggerMeta.pikkuFuncId
356
+ )
357
+ }
358
+ }
359
+ }
360
+
361
+ // Repopulate triggers.files if any triggers remain
362
+ if (Object.keys(filteredState.triggers.meta).length > 0) {
363
+ filteredState.triggers.files = new Set(state.triggers.files)
364
+ }
365
+
297
366
  // Filter scheduled tasks
298
367
  for (const name of Object.keys(filteredState.scheduledTasks.meta)) {
299
368
  const taskMeta = filteredState.scheduledTasks.meta[name]
@@ -310,10 +379,8 @@ export function filterInspectorState(
310
379
  if (!matches) {
311
380
  delete filteredState.scheduledTasks.meta[name]
312
381
  } else {
313
- if (taskMeta.pikkuFuncName) {
314
- filteredState.serviceAggregation.usedFunctions.add(
315
- taskMeta.pikkuFuncName
316
- )
382
+ if (taskMeta.pikkuFuncId) {
383
+ filteredState.serviceAggregation.usedFunctions.add(taskMeta.pikkuFuncId)
317
384
  }
318
385
  extractWireNames(taskMeta.middleware).forEach((name: string) =>
319
386
  filteredState.serviceAggregation.usedMiddleware.add(name)
@@ -342,9 +409,9 @@ export function filterInspectorState(
342
409
  if (!matches) {
343
410
  delete filteredState.queueWorkers.meta[name]
344
411
  } else {
345
- if (workerMeta.pikkuFuncName) {
412
+ if (workerMeta.pikkuFuncId) {
346
413
  filteredState.serviceAggregation.usedFunctions.add(
347
- workerMeta.pikkuFuncName
414
+ workerMeta.pikkuFuncId
348
415
  )
349
416
  }
350
417
  extractWireNames(workerMeta.middleware).forEach((name: string) =>
@@ -374,10 +441,8 @@ export function filterInspectorState(
374
441
  if (!matches) {
375
442
  delete filteredState.mcpEndpoints.toolsMeta[name]
376
443
  } else {
377
- if (toolMeta.pikkuFuncName) {
378
- filteredState.serviceAggregation.usedFunctions.add(
379
- toolMeta.pikkuFuncName
380
- )
444
+ if (toolMeta.pikkuFuncId) {
445
+ filteredState.serviceAggregation.usedFunctions.add(toolMeta.pikkuFuncId)
381
446
  }
382
447
  extractWireNames(toolMeta.middleware).forEach((name: string) =>
383
448
  filteredState.serviceAggregation.usedMiddleware.add(name)
@@ -404,9 +469,9 @@ export function filterInspectorState(
404
469
  if (!matches) {
405
470
  delete filteredState.mcpEndpoints.resourcesMeta[name]
406
471
  } else {
407
- if (resourceMeta.pikkuFuncName) {
472
+ if (resourceMeta.pikkuFuncId) {
408
473
  filteredState.serviceAggregation.usedFunctions.add(
409
- resourceMeta.pikkuFuncName
474
+ resourceMeta.pikkuFuncId
410
475
  )
411
476
  }
412
477
  extractWireNames(resourceMeta.middleware).forEach((name: string) =>
@@ -434,9 +499,9 @@ export function filterInspectorState(
434
499
  if (!matches) {
435
500
  delete filteredState.mcpEndpoints.promptsMeta[name]
436
501
  } else {
437
- if (promptMeta.pikkuFuncName) {
502
+ if (promptMeta.pikkuFuncId) {
438
503
  filteredState.serviceAggregation.usedFunctions.add(
439
- promptMeta.pikkuFuncName
504
+ promptMeta.pikkuFuncId
440
505
  )
441
506
  }
442
507
  extractWireNames(promptMeta.middleware).forEach((name: string) =>
@@ -457,6 +522,40 @@ export function filterInspectorState(
457
522
  filteredState.mcpEndpoints.files = new Set(state.mcpEndpoints.files)
458
523
  }
459
524
 
525
+ // Filter AI agents
526
+ for (const name of Object.keys(filteredState.agents.agentsMeta)) {
527
+ const agentMeta = filteredState.agents.agentsMeta[name]
528
+ const matches = matchesFilters(
529
+ filters,
530
+ {
531
+ type: 'agent' as PikkuWiringTypes,
532
+ name,
533
+ tags: agentMeta.tags,
534
+ },
535
+ logger
536
+ )
537
+
538
+ if (!matches) {
539
+ delete filteredState.agents.agentsMeta[name]
540
+ } else {
541
+ if (agentMeta.pikkuFuncId) {
542
+ filteredState.serviceAggregation.usedFunctions.add(
543
+ agentMeta.pikkuFuncId
544
+ )
545
+ }
546
+ extractWireNames(agentMeta.middleware).forEach((name: string) =>
547
+ filteredState.serviceAggregation.usedMiddleware.add(name)
548
+ )
549
+ extractWireNames(agentMeta.permissions).forEach((name: string) =>
550
+ filteredState.serviceAggregation.usedPermissions.add(name)
551
+ )
552
+ }
553
+ }
554
+
555
+ if (Object.keys(filteredState.agents.agentsMeta).length > 0) {
556
+ filteredState.agents.files = new Map(state.agents.files)
557
+ }
558
+
460
559
  // Filter CLI programs (note: CLI filtering might be more complex with nested commands)
461
560
  const referencedRenderers = new Set<string>()
462
561
 
@@ -479,9 +578,9 @@ export function filterInspectorState(
479
578
  if (!matches) {
480
579
  delete programMeta.commands[commandName]
481
580
  } else {
482
- if (commandMeta.pikkuFuncName) {
581
+ if (commandMeta.pikkuFuncId) {
483
582
  filteredState.serviceAggregation.usedFunctions.add(
484
- commandMeta.pikkuFuncName
583
+ commandMeta.pikkuFuncId
485
584
  )
486
585
  }
487
586
  extractWireNames(commandMeta.middleware).forEach((name: string) =>
@@ -517,6 +616,21 @@ export function filterInspectorState(
517
616
  filteredState.cli.files = new Set(state.cli.files)
518
617
  }
519
618
 
619
+ // Post-filter version expansion: include all versions of matched functions
620
+ const includedBaseNames = new Set<string>()
621
+ for (const funcId of filteredState.serviceAggregation.usedFunctions) {
622
+ const { baseName } = parseVersionedId(funcId)
623
+ includedBaseNames.add(baseName)
624
+ }
625
+ if (includedBaseNames.size > 0) {
626
+ for (const funcId of Object.keys(state.functions.meta)) {
627
+ const { baseName } = parseVersionedId(funcId)
628
+ if (includedBaseNames.has(baseName)) {
629
+ filteredState.serviceAggregation.usedFunctions.add(funcId)
630
+ }
631
+ }
632
+ }
633
+
520
634
  // Recalculate requiredServices based on filtered functions/middleware/permissions
521
635
  // Need to cast to InspectorState temporarily for aggregateRequiredServices
522
636
  const stateForAggregation = filteredState as InspectorState
@@ -1,6 +1,6 @@
1
1
  import { test, describe } from 'node:test'
2
2
  import { strict as assert } from 'node:assert'
3
- import { InspectorFilters } from '../types'
3
+ import type { InspectorFilters } from '../types'
4
4
  import { matchesFilters, matchesWildcard } from './filter-utils'
5
5
 
6
6
  describe('matchesFilters', () => {
@@ -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
  /**
5
5
  * Match a value against a pattern with wildcard support
@@ -1,4 +1,4 @@
1
- import {
1
+ import type {
2
2
  PathToNameAndType,
3
3
  InspectorState,
4
4
  InspectorOptions,
@@ -1,10 +1,36 @@
1
1
  import * as ts from 'typescript'
2
2
  import { ErrorCode } from '../error-codes.js'
3
3
 
4
+ /**
5
+ * Extracts an array of strings from an object property.
6
+ */
7
+ export const getArrayPropertyValue = (
8
+ obj: ts.ObjectLiteralExpression,
9
+ propertyName: string
10
+ ): string[] | null => {
11
+ const property = obj.properties.find(
12
+ (p) =>
13
+ ts.isPropertyAssignment(p) &&
14
+ ts.isIdentifier(p.name) &&
15
+ p.name.text === propertyName
16
+ )
17
+
18
+ if (property && ts.isPropertyAssignment(property)) {
19
+ const initializer = property.initializer
20
+ if (ts.isArrayLiteralExpression(initializer)) {
21
+ return initializer.elements
22
+ .filter(ts.isStringLiteral)
23
+ .map((element) => element.text)
24
+ }
25
+ }
26
+
27
+ return null
28
+ }
29
+
4
30
  export const getPropertyValue = (
5
31
  obj: ts.ObjectLiteralExpression,
6
32
  propertyName: string
7
- ): string | string[] | null | boolean => {
33
+ ): string | string[] | number | null | boolean => {
8
34
  const property = obj.properties.find(
9
35
  (p) =>
10
36
  ts.isPropertyAssignment(p) &&
@@ -41,7 +67,13 @@ export const getPropertyValue = (
41
67
  return false
42
68
  }
43
69
 
44
- // Handle string literals for other properties
70
+ if (ts.isNumericLiteral(initializer)) {
71
+ if (propertyName === 'name' || propertyName === 'schedule') {
72
+ return initializer.text
73
+ }
74
+ return Number(initializer.text)
75
+ }
76
+
45
77
  if (
46
78
  ts.isStringLiteral(initializer) ||
47
79
  ts.isNoSubstitutionTemplateLiteral(initializer)
@@ -49,7 +81,6 @@ export const getPropertyValue = (
49
81
  return initializer.text
50
82
  }
51
83
 
52
- // Handle other initializer types if necessary
53
84
  return initializer.getText()
54
85
  }
55
86
 
@@ -70,6 +101,7 @@ export const getCommonWireMetaData = (
70
101
  wiringName: string | null,
71
102
  logger?: { critical: (code: ErrorCode, message: string) => void }
72
103
  ): {
104
+ disabled?: true
73
105
  title?: string
74
106
  tags?: string[]
75
107
  summary?: string
@@ -77,6 +109,7 @@ export const getCommonWireMetaData = (
77
109
  errors?: string[]
78
110
  } => {
79
111
  const metadata: {
112
+ disabled?: true
80
113
  title?: string
81
114
  tags?: string[]
82
115
  summary?: string
@@ -88,7 +121,12 @@ export const getCommonWireMetaData = (
88
121
  if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
89
122
  const propName = prop.name.text
90
123
 
91
- if (propName === 'title' && ts.isStringLiteral(prop.initializer)) {
124
+ if (
125
+ propName === 'disabled' &&
126
+ prop.initializer.kind === ts.SyntaxKind.TrueKeyword
127
+ ) {
128
+ metadata.disabled = true
129
+ } else if (propName === 'title' && ts.isStringLiteral(prop.initializer)) {
92
130
  metadata.title = prop.initializer.text
93
131
  } else if (
94
132
  propName === 'summary' &&
@@ -0,0 +1,26 @@
1
+ import { createHash } from 'crypto'
2
+
3
+ export function canonicalJSON(obj: unknown): string {
4
+ return JSON.stringify(sortDeep(obj))
5
+ }
6
+
7
+ function sortDeep(value: unknown): unknown {
8
+ if (value === null || value === undefined) {
9
+ return value
10
+ }
11
+ if (Array.isArray(value)) {
12
+ return value.map(sortDeep)
13
+ }
14
+ if (typeof value === 'object') {
15
+ const sorted: Record<string, unknown> = {}
16
+ for (const key of Object.keys(value as Record<string, unknown>).sort()) {
17
+ sorted[key] = sortDeep((value as Record<string, unknown>)[key])
18
+ }
19
+ return sorted
20
+ }
21
+ return value
22
+ }
23
+
24
+ export function hashString(input: string, length: number = 16): string {
25
+ return createHash('sha256').update(input).digest('hex').slice(0, length)
26
+ }