@pikku/core 0.12.14 → 0.12.16

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 (136) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/errors/errors.d.ts +4 -0
  3. package/dist/errors/errors.js +12 -0
  4. package/dist/function/function-runner.js +2 -1
  5. package/dist/function/functions.types.js +1 -0
  6. package/dist/function/index.d.ts +2 -1
  7. package/dist/function/index.js +1 -0
  8. package/dist/handle-error.d.ts +2 -2
  9. package/dist/handle-error.js +8 -8
  10. package/dist/index.d.ts +2 -3
  11. package/dist/index.js +1 -2
  12. package/dist/middleware/index.d.ts +3 -0
  13. package/dist/middleware/index.js +3 -0
  14. package/dist/middleware/remote-auth.js +5 -2
  15. package/dist/middleware/telemetry.d.ts +47 -0
  16. package/dist/middleware/telemetry.js +106 -0
  17. package/dist/middleware-runner.js +31 -4
  18. package/dist/permissions.d.ts +13 -1
  19. package/dist/permissions.js +51 -0
  20. package/dist/pikku-state.d.ts +0 -1
  21. package/dist/pikku-state.js +1 -4
  22. package/dist/remote.d.ts +10 -0
  23. package/dist/remote.js +32 -0
  24. package/dist/schema.js +2 -0
  25. package/dist/services/deployment-service.d.ts +12 -1
  26. package/dist/services/in-memory-queue-service.d.ts +7 -0
  27. package/dist/services/in-memory-queue-service.js +28 -0
  28. package/dist/services/index.d.ts +4 -1
  29. package/dist/services/index.js +2 -1
  30. package/dist/services/logger-console.d.ts +26 -40
  31. package/dist/services/logger-console.js +102 -46
  32. package/dist/services/logger.d.ts +5 -0
  33. package/dist/services/meta-service.d.ts +175 -0
  34. package/dist/services/meta-service.js +310 -0
  35. package/dist/services/workflow-service.d.ts +6 -1
  36. package/dist/testing/service-tests.js +0 -8
  37. package/dist/types/core.types.d.ts +21 -0
  38. package/dist/types/core.types.js +7 -1
  39. package/dist/types/state.types.d.ts +2 -2
  40. package/dist/wirings/ai-agent/ai-agent-prepare.js +42 -21
  41. package/dist/wirings/ai-agent/ai-agent-registry.js +2 -2
  42. package/dist/wirings/ai-agent/ai-agent-runner.js +18 -1
  43. package/dist/wirings/ai-agent/ai-agent-stream.js +1 -0
  44. package/dist/wirings/ai-agent/ai-agent.types.d.ts +5 -3
  45. package/dist/wirings/channel/channel-runner.js +3 -3
  46. package/dist/wirings/cli/cli-runner.js +5 -3
  47. package/dist/wirings/cli/command-parser.js +7 -3
  48. package/dist/wirings/http/http-runner.d.ts +1 -1
  49. package/dist/wirings/http/http-runner.js +23 -11
  50. package/dist/wirings/http/http.types.d.ts +3 -0
  51. package/dist/wirings/http/pikku-fetch-http-response.d.ts +1 -0
  52. package/dist/wirings/http/pikku-fetch-http-response.js +3 -0
  53. package/dist/wirings/mcp/mcp-runner.js +5 -3
  54. package/dist/wirings/mcp/mcp.types.d.ts +1 -1
  55. package/dist/wirings/queue/queue-runner.d.ts +3 -1
  56. package/dist/wirings/queue/queue-runner.js +7 -3
  57. package/dist/wirings/rpc/rpc-runner.js +56 -90
  58. package/dist/wirings/scheduler/scheduler-runner.d.ts +3 -1
  59. package/dist/wirings/scheduler/scheduler-runner.js +9 -5
  60. package/dist/wirings/trigger/trigger-runner.js +4 -2
  61. package/dist/wirings/workflow/dsl/workflow-runner.d.ts +1 -1
  62. package/dist/wirings/workflow/dsl/workflow-runner.js +6 -9
  63. package/dist/wirings/workflow/graph/graph-runner.d.ts +1 -1
  64. package/dist/wirings/workflow/graph/graph-runner.js +18 -4
  65. package/dist/wirings/workflow/index.d.ts +4 -2
  66. package/dist/wirings/workflow/index.js +4 -2
  67. package/dist/wirings/workflow/pikku-workflow-service.d.ts +16 -4
  68. package/dist/wirings/workflow/pikku-workflow-service.js +216 -24
  69. package/dist/wirings/workflow/workflow-queue-workers.d.ts +40 -0
  70. package/dist/wirings/workflow/workflow-queue-workers.js +41 -0
  71. package/dist/wirings/workflow/workflow.types.d.ts +17 -1
  72. package/package.json +4 -1
  73. package/src/errors/errors.ts +12 -0
  74. package/src/function/function-runner.ts +5 -4
  75. package/src/function/functions.types.ts +1 -0
  76. package/src/function/index.ts +10 -0
  77. package/src/handle-error.test.ts +2 -2
  78. package/src/handle-error.ts +8 -8
  79. package/src/index.ts +2 -2
  80. package/src/middleware/index.ts +3 -0
  81. package/src/middleware/remote-auth.test.ts +4 -4
  82. package/src/middleware/remote-auth.ts +7 -2
  83. package/src/middleware/telemetry.ts +110 -0
  84. package/src/middleware-runner.test.ts +149 -1
  85. package/src/middleware-runner.ts +48 -4
  86. package/src/permissions.ts +70 -0
  87. package/src/pikku-state.test.ts +0 -26
  88. package/src/pikku-state.ts +1 -5
  89. package/src/remote.ts +46 -0
  90. package/src/schema.ts +1 -0
  91. package/src/services/deployment-service.ts +17 -1
  92. package/src/services/in-memory-queue-service.ts +46 -0
  93. package/src/services/index.ts +21 -1
  94. package/src/services/logger-console.ts +127 -47
  95. package/src/services/logger.ts +6 -0
  96. package/src/services/meta-service.ts +523 -0
  97. package/src/services/workflow-service.ts +8 -0
  98. package/src/testing/service-tests.ts +0 -10
  99. package/src/types/core.types.ts +36 -1
  100. package/src/types/state.types.ts +2 -2
  101. package/src/wirings/ai-agent/ai-agent-prepare.ts +51 -40
  102. package/src/wirings/ai-agent/ai-agent-registry.ts +3 -3
  103. package/src/wirings/ai-agent/ai-agent-runner.ts +16 -1
  104. package/src/wirings/ai-agent/ai-agent-stream.ts +8 -0
  105. package/src/wirings/ai-agent/ai-agent.types.ts +5 -3
  106. package/src/wirings/channel/channel-runner.ts +4 -5
  107. package/src/wirings/channel/local/local-channel-runner.test.ts +5 -1
  108. package/src/wirings/cli/cli-runner.test.ts +8 -7
  109. package/src/wirings/cli/cli-runner.ts +6 -4
  110. package/src/wirings/cli/command-parser.ts +8 -3
  111. package/src/wirings/http/http-runner.ts +27 -11
  112. package/src/wirings/http/http.types.ts +3 -0
  113. package/src/wirings/http/pikku-fetch-http-response.ts +4 -0
  114. package/src/wirings/mcp/mcp-runner.ts +7 -9
  115. package/src/wirings/mcp/mcp.types.ts +1 -1
  116. package/src/wirings/queue/queue-runner.test.ts +5 -11
  117. package/src/wirings/queue/queue-runner.ts +11 -3
  118. package/src/wirings/rpc/rpc-runner.ts +82 -115
  119. package/src/wirings/scheduler/scheduler-runner.test.ts +5 -11
  120. package/src/wirings/scheduler/scheduler-runner.ts +13 -5
  121. package/src/wirings/trigger/trigger-runner.test.ts +10 -11
  122. package/src/wirings/trigger/trigger-runner.ts +6 -4
  123. package/src/wirings/workflow/dsl/workflow-runner.ts +11 -10
  124. package/src/wirings/workflow/graph/graph-runner.ts +19 -4
  125. package/src/wirings/workflow/index.ts +17 -6
  126. package/src/wirings/workflow/pikku-workflow-service.ts +284 -24
  127. package/src/wirings/workflow/workflow-queue-workers.ts +85 -0
  128. package/src/wirings/workflow/workflow.types.ts +16 -1
  129. package/tsconfig.tsbuildinfo +1 -1
  130. package/dist/wirings/ai-agent/agent-dynamic-workflow.d.ts +0 -6
  131. package/dist/wirings/ai-agent/agent-dynamic-workflow.js +0 -468
  132. package/dist/wirings/workflow/workflow-helpers.d.ts +0 -36
  133. package/dist/wirings/workflow/workflow-helpers.js +0 -38
  134. package/src/wirings/ai-agent/agent-dynamic-workflow.ts +0 -610
  135. package/src/wirings/workflow/workflow-helpers.test.ts +0 -129
  136. package/src/wirings/workflow/workflow-helpers.ts +0 -79
@@ -0,0 +1,523 @@
1
+ import { readFile, readdir } from 'node:fs/promises'
2
+ import { join } from 'node:path'
3
+ import type { JSONSchema7 } from 'json-schema'
4
+ import type { HTTPWiringsMeta } from '../wirings/http/http.types.js'
5
+ import type { ChannelsMeta } from '../wirings/channel/channel.types.js'
6
+ import type { ScheduledTasksMeta } from '../wirings/scheduler/scheduler.types.js'
7
+ import type { QueueWorkersMeta } from '../wirings/queue/queue.types.js'
8
+ import type { CLIMeta } from '../wirings/cli/cli.types.js'
9
+ import type {
10
+ MCPResourceMeta,
11
+ MCPToolMeta,
12
+ MCPPromptMeta,
13
+ } from '../wirings/mcp/mcp.types.js'
14
+ import type { WorkflowsMeta } from '../wirings/workflow/workflow.types.js'
15
+ import type {
16
+ TriggerMeta,
17
+ TriggerSourceMeta,
18
+ } from '../wirings/trigger/trigger.types.js'
19
+ import type { SecretDefinitionsMeta } from '../wirings/secret/secret.types.js'
20
+ import type { CredentialDefinitionsMeta } from '../wirings/credential/credential.types.js'
21
+ import type { VariableDefinitionsMeta } from '../wirings/variable/variable.types.js'
22
+ import type {
23
+ FunctionMeta,
24
+ FunctionsMeta,
25
+ MiddlewareMetadata,
26
+ PermissionMetadata,
27
+ } from '../types/core.types.js'
28
+ import type { AIAgentMeta } from '../wirings/ai-agent/ai-agent.types.js'
29
+
30
+ // Re-export core types for consumers
31
+ export type {
32
+ FunctionMeta,
33
+ FunctionsMeta,
34
+ MiddlewareMetadata as MiddlewareMeta,
35
+ PermissionMetadata as PermissionMeta,
36
+ }
37
+ export type AgentsMeta = AIAgentMeta
38
+ export type AgentMeta = AIAgentMeta[string]
39
+
40
+ export interface MCPMeta {
41
+ resources: MCPResourceMeta
42
+ tools: MCPToolMeta
43
+ prompts: MCPPromptMeta
44
+ }
45
+
46
+ export type RPCMetaRecord = Record<string, string>
47
+
48
+ export interface ServiceMeta {
49
+ name: string
50
+ summary: string
51
+ description: string
52
+ package: string
53
+ path: string
54
+ version: string
55
+ interface: string
56
+ expandedProperties: Record<string, string>
57
+ }
58
+
59
+ export type ServicesMetaRecord = Record<string, ServiceMeta>
60
+
61
+ export interface MiddlewareDefinitionMeta {
62
+ services: { optimized: boolean; services: string[] }
63
+ sourceFile: string
64
+ position: number
65
+ exportedName: string | null
66
+ factory?: boolean
67
+ name?: string
68
+ description?: string
69
+ package?: string
70
+ }
71
+
72
+ export interface MiddlewareInstanceMeta {
73
+ definitionId: string
74
+ sourceFile: string
75
+ position: number
76
+ isFactoryCall: boolean
77
+ }
78
+
79
+ export interface GroupMeta {
80
+ exportName: string | null
81
+ sourceFile: string
82
+ position: number
83
+ services: { optimized: boolean; services: string[] }
84
+ count: number
85
+ instanceIds: string[]
86
+ isFactory: boolean
87
+ }
88
+
89
+ export interface MiddlewareGroupsMeta {
90
+ definitions: Record<string, MiddlewareDefinitionMeta>
91
+ instances: Record<string, MiddlewareInstanceMeta>
92
+ httpGroups: Record<string, GroupMeta>
93
+ tagGroups: Record<string, GroupMeta>
94
+ }
95
+
96
+ export interface PermissionDefinitionMeta {
97
+ services: { optimized: boolean; services: string[] }
98
+ sourceFile: string
99
+ position: number
100
+ exportedName: string | null
101
+ factory?: boolean
102
+ name?: string
103
+ description?: string
104
+ }
105
+
106
+ export interface PermissionsGroupsMeta {
107
+ definitions: Record<string, PermissionDefinitionMeta>
108
+ httpGroups: Record<string, GroupMeta>
109
+ tagGroups: Record<string, GroupMeta>
110
+ }
111
+
112
+ /**
113
+ * Abstraction over .pikku metadata file access.
114
+ * All paths are relative to the .pikku root directory.
115
+ *
116
+ * Node.js uses LocalMetaService (filesystem).
117
+ * Cloudflare uses an R2/KV implementation.
118
+ */
119
+ export interface MetaService {
120
+ /** Base path for local filesystem implementations. Undefined for remote/non-local implementations. */
121
+ readonly basePath?: string
122
+ /** Read a file's content by relative path. Returns null if not found. */
123
+ readFile(relativePath: string): Promise<string | null>
124
+ /** List files in a directory by relative path. Returns empty array if not found. */
125
+ readDir(relativePath: string): Promise<string[]>
126
+
127
+ // -- Typed metadata accessors --
128
+
129
+ getHttpMeta(): Promise<HTTPWiringsMeta>
130
+ getChannelsMeta(): Promise<ChannelsMeta>
131
+ getSchedulerMeta(): Promise<ScheduledTasksMeta>
132
+ getQueueMeta(): Promise<QueueWorkersMeta>
133
+ getCliMeta(): Promise<CLIMeta>
134
+ getMcpMeta(): Promise<MCPMeta>
135
+ getRpcMeta(): Promise<RPCMetaRecord>
136
+ getWorkflowMeta(): Promise<WorkflowsMeta>
137
+ getTriggerMeta(): Promise<TriggerMeta>
138
+ getTriggerSourceMeta(): Promise<TriggerSourceMeta>
139
+ getFunctionsMeta(): Promise<FunctionsMeta>
140
+ getMiddlewareGroupsMeta(): Promise<MiddlewareGroupsMeta>
141
+ getPermissionsGroupsMeta(): Promise<PermissionsGroupsMeta>
142
+ getAgentsMeta(): Promise<AgentsMeta>
143
+ getSecretsMeta(): Promise<SecretDefinitionsMeta>
144
+ getCredentialsMeta(): Promise<CredentialDefinitionsMeta>
145
+ getVariablesMeta(): Promise<VariableDefinitionsMeta>
146
+ getServicesMeta(): Promise<ServicesMetaRecord>
147
+
148
+ getSchema(schemaName: string): Promise<JSONSchema7 | null>
149
+ getSchemas(schemaNames: string[]): Promise<Record<string, JSONSchema7 | null>>
150
+
151
+ clearCache(): void
152
+ }
153
+
154
+ /**
155
+ * Node.js filesystem implementation of MetaService.
156
+ * Reads .gen.json files from a local .pikku directory.
157
+ */
158
+ export class LocalMetaService implements MetaService {
159
+ public readonly basePath: string
160
+
161
+ private httpMetaCache: HTTPWiringsMeta | null = null
162
+ private channelsMetaCache: ChannelsMeta | null = null
163
+ private schedulerMetaCache: ScheduledTasksMeta | null = null
164
+ private queueMetaCache: QueueWorkersMeta | null = null
165
+ private cliMetaCache: CLIMeta | null = null
166
+ private mcpMetaCache: MCPMeta | null = null
167
+ private rpcMetaCache: RPCMetaRecord | null = null
168
+ private workflowMetaCache: WorkflowsMeta | null = null
169
+ private triggerMetaCache: TriggerMeta | null = null
170
+ private triggerSourceMetaCache: TriggerSourceMeta | null = null
171
+ private functionsMetaCache: FunctionsMeta | null = null
172
+ private servicesMetaCache: ServicesMetaRecord | null = null
173
+ private secretsMetaCache: SecretDefinitionsMeta | null = null
174
+ private credentialsMetaCache: CredentialDefinitionsMeta | null = null
175
+ private variablesMetaCache: VariableDefinitionsMeta | null = null
176
+ private middlewareGroupsMetaCache: MiddlewareGroupsMeta | null = null
177
+ private permissionsGroupsMetaCache: PermissionsGroupsMeta | null = null
178
+ private agentsMetaCache: AgentsMeta | null = null
179
+ private schemaCache = new Map<string, JSONSchema7>()
180
+
181
+ constructor(basePath: string) {
182
+ this.basePath = basePath
183
+ }
184
+
185
+ async readFile(relativePath: string): Promise<string | null> {
186
+ try {
187
+ return await readFile(join(this.basePath, relativePath), 'utf-8')
188
+ } catch {
189
+ return null
190
+ }
191
+ }
192
+
193
+ async readDir(relativePath: string): Promise<string[]> {
194
+ try {
195
+ return await readdir(join(this.basePath, relativePath))
196
+ } catch {
197
+ return []
198
+ }
199
+ }
200
+
201
+ clearCache(): void {
202
+ this.httpMetaCache = null
203
+ this.channelsMetaCache = null
204
+ this.schedulerMetaCache = null
205
+ this.queueMetaCache = null
206
+ this.cliMetaCache = null
207
+ this.mcpMetaCache = null
208
+ this.rpcMetaCache = null
209
+ this.workflowMetaCache = null
210
+ this.triggerMetaCache = null
211
+ this.triggerSourceMetaCache = null
212
+ this.functionsMetaCache = null
213
+ this.servicesMetaCache = null
214
+ this.secretsMetaCache = null
215
+ this.credentialsMetaCache = null
216
+ this.variablesMetaCache = null
217
+ this.middlewareGroupsMetaCache = null
218
+ this.permissionsGroupsMetaCache = null
219
+ this.agentsMetaCache = null
220
+ this.schemaCache.clear()
221
+ }
222
+
223
+ // -- Private helpers --
224
+
225
+ private async readMetaJson<T>(
226
+ dir: string,
227
+ baseName: string
228
+ ): Promise<string | null> {
229
+ const verbose = await this.readFile(`${dir}/${baseName}-verbose.gen.json`)
230
+ if (verbose) return verbose
231
+ const minimal = await this.readFile(`${dir}/${baseName}.gen.json`)
232
+ if (minimal) return minimal
233
+ return null
234
+ }
235
+
236
+ // -- Typed metadata accessors --
237
+
238
+ async getHttpMeta(): Promise<HTTPWiringsMeta> {
239
+ if (this.httpMetaCache) return this.httpMetaCache
240
+
241
+ const content = await this.readMetaJson('http', 'pikku-http-wirings-meta')
242
+ this.httpMetaCache = content
243
+ ? JSON.parse(content)
244
+ : {
245
+ get: {},
246
+ post: {},
247
+ put: {},
248
+ delete: {},
249
+ patch: {},
250
+ head: {},
251
+ options: {},
252
+ }
253
+ return this.httpMetaCache!
254
+ }
255
+
256
+ async getChannelsMeta(): Promise<ChannelsMeta> {
257
+ if (this.channelsMetaCache) return this.channelsMetaCache
258
+
259
+ const content = await this.readMetaJson('channel', 'pikku-channels-meta')
260
+ this.channelsMetaCache = content ? JSON.parse(content) : {}
261
+ return this.channelsMetaCache!
262
+ }
263
+
264
+ async getSchedulerMeta(): Promise<ScheduledTasksMeta> {
265
+ if (this.schedulerMetaCache) return this.schedulerMetaCache
266
+
267
+ const content = await this.readMetaJson(
268
+ 'scheduler',
269
+ 'pikku-schedulers-wirings-meta'
270
+ )
271
+ this.schedulerMetaCache = content ? JSON.parse(content) : {}
272
+ return this.schedulerMetaCache!
273
+ }
274
+
275
+ async getQueueMeta(): Promise<QueueWorkersMeta> {
276
+ if (this.queueMetaCache) return this.queueMetaCache
277
+
278
+ const content = await this.readMetaJson(
279
+ 'queue',
280
+ 'pikku-queue-workers-wirings-meta'
281
+ )
282
+ this.queueMetaCache = content ? JSON.parse(content) : {}
283
+ return this.queueMetaCache!
284
+ }
285
+
286
+ async getCliMeta(): Promise<CLIMeta> {
287
+ if (this.cliMetaCache) return this.cliMetaCache
288
+
289
+ const content = await this.readMetaJson('cli', 'pikku-cli-wirings-meta')
290
+ this.cliMetaCache = content
291
+ ? JSON.parse(content)
292
+ : { programs: {}, renderers: {} }
293
+ return this.cliMetaCache!
294
+ }
295
+
296
+ async getMcpMeta(): Promise<MCPMeta> {
297
+ if (this.mcpMetaCache) return this.mcpMetaCache
298
+
299
+ const content = await this.readMetaJson('mcp', 'pikku-mcp-wirings-meta')
300
+ if (content) {
301
+ const mcpData = JSON.parse(content)
302
+ this.mcpMetaCache = {
303
+ resources: mcpData.resourcesMeta || {},
304
+ tools: mcpData.toolsMeta || {},
305
+ prompts: mcpData.promptsMeta || {},
306
+ }
307
+ } else {
308
+ this.mcpMetaCache = { resources: {}, tools: {}, prompts: {} }
309
+ }
310
+ return this.mcpMetaCache!
311
+ }
312
+
313
+ async getRpcMeta(): Promise<RPCMetaRecord> {
314
+ if (this.rpcMetaCache) return this.rpcMetaCache
315
+
316
+ try {
317
+ const content = await this.readFile('rpc/pikku-rpc-wirings-meta.gen.json')
318
+ this.rpcMetaCache = content ? JSON.parse(content) : {}
319
+ return this.rpcMetaCache!
320
+ } catch (error) {
321
+ console.error('Error reading RPC wirings metadata:', error)
322
+ this.rpcMetaCache = {}
323
+ return this.rpcMetaCache
324
+ }
325
+ }
326
+
327
+ async getWorkflowMeta(): Promise<WorkflowsMeta> {
328
+ if (this.workflowMetaCache) return this.workflowMetaCache
329
+
330
+ try {
331
+ const files = await this.readDir('workflow/meta')
332
+ const jsonFiles = files.filter((f) => f.endsWith('.gen.json'))
333
+ const verboseFiles = jsonFiles.filter((f) => f.includes('-verbose'))
334
+ const minimalFiles = jsonFiles.filter((f) => !f.includes('-verbose'))
335
+ const verboseNames = new Set(
336
+ verboseFiles.map((f) => f.replace('-verbose.gen.json', ''))
337
+ )
338
+ const filesToRead = [
339
+ ...verboseFiles,
340
+ ...minimalFiles.filter(
341
+ (f) => !verboseNames.has(f.replace('.gen.json', ''))
342
+ ),
343
+ ]
344
+
345
+ const result: WorkflowsMeta = {}
346
+ await Promise.all(
347
+ filesToRead.map(async (file) => {
348
+ const content = await this.readFile(`workflow/meta/${file}`)
349
+ if (content) {
350
+ const meta = JSON.parse(content)
351
+ result[meta.name] = meta
352
+ }
353
+ })
354
+ )
355
+
356
+ this.workflowMetaCache = result
357
+ return this.workflowMetaCache
358
+ } catch (error) {
359
+ console.error('Error reading Workflow wirings metadata:', error)
360
+ this.workflowMetaCache = {}
361
+ return this.workflowMetaCache
362
+ }
363
+ }
364
+
365
+ async getTriggerMeta(): Promise<TriggerMeta> {
366
+ if (this.triggerMetaCache) return this.triggerMetaCache
367
+
368
+ const content = await this.readMetaJson(
369
+ 'trigger',
370
+ 'pikku-trigger-wirings-meta'
371
+ )
372
+ this.triggerMetaCache = content ? JSON.parse(content) : {}
373
+ return this.triggerMetaCache!
374
+ }
375
+
376
+ async getTriggerSourceMeta(): Promise<TriggerSourceMeta> {
377
+ if (this.triggerSourceMetaCache) return this.triggerSourceMetaCache
378
+
379
+ const content = await this.readMetaJson(
380
+ 'trigger',
381
+ 'pikku-trigger-sources-meta'
382
+ )
383
+ this.triggerSourceMetaCache = content ? JSON.parse(content) : {}
384
+ return this.triggerSourceMetaCache!
385
+ }
386
+
387
+ async getFunctionsMeta(): Promise<FunctionsMeta> {
388
+ if (this.functionsMetaCache) return this.functionsMetaCache
389
+
390
+ const content = await this.readMetaJson('function', 'pikku-functions-meta')
391
+ this.functionsMetaCache = content ? JSON.parse(content) : {}
392
+ return this.functionsMetaCache!
393
+ }
394
+
395
+ async getMiddlewareGroupsMeta(): Promise<MiddlewareGroupsMeta> {
396
+ if (this.middlewareGroupsMetaCache) return this.middlewareGroupsMetaCache
397
+
398
+ const content = await this.readMetaJson(
399
+ 'middleware',
400
+ 'pikku-middleware-groups-meta'
401
+ )
402
+ this.middlewareGroupsMetaCache = content
403
+ ? JSON.parse(content)
404
+ : { definitions: {}, instances: {}, httpGroups: {}, tagGroups: {} }
405
+ return this.middlewareGroupsMetaCache!
406
+ }
407
+
408
+ async getPermissionsGroupsMeta(): Promise<PermissionsGroupsMeta> {
409
+ if (this.permissionsGroupsMetaCache) return this.permissionsGroupsMetaCache
410
+
411
+ const content = await this.readMetaJson(
412
+ 'permissions',
413
+ 'pikku-permissions-groups-meta'
414
+ )
415
+ this.permissionsGroupsMetaCache = content
416
+ ? JSON.parse(content)
417
+ : { definitions: {}, httpGroups: {}, tagGroups: {} }
418
+ return this.permissionsGroupsMetaCache!
419
+ }
420
+
421
+ async getAgentsMeta(): Promise<AgentsMeta> {
422
+ if (this.agentsMetaCache) return this.agentsMetaCache
423
+
424
+ const content = await this.readMetaJson('agent', 'pikku-agent-wirings-meta')
425
+ if (content) {
426
+ const parsed = JSON.parse(content)
427
+ this.agentsMetaCache = parsed.agentsMeta || parsed
428
+ } else {
429
+ this.agentsMetaCache = {}
430
+ }
431
+ return this.agentsMetaCache!
432
+ }
433
+
434
+ async getSecretsMeta(): Promise<SecretDefinitionsMeta> {
435
+ if (this.secretsMetaCache) return this.secretsMetaCache
436
+
437
+ const content = await this.readFile('secrets/pikku-secrets-meta.gen.json')
438
+ this.secretsMetaCache = content ? JSON.parse(content) : {}
439
+ return this.secretsMetaCache!
440
+ }
441
+
442
+ async getCredentialsMeta(): Promise<CredentialDefinitionsMeta> {
443
+ if (this.credentialsMetaCache) return this.credentialsMetaCache
444
+
445
+ const content = await this.readFile(
446
+ 'credentials/pikku-credentials-meta.gen.json'
447
+ )
448
+ this.credentialsMetaCache = content ? JSON.parse(content) : {}
449
+ return this.credentialsMetaCache!
450
+ }
451
+
452
+ async getVariablesMeta(): Promise<VariableDefinitionsMeta> {
453
+ if (this.variablesMetaCache) return this.variablesMetaCache
454
+
455
+ const content = await this.readFile(
456
+ 'variables/pikku-variables-meta.gen.json'
457
+ )
458
+ this.variablesMetaCache = content ? JSON.parse(content) : {}
459
+ return this.variablesMetaCache!
460
+ }
461
+
462
+ async getServicesMeta(): Promise<ServicesMetaRecord> {
463
+ if (this.servicesMetaCache) return this.servicesMetaCache
464
+
465
+ try {
466
+ const files = await this.readDir('services')
467
+ const jsonFiles = files.filter((f) => f.endsWith('.gen.json'))
468
+
469
+ const result: ServicesMetaRecord = {}
470
+ await Promise.all(
471
+ jsonFiles.map(async (file) => {
472
+ const content = await this.readFile(`services/${file}`)
473
+ if (content) {
474
+ const meta: ServiceMeta = JSON.parse(content)
475
+ result[meta.name] = meta
476
+ }
477
+ })
478
+ )
479
+
480
+ this.servicesMetaCache = result
481
+ return this.servicesMetaCache
482
+ } catch (error) {
483
+ console.error('Error reading Services metadata:', error)
484
+ this.servicesMetaCache = {}
485
+ return this.servicesMetaCache
486
+ }
487
+ }
488
+
489
+ async getSchema(schemaName: string): Promise<JSONSchema7 | null> {
490
+ if (this.schemaCache.has(schemaName)) {
491
+ return this.schemaCache.get(schemaName)!
492
+ }
493
+
494
+ if (!/^[a-zA-Z0-9_\-\.]+$/.test(schemaName)) {
495
+ return null
496
+ }
497
+
498
+ try {
499
+ const content = await this.readFile(
500
+ `schemas/schemas/${schemaName}.schema.json`
501
+ )
502
+ if (!content) return null
503
+ const schema = JSON.parse(content) as JSONSchema7
504
+ this.schemaCache.set(schemaName, schema)
505
+ return schema
506
+ } catch (error) {
507
+ console.error(`Error reading schema ${schemaName}:`, error)
508
+ return null
509
+ }
510
+ }
511
+
512
+ async getSchemas(
513
+ schemaNames: string[]
514
+ ): Promise<Record<string, JSONSchema7 | null>> {
515
+ const results: Record<string, JSONSchema7 | null> = {}
516
+ await Promise.all(
517
+ schemaNames.map(async (name) => {
518
+ results[name] = await this.getSchema(name)
519
+ })
520
+ )
521
+ return results
522
+ }
523
+ }
@@ -2,6 +2,7 @@ import type { SerializedError } from '../types/core.types.js'
2
2
  import type {
3
3
  WorkflowRun,
4
4
  WorkflowRunWire,
5
+ WorkflowRunStatus,
5
6
  StepState,
6
7
  WorkflowStatus,
7
8
  WorkflowVersionStatus,
@@ -21,6 +22,7 @@ export interface WorkflowService {
21
22
  wire: WorkflowRunWire
22
23
  ): Promise<string>
23
24
  getRun(id: string): Promise<WorkflowRun | null>
25
+ getRunStatus(id: string): Promise<WorkflowRunStatus | null>
24
26
  getRunHistory(runId: string): Promise<Array<StepState & { stepName: string }>>
25
27
  updateRunStatus(
26
28
  id: string,
@@ -40,6 +42,12 @@ export interface WorkflowService {
40
42
  rpcService: any,
41
43
  options?: { inline?: boolean; startNode?: string }
42
44
  ): Promise<{ runId: string }>
45
+ runToCompletion<I>(
46
+ name: string,
47
+ input: I,
48
+ rpcService: any,
49
+ options?: { pollIntervalMs?: number; wire?: WorkflowRunWire }
50
+ ): Promise<unknown>
43
51
  runWorkflowJob(runId: string, rpcService: any): Promise<void>
44
52
  orchestrateWorkflow(runId: string, rpcService: any): Promise<void>
45
53
  executeWorkflowSleepCompleted(runId: string, stepId: string): Promise<void>
@@ -735,16 +735,6 @@ export function defineServiceTests(config: ServiceTestConfig): void {
735
735
  endpoint: 'http://localhost:3000',
736
736
  functions: ['funcA', 'funcB'],
737
737
  })
738
-
739
- const infos = await service.findFunction('funcA')
740
- assert.ok(infos.length >= 1)
741
- assert.equal(infos[0]!.deploymentId, 'deploy-1')
742
- assert.equal(infos[0]!.endpoint, 'http://localhost:3000')
743
- })
744
-
745
- test('findFunction returns empty for unknown function', async () => {
746
- const infos = await service.findFunction('unknown-func')
747
- assert.deepEqual(infos, [])
748
738
  })
749
739
  })
750
740
  }
@@ -34,6 +34,7 @@ import type { AgentRunService } from '../wirings/ai-agent/ai-agent.types.js'
34
34
  import type { PikkuAIMiddlewareHooks } from '../wirings/ai-agent/ai-agent.types.js'
35
35
  import type { WorkflowRunService } from '../wirings/workflow/workflow.types.js'
36
36
  import type { CredentialService } from '../services/credential-service.js'
37
+ import type { MetaService } from '../services/meta-service.js'
37
38
 
38
39
  export type PikkuWiringTypes =
39
40
  | 'http'
@@ -108,6 +109,7 @@ export type FunctionRuntimeMeta = {
108
109
  remote?: boolean
109
110
  mcp?: boolean
110
111
  readonly?: boolean
112
+ deploy?: 'serverless' | 'server' | 'auto'
111
113
  sessionless?: boolean
112
114
  version?: number
113
115
  approvalRequired?: boolean
@@ -130,6 +132,8 @@ export type FunctionMeta = FunctionRuntimeMeta &
130
132
  middleware: MiddlewareMetadata[]
131
133
  permissions: PermissionMetadata[]
132
134
  isDirectFunction: boolean
135
+ sourceFile: string
136
+ exportedName: string
133
137
  } & CommonWireMeta
134
138
  >
135
139
 
@@ -230,6 +234,8 @@ export interface CoreSingletonServices<Config extends CoreConfig = CoreConfig> {
230
234
  workflowRunService?: WorkflowRunService
231
235
  /** Credential service for dynamic/managed credentials (OAuth tokens, per-user API keys) */
232
236
  credentialService?: CredentialService
237
+ /** Meta service for reading .pikku metadata files (filesystem on Node, R2/KV on CF) */
238
+ metaService?: MetaService
233
239
  }
234
240
 
235
241
  /**
@@ -248,6 +254,8 @@ export type PikkuWire<
248
254
  > = Partial<{
249
255
  wireType: PikkuWiringTypes
250
256
  wireId: string
257
+ /** Trace ID for distributed tracing — propagated across remote RPC calls via x-trace-id header */
258
+ traceId: string
251
259
  http: PikkuHTTP<In>
252
260
  mcp: PikkuMCP<MCPTools>
253
261
  rpc: TypedRPC
@@ -297,6 +305,23 @@ export type CorePikkuMiddleware<
297
305
  next: () => Promise<void>
298
306
  ) => Promise<void>
299
307
 
308
+ /**
309
+ * Priority levels for middleware execution order.
310
+ * Lower priority runs first (outermost in the onion model).
311
+ *
312
+ * - `highest` — Runs first (outermost). Use for telemetry, request tracing.
313
+ * - `high` — Runs early. Use for CORS, rate limiting.
314
+ * - `medium` — Default. Use for auth, most user middleware.
315
+ * - `low` — Runs late. Use for post-auth processing.
316
+ * - `lowest` — Runs last (innermost, closest to function). Use for inner telemetry.
317
+ */
318
+ export type MiddlewarePriority =
319
+ | 'highest'
320
+ | 'high'
321
+ | 'medium'
322
+ | 'low'
323
+ | 'lowest'
324
+
300
325
  /**
301
326
  * Configuration object for creating middleware with metadata
302
327
  *
@@ -313,6 +338,8 @@ export type CorePikkuMiddlewareConfig<
313
338
  name?: string
314
339
  /** Optional description of what the middleware does */
315
340
  description?: string
341
+ /** Execution priority. Lower runs first (outermost). Defaults to 'medium'. */
342
+ priority?: MiddlewarePriority
316
343
  }
317
344
 
318
345
  /**
@@ -370,7 +397,15 @@ export const pikkuMiddleware = <
370
397
  | CorePikkuMiddleware<SingletonServices, UserSession>
371
398
  | CorePikkuMiddlewareConfig<SingletonServices, UserSession>
372
399
  ): CorePikkuMiddleware<SingletonServices, UserSession> => {
373
- return typeof middleware === 'function' ? middleware : middleware.func
400
+ if (typeof middleware === 'function') return middleware
401
+ const func = middleware.func as CorePikkuMiddleware<
402
+ SingletonServices,
403
+ UserSession
404
+ > & { __priority?: MiddlewarePriority }
405
+ if (middleware.priority) {
406
+ func.__priority = middleware.priority
407
+ }
408
+ return func
374
409
  }
375
410
 
376
411
  /**
@@ -182,12 +182,12 @@ export interface PikkuPackageState {
182
182
  } | null
183
183
  /** Cached singleton services for this package */
184
184
  singletonServices: CoreSingletonServices | null
185
- /** Absolute path to this package's .pikku directory */
186
- metaDir: string | null
187
185
  /** Credential metadata for this addon package */
188
186
  credentialsMeta: Record<
189
187
  string,
190
188
  { name: string; displayName: string; type: string; oauth2?: boolean }
191
189
  > | null
190
+ /** Services this addon needs from the parent project */
191
+ requiredParentServices: string[] | null
192
192
  }
193
193
  }