@pikku/core 0.12.19 → 0.12.21
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.
- package/CHANGELOG.md +77 -0
- package/dist/dev/hot-reload.js +20 -6
- package/dist/errors/error-handler.d.ts +1 -1
- package/dist/errors/error-handler.js +2 -2
- package/dist/function/function-runner.js +53 -3
- package/dist/function/functions.types.d.ts +3 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/middleware/index.d.ts +2 -2
- package/dist/middleware/index.js +2 -2
- package/dist/middleware/telemetry.d.ts +2 -2
- package/dist/middleware/telemetry.js +2 -2
- package/dist/middleware-runner.d.ts +15 -27
- package/dist/middleware-runner.js +25 -30
- package/dist/permissions.d.ts +15 -22
- package/dist/permissions.js +30 -25
- package/dist/pikku-request.js +1 -1
- package/dist/pikku-state.js +2 -3
- package/dist/services/ai-agent-runner-service.d.ts +1 -0
- package/dist/services/content-service.d.ts +66 -37
- package/dist/services/in-memory-queue-service.js +1 -1
- package/dist/services/in-memory-workflow-service.d.ts +15 -13
- package/dist/services/in-memory-workflow-service.js +31 -13
- package/dist/services/index.d.ts +1 -1
- package/dist/services/local-content.d.ts +10 -12
- package/dist/services/local-content.js +54 -38
- package/dist/services/user-session-service.d.ts +1 -1
- package/dist/testing/service-tests.js +24 -0
- package/dist/types/core.types.d.ts +4 -0
- package/dist/types/state.types.d.ts +2 -18
- package/dist/wirings/ai-agent/ai-agent-memory.d.ts +24 -5
- package/dist/wirings/ai-agent/ai-agent-memory.js +128 -23
- package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +10 -1
- package/dist/wirings/ai-agent/ai-agent-model-config.js +15 -35
- package/dist/wirings/ai-agent/ai-agent-prepare.js +4 -1
- package/dist/wirings/ai-agent/ai-agent-runner.js +45 -31
- package/dist/wirings/ai-agent/ai-agent-stream.js +63 -34
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +20 -0
- package/dist/wirings/channel/channel-handler.js +1 -1
- package/dist/wirings/channel/channel-store.d.ts +13 -0
- package/dist/wirings/channel/channel.types.d.ts +3 -0
- package/dist/wirings/channel/local/local-channel-runner.js +23 -5
- package/dist/wirings/channel/pikku-abstract-channel-handler.js +8 -0
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +9 -0
- package/dist/wirings/cli/cli-runner.js +24 -5
- package/dist/wirings/cli/command-parser.js +19 -4
- package/dist/wirings/http/http-runner.js +72 -36
- package/dist/wirings/http/http.types.d.ts +1 -0
- package/dist/wirings/http/pikku-fetch-http-request.js +3 -1
- package/dist/wirings/http/web-request.js +32 -0
- package/dist/wirings/rpc/rpc-runner.d.ts +3 -2
- package/dist/wirings/rpc/rpc-runner.js +45 -11
- package/dist/wirings/workflow/graph/graph-node.d.ts +8 -0
- package/dist/wirings/workflow/graph/graph-node.js +4 -0
- package/dist/wirings/workflow/graph/graph-runner.js +12 -10
- package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +4 -0
- package/dist/wirings/workflow/index.d.ts +1 -1
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +81 -16
- package/dist/wirings/workflow/pikku-workflow-service.js +266 -45
- package/dist/wirings/workflow/workflow.types.d.ts +34 -0
- package/package.json +1 -1
- package/run-tests.sh +4 -1
- package/src/dev/hot-reload.test.ts +62 -11
- package/src/dev/hot-reload.ts +28 -7
- package/src/errors/error-handler.ts +8 -2
- package/src/errors/error.test.ts +2 -0
- package/src/function/function-runner.test.ts +500 -10
- package/src/function/function-runner.ts +68 -3
- package/src/function/functions.types.ts +3 -2
- package/src/index.ts +22 -3
- package/src/middleware/index.ts +11 -2
- package/src/middleware/telemetry.ts +2 -2
- package/src/middleware-runner.test.ts +16 -16
- package/src/middleware-runner.ts +42 -30
- package/src/permissions.test.ts +27 -24
- package/src/permissions.ts +41 -25
- package/src/pikku-request.test.ts +35 -0
- package/src/pikku-request.ts +1 -1
- package/src/pikku-state.ts +2 -3
- package/src/run-tests-script.test.ts +18 -0
- package/src/services/ai-agent-runner-service.ts +1 -0
- package/src/services/content-service.ts +79 -51
- package/src/services/in-memory-queue-service.ts +1 -1
- package/src/services/in-memory-session-store.ts +1 -2
- package/src/services/in-memory-workflow-service.test.ts +33 -11
- package/src/services/in-memory-workflow-service.ts +49 -13
- package/src/services/index.ts +10 -1
- package/src/services/local-content.test.ts +54 -0
- package/src/services/local-content.ts +80 -53
- package/src/services/typed-credential-service.ts +3 -3
- package/src/services/typed-secret-service.ts +3 -3
- package/src/services/typed-variables-service.ts +3 -3
- package/src/services/user-session-service.ts +4 -4
- package/src/testing/service-tests.ts +30 -0
- package/src/types/core.types.ts +6 -2
- package/src/types/state.types.ts +2 -13
- package/src/wirings/ai-agent/ai-agent-memory.test.ts +324 -0
- package/src/wirings/ai-agent/ai-agent-memory.ts +187 -36
- package/src/wirings/ai-agent/ai-agent-model-config.test.ts +12 -90
- package/src/wirings/ai-agent/ai-agent-model-config.ts +14 -38
- package/src/wirings/ai-agent/ai-agent-prepare.test.ts +292 -0
- package/src/wirings/ai-agent/ai-agent-prepare.ts +4 -1
- package/src/wirings/ai-agent/ai-agent-registry.test.ts +230 -3
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +625 -6
- package/src/wirings/ai-agent/ai-agent-runner.ts +65 -50
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +544 -5
- package/src/wirings/ai-agent/ai-agent-stream.ts +71 -69
- package/src/wirings/ai-agent/ai-agent.types.ts +24 -0
- package/src/wirings/channel/channel-handler.test.ts +272 -0
- package/src/wirings/channel/channel-handler.ts +1 -1
- package/src/wirings/channel/channel-middleware-runner.test.ts +163 -0
- package/src/wirings/channel/channel-store.ts +19 -0
- package/src/wirings/channel/channel.types.ts +4 -0
- package/src/wirings/channel/local/local-channel-runner.test.ts +63 -0
- package/src/wirings/channel/local/local-channel-runner.ts +41 -5
- package/src/wirings/channel/local/local-eventhub-service.ts +3 -3
- package/src/wirings/channel/pikku-abstract-channel-handler.ts +9 -2
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +23 -5
- package/src/wirings/cli/channel/cli-raw-channel-runner.ts +7 -2
- package/src/wirings/cli/cli-runner.test.ts +255 -2
- package/src/wirings/cli/cli-runner.ts +31 -10
- package/src/wirings/cli/cli.types.ts +4 -8
- package/src/wirings/cli/command-parser.test.ts +83 -0
- package/src/wirings/cli/command-parser.ts +23 -4
- package/src/wirings/http/http-runner.test.ts +296 -1
- package/src/wirings/http/http-runner.ts +87 -57
- package/src/wirings/http/http.types.ts +11 -26
- package/src/wirings/http/pikku-fetch-http-request.ts +8 -4
- package/src/wirings/http/pikku-fetch-http-response.test.ts +41 -0
- package/src/wirings/http/web-request.test.ts +115 -0
- package/src/wirings/http/web-request.ts +43 -5
- package/src/wirings/mcp/mcp-runner.test.ts +367 -0
- package/src/wirings/queue/queue.types.ts +2 -5
- package/src/wirings/rpc/rpc-runner.test.ts +511 -0
- package/src/wirings/rpc/rpc-runner.ts +60 -21
- package/src/wirings/rpc/wire-addon.test.ts +57 -0
- package/src/wirings/workflow/graph/graph-node.ts +12 -0
- package/src/wirings/workflow/graph/graph-runner.test.ts +98 -0
- package/src/wirings/workflow/graph/graph-runner.ts +28 -10
- package/src/wirings/workflow/graph/workflow-graph.types.ts +4 -0
- package/src/wirings/workflow/index.ts +1 -0
- package/src/wirings/workflow/pikku-workflow-service.test.ts +19 -2
- package/src/wirings/workflow/pikku-workflow-service.ts +370 -71
- package/src/wirings/workflow/workflow-queue-workers.test.ts +131 -0
- package/src/wirings/workflow/workflow.types.ts +68 -5
- package/tsconfig.tsbuildinfo +1 -1
package/run-tests.sh
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
|
|
3
|
+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
4
|
+
cd "$script_dir"
|
|
5
|
+
|
|
3
6
|
# Enable nullglob to handle cases where no files match the pattern
|
|
4
7
|
shopt -s nullglob
|
|
5
8
|
|
|
@@ -34,7 +37,7 @@ files=($(find src -type f -name "*.test.ts"))
|
|
|
34
37
|
# Check if any files matched the pattern
|
|
35
38
|
if [ ${#files[@]} -eq 0 ]; then
|
|
36
39
|
echo "No test files found matching pattern: $pattern"
|
|
37
|
-
if [ "${STRICT_TEST_DISCOVERY}" = "1" ]
|
|
40
|
+
if [ "${STRICT_TEST_DISCOVERY}" = "1" ]; then
|
|
38
41
|
exit 1
|
|
39
42
|
fi
|
|
40
43
|
exit 0
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
describe,
|
|
3
|
+
test,
|
|
4
|
+
beforeEach,
|
|
5
|
+
afterEach,
|
|
6
|
+
type TestContext,
|
|
7
|
+
} from 'node:test'
|
|
2
8
|
import assert from 'node:assert/strict'
|
|
9
|
+
import { watch } from 'node:fs'
|
|
3
10
|
import { mkdtemp, writeFile, rm, mkdir } from 'node:fs/promises'
|
|
4
11
|
import { join } from 'node:path'
|
|
5
12
|
import { tmpdir } from 'node:os'
|
|
@@ -21,6 +28,32 @@ import {
|
|
|
21
28
|
|
|
22
29
|
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
23
30
|
|
|
31
|
+
const ensureRecursiveWatchAvailable = async (
|
|
32
|
+
t: TestContext,
|
|
33
|
+
dir: string
|
|
34
|
+
): Promise<boolean> => {
|
|
35
|
+
if (process.platform === 'darwin') {
|
|
36
|
+
t.skip('recursive fs.watch is unreliable on darwin')
|
|
37
|
+
return false
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const watcher = watch(dir, { recursive: true }, () => {})
|
|
42
|
+
watcher.close()
|
|
43
|
+
return true
|
|
44
|
+
} catch (error: any) {
|
|
45
|
+
if (
|
|
46
|
+
error?.code === 'EMFILE' ||
|
|
47
|
+
error?.code === 'ERR_FEATURE_UNAVAILABLE_ON_PLATFORM' ||
|
|
48
|
+
error?.code === 'ERR_INVALID_ARG_VALUE'
|
|
49
|
+
) {
|
|
50
|
+
t.skip(`recursive fs.watch unavailable: ${error.code}`)
|
|
51
|
+
return false
|
|
52
|
+
}
|
|
53
|
+
throw error
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
24
57
|
const createMockLogger = () => {
|
|
25
58
|
const logs: Array<{ level: string; message: string }> = []
|
|
26
59
|
return {
|
|
@@ -47,7 +80,7 @@ const writeFunctionModule = async (
|
|
|
47
80
|
await writeFile(join(dir, filename), `// ts trigger ${Date.now()}`)
|
|
48
81
|
}
|
|
49
82
|
|
|
50
|
-
describe('pikkuDevReloader', () => {
|
|
83
|
+
describe('pikkuDevReloader', { concurrency: false }, () => {
|
|
51
84
|
let tmpDir: string
|
|
52
85
|
let reloader: { close: () => void } | undefined
|
|
53
86
|
let mockLogger: ReturnType<typeof createMockLogger>
|
|
@@ -76,7 +109,9 @@ describe('pikkuDevReloader', () => {
|
|
|
76
109
|
await rm(tmpDir, { recursive: true, force: true })
|
|
77
110
|
})
|
|
78
111
|
|
|
79
|
-
test('should hot-reload a function and pick up new return value', async () => {
|
|
112
|
+
test('should hot-reload a function and pick up new return value', async (t) => {
|
|
113
|
+
if (!(await ensureRecursiveWatchAvailable(t, tmpDir))) return
|
|
114
|
+
|
|
80
115
|
addFunction('myFunc', {
|
|
81
116
|
func: async () => ({ version: 1 }),
|
|
82
117
|
})
|
|
@@ -112,7 +147,9 @@ describe('pikkuDevReloader', () => {
|
|
|
112
147
|
assert.ok(reloadLog, 'Should log hot-reload message')
|
|
113
148
|
})
|
|
114
149
|
|
|
115
|
-
test('should not replace a function that is not registered', async () => {
|
|
150
|
+
test('should not replace a function that is not registered', async (t) => {
|
|
151
|
+
if (!(await ensureRecursiveWatchAvailable(t, tmpDir))) return
|
|
152
|
+
|
|
116
153
|
addFunction('registeredFunc', {
|
|
117
154
|
func: async () => ({ name: 'registered' }),
|
|
118
155
|
})
|
|
@@ -135,7 +172,9 @@ describe('pikkuDevReloader', () => {
|
|
|
135
172
|
)
|
|
136
173
|
})
|
|
137
174
|
|
|
138
|
-
test('should keep old code when JS import fails', async () => {
|
|
175
|
+
test('should keep old code when JS import fails', async (t) => {
|
|
176
|
+
if (!(await ensureRecursiveWatchAvailable(t, tmpDir))) return
|
|
177
|
+
|
|
139
178
|
addFunction('badFunc', {
|
|
140
179
|
func: async () => ({ working: true }),
|
|
141
180
|
})
|
|
@@ -166,7 +205,9 @@ describe('pikkuDevReloader', () => {
|
|
|
166
205
|
})
|
|
167
206
|
})
|
|
168
207
|
|
|
169
|
-
test('should ignore non-ts files, test files, and gen files', async () => {
|
|
208
|
+
test('should ignore non-ts files, test files, and gen files', async (t) => {
|
|
209
|
+
if (!(await ensureRecursiveWatchAvailable(t, tmpDir))) return
|
|
210
|
+
|
|
170
211
|
addFunction('someFunc', {
|
|
171
212
|
func: async () => ({ original: true }),
|
|
172
213
|
})
|
|
@@ -190,7 +231,9 @@ describe('pikkuDevReloader', () => {
|
|
|
190
231
|
assert.equal(reloadLogs.length, 0)
|
|
191
232
|
})
|
|
192
233
|
|
|
193
|
-
test('should hot-reload function used via HTTP wire', async () => {
|
|
234
|
+
test('should hot-reload function used via HTTP wire', async (t) => {
|
|
235
|
+
if (!(await ensureRecursiveWatchAvailable(t, tmpDir))) return
|
|
236
|
+
|
|
194
237
|
const sessionMiddleware = async (_services: any, wire: any, next: any) => {
|
|
195
238
|
wire.setSession?.({ userId: 'test' } as any)
|
|
196
239
|
await next()
|
|
@@ -251,7 +294,9 @@ describe('pikkuDevReloader', () => {
|
|
|
251
294
|
})
|
|
252
295
|
})
|
|
253
296
|
|
|
254
|
-
test('should hot-reload function used via scheduler wire', async () => {
|
|
297
|
+
test('should hot-reload function used via scheduler wire', async (t) => {
|
|
298
|
+
if (!(await ensureRecursiveWatchAvailable(t, tmpDir))) return
|
|
299
|
+
|
|
255
300
|
const taskResult = { ref: 'initial' }
|
|
256
301
|
|
|
257
302
|
pikkuState(null, 'scheduler', 'meta')['hotTask'] = {
|
|
@@ -371,7 +416,9 @@ describe('pikkuDevReloader', () => {
|
|
|
371
416
|
assert.deepEqual(resultV2, { result: 'v2' })
|
|
372
417
|
})
|
|
373
418
|
|
|
374
|
-
test('should debounce rapid file changes', async () => {
|
|
419
|
+
test('should debounce rapid file changes', async (t) => {
|
|
420
|
+
if (!(await ensureRecursiveWatchAvailable(t, tmpDir))) return
|
|
421
|
+
|
|
375
422
|
addFunction('debounceFunc', {
|
|
376
423
|
func: async () => ({ count: 0 }),
|
|
377
424
|
})
|
|
@@ -396,7 +443,9 @@ describe('pikkuDevReloader', () => {
|
|
|
396
443
|
assert.equal(result.count, 5)
|
|
397
444
|
})
|
|
398
445
|
|
|
399
|
-
test('should watch subdirectories', async () => {
|
|
446
|
+
test('should watch subdirectories', async (t) => {
|
|
447
|
+
if (!(await ensureRecursiveWatchAvailable(t, tmpDir))) return
|
|
448
|
+
|
|
400
449
|
const subDir = join(tmpDir, 'functions')
|
|
401
450
|
await mkdir(subDir)
|
|
402
451
|
|
|
@@ -426,7 +475,9 @@ describe('pikkuDevReloader', () => {
|
|
|
426
475
|
})
|
|
427
476
|
})
|
|
428
477
|
|
|
429
|
-
test('should properly clean up on close', async () => {
|
|
478
|
+
test('should properly clean up on close', async (t) => {
|
|
479
|
+
if (!(await ensureRecursiveWatchAvailable(t, tmpDir))) return
|
|
480
|
+
|
|
430
481
|
addFunction('cleanupFunc', {
|
|
431
482
|
func: async () => ({ v: 1 }),
|
|
432
483
|
})
|
package/src/dev/hot-reload.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { watch, type FSWatcher } from 'node:fs'
|
|
2
2
|
import { stat, readFile } from 'node:fs/promises'
|
|
3
3
|
import { join, resolve, relative } from 'node:path'
|
|
4
|
+
import { pathToFileURL } from 'node:url'
|
|
5
|
+
|
|
6
|
+
import { register } from 'tsx/esm/api'
|
|
4
7
|
|
|
5
8
|
import { pikkuState } from '../pikku-state.js'
|
|
6
9
|
import { addFunction } from '../function/function-runner.js'
|
|
@@ -54,10 +57,26 @@ const findCompiledFile = async (
|
|
|
54
57
|
// Use data: URLs to import modules. This bypasses TypeScript loaders
|
|
55
58
|
// (e.g. tsx) that intercept file:// imports and break dynamic ESM loading.
|
|
56
59
|
// Each import gets unique content so there's no module cache to worry about.
|
|
60
|
+
let tsxRegistered = false
|
|
61
|
+
|
|
62
|
+
const ensureTsxRegistered = () => {
|
|
63
|
+
if (tsxRegistered) return
|
|
64
|
+
register()
|
|
65
|
+
tsxRegistered = true
|
|
66
|
+
}
|
|
67
|
+
|
|
57
68
|
const reimportModule = async (
|
|
58
|
-
filePath: string
|
|
69
|
+
filePath: string,
|
|
70
|
+
useTsx = false
|
|
59
71
|
): Promise<Record<string, unknown> | null> => {
|
|
60
72
|
try {
|
|
73
|
+
if (useTsx) {
|
|
74
|
+
ensureTsxRegistered()
|
|
75
|
+
return await import(
|
|
76
|
+
`${pathToFileURL(resolve(filePath)).href}?t=${Date.now()}`
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
61
80
|
const content = await readFile(resolve(filePath), 'utf-8')
|
|
62
81
|
const dataUrl =
|
|
63
82
|
'data:text/javascript;base64,' + Buffer.from(content).toString('base64')
|
|
@@ -98,17 +117,19 @@ export async function pikkuDevReloader(
|
|
|
98
117
|
srcDir,
|
|
99
118
|
absPikkuDir
|
|
100
119
|
)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
120
|
+
const importPath = compiledFile ?? changedTsFile
|
|
121
|
+
const usedTsxFallback = !compiledFile
|
|
122
|
+
|
|
123
|
+
if (usedTsxFallback) {
|
|
124
|
+
logger.debug(
|
|
125
|
+
`Hot-reload using tsx fallback for: ${relative(process.cwd(), changedTsFile)}`
|
|
104
126
|
)
|
|
105
|
-
return
|
|
106
127
|
}
|
|
107
128
|
|
|
108
|
-
const mod = await reimportModule(
|
|
129
|
+
const mod = await reimportModule(importPath, usedTsxFallback)
|
|
109
130
|
if (!mod) {
|
|
110
131
|
logger.error(
|
|
111
|
-
`Failed to import: ${relative(process.cwd(),
|
|
132
|
+
`Failed to import: ${relative(process.cwd(), importPath)} (keeping old code)`
|
|
112
133
|
)
|
|
113
134
|
return
|
|
114
135
|
}
|
|
@@ -29,8 +29,14 @@ export interface ErrorDetails {
|
|
|
29
29
|
* @param error - The error to add.
|
|
30
30
|
* @param details - The details of the error.
|
|
31
31
|
*/
|
|
32
|
-
export const addError = (
|
|
33
|
-
|
|
32
|
+
export const addError = (
|
|
33
|
+
error: any,
|
|
34
|
+
{ status, message, mcpCode }: ErrorDetails
|
|
35
|
+
) => {
|
|
36
|
+
pikkuState(null, 'misc', 'errors').set(
|
|
37
|
+
error,
|
|
38
|
+
mcpCode === undefined ? { status, message } : { status, message, mcpCode }
|
|
39
|
+
)
|
|
34
40
|
}
|
|
35
41
|
|
|
36
42
|
/**
|
package/src/errors/error.test.ts
CHANGED
|
@@ -77,6 +77,7 @@ describe('getErrorResponse', () => {
|
|
|
77
77
|
const response = getErrorResponse(error)
|
|
78
78
|
assert.deepStrictEqual(response, {
|
|
79
79
|
status: 400,
|
|
80
|
+
mcpCode: -32600,
|
|
80
81
|
message:
|
|
81
82
|
'The server cannot or will not process the request due to client error (e.g., malformed request syntax).',
|
|
82
83
|
})
|
|
@@ -87,6 +88,7 @@ describe('getErrorResponse', () => {
|
|
|
87
88
|
const response = getErrorResponse(error)
|
|
88
89
|
assert.deepStrictEqual(response, {
|
|
89
90
|
status: 404,
|
|
91
|
+
mcpCode: -32601,
|
|
90
92
|
message: 'The server cannot find the requested resource.',
|
|
91
93
|
})
|
|
92
94
|
})
|