@pikku/core 0.12.20 → 0.12.22

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 (141) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/dev/hot-reload.js +20 -6
  3. package/dist/errors/error-handler.d.ts +1 -1
  4. package/dist/errors/error-handler.js +2 -2
  5. package/dist/function/functions.types.d.ts +3 -2
  6. package/dist/handle-error.js +3 -1
  7. package/dist/index.d.ts +2 -2
  8. package/dist/index.js +2 -2
  9. package/dist/middleware/index.d.ts +2 -2
  10. package/dist/middleware/index.js +2 -2
  11. package/dist/middleware/telemetry.d.ts +2 -2
  12. package/dist/middleware/telemetry.js +2 -2
  13. package/dist/middleware-runner.d.ts +15 -27
  14. package/dist/middleware-runner.js +25 -30
  15. package/dist/permissions.d.ts +15 -22
  16. package/dist/permissions.js +30 -25
  17. package/dist/pikku-request.js +1 -1
  18. package/dist/pikku-state.js +2 -3
  19. package/dist/services/ai-agent-runner-service.d.ts +1 -0
  20. package/dist/services/in-memory-queue-service.js +1 -1
  21. package/dist/services/in-memory-workflow-service.d.ts +15 -13
  22. package/dist/services/in-memory-workflow-service.js +31 -13
  23. package/dist/services/local-content.js +8 -1
  24. package/dist/services/user-session-service.d.ts +1 -1
  25. package/dist/testing/service-tests.js +24 -0
  26. package/dist/types/core.types.d.ts +4 -0
  27. package/dist/types/state.types.d.ts +2 -18
  28. package/dist/wirings/ai-agent/ai-agent-memory.d.ts +24 -5
  29. package/dist/wirings/ai-agent/ai-agent-memory.js +128 -23
  30. package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +10 -1
  31. package/dist/wirings/ai-agent/ai-agent-model-config.js +15 -35
  32. package/dist/wirings/ai-agent/ai-agent-prepare.js +4 -1
  33. package/dist/wirings/ai-agent/ai-agent-runner.js +45 -31
  34. package/dist/wirings/ai-agent/ai-agent-stream.js +63 -34
  35. package/dist/wirings/ai-agent/ai-agent.types.d.ts +20 -0
  36. package/dist/wirings/channel/channel-handler.js +1 -1
  37. package/dist/wirings/channel/channel-store.d.ts +13 -0
  38. package/dist/wirings/channel/channel.types.d.ts +3 -0
  39. package/dist/wirings/channel/local/local-channel-runner.js +23 -5
  40. package/dist/wirings/channel/pikku-abstract-channel-handler.js +8 -0
  41. package/dist/wirings/channel/serverless/serverless-channel-runner.js +9 -0
  42. package/dist/wirings/cli/cli-runner.js +24 -5
  43. package/dist/wirings/cli/command-parser.js +19 -4
  44. package/dist/wirings/http/http-runner.js +72 -36
  45. package/dist/wirings/http/http.types.d.ts +1 -0
  46. package/dist/wirings/http/pikku-fetch-http-request.js +3 -1
  47. package/dist/wirings/http/web-request.js +32 -0
  48. package/dist/wirings/rpc/rpc-runner.js +13 -3
  49. package/dist/wirings/workflow/graph/graph-node.d.ts +8 -0
  50. package/dist/wirings/workflow/graph/graph-node.js +4 -0
  51. package/dist/wirings/workflow/graph/graph-runner.js +12 -10
  52. package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +4 -0
  53. package/dist/wirings/workflow/index.d.ts +1 -1
  54. package/dist/wirings/workflow/pikku-workflow-service.d.ts +81 -16
  55. package/dist/wirings/workflow/pikku-workflow-service.js +266 -45
  56. package/dist/wirings/workflow/workflow.types.d.ts +34 -0
  57. package/package.json +1 -1
  58. package/run-tests.sh +4 -1
  59. package/src/dev/hot-reload.test.ts +62 -11
  60. package/src/dev/hot-reload.ts +28 -7
  61. package/src/errors/error-handler.ts +8 -2
  62. package/src/errors/error.test.ts +2 -0
  63. package/src/function/function-runner.test.ts +500 -10
  64. package/src/function/functions.types.ts +3 -2
  65. package/src/handle-error.test.ts +1 -1
  66. package/src/handle-error.ts +4 -1
  67. package/src/index.ts +12 -2
  68. package/src/middleware/index.ts +11 -2
  69. package/src/middleware/telemetry.ts +2 -2
  70. package/src/middleware-runner.test.ts +16 -16
  71. package/src/middleware-runner.ts +42 -30
  72. package/src/permissions.test.ts +27 -24
  73. package/src/permissions.ts +41 -25
  74. package/src/pikku-request.test.ts +35 -0
  75. package/src/pikku-request.ts +1 -1
  76. package/src/pikku-state.ts +2 -3
  77. package/src/run-tests-script.test.ts +18 -0
  78. package/src/services/ai-agent-runner-service.ts +1 -0
  79. package/src/services/in-memory-queue-service.ts +1 -1
  80. package/src/services/in-memory-session-store.ts +1 -2
  81. package/src/services/in-memory-workflow-service.test.ts +33 -11
  82. package/src/services/in-memory-workflow-service.ts +49 -13
  83. package/src/services/local-content.test.ts +54 -0
  84. package/src/services/local-content.ts +12 -1
  85. package/src/services/typed-credential-service.ts +3 -3
  86. package/src/services/typed-secret-service.ts +3 -3
  87. package/src/services/typed-variables-service.ts +3 -3
  88. package/src/services/user-session-service.ts +4 -4
  89. package/src/testing/service-tests.ts +30 -0
  90. package/src/types/core.types.ts +6 -2
  91. package/src/types/state.types.ts +2 -13
  92. package/src/wirings/ai-agent/ai-agent-memory.test.ts +324 -0
  93. package/src/wirings/ai-agent/ai-agent-memory.ts +187 -36
  94. package/src/wirings/ai-agent/ai-agent-model-config.test.ts +12 -90
  95. package/src/wirings/ai-agent/ai-agent-model-config.ts +14 -38
  96. package/src/wirings/ai-agent/ai-agent-prepare.test.ts +292 -0
  97. package/src/wirings/ai-agent/ai-agent-prepare.ts +4 -1
  98. package/src/wirings/ai-agent/ai-agent-registry.test.ts +230 -3
  99. package/src/wirings/ai-agent/ai-agent-runner.test.ts +625 -6
  100. package/src/wirings/ai-agent/ai-agent-runner.ts +65 -50
  101. package/src/wirings/ai-agent/ai-agent-stream.test.ts +544 -5
  102. package/src/wirings/ai-agent/ai-agent-stream.ts +71 -69
  103. package/src/wirings/ai-agent/ai-agent.types.ts +24 -0
  104. package/src/wirings/channel/channel-handler.test.ts +272 -0
  105. package/src/wirings/channel/channel-handler.ts +1 -1
  106. package/src/wirings/channel/channel-middleware-runner.test.ts +163 -0
  107. package/src/wirings/channel/channel-store.ts +19 -0
  108. package/src/wirings/channel/channel.types.ts +4 -0
  109. package/src/wirings/channel/local/local-channel-runner.test.ts +63 -0
  110. package/src/wirings/channel/local/local-channel-runner.ts +41 -5
  111. package/src/wirings/channel/local/local-eventhub-service.ts +3 -3
  112. package/src/wirings/channel/pikku-abstract-channel-handler.ts +9 -2
  113. package/src/wirings/channel/serverless/serverless-channel-runner.ts +23 -5
  114. package/src/wirings/cli/channel/cli-raw-channel-runner.ts +7 -2
  115. package/src/wirings/cli/cli-runner.test.ts +255 -2
  116. package/src/wirings/cli/cli-runner.ts +31 -10
  117. package/src/wirings/cli/cli.types.ts +4 -8
  118. package/src/wirings/cli/command-parser.test.ts +83 -0
  119. package/src/wirings/cli/command-parser.ts +23 -4
  120. package/src/wirings/http/http-runner.test.ts +296 -1
  121. package/src/wirings/http/http-runner.ts +87 -57
  122. package/src/wirings/http/http.types.ts +11 -26
  123. package/src/wirings/http/pikku-fetch-http-request.ts +8 -4
  124. package/src/wirings/http/pikku-fetch-http-response.test.ts +41 -0
  125. package/src/wirings/http/web-request.test.ts +115 -0
  126. package/src/wirings/http/web-request.ts +43 -5
  127. package/src/wirings/mcp/mcp-runner.test.ts +367 -0
  128. package/src/wirings/queue/queue.types.ts +2 -5
  129. package/src/wirings/rpc/rpc-runner.test.ts +511 -0
  130. package/src/wirings/rpc/rpc-runner.ts +15 -3
  131. package/src/wirings/rpc/wire-addon.test.ts +57 -0
  132. package/src/wirings/workflow/graph/graph-node.ts +12 -0
  133. package/src/wirings/workflow/graph/graph-runner.test.ts +98 -0
  134. package/src/wirings/workflow/graph/graph-runner.ts +28 -10
  135. package/src/wirings/workflow/graph/workflow-graph.types.ts +4 -0
  136. package/src/wirings/workflow/index.ts +1 -0
  137. package/src/wirings/workflow/pikku-workflow-service.test.ts +19 -2
  138. package/src/wirings/workflow/pikku-workflow-service.ts +370 -71
  139. package/src/wirings/workflow/workflow-queue-workers.test.ts +131 -0
  140. package/src/wirings/workflow/workflow.types.ts +68 -5
  141. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,163 @@
1
+ import { beforeEach, describe, test } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+
4
+ import { pikkuState, resetPikkuState } from '../../pikku-state.js'
5
+ import {
6
+ addChannelMiddleware,
7
+ clearChannelMiddlewareCache,
8
+ combineChannelMiddleware,
9
+ wrapChannelWithMiddleware,
10
+ } from './channel-middleware-runner.js'
11
+
12
+ beforeEach(() => {
13
+ resetPikkuState()
14
+ clearChannelMiddlewareCache()
15
+ })
16
+
17
+ describe('addChannelMiddleware', () => {
18
+ test('registers tag-scoped channel middleware for a package', () => {
19
+ const middleware = [async () => {}]
20
+
21
+ const result = addChannelMiddleware(
22
+ 'chat:outbound',
23
+ middleware,
24
+ '@addon/pkg'
25
+ )
26
+
27
+ assert.strictEqual(result, middleware)
28
+ assert.strictEqual(
29
+ pikkuState('@addon/pkg', 'channelMiddleware', 'tagGroup')[
30
+ 'chat:outbound'
31
+ ],
32
+ middleware
33
+ )
34
+ })
35
+ })
36
+
37
+ describe('combineChannelMiddleware', () => {
38
+ test('combines tag, named wire middleware, and inline channel middleware', () => {
39
+ const execution: string[] = []
40
+ const tagMiddleware = async () => {
41
+ execution.push('tag')
42
+ }
43
+ const namedMiddleware = async () => {
44
+ execution.push('named')
45
+ }
46
+ const inlineMiddleware = async () => {
47
+ execution.push('inline')
48
+ }
49
+
50
+ addChannelMiddleware('chat:outbound', [tagMiddleware], '@addon/pkg')
51
+ pikkuState(null, 'misc', 'channelMiddleware').named = [
52
+ namedMiddleware,
53
+ ] as never
54
+
55
+ const combined = combineChannelMiddleware('channel', 'combined-1', {
56
+ packageName: '@addon/pkg',
57
+ wireInheritedChannelMiddleware: [
58
+ { type: 'tag', tag: 'chat:outbound' },
59
+ { type: 'wire', name: 'named' },
60
+ ],
61
+ wireChannelMiddleware: [inlineMiddleware],
62
+ })
63
+
64
+ assert.deepEqual(combined, [
65
+ tagMiddleware,
66
+ namedMiddleware,
67
+ inlineMiddleware,
68
+ ])
69
+ void execution
70
+ })
71
+
72
+ test('deduplicates middleware and returns cached results until the cache is cleared', () => {
73
+ const shared = async () => {}
74
+ addChannelMiddleware('chat:outbound', [shared])
75
+
76
+ const first = combineChannelMiddleware('channel', 'cached-1', {
77
+ wireInheritedChannelMiddleware: [{ type: 'tag', tag: 'chat:outbound' }],
78
+ wireChannelMiddleware: [shared],
79
+ })
80
+
81
+ assert.deepEqual(first, [shared])
82
+
83
+ addChannelMiddleware('chat:outbound', [async () => {}])
84
+
85
+ const cached = combineChannelMiddleware('channel', 'cached-1', {
86
+ wireInheritedChannelMiddleware: [{ type: 'tag', tag: 'chat:outbound' }],
87
+ })
88
+ assert.strictEqual(cached, first)
89
+
90
+ clearChannelMiddlewareCache()
91
+
92
+ const refreshed = combineChannelMiddleware('channel', 'cached-1', {
93
+ wireInheritedChannelMiddleware: [{ type: 'tag', tag: 'chat:outbound' }],
94
+ })
95
+ assert.notStrictEqual(refreshed, first)
96
+ assert.equal(refreshed.length, 1)
97
+ assert.notStrictEqual(refreshed[0], shared)
98
+ })
99
+
100
+ test('ignores missing named middleware references', () => {
101
+ const combined = combineChannelMiddleware('channel', 'missing-wire', {
102
+ wireInheritedChannelMiddleware: [{ type: 'wire', name: 'missing' }],
103
+ })
104
+
105
+ assert.deepEqual(combined, [])
106
+ })
107
+ })
108
+
109
+ describe('wrapChannelWithMiddleware', () => {
110
+ test('returns the original wire when there is no channel or middleware', () => {
111
+ const wireWithoutChannel = { traceId: 'trace-1' }
112
+ const wireWithChannel = {
113
+ channel: {
114
+ send: async () => {},
115
+ sendBinary: async () => {},
116
+ },
117
+ }
118
+
119
+ assert.strictEqual(
120
+ wrapChannelWithMiddleware(wireWithoutChannel as never, {} as never, [
121
+ async () => {},
122
+ ]),
123
+ wireWithoutChannel
124
+ )
125
+ assert.strictEqual(
126
+ wrapChannelWithMiddleware(wireWithChannel as never, {} as never, []),
127
+ wireWithChannel
128
+ )
129
+ })
130
+
131
+ test('wraps outbound channel sends and supports transformed arrays and null drops', async () => {
132
+ const sent: string[] = []
133
+ const wrapped = wrapChannelWithMiddleware(
134
+ {
135
+ channel: {
136
+ send: async (data: string) => {
137
+ sent.push(data)
138
+ },
139
+ sendBinary: async () => {},
140
+ },
141
+ } as never,
142
+ {} as never,
143
+ [
144
+ async (_services, event: string, next) => {
145
+ if (event === 'drop') {
146
+ await next(null)
147
+ return
148
+ }
149
+ await next([`${event}:a`, `${event}:b`])
150
+ },
151
+ async (_services, event: string, next) => {
152
+ await next(event.toUpperCase())
153
+ },
154
+ ]
155
+ )
156
+
157
+ await wrapped.channel!.send('hello')
158
+ await wrapped.channel!.send('drop')
159
+
160
+ assert.deepEqual(sent, ['HELLO:A', 'HELLO:B'])
161
+ assert.strictEqual(wrapped.channel!.sendBinary, wrapped.channel!.sendBinary)
162
+ })
163
+ })
@@ -23,4 +23,23 @@ export abstract class ChannelStore<
23
23
  ):
24
24
  | Promise<TypedChannel & { pikkuUserId?: string }>
25
25
  | (TypedChannel & { pikkuUserId?: string })
26
+
27
+ /**
28
+ * Persist per-socket scratch state scoped to a single channel (keyed by
29
+ * channelId). This is intentionally separate from `SessionStore` (which is
30
+ * keyed by `pikkuUserId` and holds the user session — shared across HTTP
31
+ * and channel transports).
32
+ *
33
+ * Use this for ephemeral, channel-local data: a per-socket subscription
34
+ * filter, a step in a connection-bound state machine, the last command sent
35
+ * on this socket, etc. It is cleared when the channel is removed.
36
+ */
37
+ public abstract setState(
38
+ channelId: string,
39
+ state: unknown
40
+ ): Promise<void> | void
41
+ public abstract getState(
42
+ channelId: string
43
+ ): Promise<unknown | undefined> | unknown | undefined
44
+ public abstract clearState(channelId: string): Promise<void> | void
26
45
  }
@@ -157,6 +157,10 @@ export interface PikkuChannel<OpeningData, out Out> {
157
157
  close(): Promise<void> | void
158
158
  // The current state of the channel
159
159
  state: 'initial' | 'open' | 'closed'
160
+ // Per-socket ephemeral state scoped to this channel connection.
161
+ setState<T>(state: T): Promise<void> | void
162
+ getState<T>(): Promise<T | undefined> | T | undefined
163
+ clearState(): Promise<void> | void
160
164
  }
161
165
 
162
166
  export interface PikkuChannelHandler<OpeningData = unknown, Out = unknown> {
@@ -3,6 +3,7 @@ import * as assert from 'node:assert/strict'
3
3
  import { runLocalChannel } from './local-channel-runner.js'
4
4
  import { pikkuState, resetPikkuState } from '../../../pikku-state.js'
5
5
  import { wireChannel } from '../channel-runner.js'
6
+ import { addHTTPMiddleware } from '../../http/http-runner.js'
6
7
  import type {
7
8
  HTTPMethod,
8
9
  PikkuHTTPRequest,
@@ -11,6 +12,7 @@ import type {
11
12
  } from '../../http/http.types.js'
12
13
  import type { SerializeOptions } from 'cookie'
13
14
  import { httpRouter } from '../../http/routers/http-router.js'
15
+ import { pikkuMiddleware } from '../../../types/core.types.js'
14
16
 
15
17
  /**
16
18
  * Minimal stubs for dependencies that runChannel expects.
@@ -217,3 +219,64 @@ test('runChannel should close wire services once when channel closes', async ()
217
219
  await new Promise((resolve) => setTimeout(resolve, 0))
218
220
  assert.equal(closeCount, 1)
219
221
  })
222
+
223
+ test('runChannel should run HTTP middleware on websocket upgrade and establish session', async () => {
224
+ resetPikkuState()
225
+ pikkuState(null, 'package', 'singletonServices', mockSingletonServices as any)
226
+ pikkuState(null, 'package', 'factories', {
227
+ createWireServices: mockCreateWireServices,
228
+ } as any)
229
+
230
+ addHTTPMiddleware(
231
+ '*',
232
+ [
233
+ pikkuMiddleware(async (_services, { setSession }, next) => {
234
+ setSession?.({ userId: 'user-1' } as any)
235
+ await next()
236
+ }),
237
+ ],
238
+ null
239
+ )
240
+
241
+ pikkuState(null, 'channel', 'meta', {
242
+ test: {
243
+ name: 'test',
244
+ route: '/test-channel',
245
+ messageWirings: {
246
+ action: {
247
+ ping: {
248
+ pikkuFuncId: 'test:ping',
249
+ },
250
+ },
251
+ },
252
+ },
253
+ } as any)
254
+ wireChannel({
255
+ name: 'test',
256
+ route: '/test-channel',
257
+ onMessageWiring: {
258
+ action: {
259
+ ping: {
260
+ func: async (_services, data) => data,
261
+ },
262
+ },
263
+ },
264
+ } as any)
265
+
266
+ httpRouter.initialize()
267
+
268
+ const result = await runLocalChannel({
269
+ channelId: 'test-channel-id',
270
+ request: new PikkuMockRequest('/test-channel', 'get'),
271
+ response: new PikkuMockResponse(),
272
+ route: '/test-channel',
273
+ })
274
+
275
+ assert.ok(result)
276
+ let sent: unknown
277
+ result.registerOnSend((message) => {
278
+ sent = message
279
+ })
280
+ await result.message(JSON.stringify({ action: 'ping' }))
281
+ assert.deepEqual(sent, { action: 'ping' })
282
+ })
@@ -12,6 +12,7 @@ import {
12
12
  PikkuSessionService,
13
13
  createMiddlewareSessionWireProps,
14
14
  } from '../../../services/user-session-service.js'
15
+ import { combineMiddleware, runMiddleware } from '../../../middleware-runner.js'
15
16
  import type {
16
17
  PikkuHTTP,
17
18
  PikkuHTTPRequest,
@@ -21,8 +22,38 @@ import { runChannelLifecycleWithMiddleware } from '../channel-common.js'
21
22
  import {
22
23
  getSingletonServices,
23
24
  getCreateWireServices,
25
+ pikkuState,
24
26
  } from '../../../pikku-state.js'
25
27
 
28
+ const runUpgradeMiddleware = async (
29
+ singletonServices: ReturnType<typeof getSingletonServices>,
30
+ request: PikkuHTTPRequest,
31
+ response: PikkuHTTPResponse | undefined,
32
+ userSession: PikkuSessionService<any>
33
+ ) => {
34
+ const http = createHTTPWire(request, response)
35
+ const httpGroups = pikkuState(null, 'middleware', 'httpGroup')
36
+ const wireInheritedMiddleware = httpGroups['*']
37
+ ? [{ type: 'http' as const, route: '*' }]
38
+ : undefined
39
+ const middleware = combineMiddleware('channel', `upgrade:${request.path()}`, {
40
+ wireInheritedMiddleware,
41
+ })
42
+
43
+ if (middleware.length > 0) {
44
+ await runMiddleware(
45
+ singletonServices,
46
+ {
47
+ http,
48
+ ...createMiddlewareSessionWireProps(userSession),
49
+ },
50
+ middleware
51
+ )
52
+ }
53
+
54
+ return http
55
+ }
56
+
26
57
  export const runLocalChannel = async ({
27
58
  channelId,
28
59
  request,
@@ -48,13 +79,18 @@ export const runLocalChannel = async ({
48
79
  const userSession = new PikkuSessionService(singletonServices.sessionStore)
49
80
 
50
81
  let http: PikkuHTTP | undefined
51
- if (request) {
52
- http = createHTTPWire(request, response)
53
- route = http?.request?.path()
54
- }
55
-
56
82
  let openingData, channelConfig, meta
57
83
  try {
84
+ if (request) {
85
+ http = await runUpgradeMiddleware(
86
+ singletonServices,
87
+ request,
88
+ response,
89
+ userSession
90
+ )
91
+ route = request.path()
92
+ }
93
+
58
94
  ;({ openingData, channelConfig, meta } = await openChannel({
59
95
  channelId,
60
96
  respondWith404,
@@ -5,9 +5,9 @@ import type { EventHubService } from '../eventhub-service.js'
5
5
  * Implementation of the SubscriptionService interface.
6
6
  * Manages subscriptions and publishes messages to subscribers.
7
7
  */
8
- export class LocalEventHubService<Data extends Record<string, any> = {}>
9
- implements EventHubService<Data>
10
- {
8
+ export class LocalEventHubService<
9
+ Data extends Record<string, any> = {},
10
+ > implements EventHubService<Data> {
11
11
  private channels = new Map<string, PikkuChannelHandler>()
12
12
 
13
13
  /**
@@ -7,8 +7,7 @@ import type {
7
7
  export abstract class PikkuAbstractChannelHandler<
8
8
  OpeningData = unknown,
9
9
  Out = unknown,
10
- > implements PikkuChannelHandler<OpeningData, Out>
11
- {
10
+ > implements PikkuChannelHandler<OpeningData, Out> {
12
11
  protected channel?: PikkuChannel<OpeningData, Out>
13
12
 
14
13
  constructor(
@@ -23,6 +22,7 @@ export abstract class PikkuAbstractChannelHandler<
23
22
 
24
23
  public getChannel(): PikkuChannel<OpeningData, Out> {
25
24
  if (!this.channel) {
25
+ let channelState: unknown
26
26
  this.channel = {
27
27
  channelId: this.channelId,
28
28
  openingData: this.openingData,
@@ -30,6 +30,13 @@ export abstract class PikkuAbstractChannelHandler<
30
30
  sendBinary: this.sendBinary.bind(this),
31
31
  close: this.close.bind(this),
32
32
  state: 'initial',
33
+ setState: (s) => {
34
+ channelState = s
35
+ },
36
+ getState: () => channelState as any,
37
+ clearState: () => {
38
+ channelState = undefined
39
+ },
33
40
  }
34
41
  }
35
42
  return this.channel
@@ -1,4 +1,8 @@
1
- import type { PikkuWire, WireServices } from '../../../types/core.types.js'
1
+ import type {
2
+ CoreUserSession,
3
+ PikkuWire,
4
+ WireServices,
5
+ } from '../../../types/core.types.js'
2
6
  import { closeWireServices } from '../../../utils.js'
3
7
  import { processMessageHandlers } from '../channel-handler.js'
4
8
  import { openChannel } from '../channel-runner.js'
@@ -24,8 +28,9 @@ import { PikkuFetchHTTPRequest } from '../../http/pikku-fetch-http-request.js'
24
28
  import type { PikkuHTTP } from '../../http/http.types.js'
25
29
  import { runChannelLifecycleWithMiddleware } from '../channel-common.js'
26
30
 
27
- export interface RunServerlessChannelParams<ChannelData>
28
- extends RunChannelParams<ChannelData> {
31
+ export interface RunServerlessChannelParams<
32
+ ChannelData,
33
+ > extends RunChannelParams<ChannelData> {
29
34
  channelStore: ChannelStore
30
35
  channelHandlerFactory: PikkuChannelHandlerFactory
31
36
  channelObject?: unknown
@@ -111,6 +116,9 @@ export const runChannelConnect = async ({
111
116
  channelHandlerFactory,
112
117
  channelName: channelConfig.name,
113
118
  })
119
+ channel.setState = (state) => channelStore.setState(channelId, state)
120
+ channel.getState = () => channelStore.getState(channelId) as any
121
+ channel.clearState = () => channelStore.clearState(channelId)
114
122
 
115
123
  const wire: PikkuWire = {
116
124
  channel,
@@ -193,8 +201,13 @@ export const runChannelDisconnect = async ({
193
201
  openingData,
194
202
  channelName,
195
203
  })
204
+ channel.setState = (state) => channelStore.setState(channelId, state)
205
+ channel.getState = () => channelStore.getState(channelId) as any
206
+ channel.clearState = () => channelStore.clearState(channelId)
196
207
 
197
- const userSession = new PikkuSessionService(singletonServices.sessionStore)
208
+ const userSession = new PikkuSessionService<CoreUserSession>(
209
+ singletonServices.sessionStore
210
+ )
198
211
  if (pikkuUserId) {
199
212
  userSession.setPikkuUserId(pikkuUserId)
200
213
  const session = await singletonServices.sessionStore?.get(pikkuUserId)
@@ -260,8 +273,13 @@ export const runChannelMessage = async (
260
273
  openingData,
261
274
  channelName,
262
275
  })
276
+ channel.setState = (state) => channelStore.setState(channelId, state)
277
+ channel.getState = () => channelStore.getState(channelId) as any
278
+ channel.clearState = () => channelStore.clearState(channelId)
263
279
 
264
- const userSession = new PikkuSessionService(singletonServices.sessionStore)
280
+ const userSession = new PikkuSessionService<CoreUserSession>(
281
+ singletonServices.sessionStore
282
+ )
265
283
  if (pikkuUserId) {
266
284
  userSession.setPikkuUserId(pikkuUserId)
267
285
  const session = await singletonServices.sessionStore?.get(pikkuUserId)
@@ -2,7 +2,10 @@ import { pikkuState } from '../../../pikku-state.js'
2
2
  import type { CLIMeta } from '../cli.types.js'
3
3
  import { parseCLIArguments, generateCommandHelp } from '../command-parser.js'
4
4
  import { runCLICommand } from '../cli-runner.js'
5
- import type { CoreSingletonServices, CreateWireServices } from '../../../types/core.types.js'
5
+ import type {
6
+ CoreSingletonServices,
7
+ CreateWireServices,
8
+ } from '../../../types/core.types.js'
6
9
 
7
10
  /**
8
11
  * Handles raw CLI input from a WebSocket channel.
@@ -57,7 +60,9 @@ export async function handleRawCLI({
57
60
  }
58
61
 
59
62
  // Check if the resolved command has a pikkuFuncId (is executable)
60
- let current: { pikkuFuncId?: string; subcommands?: Record<string, any> } | undefined = programMeta.commands[parsed.commandPath[0]]
63
+ let current:
64
+ | { pikkuFuncId?: string; subcommands?: Record<string, any> }
65
+ | undefined = programMeta.commands[parsed.commandPath[0]]
61
66
  for (let i = 1; i < parsed.commandPath.length; i++) {
62
67
  current = current?.subcommands?.[parsed.commandPath[i]]
63
68
  }