@pikku/core 0.12.62 → 0.12.64
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 +153 -0
- package/dist/dev/hot-reload.js +13 -55
- package/dist/dev/module-runner.d.ts +14 -0
- package/dist/dev/module-runner.js +59 -0
- package/dist/dev/reload-meta.d.ts +13 -0
- package/dist/dev/reload-meta.js +22 -0
- package/dist/errors/errors.d.ts +12 -0
- package/dist/errors/errors.js +19 -0
- package/dist/function/function-runner.d.ts +5 -5
- package/dist/function/function-runner.js +39 -111
- package/dist/function/functions.types.d.ts +17 -7
- package/dist/index.d.ts +5 -1
- package/dist/index.js +3 -1
- package/dist/middleware/auth-apikey.d.ts +1 -0
- package/dist/middleware/auth-bearer.d.ts +1 -0
- package/dist/middleware/auth-cookie.d.ts +1 -0
- package/dist/middleware/cors.d.ts +1 -0
- package/dist/middleware/index.d.ts +1 -1
- package/dist/middleware/index.js +1 -1
- package/dist/middleware/remote-auth.d.ts +1 -0
- package/dist/middleware/telemetry.d.ts +2 -0
- package/dist/middleware/timeout.d.ts +1 -0
- package/dist/permissions.d.ts +32 -25
- package/dist/permissions.js +104 -185
- package/dist/pikku-state.js +0 -2
- package/dist/scopes.d.ts +17 -0
- package/dist/scopes.js +59 -0
- package/dist/services/ai-embedding-service.d.ts +31 -0
- package/dist/services/ai-embedding-service.js +1 -0
- package/dist/services/credential-wire-service.d.ts +3 -1
- package/dist/services/credential-wire-service.js +10 -4
- package/dist/services/index.d.ts +4 -0
- package/dist/services/index.js +2 -0
- package/dist/services/meta-service.d.ts +15 -2
- package/dist/services/meta-service.js +51 -1
- package/dist/services/queue-webhook-service.d.ts +55 -0
- package/dist/services/queue-webhook-service.js +136 -0
- package/dist/services/scope-service.d.ts +67 -0
- package/dist/services/scope-service.js +1 -0
- package/dist/services/typed-secret-service.d.ts +8 -0
- package/dist/services/typed-secret-service.js +39 -4
- package/dist/services/webhook-service.d.ts +140 -0
- package/dist/services/webhook-service.js +44 -0
- package/dist/services/workflow-service.d.ts +7 -0
- package/dist/types/core.types.d.ts +34 -6
- package/dist/types/state.types.d.ts +20 -2
- package/dist/utils/hmac.d.ts +16 -0
- package/dist/utils/hmac.js +26 -0
- package/dist/utils/safe-fetch.d.ts +51 -0
- package/dist/utils/safe-fetch.js +192 -0
- package/dist/wirings/ai-agent/ai-agent-agui.d.ts +84 -0
- package/dist/wirings/ai-agent/ai-agent-agui.js +305 -0
- package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +41 -1
- package/dist/wirings/ai-agent/ai-agent-prepare.js +146 -6
- package/dist/wirings/ai-agent/ai-agent-runner.js +6 -1
- package/dist/wirings/ai-agent/ai-agent-stream.js +61 -10
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +27 -1
- package/dist/wirings/ai-agent/index.d.ts +1 -0
- package/dist/wirings/ai-agent/index.js +1 -0
- package/dist/wirings/ai-agent/voice-input.d.ts +1 -0
- package/dist/wirings/ai-agent/voice-input.js +2 -38
- package/dist/wirings/ai-agent/voice-output.d.ts +1 -0
- package/dist/wirings/channel/channel-common.js +0 -1
- package/dist/wirings/channel/channel-handler.js +1 -4
- package/dist/wirings/channel/channel-middleware-runner.d.ts +12 -0
- package/dist/wirings/channel/channel-middleware-runner.js +34 -19
- package/dist/wirings/channel/channel.types.d.ts +2 -6
- package/dist/wirings/cli/cli-runner.js +0 -2
- package/dist/wirings/cli/cli.types.d.ts +1 -2
- package/dist/wirings/http/http-routes.js +0 -3
- package/dist/wirings/http/http-runner.d.ts +9 -29
- package/dist/wirings/http/http-runner.js +13 -35
- package/dist/wirings/http/http.types.d.ts +1 -9
- package/dist/wirings/mcp/mcp-runner.js +0 -2
- package/dist/wirings/mcp/mcp.types.d.ts +5 -11
- package/dist/wirings/oauth2/index.d.ts +0 -3
- package/dist/wirings/oauth2/index.js +1 -2
- package/dist/wirings/rpc/addon-runner.d.ts +28 -0
- package/dist/wirings/rpc/addon-runner.js +173 -0
- package/dist/wirings/rpc/index.d.ts +5 -1
- package/dist/wirings/rpc/index.js +3 -1
- package/dist/wirings/rpc/remote-addon-auth.d.ts +26 -0
- package/dist/wirings/rpc/remote-addon-auth.js +43 -0
- package/dist/wirings/rpc/rpc-runner.d.ts +19 -0
- package/dist/wirings/rpc/rpc-runner.js +109 -3
- package/dist/wirings/rpc/rpc-types.d.ts +15 -1
- package/dist/wirings/rpc/wire-addon.js +9 -0
- package/dist/wirings/rpc/wire-remote-addon.d.ts +47 -0
- package/dist/wirings/rpc/wire-remote-addon.js +19 -0
- package/dist/wirings/scope/index.d.ts +3 -0
- package/dist/wirings/scope/index.js +2 -0
- package/dist/wirings/scope/scope.types.d.ts +39 -0
- package/dist/wirings/scope/scope.types.js +1 -0
- package/dist/wirings/scope/validate-scope-definitions.d.ts +16 -0
- package/dist/wirings/scope/validate-scope-definitions.js +76 -0
- package/dist/wirings/scope/wire-scope.d.ts +33 -0
- package/dist/wirings/scope/wire-scope.js +32 -0
- package/dist/wirings/workflow/dsl/index.d.ts +1 -1
- package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +53 -1
- package/dist/wirings/workflow/graph/graph-node.d.ts +2 -0
- package/dist/wirings/workflow/graph/graph-runner.js +3 -0
- package/dist/wirings/workflow/graph/wire-workflow-graph.d.ts +6 -0
- package/dist/wirings/workflow/graph/wire-workflow-graph.js +1 -0
- package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +5 -0
- package/dist/wirings/workflow/index.d.ts +2 -2
- package/dist/wirings/workflow/index.js +1 -1
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +51 -1
- package/dist/wirings/workflow/pikku-workflow-service.js +171 -0
- package/dist/wirings/workflow/workflow.types.d.ts +1 -1
- package/package.json +5 -1
- package/run-tests.sh +1 -0
- package/src/dev/hot-reload.ts +13 -68
- package/src/dev/module-runner.test.ts +169 -0
- package/src/dev/module-runner.ts +103 -0
- package/src/dev/reload-meta.test.ts +31 -2
- package/src/dev/reload-meta.ts +26 -0
- package/src/errors/errors.ts +24 -0
- package/src/function/function-runner.test.ts +276 -142
- package/src/function/function-runner.ts +52 -145
- package/src/function/functions.types.ts +28 -38
- package/src/index.ts +8 -6
- package/src/middleware/index.ts +1 -5
- package/src/permissions.test.ts +153 -372
- package/src/permissions.ts +139 -259
- package/src/pikku-state.ts +0 -2
- package/src/scopes.test.ts +167 -0
- package/src/scopes.ts +69 -0
- package/src/services/ai-embedding-service.ts +31 -0
- package/src/services/credential-wire-service.ts +10 -4
- package/src/services/index.ts +20 -0
- package/src/services/meta-service.ts +68 -3
- package/src/services/queue-webhook-service.test.ts +408 -0
- package/src/services/queue-webhook-service.ts +182 -0
- package/src/services/scope-service.ts +75 -0
- package/src/services/typed-secret-service.test.ts +35 -0
- package/src/services/typed-secret-service.ts +39 -4
- package/src/services/webhook-service.ts +180 -0
- package/src/services/workflow-service.ts +7 -0
- package/src/types/core.types.ts +42 -14
- package/src/types/state.types.ts +23 -3
- package/src/utils/hmac.ts +27 -0
- package/src/utils/safe-fetch.test.ts +373 -0
- package/src/utils/safe-fetch.ts +213 -0
- package/src/wirings/ai-agent/ai-agent-agui.test.ts +1127 -0
- package/src/wirings/ai-agent/ai-agent-agui.ts +386 -0
- package/src/wirings/ai-agent/ai-agent-prepare.test.ts +308 -0
- package/src/wirings/ai-agent/ai-agent-prepare.ts +192 -8
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +48 -1
- package/src/wirings/ai-agent/ai-agent-runner.ts +21 -0
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +198 -10
- package/src/wirings/ai-agent/ai-agent-stream.ts +84 -10
- package/src/wirings/ai-agent/ai-agent.types.ts +28 -0
- package/src/wirings/ai-agent/index.ts +1 -0
- package/src/wirings/ai-agent/voice-input.ts +6 -35
- package/src/wirings/channel/channel-common.ts +0 -1
- package/src/wirings/channel/channel-handler.ts +1 -9
- package/src/wirings/channel/channel-middleware-runner.test.ts +28 -2
- package/src/wirings/channel/channel-middleware-runner.ts +39 -27
- package/src/wirings/channel/channel.types.ts +0 -6
- package/src/wirings/cli/cli-runner.ts +0 -2
- package/src/wirings/cli/cli.types.ts +0 -2
- package/src/wirings/http/http-routes.ts +0 -3
- package/src/wirings/http/http-runner.test.ts +1 -11
- package/src/wirings/http/http-runner.ts +19 -40
- package/src/wirings/http/http.types.ts +1 -10
- package/src/wirings/mcp/mcp-runner.ts +0 -2
- package/src/wirings/mcp/mcp.types.ts +3 -11
- package/src/wirings/oauth2/index.ts +0 -3
- package/src/wirings/rpc/addon-runner.ts +233 -0
- package/src/wirings/rpc/index.ts +17 -1
- package/src/wirings/rpc/remote-addon-auth.ts +69 -0
- package/src/wirings/rpc/rpc-runner.test.ts +319 -16
- package/src/wirings/rpc/rpc-runner.ts +155 -3
- package/src/wirings/rpc/rpc-types.ts +16 -5
- package/src/wirings/rpc/wire-addon.test.ts +6 -3
- package/src/wirings/rpc/wire-addon.ts +9 -0
- package/src/wirings/rpc/wire-remote-addon.ts +57 -0
- package/src/wirings/scope/index.ts +14 -0
- package/src/wirings/scope/scope.test.ts +135 -0
- package/src/wirings/scope/scope.types.ts +44 -0
- package/src/wirings/scope/validate-scope-definitions.ts +110 -0
- package/src/wirings/scope/wire-scope.ts +34 -0
- package/src/wirings/workflow/dsl/index.ts +4 -0
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +61 -0
- package/src/wirings/workflow/graph/graph-node.ts +2 -0
- package/src/wirings/workflow/graph/graph-runner.test.ts +58 -0
- package/src/wirings/workflow/graph/graph-runner.ts +3 -0
- package/src/wirings/workflow/graph/wire-workflow-graph.ts +7 -0
- package/src/wirings/workflow/graph/workflow-graph.types.ts +5 -0
- package/src/wirings/workflow/index.ts +5 -0
- package/src/wirings/workflow/pikku-workflow-service.test.ts +341 -0
- package/src/wirings/workflow/pikku-workflow-service.ts +236 -0
- package/src/wirings/workflow/workflow-step-session.test.ts +21 -5
- package/src/wirings/workflow/workflow.types.ts +4 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/src/wirings/oauth2/oauth2-client.test.ts +0 -930
- package/src/wirings/oauth2/oauth2-client.ts +0 -331
- package/src/wirings/oauth2/oauth2-routes.ts +0 -234
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AIAgentRunnerService } from '../../services/ai-agent-runner-service.js'
|
|
2
2
|
import { pikkuAIMiddleware } from '../../types/core.types.js'
|
|
3
|
+
import { safeFetch } from '../../utils/safe-fetch.js'
|
|
3
4
|
import type { AIContentPart } from './ai-agent.types.js'
|
|
4
5
|
|
|
5
6
|
function base64ToUint8Array(base64: string): Uint8Array {
|
|
@@ -13,45 +14,15 @@ function base64ToUint8Array(base64: string): Uint8Array {
|
|
|
13
14
|
|
|
14
15
|
const MAX_AUDIO_SIZE = 50 * 1024 * 1024
|
|
15
16
|
|
|
16
|
-
// Portable SSRF guard: @pikku/core runs in edge runtimes (CF Workers) with no
|
|
17
|
-
// Node `dns`, so we cannot resolve hostnames to check for private targets.
|
|
18
|
-
// Reject the obvious internal literals; callers wanting stricter control pass
|
|
19
|
-
// an explicit `allowedAudioHosts` allowlist. (Does not defend against a public
|
|
20
|
-
// hostname that resolves to a private IP / DNS rebinding — out of reach here.)
|
|
21
|
-
function isPrivateHost(hostname: string): boolean {
|
|
22
|
-
const host = hostname.replace(/^\[|\]$/g, '').toLowerCase()
|
|
23
|
-
if (host === 'localhost' || host === '0.0.0.0' || host === '::1') return true
|
|
24
|
-
if (host.startsWith('fe80:') || host.startsWith('fc') || host.startsWith('fd'))
|
|
25
|
-
return true
|
|
26
|
-
const v4 = host.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.\d{1,3}$/)
|
|
27
|
-
if (v4) {
|
|
28
|
-
const [a, b] = [Number(v4[1]), Number(v4[2])]
|
|
29
|
-
if (a === 127 || a === 10 || a === 0) return true
|
|
30
|
-
if (a === 169 && b === 254) return true // link-local incl. cloud metadata
|
|
31
|
-
if (a === 172 && b >= 16 && b <= 31) return true
|
|
32
|
-
if (a === 192 && b === 168) return true
|
|
33
|
-
}
|
|
34
|
-
return false
|
|
35
|
-
}
|
|
36
|
-
|
|
37
17
|
async function fetchAsUint8Array(
|
|
38
18
|
url: string,
|
|
39
19
|
allowedAudioHosts?: string[]
|
|
40
20
|
): Promise<Uint8Array> {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (!allowedAudioHosts.includes(parsed.hostname)) {
|
|
47
|
-
throw new Error(`Audio URL host is not allowed: ${parsed.hostname}`)
|
|
48
|
-
}
|
|
49
|
-
} else if (isPrivateHost(parsed.hostname)) {
|
|
50
|
-
throw new Error(
|
|
51
|
-
`Refusing to fetch audio from a private/internal host: ${parsed.hostname}`
|
|
52
|
-
)
|
|
53
|
-
}
|
|
54
|
-
const response = await fetch(url)
|
|
21
|
+
const response = await safeFetch(
|
|
22
|
+
url,
|
|
23
|
+
{},
|
|
24
|
+
{ allowedHosts: allowedAudioHosts }
|
|
25
|
+
)
|
|
55
26
|
const contentLength = response.headers.get('content-length')
|
|
56
27
|
if (contentLength && parseInt(contentLength, 10) > MAX_AUDIO_SIZE) {
|
|
57
28
|
throw new Error('Audio file exceeds maximum size')
|
|
@@ -102,11 +102,7 @@ export const processMessageHandlers = (
|
|
|
102
102
|
routingProperty,
|
|
103
103
|
routerValue
|
|
104
104
|
)
|
|
105
|
-
const {
|
|
106
|
-
packageName,
|
|
107
|
-
middleware: routeInheritedMiddleware,
|
|
108
|
-
permissions: inheritedPermissions,
|
|
109
|
-
} = routeMeta
|
|
105
|
+
const { packageName, middleware: routeInheritedMiddleware } = routeMeta
|
|
110
106
|
|
|
111
107
|
const pikkuFuncId = routeMeta.pikkuFuncId
|
|
112
108
|
|
|
@@ -132,8 +128,6 @@ export const processMessageHandlers = (
|
|
|
132
128
|
// Inherited middleware comes from metadata (tag groups, non-inline wire)
|
|
133
129
|
const inheritedMiddleware = routeInheritedMiddleware || []
|
|
134
130
|
|
|
135
|
-
const wirePermissions = isWrapper ? onMessage.permissions : undefined
|
|
136
|
-
|
|
137
131
|
// Create unique cache key that includes routing info to avoid cache collisions
|
|
138
132
|
// when multiple message handlers use the same function
|
|
139
133
|
const cacheKey = routingProperty
|
|
@@ -150,8 +144,6 @@ export const processMessageHandlers = (
|
|
|
150
144
|
wireMiddleware,
|
|
151
145
|
inheritedChannelMiddleware: channelMeta?.channelMiddleware,
|
|
152
146
|
wireChannelMiddleware: wireChannelMiddleware as any,
|
|
153
|
-
inheritedPermissions,
|
|
154
|
-
wirePermissions,
|
|
155
147
|
coerceDataFromSchema: true,
|
|
156
148
|
tags: channelConfig.tags,
|
|
157
149
|
sessionService: userSession,
|
|
@@ -69,17 +69,17 @@ describe('combineChannelMiddleware', () => {
|
|
|
69
69
|
void execution
|
|
70
70
|
})
|
|
71
71
|
|
|
72
|
-
test('
|
|
72
|
+
test('caches the statically-resolved inherited middleware until the cache is cleared', () => {
|
|
73
73
|
const shared = async () => {}
|
|
74
74
|
addChannelMiddleware('chat:outbound', [shared])
|
|
75
75
|
|
|
76
76
|
const first = combineChannelMiddleware('channel', 'cached-1', {
|
|
77
77
|
wireInheritedChannelMiddleware: [{ type: 'tag', tag: 'chat:outbound' }],
|
|
78
|
-
wireChannelMiddleware: [shared],
|
|
79
78
|
})
|
|
80
79
|
|
|
81
80
|
assert.deepEqual(first, [shared])
|
|
82
81
|
|
|
82
|
+
// Re-registering after the first resolve does not change the cached result.
|
|
83
83
|
addChannelMiddleware('chat:outbound', [async () => {}])
|
|
84
84
|
|
|
85
85
|
const cached = combineChannelMiddleware('channel', 'cached-1', {
|
|
@@ -97,6 +97,32 @@ describe('combineChannelMiddleware', () => {
|
|
|
97
97
|
assert.notStrictEqual(refreshed[0], shared)
|
|
98
98
|
})
|
|
99
99
|
|
|
100
|
+
test('does not cache per-run wireChannelMiddleware across calls (C4 cross-run leak)', () => {
|
|
101
|
+
const shared = async () => {}
|
|
102
|
+
addChannelMiddleware('chat:outbound', [shared])
|
|
103
|
+
|
|
104
|
+
const runA = async () => {}
|
|
105
|
+
const first = combineChannelMiddleware('agent', 'stream:bot', {
|
|
106
|
+
wireInheritedChannelMiddleware: [{ type: 'tag', tag: 'chat:outbound' }],
|
|
107
|
+
wireChannelMiddleware: [runA],
|
|
108
|
+
})
|
|
109
|
+
assert.deepEqual(first, [shared, runA])
|
|
110
|
+
|
|
111
|
+
const runB = async () => {}
|
|
112
|
+
const second = combineChannelMiddleware('agent', 'stream:bot', {
|
|
113
|
+
wireInheritedChannelMiddleware: [{ type: 'tag', tag: 'chat:outbound' }],
|
|
114
|
+
wireChannelMiddleware: [runB],
|
|
115
|
+
})
|
|
116
|
+
assert.deepEqual(second, [shared, runB])
|
|
117
|
+
assert.strictEqual(second[1], runB)
|
|
118
|
+
assert.notStrictEqual(second[1], runA)
|
|
119
|
+
|
|
120
|
+
const third = combineChannelMiddleware('agent', 'stream:bot', {
|
|
121
|
+
wireInheritedChannelMiddleware: [{ type: 'tag', tag: 'chat:outbound' }],
|
|
122
|
+
})
|
|
123
|
+
assert.deepEqual(third, [shared])
|
|
124
|
+
})
|
|
125
|
+
|
|
100
126
|
test('ignores missing named middleware references', () => {
|
|
101
127
|
const combined = combineChannelMiddleware('channel', 'missing-wire', {
|
|
102
128
|
wireInheritedChannelMiddleware: [{ type: 'wire', name: 'missing' }],
|
|
@@ -36,6 +36,18 @@ export const clearChannelMiddlewareCache = () => {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Combine the inherited (tag/named) channel middleware with any per-run
|
|
41
|
+
* middleware for a wiring.
|
|
42
|
+
*
|
|
43
|
+
* Only the statically-resolved inherited middleware is deterministic per `uid`
|
|
44
|
+
* and safe to cache. `wireChannelMiddleware` is a per-run set of closures (e.g.
|
|
45
|
+
* an AI agent's per-invocation stream middleware holding that run's
|
|
46
|
+
* thread/session state) and MUST NOT be cached — caching it lets a later run of
|
|
47
|
+
* the same `uid` reuse an earlier run's closures, leaking that run's state (and
|
|
48
|
+
* growing memory) across invocations. It is therefore appended fresh on every
|
|
49
|
+
* call after the cached inherited slice.
|
|
50
|
+
*/
|
|
39
51
|
export const combineChannelMiddleware = (
|
|
40
52
|
wireType: string,
|
|
41
53
|
uid: string,
|
|
@@ -50,40 +62,40 @@ export const combineChannelMiddleware = (
|
|
|
50
62
|
} = {}
|
|
51
63
|
): readonly CorePikkuChannelMiddleware[] => {
|
|
52
64
|
const cacheKey = `${wireType}:${uid}`
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (middleware) {
|
|
72
|
-
resolved.push(middleware)
|
|
65
|
+
let inherited = channelMiddlewareCache[cacheKey]
|
|
66
|
+
if (!inherited) {
|
|
67
|
+
const resolved: CorePikkuChannelMiddleware[] = []
|
|
68
|
+
if (wireInheritedChannelMiddleware) {
|
|
69
|
+
for (const meta of wireInheritedChannelMiddleware) {
|
|
70
|
+
if (meta.type === 'tag') {
|
|
71
|
+
const groups = getTagGroups(
|
|
72
|
+
pikkuState(packageName, 'channelMiddleware', 'tagGroup'),
|
|
73
|
+
meta.tag
|
|
74
|
+
)
|
|
75
|
+
for (const group of groups) {
|
|
76
|
+
resolved.push(...group)
|
|
77
|
+
}
|
|
78
|
+
} else if (meta.type === 'wire') {
|
|
79
|
+
const middleware = getChannelMiddlewareByName(meta.name)
|
|
80
|
+
if (middleware) {
|
|
81
|
+
resolved.push(middleware)
|
|
82
|
+
}
|
|
73
83
|
}
|
|
74
84
|
}
|
|
75
85
|
}
|
|
86
|
+
inherited = channelMiddlewareCache[cacheKey] = freezeDedupe(
|
|
87
|
+
resolved
|
|
88
|
+
) as readonly CorePikkuChannelMiddleware[]
|
|
76
89
|
}
|
|
77
90
|
|
|
78
|
-
if (wireChannelMiddleware) {
|
|
79
|
-
|
|
91
|
+
if (!wireChannelMiddleware?.length) {
|
|
92
|
+
return inherited
|
|
80
93
|
}
|
|
81
94
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return channelMiddlewareCache[cacheKey]
|
|
95
|
+
return freezeDedupe([
|
|
96
|
+
...inherited,
|
|
97
|
+
...wireChannelMiddleware,
|
|
98
|
+
]) as readonly CorePikkuChannelMiddleware[]
|
|
87
99
|
}
|
|
88
100
|
|
|
89
101
|
export function wrapChannelWithMiddleware<Out>(
|
|
@@ -6,7 +6,6 @@ import type {
|
|
|
6
6
|
import type {
|
|
7
7
|
CorePikkuMiddleware,
|
|
8
8
|
MiddlewareMetadata,
|
|
9
|
-
PermissionMetadata,
|
|
10
9
|
} from '../../types/core.types.js'
|
|
11
10
|
|
|
12
11
|
export type BinaryData = ArrayBuffer | Uint8Array
|
|
@@ -23,7 +22,6 @@ export type CorePikkuChannelMiddlewareFactory<
|
|
|
23
22
|
Event = unknown,
|
|
24
23
|
> = (input: In) => CorePikkuChannelMiddleware<Services, Event>
|
|
25
24
|
import type {
|
|
26
|
-
CorePermissionGroup,
|
|
27
25
|
CorePikkuFunction,
|
|
28
26
|
CorePikkuFunctionConfig,
|
|
29
27
|
CorePikkuFunctionSessionless,
|
|
@@ -52,7 +50,6 @@ export interface ChannelMessageMeta {
|
|
|
52
50
|
errors?: string[]
|
|
53
51
|
tags?: string[]
|
|
54
52
|
middleware?: MiddlewareMetadata[]
|
|
55
|
-
permissions?: PermissionMetadata[]
|
|
56
53
|
}
|
|
57
54
|
|
|
58
55
|
export interface ChannelMeta {
|
|
@@ -72,7 +69,6 @@ export interface ChannelMeta {
|
|
|
72
69
|
errors?: string[]
|
|
73
70
|
tags?: string[]
|
|
74
71
|
middleware?: MiddlewareMetadata[] // Pre-resolved middleware chain (tag + explicit)
|
|
75
|
-
permissions?: PermissionMetadata[] // Pre-resolved permission chain (tag + explicit)
|
|
76
72
|
channelMiddleware?: MiddlewareMetadata[]
|
|
77
73
|
}
|
|
78
74
|
|
|
@@ -123,7 +119,6 @@ export type CoreChannel<
|
|
|
123
119
|
| ChannelFunctionMessage
|
|
124
120
|
| {
|
|
125
121
|
func: ChannelFunctionMessage
|
|
126
|
-
permissions?: CorePermissionGroup<PikkuPermission>
|
|
127
122
|
auth?: boolean
|
|
128
123
|
middleware?: PikkuMiddleware[]
|
|
129
124
|
}
|
|
@@ -133,7 +128,6 @@ export type CoreChannel<
|
|
|
133
128
|
channelMiddleware?: Array<
|
|
134
129
|
CorePikkuChannelMiddleware | CorePikkuChannelMiddlewareFactory
|
|
135
130
|
>
|
|
136
|
-
permissions?: CorePermissionGroup<PikkuPermission>
|
|
137
131
|
auth?: boolean
|
|
138
132
|
binary?: boolean | null
|
|
139
133
|
onBinaryMessage?: (
|
|
@@ -374,8 +374,6 @@ export async function runCLICommand({
|
|
|
374
374
|
auth: false,
|
|
375
375
|
inheritedMiddleware: currentCommand.middleware,
|
|
376
376
|
wireMiddleware: allWireMiddleware,
|
|
377
|
-
inheritedPermissions: currentCommand.permissions,
|
|
378
|
-
wirePermissions: undefined,
|
|
379
377
|
coerceDataFromSchema: true,
|
|
380
378
|
tags: programData?.tags,
|
|
381
379
|
wire,
|
|
@@ -4,7 +4,6 @@ import type {
|
|
|
4
4
|
CoreUserSession,
|
|
5
5
|
CoreServices,
|
|
6
6
|
MiddlewareMetadata,
|
|
7
|
-
PermissionMetadata,
|
|
8
7
|
} from '../../types/core.types.js'
|
|
9
8
|
import type {
|
|
10
9
|
CorePikkuFunctionConfig,
|
|
@@ -80,7 +79,6 @@ export interface CLICommandMeta {
|
|
|
80
79
|
tags?: string[]
|
|
81
80
|
subcommands?: Record<string, CLICommandMeta>
|
|
82
81
|
middleware?: MiddlewareMetadata[] // Pre-resolved middleware chain (tag + explicit)
|
|
83
|
-
permissions?: PermissionMetadata[] // Pre-resolved permission chain (tag + explicit)
|
|
84
82
|
isDefault?: boolean
|
|
85
83
|
}
|
|
86
84
|
|
|
@@ -128,7 +128,6 @@ function processRouteMap(
|
|
|
128
128
|
* - tags: Merges (parent + child)
|
|
129
129
|
* - middleware: Merges (parent runs first)
|
|
130
130
|
* - auth: Inner overrides outer
|
|
131
|
-
* - permissions: Inner overrides outer
|
|
132
131
|
*/
|
|
133
132
|
function mergeGroupConfig(
|
|
134
133
|
parent: HTTPRoutesGroupConfig,
|
|
@@ -139,7 +138,6 @@ function mergeGroupConfig(
|
|
|
139
138
|
tags: [...(parent.tags || []), ...(child.tags || [])],
|
|
140
139
|
middleware: [...(parent.middleware || []), ...(child.middleware || [])],
|
|
141
140
|
auth: child.auth ?? parent.auth,
|
|
142
|
-
permissions: child.permissions ?? parent.permissions,
|
|
143
141
|
}
|
|
144
142
|
}
|
|
145
143
|
|
|
@@ -162,7 +160,6 @@ function registerRoute(
|
|
|
162
160
|
...(groupConfig.middleware || []),
|
|
163
161
|
...(route.middleware || []),
|
|
164
162
|
],
|
|
165
|
-
permissions: route.permissions ?? groupConfig.permissions,
|
|
166
163
|
contentType: route.contentType,
|
|
167
164
|
timeout: route.timeout,
|
|
168
165
|
headers: route.headers,
|
|
@@ -4,7 +4,6 @@ import { NotFoundError } from '../../errors/errors.js'
|
|
|
4
4
|
import type { JSONValue, CorePikkuMiddleware } from '../../types/core.types.js'
|
|
5
5
|
import {
|
|
6
6
|
addHTTPMiddleware,
|
|
7
|
-
addHTTPPermission,
|
|
8
7
|
createHTTPWire,
|
|
9
8
|
fetch,
|
|
10
9
|
fetchData,
|
|
@@ -253,23 +252,14 @@ describe('http-runner helpers', () => {
|
|
|
253
252
|
} as any)
|
|
254
253
|
})
|
|
255
254
|
|
|
256
|
-
test('addHTTPMiddleware
|
|
255
|
+
test('addHTTPMiddleware registers a route middleware group', () => {
|
|
257
256
|
const middleware = [sessionMiddleware]
|
|
258
|
-
const permissions = [async () => true]
|
|
259
257
|
|
|
260
258
|
assert.strictEqual(addHTTPMiddleware('/api/*', middleware), middleware)
|
|
261
|
-
assert.strictEqual(
|
|
262
|
-
addHTTPPermission('/api/*', permissions as any),
|
|
263
|
-
permissions
|
|
264
|
-
)
|
|
265
259
|
assert.strictEqual(
|
|
266
260
|
pikkuState(null, 'middleware', 'httpGroup')['/api/*'],
|
|
267
261
|
middleware
|
|
268
262
|
)
|
|
269
|
-
assert.strictEqual(
|
|
270
|
-
pikkuState(null, 'permissions', 'httpGroup')['/api/*'],
|
|
271
|
-
permissions
|
|
272
|
-
)
|
|
273
263
|
})
|
|
274
264
|
|
|
275
265
|
test('addHTTPMiddleware composes repeated registrations for the same pattern', () => {
|
|
@@ -11,7 +11,6 @@ import type {
|
|
|
11
11
|
CorePikkuFunction,
|
|
12
12
|
CorePikkuFunctionSessionless,
|
|
13
13
|
CorePikkuPermission,
|
|
14
|
-
CorePermissionGroup,
|
|
15
14
|
} from '../../function/functions.types.js'
|
|
16
15
|
import type {
|
|
17
16
|
CoreUserSession,
|
|
@@ -35,6 +34,7 @@ import {
|
|
|
35
34
|
import { PikkuSessionService } from '../../services/user-session-service.js'
|
|
36
35
|
import { getErrorResponse } from '../../errors/error-handler.js'
|
|
37
36
|
import { handleHTTPError } from '../../handle-error.js'
|
|
37
|
+
import { isProduction } from '../../env.js'
|
|
38
38
|
import { pikkuState } from '../../pikku-state.js'
|
|
39
39
|
import { PikkuFetchHTTPResponse } from './pikku-fetch-http-response.js'
|
|
40
40
|
import { PikkuFetchHTTPRequest } from './pikku-fetch-http-request.js'
|
|
@@ -105,42 +105,23 @@ export const addHTTPMiddleware = <PikkuMiddleware extends CorePikkuMiddleware>(
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
/**
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
* @template PikkuPermission The permission type.
|
|
117
|
-
* @param {string} pattern - Route pattern (e.g., '*' for all routes, '/api/*' for specific routes).
|
|
118
|
-
* @param {CorePermissionGroup | CorePikkuPermission[]} permissions - Permissions for this route pattern.
|
|
119
|
-
*
|
|
120
|
-
* @returns {CorePermissionGroup | CorePikkuPermission[]} The permissions (for chaining/wrapping).
|
|
121
|
-
*
|
|
122
|
-
* @example
|
|
123
|
-
* ```typescript
|
|
124
|
-
* // Recommended: tree-shakeable
|
|
125
|
-
* export const httpGlobalPermissions = () => addHTTPPermission('*', [
|
|
126
|
-
* authenticatedPermission,
|
|
127
|
-
* rateLimitPermission
|
|
128
|
-
* ])
|
|
129
|
-
*
|
|
130
|
-
* // Also works: no tree-shaking
|
|
131
|
-
* export const apiPermissions = addHTTPPermission('/api/*', [
|
|
132
|
-
* adminPermission
|
|
133
|
-
* ])
|
|
134
|
-
* ```
|
|
108
|
+
* @deprecated HTTP-route-level permissions were removed in #972 — permissions
|
|
109
|
+
* are now function-scoped only (declare them on the function via
|
|
110
|
+
* `pikkuFunc({ permissions })`). This throwing stub exists solely so the pinned
|
|
111
|
+
* bootstrap CLI (which still generates an `addHTTPPermission` wrapper) can
|
|
112
|
+
* resolve the import at build time; it is never called. Delete once
|
|
113
|
+
* `PIKKU_CLI_VERSION` in the CLI build is bumped past the release that removes
|
|
114
|
+
* HTTP-route permissions.
|
|
135
115
|
*/
|
|
136
|
-
export const addHTTPPermission =
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
116
|
+
export const addHTTPPermission = (
|
|
117
|
+
_pattern: string,
|
|
118
|
+
_permissions:
|
|
119
|
+
| Record<string, CorePikkuPermission | CorePikkuPermission[]>
|
|
120
|
+
| CorePikkuPermission[]
|
|
121
|
+
): never => {
|
|
122
|
+
throw new Error(
|
|
123
|
+
'addHTTPPermission was removed in #972 — HTTP-route-level permissions no longer exist. Declare permissions on the function definition instead: pikkuFunc({ permissions }).'
|
|
124
|
+
)
|
|
144
125
|
}
|
|
145
126
|
|
|
146
127
|
/**
|
|
@@ -232,7 +213,6 @@ const getMatchingRoute = (requestType: string, requestPath: string) => {
|
|
|
232
213
|
matchedPath,
|
|
233
214
|
params: matchedPath.params,
|
|
234
215
|
route,
|
|
235
|
-
permissions: route.permissions,
|
|
236
216
|
meta: meta!,
|
|
237
217
|
}
|
|
238
218
|
}
|
|
@@ -431,8 +411,6 @@ const executeRoute = async (
|
|
|
431
411
|
data,
|
|
432
412
|
inheritedMiddleware: meta.middleware,
|
|
433
413
|
wireMiddleware: route.middleware,
|
|
434
|
-
inheritedPermissions: meta.permissions,
|
|
435
|
-
wirePermissions: route.permissions,
|
|
436
414
|
coerceDataFromSchema: options.coerceDataFromSchema,
|
|
437
415
|
tags: route.tags,
|
|
438
416
|
wire,
|
|
@@ -549,7 +527,8 @@ export const fetchData = async <In, Out>(
|
|
|
549
527
|
logWarningsForStatusCodes = [],
|
|
550
528
|
coerceDataFromSchema = true,
|
|
551
529
|
bubbleErrors = false,
|
|
552
|
-
|
|
530
|
+
// Surface the error message + stack on unexpected 500s unless in production.
|
|
531
|
+
exposeErrors = !isProduction(),
|
|
553
532
|
generateRequestId,
|
|
554
533
|
traceId: externalTraceId,
|
|
555
534
|
}: RunHTTPWiringOptions = {}
|
|
@@ -8,7 +8,6 @@ import type {
|
|
|
8
8
|
CorePikkuFunction,
|
|
9
9
|
CorePikkuFunctionSessionless,
|
|
10
10
|
CorePikkuPermission,
|
|
11
|
-
CorePermissionGroup,
|
|
12
11
|
CorePikkuFunctionConfig,
|
|
13
12
|
} from '../../function/functions.types.js'
|
|
14
13
|
|
|
@@ -138,7 +137,6 @@ export type CoreHTTPFunctionWiring<
|
|
|
138
137
|
PikkuPermission,
|
|
139
138
|
PikkuMiddleware
|
|
140
139
|
>
|
|
141
|
-
permissions?: CorePermissionGroup<PikkuPermission>
|
|
142
140
|
auth?: true
|
|
143
141
|
middleware?: PikkuMiddleware[]
|
|
144
142
|
sse?: undefined
|
|
@@ -151,7 +149,6 @@ export type CoreHTTPFunctionWiring<
|
|
|
151
149
|
PikkuPermission,
|
|
152
150
|
PikkuMiddleware
|
|
153
151
|
>
|
|
154
|
-
permissions?: undefined
|
|
155
152
|
auth?: false
|
|
156
153
|
middleware?: PikkuMiddleware[]
|
|
157
154
|
sse?: undefined
|
|
@@ -164,7 +161,6 @@ export type CoreHTTPFunctionWiring<
|
|
|
164
161
|
PikkuPermission,
|
|
165
162
|
PikkuMiddleware
|
|
166
163
|
>
|
|
167
|
-
permissions?: CorePermissionGroup<PikkuPermission>
|
|
168
164
|
auth?: true
|
|
169
165
|
middleware?: PikkuMiddleware[]
|
|
170
166
|
sse?: boolean
|
|
@@ -177,7 +173,6 @@ export type CoreHTTPFunctionWiring<
|
|
|
177
173
|
PikkuPermission,
|
|
178
174
|
PikkuMiddleware
|
|
179
175
|
>
|
|
180
|
-
permissions?: undefined
|
|
181
176
|
auth?: false
|
|
182
177
|
middleware?: PikkuMiddleware[]
|
|
183
178
|
sse?: boolean
|
|
@@ -190,7 +185,6 @@ export type CoreHTTPFunctionWiring<
|
|
|
190
185
|
PikkuPermission,
|
|
191
186
|
PikkuMiddleware
|
|
192
187
|
>
|
|
193
|
-
permissions?: CorePermissionGroup<PikkuPermission>
|
|
194
188
|
auth?: true
|
|
195
189
|
middleware?: PikkuMiddleware[]
|
|
196
190
|
query?: Array<keyof In>
|
|
@@ -204,7 +198,6 @@ export type CoreHTTPFunctionWiring<
|
|
|
204
198
|
PikkuPermission,
|
|
205
199
|
PikkuMiddleware
|
|
206
200
|
>
|
|
207
|
-
permissions?: undefined
|
|
208
201
|
auth?: false
|
|
209
202
|
middleware?: PikkuMiddleware[]
|
|
210
203
|
query?: Array<keyof In>
|
|
@@ -305,9 +298,8 @@ export type HTTPRouteConfig<
|
|
|
305
298
|
route: string
|
|
306
299
|
func: CorePikkuFunctionConfig<PikkuFunction, PikkuPermission, PikkuMiddleware>
|
|
307
300
|
|
|
308
|
-
// Auth
|
|
301
|
+
// Auth
|
|
309
302
|
auth?: boolean
|
|
310
|
-
permissions?: CorePermissionGroup<PikkuPermission>
|
|
311
303
|
|
|
312
304
|
// Middleware
|
|
313
305
|
middleware?: PikkuMiddleware[]
|
|
@@ -331,7 +323,6 @@ export type HTTPRoutesGroupConfig<
|
|
|
331
323
|
tags?: string[]
|
|
332
324
|
auth?: boolean
|
|
333
325
|
middleware?: PikkuMiddleware[]
|
|
334
|
-
permissions?: CorePermissionGroup<PikkuPermission>
|
|
335
326
|
}
|
|
336
327
|
|
|
337
328
|
/**
|
|
@@ -252,8 +252,6 @@ async function runMCPPikkuFunc(
|
|
|
252
252
|
data: () => request.params,
|
|
253
253
|
inheritedMiddleware: meta?.middleware,
|
|
254
254
|
wireMiddleware: mcp?.middleware,
|
|
255
|
-
inheritedPermissions: meta?.permissions,
|
|
256
|
-
wirePermissions: mcp?.permissions,
|
|
257
255
|
tags: mcp?.tags,
|
|
258
256
|
wire,
|
|
259
257
|
sessionService: mcpSessionService,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
CorePermissionGroup,
|
|
3
2
|
CorePikkuFunctionConfig,
|
|
4
3
|
CorePikkuFunctionSessionless,
|
|
5
4
|
CorePikkuPermission,
|
|
@@ -7,7 +6,6 @@ import type {
|
|
|
7
6
|
import type {
|
|
8
7
|
CorePikkuMiddleware,
|
|
9
8
|
MiddlewareMetadata,
|
|
10
|
-
PermissionMetadata,
|
|
11
9
|
} from '../../types/core.types.js'
|
|
12
10
|
|
|
13
11
|
/**
|
|
@@ -49,12 +47,11 @@ export type PikkuMCP<Tools extends string = any> = {
|
|
|
49
47
|
*/
|
|
50
48
|
export type MCPResourceMeta = Record<
|
|
51
49
|
string,
|
|
52
|
-
Omit<CoreMCPResource, 'func' | 'middleware'
|
|
50
|
+
Omit<CoreMCPResource, 'func' | 'middleware'> & {
|
|
53
51
|
pikkuFuncId: string
|
|
54
52
|
inputSchema: string | null
|
|
55
53
|
outputSchema: string | null
|
|
56
54
|
middleware?: MiddlewareMetadata[] // Pre-resolved middleware chain (tag + explicit)
|
|
57
|
-
permissions?: PermissionMetadata[] // Pre-resolved permission chain (tag + explicit)
|
|
58
55
|
}
|
|
59
56
|
>
|
|
60
57
|
|
|
@@ -63,12 +60,11 @@ export type MCPResourceMeta = Record<
|
|
|
63
60
|
*/
|
|
64
61
|
export type MCPToolMeta = Record<
|
|
65
62
|
string,
|
|
66
|
-
Omit<CoreMCPTool, 'func' | 'middleware'
|
|
63
|
+
Omit<CoreMCPTool, 'func' | 'middleware'> & {
|
|
67
64
|
pikkuFuncId: string
|
|
68
65
|
inputSchema: string | null
|
|
69
66
|
outputSchema: string | null
|
|
70
67
|
middleware?: MiddlewareMetadata[] // Pre-resolved middleware chain (tag + explicit)
|
|
71
|
-
permissions?: PermissionMetadata[] // Pre-resolved permission chain (tag + explicit)
|
|
72
68
|
}
|
|
73
69
|
>
|
|
74
70
|
|
|
@@ -77,7 +73,7 @@ export type MCPToolMeta = Record<
|
|
|
77
73
|
*/
|
|
78
74
|
export type MCPPromptMeta = Record<
|
|
79
75
|
string,
|
|
80
|
-
Omit<CoreMCPPrompt, 'func' | 'middleware'
|
|
76
|
+
Omit<CoreMCPPrompt, 'func' | 'middleware'> & {
|
|
81
77
|
pikkuFuncId: string
|
|
82
78
|
inputSchema: string | null
|
|
83
79
|
outputSchema: string | null
|
|
@@ -87,7 +83,6 @@ export type MCPPromptMeta = Record<
|
|
|
87
83
|
required: boolean
|
|
88
84
|
}>
|
|
89
85
|
middleware?: MiddlewareMetadata[] // Pre-resolved middleware chain (tag + explicit)
|
|
90
|
-
permissions?: PermissionMetadata[] // Pre-resolved permission chain (tag + explicit)
|
|
91
86
|
}
|
|
92
87
|
>
|
|
93
88
|
|
|
@@ -112,7 +107,6 @@ export type CoreMCPResource<
|
|
|
112
107
|
func: PikkuFunctionConfig
|
|
113
108
|
tags?: string[]
|
|
114
109
|
middleware?: PikkuMiddleware[]
|
|
115
|
-
permissions?: CorePermissionGroup<PikkuPermission>
|
|
116
110
|
}
|
|
117
111
|
|
|
118
112
|
/**
|
|
@@ -134,7 +128,6 @@ export type CoreMCPTool<
|
|
|
134
128
|
tags?: string[]
|
|
135
129
|
streaming?: boolean
|
|
136
130
|
middleware?: PikkuMiddleware[]
|
|
137
|
-
permissions?: CorePermissionGroup<PikkuPermission>
|
|
138
131
|
}
|
|
139
132
|
|
|
140
133
|
/**
|
|
@@ -154,7 +147,6 @@ export type CoreMCPPrompt<
|
|
|
154
147
|
func: PikkuFunctionConfig
|
|
155
148
|
tags?: string[]
|
|
156
149
|
middleware?: PikkuMiddleware[]
|
|
157
|
-
permissions?: CorePermissionGroup<PikkuPermission>
|
|
158
150
|
}
|
|
159
151
|
|
|
160
152
|
export type JsonRpcRequest = {
|