@pikku/core 0.12.1 → 0.12.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/crypto-utils.d.ts +10 -0
  3. package/dist/crypto-utils.js +62 -0
  4. package/dist/function/function-runner.js +4 -4
  5. package/dist/function/functions.types.d.ts +26 -1
  6. package/dist/function/functions.types.js +16 -0
  7. package/dist/handle-error.d.ts +1 -1
  8. package/dist/handle-error.js +7 -3
  9. package/dist/index.d.ts +14 -2
  10. package/dist/index.js +1 -1
  11. package/dist/middleware-runner.js +1 -0
  12. package/dist/permissions.js +1 -0
  13. package/dist/pikku-state.js +7 -1
  14. package/dist/schema.js +1 -1
  15. package/dist/services/gateway-service.d.ts +19 -0
  16. package/dist/services/gateway-service.js +1 -0
  17. package/dist/services/in-memory-workflow-service.d.ts +8 -2
  18. package/dist/services/in-memory-workflow-service.js +28 -1
  19. package/dist/services/index.d.ts +2 -0
  20. package/dist/services/index.js +1 -0
  21. package/dist/services/local-gateway-service.d.ts +22 -0
  22. package/dist/services/local-gateway-service.js +51 -0
  23. package/dist/services/workflow-service.d.ts +8 -2
  24. package/dist/testing/index.d.ts +2 -0
  25. package/dist/testing/index.js +1 -0
  26. package/dist/testing/service-tests.d.ts +31 -0
  27. package/dist/testing/service-tests.js +598 -0
  28. package/dist/types/core.types.d.ts +12 -2
  29. package/dist/types/state.types.d.ts +9 -2
  30. package/dist/utils/hash.d.ts +2 -0
  31. package/dist/utils/hash.js +23 -0
  32. package/dist/utils.js +1 -1
  33. package/dist/wirings/ai-agent/agent-dynamic-workflow.d.ts +6 -0
  34. package/dist/wirings/ai-agent/agent-dynamic-workflow.js +348 -0
  35. package/dist/wirings/ai-agent/ai-agent-memory.d.ts +1 -1
  36. package/dist/wirings/ai-agent/ai-agent-memory.js +19 -7
  37. package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +10 -7
  38. package/dist/wirings/ai-agent/ai-agent-prepare.js +141 -48
  39. package/dist/wirings/ai-agent/ai-agent-runner.d.ts +4 -0
  40. package/dist/wirings/ai-agent/ai-agent-runner.js +381 -27
  41. package/dist/wirings/ai-agent/ai-agent-stream.d.ts +7 -2
  42. package/dist/wirings/ai-agent/ai-agent-stream.js +126 -48
  43. package/dist/wirings/ai-agent/ai-agent.types.d.ts +57 -3
  44. package/dist/wirings/ai-agent/index.d.ts +2 -3
  45. package/dist/wirings/ai-agent/index.js +1 -2
  46. package/dist/wirings/channel/channel-handler.d.ts +5 -2
  47. package/dist/wirings/channel/channel-handler.js +16 -2
  48. package/dist/wirings/channel/channel-middleware-runner.js +1 -0
  49. package/dist/wirings/channel/channel.types.d.ts +7 -7
  50. package/dist/wirings/channel/index.d.ts +1 -1
  51. package/dist/wirings/channel/local/local-channel-handler.d.ts +8 -1
  52. package/dist/wirings/channel/local/local-channel-handler.js +22 -3
  53. package/dist/wirings/channel/local/local-channel-runner.js +28 -5
  54. package/dist/wirings/channel/pikku-abstract-channel-handler.d.ts +2 -1
  55. package/dist/wirings/channel/pikku-abstract-channel-handler.js +1 -0
  56. package/dist/wirings/channel/serverless/serverless-channel-runner.js +5 -2
  57. package/dist/wirings/cli/cli-runner.js +13 -2
  58. package/dist/wirings/cli/cli.types.d.ts +1 -0
  59. package/dist/wirings/gateway/gateway-runner.d.ts +24 -0
  60. package/dist/wirings/gateway/gateway-runner.js +325 -0
  61. package/dist/wirings/gateway/gateway.types.d.ts +127 -0
  62. package/dist/wirings/gateway/gateway.types.js +1 -0
  63. package/dist/wirings/gateway/index.d.ts +2 -0
  64. package/dist/wirings/gateway/index.js +1 -0
  65. package/dist/wirings/http/http-routes.js +0 -1
  66. package/dist/wirings/http/http-runner.d.ts +1 -1
  67. package/dist/wirings/http/http-runner.js +28 -3
  68. package/dist/wirings/http/http.types.d.ts +3 -11
  69. package/dist/wirings/http/index.d.ts +2 -1
  70. package/dist/wirings/http/index.js +1 -0
  71. package/dist/wirings/http/pikku-fetch-http-request.d.ts +1 -0
  72. package/dist/wirings/http/pikku-fetch-http-request.js +3 -0
  73. package/dist/wirings/http/pikku-fetch-http-response.d.ts +1 -0
  74. package/dist/wirings/http/pikku-fetch-http-response.js +4 -4
  75. package/dist/wirings/http/web-request.d.ts +12 -0
  76. package/dist/wirings/http/web-request.js +85 -0
  77. package/dist/wirings/mcp/mcp-endpoint-registry.js +1 -1
  78. package/dist/wirings/mcp/mcp-runner.js +13 -1
  79. package/dist/wirings/rpc/rpc-runner.d.ts +30 -6
  80. package/dist/wirings/rpc/rpc-runner.js +36 -14
  81. package/dist/wirings/rpc/rpc-types.d.ts +6 -1
  82. package/dist/wirings/rpc/wire-addon.d.ts +1 -0
  83. package/dist/wirings/rpc/wire-addon.js +1 -1
  84. package/dist/wirings/scheduler/scheduler-runner.js +1 -0
  85. package/dist/wirings/workflow/graph/graph-validation.d.ts +3 -0
  86. package/dist/wirings/workflow/graph/graph-validation.js +175 -0
  87. package/dist/wirings/workflow/graph/index.d.ts +1 -0
  88. package/dist/wirings/workflow/graph/index.js +1 -0
  89. package/dist/wirings/workflow/index.d.ts +1 -1
  90. package/dist/wirings/workflow/pikku-workflow-service.d.ts +8 -2
  91. package/dist/wirings/workflow/pikku-workflow-service.js +1 -2
  92. package/dist/wirings/workflow/workflow.types.d.ts +10 -1
  93. package/package.json +6 -3
  94. package/src/crypto-utils.test.ts +99 -1
  95. package/src/crypto-utils.ts +114 -0
  96. package/src/function/function-runner.ts +5 -5
  97. package/src/function/functions.types.ts +38 -1
  98. package/src/handle-error.test.ts +65 -2
  99. package/src/handle-error.ts +8 -4
  100. package/src/index.ts +14 -0
  101. package/src/middleware-runner.ts +1 -0
  102. package/src/permissions.ts +1 -0
  103. package/src/pikku-state.ts +8 -1
  104. package/src/schema.ts +1 -1
  105. package/src/services/gateway-service.ts +20 -0
  106. package/src/services/in-memory-workflow-service.ts +45 -2
  107. package/src/services/index.ts +2 -0
  108. package/src/services/local-gateway-service.ts +62 -0
  109. package/src/services/workflow-service.ts +12 -1
  110. package/src/testing/index.ts +2 -0
  111. package/src/testing/service-tests.ts +873 -0
  112. package/src/types/core.types.ts +12 -1
  113. package/src/types/state.types.ts +12 -2
  114. package/src/utils/hash.test.ts +68 -0
  115. package/src/utils/hash.ts +26 -0
  116. package/src/utils.ts +1 -1
  117. package/src/wirings/ai-agent/agent-dynamic-workflow.ts +469 -0
  118. package/src/wirings/ai-agent/ai-agent-memory.ts +17 -7
  119. package/src/wirings/ai-agent/ai-agent-prepare.ts +194 -62
  120. package/src/wirings/ai-agent/ai-agent-runner.test.ts +3 -1
  121. package/src/wirings/ai-agent/ai-agent-runner.ts +553 -31
  122. package/src/wirings/ai-agent/ai-agent-stream.test.ts +209 -2
  123. package/src/wirings/ai-agent/ai-agent-stream.ts +195 -91
  124. package/src/wirings/ai-agent/ai-agent.types.ts +57 -3
  125. package/src/wirings/ai-agent/index.ts +3 -5
  126. package/src/wirings/channel/channel-handler.ts +41 -4
  127. package/src/wirings/channel/channel-middleware-runner.ts +1 -0
  128. package/src/wirings/channel/channel.types.ts +12 -7
  129. package/src/wirings/channel/index.ts +1 -0
  130. package/src/wirings/channel/local/local-channel-handler.ts +31 -3
  131. package/src/wirings/channel/local/local-channel-runner.ts +32 -8
  132. package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +2 -0
  133. package/src/wirings/channel/pikku-abstract-channel-handler.ts +8 -1
  134. package/src/wirings/channel/serverless/serverless-channel-runner.ts +5 -2
  135. package/src/wirings/cli/cli-runner.ts +15 -2
  136. package/src/wirings/cli/cli.types.ts +1 -0
  137. package/src/wirings/gateway/gateway-runner.test.ts +474 -0
  138. package/src/wirings/gateway/gateway-runner.ts +411 -0
  139. package/src/wirings/gateway/gateway.types.ts +149 -0
  140. package/src/wirings/gateway/index.ts +13 -0
  141. package/src/wirings/http/http-routes.ts +0 -1
  142. package/src/wirings/http/http-runner.ts +38 -10
  143. package/src/wirings/http/http.types.ts +3 -12
  144. package/src/wirings/http/index.ts +3 -0
  145. package/src/wirings/http/pikku-fetch-http-request.ts +4 -0
  146. package/src/wirings/http/pikku-fetch-http-response.ts +4 -4
  147. package/src/wirings/http/web-request.test.ts +236 -0
  148. package/src/wirings/http/web-request.ts +104 -0
  149. package/src/wirings/mcp/mcp-endpoint-registry.ts +1 -1
  150. package/src/wirings/mcp/mcp-runner.ts +30 -12
  151. package/src/wirings/rpc/rpc-runner.ts +45 -24
  152. package/src/wirings/rpc/rpc-types.ts +6 -1
  153. package/src/wirings/rpc/wire-addon.ts +2 -1
  154. package/src/wirings/scheduler/scheduler-runner.ts +1 -0
  155. package/src/wirings/workflow/graph/graph-validation.test.ts +170 -0
  156. package/src/wirings/workflow/graph/graph-validation.ts +237 -0
  157. package/src/wirings/workflow/graph/index.ts +5 -0
  158. package/src/wirings/workflow/index.ts +1 -0
  159. package/src/wirings/workflow/pikku-workflow-service.ts +14 -3
  160. package/src/wirings/workflow/workflow.types.ts +9 -1
  161. package/tsconfig.tsbuildinfo +1 -1
  162. package/dist/wirings/ai-agent/ai-agent-assistant-ui.d.ts +0 -5
  163. package/dist/wirings/ai-agent/ai-agent-assistant-ui.js +0 -146
  164. package/src/wirings/ai-agent/ai-agent-assistant-ui.test.ts +0 -363
  165. package/src/wirings/ai-agent/ai-agent-assistant-ui.ts +0 -214
@@ -4,6 +4,7 @@ import type {
4
4
  CoreUserSession,
5
5
  } from '../../types/core.types.js'
6
6
  import type {
7
+ BinaryData,
7
8
  ChannelMessageMeta,
8
9
  CoreChannel,
9
10
  PikkuChannelHandler,
@@ -97,11 +98,18 @@ export const processMessageHandlers = (
97
98
  return
98
99
  }
99
100
 
101
+ const routeMeta = getRouteMeta(
102
+ channelConfig.name,
103
+ routingProperty,
104
+ routerValue
105
+ )
100
106
  const {
101
- pikkuFuncId,
107
+ packageName,
102
108
  middleware: routeInheritedMiddleware,
103
109
  permissions: inheritedPermissions,
104
- } = getRouteMeta(channelConfig.name, routingProperty, routerValue)
110
+ } = routeMeta
111
+
112
+ const pikkuFuncId = routeMeta.pikkuFuncId
105
113
 
106
114
  // Get wire middleware: channel-level middleware + message-specific middleware
107
115
  const channelWireMiddleware = channelConfig.middleware || []
@@ -145,16 +153,18 @@ export const processMessageHandlers = (
145
153
  wireChannelMiddleware: wireChannelMiddleware as any,
146
154
  inheritedPermissions,
147
155
  wirePermissions,
156
+ coerceDataFromSchema: true,
148
157
  tags: channelConfig.tags,
149
158
  sessionService: userSession,
150
159
  wire: {
151
160
  channel: channelHandler.getChannel(),
152
161
  ...(userSession && createMiddlewareSessionWireProps(userSession)),
153
162
  },
163
+ packageName,
154
164
  })
155
165
  }
156
166
 
157
- return async (rawData): Promise<unknown> => {
167
+ const onMessage = async (rawData: unknown): Promise<unknown> => {
158
168
  let result: unknown
159
169
  let processed = false
160
170
 
@@ -198,7 +208,10 @@ export const processMessageHandlers = (
198
208
  // Default handler if no routes matched and json data wasn't parsed
199
209
  if (!processed && channelConfig.onMessage) {
200
210
  processed = true
201
- result = await processMessage(rawData, channelConfig.onMessage)
211
+ result = await processMessage(
212
+ rawData as JSONValue,
213
+ channelConfig.onMessage
214
+ )
202
215
  }
203
216
 
204
217
  if (!processed) {
@@ -210,4 +223,28 @@ export const processMessageHandlers = (
210
223
 
211
224
  return result
212
225
  }
226
+
227
+ const onBinaryMessage = channelConfig.onBinaryMessage
228
+ ? async (data: BinaryData): Promise<BinaryData | void> => {
229
+ if (
230
+ !validateAuth(
231
+ requiresSession,
232
+ userSession?.get() as CoreUserSession | undefined,
233
+ channelConfig
234
+ )
235
+ ) {
236
+ logger.error(
237
+ `Channel ${channelConfig.name} with id ${channelHandler.getChannel().channelId} requires a session for binary message`
238
+ )
239
+ return
240
+ }
241
+ return channelConfig.onBinaryMessage!(
242
+ services,
243
+ data,
244
+ channelHandler.getChannel()
245
+ )
246
+ }
247
+ : undefined
248
+
249
+ return { onMessage, onBinaryMessage }
213
250
  }
@@ -114,6 +114,7 @@ export function wrapChannelWithMiddleware<Out>(
114
114
  channel: {
115
115
  ...channel,
116
116
  send: ((data: Out) => dispatch(0, data)) as typeof channel.send,
117
+ sendBinary: channel.sendBinary,
117
118
  },
118
119
  }
119
120
  }
@@ -1,4 +1,3 @@
1
- import type { PikkuError } from '../../errors/error-handler.js'
2
1
  import type {
3
2
  HTTPFunctionMetaInputTypes,
4
3
  PikkuHTTPRequest,
@@ -10,6 +9,8 @@ import type {
10
9
  PermissionMetadata,
11
10
  } from '../../types/core.types.js'
12
11
 
12
+ export type BinaryData = ArrayBuffer | Uint8Array
13
+
13
14
  export type CorePikkuChannelMiddleware<Services = any, Event = unknown> = (
14
15
  services: Services,
15
16
  event: Event,
@@ -45,6 +46,7 @@ export type RunChannelParams<ChannelData> = {
45
46
 
46
47
  export interface ChannelMessageMeta {
47
48
  pikkuFuncId: string
49
+ packageName?: string
48
50
  summary?: string
49
51
  description?: string
50
52
  errors?: string[]
@@ -64,6 +66,7 @@ export interface ChannelMeta {
64
66
  disconnect: ChannelMessageMeta | null
65
67
  message: ChannelMessageMeta | null
66
68
  messageWirings: Record<string, Record<string, ChannelMessageMeta>>
69
+ binary?: boolean | null
67
70
  summary?: string
68
71
  description?: string
69
72
  errors?: string[]
@@ -132,12 +135,12 @@ export type CoreChannel<
132
135
  >
133
136
  permissions?: CorePermissionGroup<PikkuPermission>
134
137
  auth?: boolean
135
- docs?: Partial<{
136
- description: string
137
- response: string
138
- errors: Array<typeof PikkuError>
139
- tags: string[]
140
- }>
138
+ binary?: boolean | null
139
+ onBinaryMessage?: (
140
+ services: any,
141
+ data: BinaryData,
142
+ channel: PikkuChannel<ChannelData, any>
143
+ ) => Promise<BinaryData | void> | BinaryData | void
141
144
  tags?: string[]
142
145
  }
143
146
 
@@ -149,6 +152,7 @@ export interface PikkuChannel<OpeningData, out Out> {
149
152
  openingData: OpeningData
150
153
  // The data to send. This will fail is the stream has been closed.
151
154
  send(data: Out, isBinary?: boolean): Promise<void> | void
155
+ sendBinary(data: BinaryData): Promise<void> | void
152
156
  // This will close the channel.
153
157
  close(): Promise<void> | void
154
158
  // The current state of the channel
@@ -157,6 +161,7 @@ export interface PikkuChannel<OpeningData, out Out> {
157
161
 
158
162
  export interface PikkuChannelHandler<OpeningData = unknown, Out = unknown> {
159
163
  send(message: Out, isBinary?: boolean): Promise<void> | void
164
+ sendBinary(data: BinaryData): Promise<void> | void
160
165
  getChannel(): PikkuChannel<OpeningData, Out>
161
166
  }
162
167
 
@@ -11,6 +11,7 @@ export { ChannelStore } from './channel-store.js'
11
11
  export type { Channel } from './channel-store.js'
12
12
  export { EventHubStore } from './eventhub-store.js'
13
13
  export type {
14
+ BinaryData,
14
15
  ChannelsMeta,
15
16
  CoreChannel,
16
17
  CorePikkuChannelMiddleware,
@@ -1,3 +1,4 @@
1
+ import type { BinaryData } from '../channel.types.js'
1
2
  import { PikkuAbstractChannelHandler } from '../pikku-abstract-channel-handler.js'
2
3
 
3
4
  export class PikkuLocalChannelHandler<
@@ -5,9 +6,11 @@ export class PikkuLocalChannelHandler<
5
6
  Out = unknown,
6
7
  > extends PikkuAbstractChannelHandler<OpeningData, Out> {
7
8
  private onMessageCallback?: (message: unknown) => void
9
+ private onBinaryMessageCallback?: (data: BinaryData) => void
8
10
  private openCallBack?: () => void
9
- private closeCallback?: () => void
11
+ private closeCallbacks: (() => void)[] = []
10
12
  private sendCallback?: (message: Out, isBinary?: boolean) => void
13
+ private sendBinaryCallback?: (data: BinaryData) => void
11
14
 
12
15
  public registerOnOpen(callback: () => void): void {
13
16
  this.openCallBack = callback
@@ -28,8 +31,20 @@ export class PikkuLocalChannelHandler<
28
31
  return this.onMessageCallback?.(data)
29
32
  }
30
33
 
34
+ public registerOnBinaryMessage(
35
+ callback: (
36
+ data: BinaryData
37
+ ) => Promise<BinaryData | void> | BinaryData | void
38
+ ): void {
39
+ this.onBinaryMessageCallback = callback
40
+ }
41
+
42
+ public async binaryMessage(data: BinaryData): Promise<BinaryData | void> {
43
+ return this.onBinaryMessageCallback?.(data)
44
+ }
45
+
31
46
  public registerOnClose(callback: () => void): void {
32
- this.closeCallback = callback
47
+ this.closeCallbacks.push(callback)
33
48
  }
34
49
 
35
50
  public close() {
@@ -37,7 +52,9 @@ export class PikkuLocalChannelHandler<
37
52
  return
38
53
  }
39
54
  super.close()
40
- this.closeCallback?.()
55
+ for (const cb of this.closeCallbacks) {
56
+ cb()
57
+ }
41
58
  }
42
59
 
43
60
  public registerOnSend(send: (message: Out) => void) {
@@ -50,4 +67,15 @@ export class PikkuLocalChannelHandler<
50
67
  }
51
68
  return this.sendCallback?.(message, isBinary)
52
69
  }
70
+
71
+ public registerOnSendBinary(send: (data: BinaryData) => void) {
72
+ this.sendBinaryCallback = send
73
+ }
74
+
75
+ public sendBinary(data: BinaryData): void {
76
+ if (!this.sendBinaryCallback) {
77
+ throw new Error('No sendBinary callback registered')
78
+ }
79
+ this.sendBinaryCallback(data)
80
+ }
53
81
  }
@@ -119,9 +119,12 @@ export const runLocalChannel = async ({
119
119
  if (result !== undefined) {
120
120
  await channel.send(result)
121
121
  }
122
- } catch (e) {
122
+ } catch (e: any) {
123
123
  singletonServices.logger.error(`Error handling onConnect: ${e}`)
124
- channel.send({ error: e.message || 'Unknown error' })
124
+ channel.send({
125
+ error: e.message || 'Unknown error',
126
+ errorName: e.constructor?.name,
127
+ })
125
128
  }
126
129
  }
127
130
  })
@@ -138,16 +141,19 @@ export const runLocalChannel = async ({
138
141
  channel,
139
142
  channelMiddlewareMeta: meta.channelMiddleware,
140
143
  })
141
- } catch (e) {
144
+ } catch (e: any) {
142
145
  singletonServices.logger.error(`Error handling onDisconnect: ${e}`)
143
- channel.send({ error: e.message || 'Unknown error' })
146
+ channel.send({
147
+ error: e.message || 'Unknown error',
148
+ errorName: e.constructor?.name,
149
+ })
144
150
  }
145
151
  }
146
152
 
147
153
  await closeServices()
148
154
  })
149
155
 
150
- const onMessage = processMessageHandlers(
156
+ const { onMessage, onBinaryMessage } = processMessageHandlers(
151
157
  services,
152
158
  channelConfig as any,
153
159
  channelHandler,
@@ -157,11 +163,29 @@ export const runLocalChannel = async ({
157
163
  try {
158
164
  const result = await onMessage(data)
159
165
  await channel.send(result)
160
- } catch (e) {
161
- singletonServices.logger.error(`Error handling message: ${e.message}`)
162
- channel.send({ error: e.message || 'Unknown error' })
166
+ } catch (e: any) {
167
+ singletonServices.logger.error(e)
168
+ channel.send({
169
+ error: e.message || 'Unknown error',
170
+ errorName: e.constructor?.name,
171
+ })
172
+ setTimeout(() => channel.close(), 200)
163
173
  }
164
174
  })
175
+ if (onBinaryMessage) {
176
+ channelHandler.registerOnBinaryMessage(async (data) => {
177
+ try {
178
+ const result = await onBinaryMessage(data)
179
+ if (result) {
180
+ await channel.sendBinary(result)
181
+ }
182
+ } catch (e) {
183
+ singletonServices.logger.error(
184
+ `Error handling binary message: ${e.message}`
185
+ )
186
+ }
187
+ })
188
+ }
165
189
  } catch (e: any) {
166
190
  handleHTTPError(
167
191
  e,
@@ -10,6 +10,8 @@ class TestChannelHandler extends PikkuAbstractChannelHandler<
10
10
  public async send(message: { msg: string }, isBinary = false): Promise<void> {
11
11
  // Mock send implementation
12
12
  }
13
+
14
+ public sendBinary(_data: ArrayBuffer | Uint8Array): void {}
13
15
  }
14
16
 
15
17
  let handler: TestChannelHandler
@@ -1,4 +1,8 @@
1
- import type { PikkuChannel, PikkuChannelHandler } from './channel.types.js'
1
+ import type {
2
+ BinaryData,
3
+ PikkuChannel,
4
+ PikkuChannelHandler,
5
+ } from './channel.types.js'
2
6
 
3
7
  export abstract class PikkuAbstractChannelHandler<
4
8
  OpeningData = unknown,
@@ -15,12 +19,15 @@ export abstract class PikkuAbstractChannelHandler<
15
19
 
16
20
  public abstract send(message: Out, isBinary?: boolean): Promise<void> | void
17
21
 
22
+ public abstract sendBinary(data: BinaryData): Promise<void> | void
23
+
18
24
  public getChannel(): PikkuChannel<OpeningData, Out> {
19
25
  if (!this.channel) {
20
26
  this.channel = {
21
27
  channelId: this.channelId,
22
28
  openingData: this.openingData,
23
29
  send: this.send.bind(this),
30
+ sendBinary: this.sendBinary.bind(this),
24
31
  close: this.close.bind(this),
25
32
  state: 'initial',
26
33
  }
@@ -265,7 +265,7 @@ export const runChannelMessage = async (
265
265
 
266
266
  let response: unknown
267
267
  try {
268
- const onMessage = processMessageHandlers(
268
+ const { onMessage } = processMessageHandlers(
269
269
  services,
270
270
  channelConfig,
271
271
  channelHandler,
@@ -276,7 +276,10 @@ export const runChannelMessage = async (
276
276
  singletonServices.logger.error(
277
277
  `Error processing message: ${e.message || e}`
278
278
  )
279
- return { error: e.message || 'Unknown error' }
279
+ return {
280
+ error: e.message || 'Unknown error',
281
+ errorName: e.constructor?.name,
282
+ }
280
283
  } finally {
281
284
  if (wireServices) {
282
285
  await closeWireServices(singletonServices.logger, wireServices)
@@ -218,7 +218,9 @@ function pluckCLIData(
218
218
  const funcMeta = pikkuState(null, 'function', 'meta')[funcName]
219
219
  const schemaName = funcMeta?.inputSchemaName
220
220
  const schema = schemaName
221
- ? pikkuState(null, 'misc', 'schemas').get(schemaName)
221
+ ? pikkuState(funcMeta?.packageName ?? null, 'misc', 'schemas').get(
222
+ schemaName
223
+ )
222
224
  : null
223
225
 
224
226
  if (schema && schema.properties) {
@@ -226,7 +228,13 @@ function pluckCLIData(
226
228
  const result: Record<string, any> = {}
227
229
  for (const key of Object.keys(schema.properties)) {
228
230
  if (key in mergedData) {
229
- result[key] = mergedData[key]
231
+ let value = mergedData[key]
232
+ // CLI passes all values as strings — coerce to array when schema expects one
233
+ const propSchema = schema.properties[key]
234
+ if (propSchema?.type === 'array' && !Array.isArray(value)) {
235
+ value = typeof value === 'string' ? value.split(',') : [value]
236
+ }
237
+ result[key] = value
230
238
  } else if (availableOptions[key]?.default !== undefined) {
231
239
  // Apply default if not provided
232
240
  result[key] = availableOptions[key].default
@@ -321,6 +329,9 @@ export async function runCLICommand({
321
329
  await Promise.resolve(renderer(singletonServices, data, undefined))
322
330
  }
323
331
  },
332
+ sendBinary: () => {
333
+ throw new Error('Binary data is not supported on CLI channels')
334
+ },
324
335
  close: () => {
325
336
  if (channel) {
326
337
  channel.state = 'closed'
@@ -351,9 +362,11 @@ export async function runCLICommand({
351
362
  wireMiddleware: allWireMiddleware,
352
363
  inheritedPermissions: currentCommand.permissions,
353
364
  wirePermissions: undefined,
365
+ coerceDataFromSchema: true,
354
366
  tags: programData?.tags,
355
367
  wire,
356
368
  sessionService: userSession,
369
+ packageName: currentCommand.packageName,
357
370
  })
358
371
 
359
372
  // Apply renderer one final time with the final output (if renderer exists)
@@ -70,6 +70,7 @@ export interface CLIProgramState {
70
70
  export interface CLICommandMeta {
71
71
  parameters?: string
72
72
  pikkuFuncId: string
73
+ packageName?: string
73
74
  positionals: CLIPositional[]
74
75
  options: Record<string, CLIOption>
75
76
  renderName?: string