@pikku/core 0.12.4 → 0.12.6
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 +21 -1
- package/dist/dev/hot-reload.d.ts +10 -0
- package/dist/dev/hot-reload.js +158 -0
- package/dist/middleware-runner.d.ts +1 -0
- package/dist/middleware-runner.js +5 -0
- package/dist/permissions.d.ts +1 -0
- package/dist/permissions.js +5 -0
- package/dist/services/content-service.d.ts +2 -0
- package/dist/services/in-memory-workflow-service.d.ts +1 -0
- package/dist/services/in-memory-workflow-service.js +16 -11
- package/dist/services/workflow-service.d.ts +1 -0
- package/dist/wirings/ai-agent/ai-agent-prepare.js +16 -1
- package/dist/wirings/channel/channel-middleware-runner.d.ts +1 -0
- package/dist/wirings/channel/channel-middleware-runner.js +5 -0
- package/dist/wirings/http/http-runner.js +1 -1
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +6 -0
- package/dist/wirings/workflow/pikku-workflow-service.js +51 -16
- package/dist/wirings/workflow/workflow.types.d.ts +2 -0
- package/package.json +5 -3
- package/src/crypto-utils.test.ts +214 -0
- package/src/crypto-utils.ts +213 -0
- package/src/dev/hot-reload.test.ts +484 -0
- package/src/dev/hot-reload.ts +212 -0
- package/src/errors/error-handler.ts +62 -0
- package/src/errors/error.test.ts +195 -0
- package/src/errors/errors.ts +374 -0
- package/src/errors/index.ts +2 -0
- package/src/factory-functions.test.ts +109 -0
- package/src/function/function-runner.test.ts +536 -0
- package/src/function/function-runner.ts +365 -0
- package/src/function/functions.types.ts +304 -0
- package/src/function/index.ts +5 -0
- package/src/handle-error.test.ts +424 -0
- package/src/handle-error.ts +69 -0
- package/src/index.ts +118 -0
- package/src/internal.ts +6 -0
- package/src/middleware/auth-apikey.test.ts +363 -0
- package/src/middleware/auth-apikey.ts +47 -0
- package/src/middleware/auth-bearer.test.ts +450 -0
- package/src/middleware/auth-bearer.ts +72 -0
- package/src/middleware/auth-cookie.test.ts +528 -0
- package/src/middleware/auth-cookie.ts +80 -0
- package/src/middleware/cors.test.ts +424 -0
- package/src/middleware/cors.ts +104 -0
- package/src/middleware/index.ts +5 -0
- package/src/middleware/remote-auth.test.ts +488 -0
- package/src/middleware/remote-auth.ts +68 -0
- package/src/middleware/timeout.ts +15 -0
- package/src/middleware-runner.test.ts +418 -0
- package/src/middleware-runner.ts +240 -0
- package/src/permissions.test.ts +434 -0
- package/src/permissions.ts +327 -0
- package/src/pikku-request.ts +23 -0
- package/src/pikku-response.ts +5 -0
- package/src/pikku-state.test.ts +224 -0
- package/src/pikku-state.ts +216 -0
- package/src/run-tests-script.test.ts +49 -0
- package/src/schema.test.ts +249 -0
- package/src/schema.ts +151 -0
- package/src/services/ai-agent-runner-service.ts +41 -0
- package/src/services/ai-run-state-service.ts +20 -0
- package/src/services/ai-storage-service.ts +39 -0
- package/src/services/content-service.ts +81 -0
- package/src/services/deployment-service.ts +22 -0
- package/src/services/gateway-service.ts +20 -0
- package/src/services/gopass-secrets.ts +98 -0
- package/src/services/in-memory-ai-run-state-service.ts +73 -0
- package/src/services/in-memory-trigger-service.ts +64 -0
- package/src/services/in-memory-workflow-service.test.ts +351 -0
- package/src/services/in-memory-workflow-service.ts +512 -0
- package/src/services/index.ts +56 -0
- package/src/services/jwt-service.ts +30 -0
- package/src/services/local-content.ts +120 -0
- package/src/services/local-gateway-service.ts +62 -0
- package/src/services/local-secrets.test.ts +80 -0
- package/src/services/local-secrets.ts +94 -0
- package/src/services/local-variables.test.ts +61 -0
- package/src/services/local-variables.ts +39 -0
- package/src/services/logger-console.test.ts +118 -0
- package/src/services/logger-console.ts +98 -0
- package/src/services/logger.ts +57 -0
- package/src/services/scheduler-service.ts +86 -0
- package/src/services/schema-service.ts +33 -0
- package/src/services/scoped-secret-service.test.ts +74 -0
- package/src/services/scoped-secret-service.ts +41 -0
- package/src/services/secret-service.ts +38 -0
- package/src/services/trigger-service.ts +17 -0
- package/src/services/typed-secret-service.test.ts +93 -0
- package/src/services/typed-secret-service.ts +70 -0
- package/src/services/typed-variables-service.test.ts +73 -0
- package/src/services/typed-variables-service.ts +81 -0
- package/src/services/user-session-service.test.ts +113 -0
- package/src/services/user-session-service.ts +86 -0
- package/src/services/variables-service.ts +11 -0
- package/src/services/workflow-service.ts +96 -0
- package/src/testing/index.ts +2 -0
- package/src/testing/service-tests.ts +873 -0
- package/src/time-utils.test.ts +56 -0
- package/src/time-utils.ts +107 -0
- package/src/types/core.types.ts +478 -0
- package/src/types/state.types.ts +188 -0
- package/src/utils/hash.test.ts +68 -0
- package/src/utils/hash.ts +26 -0
- package/src/utils.test.ts +350 -0
- package/src/utils.ts +129 -0
- package/src/version.test.ts +80 -0
- package/src/version.ts +25 -0
- package/src/wirings/ai-agent/agent-dynamic-workflow.ts +469 -0
- package/src/wirings/ai-agent/ai-agent-helpers.test.ts +152 -0
- package/src/wirings/ai-agent/ai-agent-helpers.ts +76 -0
- package/src/wirings/ai-agent/ai-agent-memory.ts +310 -0
- package/src/wirings/ai-agent/ai-agent-model-config.test.ts +115 -0
- package/src/wirings/ai-agent/ai-agent-model-config.ts +43 -0
- package/src/wirings/ai-agent/ai-agent-prepare.ts +659 -0
- package/src/wirings/ai-agent/ai-agent-registry.test.ts +37 -0
- package/src/wirings/ai-agent/ai-agent-registry.ts +82 -0
- package/src/wirings/ai-agent/ai-agent-runner.test.ts +319 -0
- package/src/wirings/ai-agent/ai-agent-runner.ts +773 -0
- package/src/wirings/ai-agent/ai-agent-stream.test.ts +859 -0
- package/src/wirings/ai-agent/ai-agent-stream.ts +1153 -0
- package/src/wirings/ai-agent/ai-agent.types.ts +382 -0
- package/src/wirings/ai-agent/index.ts +37 -0
- package/src/wirings/channel/channel-common.ts +90 -0
- package/src/wirings/channel/channel-handler.ts +250 -0
- package/src/wirings/channel/channel-middleware-runner.ts +126 -0
- package/src/wirings/channel/channel-runner.ts +166 -0
- package/src/wirings/channel/channel-store.ts +29 -0
- package/src/wirings/channel/channel.types.ts +172 -0
- package/src/wirings/channel/define-channel-routes.ts +25 -0
- package/src/wirings/channel/eventhub-service.ts +34 -0
- package/src/wirings/channel/eventhub-store.ts +13 -0
- package/src/wirings/channel/index.ts +24 -0
- package/src/wirings/channel/local/index.ts +3 -0
- package/src/wirings/channel/local/local-channel-handler.ts +81 -0
- package/src/wirings/channel/local/local-channel-runner.test.ts +215 -0
- package/src/wirings/channel/local/local-channel-runner.ts +210 -0
- package/src/wirings/channel/local/local-eventhub-service.test.ts +95 -0
- package/src/wirings/channel/local/local-eventhub-service.ts +101 -0
- package/src/wirings/channel/log-channels.ts +20 -0
- package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +54 -0
- package/src/wirings/channel/pikku-abstract-channel-handler.ts +45 -0
- package/src/wirings/channel/serverless/index.ts +5 -0
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +289 -0
- package/src/wirings/cli/channel/cli-channel-runner.ts +136 -0
- package/src/wirings/cli/channel/index.ts +1 -0
- package/src/wirings/cli/cli-runner.test.ts +403 -0
- package/src/wirings/cli/cli-runner.ts +529 -0
- package/src/wirings/cli/cli.types.ts +358 -0
- package/src/wirings/cli/command-parser.test.ts +445 -0
- package/src/wirings/cli/command-parser.ts +504 -0
- package/src/wirings/cli/define-cli-commands.ts +24 -0
- package/src/wirings/cli/index.ts +19 -0
- package/src/wirings/gateway/gateway-runner.test.ts +474 -0
- package/src/wirings/gateway/gateway-runner.ts +411 -0
- package/src/wirings/gateway/gateway.types.ts +149 -0
- package/src/wirings/gateway/index.ts +13 -0
- package/src/wirings/http/http-routes.test.ts +322 -0
- package/src/wirings/http/http-routes.ts +197 -0
- package/src/wirings/http/http-runner.test.ts +144 -0
- package/src/wirings/http/http-runner.ts +588 -0
- package/src/wirings/http/http.types.ts +369 -0
- package/src/wirings/http/index.ts +28 -0
- package/src/wirings/http/log-http-routes.ts +22 -0
- package/src/wirings/http/pikku-fetch-http-request.test.ts +237 -0
- package/src/wirings/http/pikku-fetch-http-request.ts +170 -0
- package/src/wirings/http/pikku-fetch-http-response.test.ts +82 -0
- package/src/wirings/http/pikku-fetch-http-response.ts +147 -0
- package/src/wirings/http/routers/http-router.ts +13 -0
- package/src/wirings/http/routers/path-to-regex.test.ts +319 -0
- package/src/wirings/http/routers/path-to-regex.ts +126 -0
- package/src/wirings/http/web-request.test.ts +236 -0
- package/src/wirings/http/web-request.ts +104 -0
- package/src/wirings/mcp/index.ts +27 -0
- package/src/wirings/mcp/mcp-endpoint-registry.test.ts +389 -0
- package/src/wirings/mcp/mcp-endpoint-registry.ts +159 -0
- package/src/wirings/mcp/mcp-runner.ts +323 -0
- package/src/wirings/mcp/mcp.types.ts +216 -0
- package/src/wirings/node/index.ts +2 -0
- package/src/wirings/node/node.types.ts +23 -0
- package/src/wirings/oauth2/index.ts +3 -0
- package/src/wirings/oauth2/oauth2-client.test.ts +929 -0
- package/src/wirings/oauth2/oauth2-client.ts +335 -0
- package/src/wirings/oauth2/oauth2.types.ts +69 -0
- package/src/wirings/oauth2/wire-oauth2-credential.ts +23 -0
- package/src/wirings/queue/index.ts +31 -0
- package/src/wirings/queue/queue-runner.test.ts +710 -0
- package/src/wirings/queue/queue-runner.ts +192 -0
- package/src/wirings/queue/queue.types.ts +189 -0
- package/src/wirings/queue/register-queue-helper.ts +60 -0
- package/src/wirings/queue/validate-worker-config.test.ts +108 -0
- package/src/wirings/queue/validate-worker-config.ts +116 -0
- package/src/wirings/rpc/index.ts +4 -0
- package/src/wirings/rpc/rpc-runner.ts +460 -0
- package/src/wirings/rpc/rpc-types.ts +56 -0
- package/src/wirings/rpc/wire-addon.ts +21 -0
- package/src/wirings/scheduler/index.ts +11 -0
- package/src/wirings/scheduler/log-schedulers.ts +20 -0
- package/src/wirings/scheduler/scheduler-runner.test.ts +660 -0
- package/src/wirings/scheduler/scheduler-runner.ts +133 -0
- package/src/wirings/scheduler/scheduler.types.ts +53 -0
- package/src/wirings/secret/index.ts +9 -0
- package/src/wirings/secret/secret.types.ts +32 -0
- package/src/wirings/secret/validate-secret-definitions.test.ts +140 -0
- package/src/wirings/secret/validate-secret-definitions.ts +82 -0
- package/src/wirings/trigger/index.ts +10 -0
- package/src/wirings/trigger/pikku-trigger-service.ts +112 -0
- package/src/wirings/trigger/trigger-runner.test.ts +79 -0
- package/src/wirings/trigger/trigger-runner.ts +135 -0
- package/src/wirings/trigger/trigger.types.ts +178 -0
- package/src/wirings/variable/index.ts +8 -0
- package/src/wirings/variable/validate-variable-definitions.test.ts +91 -0
- package/src/wirings/variable/validate-variable-definitions.ts +69 -0
- package/src/wirings/variable/variable.types.ts +22 -0
- package/src/wirings/workflow/dsl/index.ts +31 -0
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +320 -0
- package/src/wirings/workflow/dsl/workflow-runner.ts +28 -0
- package/src/wirings/workflow/graph/graph-node.ts +222 -0
- package/src/wirings/workflow/graph/graph-runner.test.ts +487 -0
- package/src/wirings/workflow/graph/graph-runner.ts +816 -0
- package/src/wirings/workflow/graph/graph-validation.test.ts +170 -0
- package/src/wirings/workflow/graph/graph-validation.ts +237 -0
- package/src/wirings/workflow/graph/index.ts +19 -0
- package/src/wirings/workflow/graph/template.test.ts +49 -0
- package/src/wirings/workflow/graph/template.ts +42 -0
- package/src/wirings/workflow/graph/wire-workflow-graph.ts +29 -0
- package/src/wirings/workflow/graph/workflow-graph.types.ts +104 -0
- package/src/wirings/workflow/index.ts +82 -0
- package/src/wirings/workflow/pikku-workflow-service.test.ts +200 -0
- package/src/wirings/workflow/pikku-workflow-service.ts +1229 -0
- package/src/wirings/workflow/workflow-helpers.test.ts +129 -0
- package/src/wirings/workflow/workflow-helpers.ts +79 -0
- package/src/wirings/workflow/workflow.types.ts +276 -0
- package/tsconfig.json +14 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/lcov.info +0 -18891
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
import { pikkuState } from '../../pikku-state.js'
|
|
2
|
+
import { addFunction } from '../../function/function-runner.js'
|
|
3
|
+
import { runMiddleware } from '../../middleware-runner.js'
|
|
4
|
+
import { httpRouter } from '../http/routers/http-router.js'
|
|
5
|
+
import type {
|
|
6
|
+
CoreGateway,
|
|
7
|
+
GatewayAdapter,
|
|
8
|
+
GatewayOutboundMessage,
|
|
9
|
+
PikkuGateway,
|
|
10
|
+
} from './gateway.types.js'
|
|
11
|
+
import type {
|
|
12
|
+
PikkuWire,
|
|
13
|
+
CorePikkuMiddleware,
|
|
14
|
+
CoreSingletonServices,
|
|
15
|
+
} from '../../types/core.types.js'
|
|
16
|
+
import type { HTTPWiringMeta } from '../http/http.types.js'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Register a messaging gateway.
|
|
20
|
+
*
|
|
21
|
+
* `wireGateway` is a meta-wiring that composes existing primitives
|
|
22
|
+
* (`wireHTTP`, `wireChannel`) under the hood. It does not replace them;
|
|
23
|
+
* it layers on top.
|
|
24
|
+
*
|
|
25
|
+
* @param config - Gateway configuration (name, type, adapter, func, middleware, etc.)
|
|
26
|
+
*/
|
|
27
|
+
export const wireGateway = (config: CoreGateway): void => {
|
|
28
|
+
// Store gateway config
|
|
29
|
+
pikkuState(null, 'gateway', 'gateways').set(config.name, config)
|
|
30
|
+
|
|
31
|
+
switch (config.type) {
|
|
32
|
+
case 'webhook':
|
|
33
|
+
wireWebhookGateway(config)
|
|
34
|
+
break
|
|
35
|
+
case 'websocket':
|
|
36
|
+
wireWebsocketGateway(config)
|
|
37
|
+
break
|
|
38
|
+
case 'listener':
|
|
39
|
+
wireListenerGateway(config)
|
|
40
|
+
break
|
|
41
|
+
default:
|
|
42
|
+
throw new Error(
|
|
43
|
+
`Unknown gateway type '${(config as any).type}' for gateway '${config.name}'`
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// Webhook gateway — platform POSTs to us
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
const wireWebhookGateway = (config: CoreGateway): void => {
|
|
53
|
+
const { name, route, adapter } = config
|
|
54
|
+
if (!route) {
|
|
55
|
+
throw new Error(`Webhook gateway '${name}' requires a route`)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const postFuncId = `gateway__${name}__post`
|
|
59
|
+
const httpMeta = pikkuState(null, 'http', 'meta')
|
|
60
|
+
const funcMeta = pikkuState(null, 'function', 'meta')
|
|
61
|
+
const routes = pikkuState(null, 'http', 'routes')
|
|
62
|
+
|
|
63
|
+
// --- POST handler (main message receiver) --------------------------------
|
|
64
|
+
|
|
65
|
+
funcMeta[postFuncId] = {
|
|
66
|
+
pikkuFuncId: postFuncId,
|
|
67
|
+
inputSchemaName: null,
|
|
68
|
+
outputSchemaName: null,
|
|
69
|
+
sessionless: true,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
httpMeta['post'][route] = {
|
|
73
|
+
pikkuFuncId: postFuncId,
|
|
74
|
+
route,
|
|
75
|
+
method: 'post',
|
|
76
|
+
} as HTTPWiringMeta
|
|
77
|
+
|
|
78
|
+
const postHandler = {
|
|
79
|
+
auth: false,
|
|
80
|
+
func: createWebhookPostHandler(config),
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
addFunction(postFuncId, postHandler as any)
|
|
84
|
+
|
|
85
|
+
if (!routes.has('post')) {
|
|
86
|
+
routes.set('post', new Map())
|
|
87
|
+
}
|
|
88
|
+
routes.get('post')!.set(route, {
|
|
89
|
+
method: 'post',
|
|
90
|
+
route,
|
|
91
|
+
func: postHandler,
|
|
92
|
+
auth: false,
|
|
93
|
+
} as any)
|
|
94
|
+
|
|
95
|
+
// --- GET handler (webhook verification, e.g. WhatsApp challenge) ---------
|
|
96
|
+
|
|
97
|
+
if (adapter.verifyWebhook) {
|
|
98
|
+
const verifyFuncId = `gateway__${name}__verify`
|
|
99
|
+
|
|
100
|
+
funcMeta[verifyFuncId] = {
|
|
101
|
+
pikkuFuncId: verifyFuncId,
|
|
102
|
+
inputSchemaName: null,
|
|
103
|
+
outputSchemaName: null,
|
|
104
|
+
sessionless: true,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
httpMeta['get'][route] = {
|
|
108
|
+
pikkuFuncId: verifyFuncId,
|
|
109
|
+
route,
|
|
110
|
+
method: 'get',
|
|
111
|
+
} as HTTPWiringMeta
|
|
112
|
+
|
|
113
|
+
const verifyHandler = {
|
|
114
|
+
auth: false,
|
|
115
|
+
func: createWebhookVerifyHandler(adapter),
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
addFunction(verifyFuncId, verifyHandler as any)
|
|
119
|
+
|
|
120
|
+
if (!routes.has('get')) {
|
|
121
|
+
routes.set('get', new Map())
|
|
122
|
+
}
|
|
123
|
+
routes.get('get')!.set(route, {
|
|
124
|
+
method: 'get',
|
|
125
|
+
route,
|
|
126
|
+
func: verifyHandler,
|
|
127
|
+
auth: false,
|
|
128
|
+
} as any)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Force router to re-initialize on next match
|
|
132
|
+
httpRouter.reset()
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Creates the POST handler function for a webhook gateway.
|
|
137
|
+
*
|
|
138
|
+
* Flow:
|
|
139
|
+
* 1. Check for POST-based verification (e.g. Slack `url_verification`)
|
|
140
|
+
* 2. Parse body via adapter → GatewayInboundMessage (or null to ignore)
|
|
141
|
+
* 3. Populate `wire.gateway`
|
|
142
|
+
* 4. Run user middleware (which can read `wire.gateway` for auth)
|
|
143
|
+
* 5. Call user func with parsed message
|
|
144
|
+
* 6. Auto-send response via adapter if func returns outbound content
|
|
145
|
+
*/
|
|
146
|
+
const createWebhookPostHandler = (config: CoreGateway) => {
|
|
147
|
+
const { name, adapter, func: userFunc, middleware: userMiddleware } = config
|
|
148
|
+
const userFuncConfig = userFunc as {
|
|
149
|
+
func: Function
|
|
150
|
+
middleware?: CorePikkuMiddleware[]
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return async (
|
|
154
|
+
services: CoreSingletonServices,
|
|
155
|
+
data: unknown,
|
|
156
|
+
wire: PikkuWire
|
|
157
|
+
) => {
|
|
158
|
+
// Check for POST-based webhook verification (e.g. Slack url_verification)
|
|
159
|
+
if (adapter.verifyWebhook) {
|
|
160
|
+
const verifyResult = await adapter.verifyWebhook(data, wire.http?.request)
|
|
161
|
+
if (verifyResult.verified) {
|
|
162
|
+
return verifyResult.response
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Parse the platform-specific payload
|
|
167
|
+
const parsed = adapter.parse(data)
|
|
168
|
+
if (!parsed) {
|
|
169
|
+
// Ignored event (delivery receipt, typing indicator, etc.)
|
|
170
|
+
return { ok: true }
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Populate wire.gateway
|
|
174
|
+
const gateway: PikkuGateway = {
|
|
175
|
+
gatewayName: name,
|
|
176
|
+
senderId: parsed.senderId,
|
|
177
|
+
platform: adapter.name,
|
|
178
|
+
send: (msg: GatewayOutboundMessage) => adapter.send(parsed.senderId, msg),
|
|
179
|
+
}
|
|
180
|
+
;(wire as any).gateway = gateway
|
|
181
|
+
|
|
182
|
+
// Build combined middleware chain: gateway-level + func-level
|
|
183
|
+
const allMiddleware: CorePikkuMiddleware[] = [
|
|
184
|
+
...((userMiddleware as CorePikkuMiddleware[] | undefined) || []),
|
|
185
|
+
...(userFuncConfig.middleware || []),
|
|
186
|
+
]
|
|
187
|
+
|
|
188
|
+
const exec = async () => {
|
|
189
|
+
const result = await userFuncConfig.func(services, parsed, wire)
|
|
190
|
+
// Auto-send response if the func returns outbound content
|
|
191
|
+
if (result && (result.text || result.richContent || result.attachments)) {
|
|
192
|
+
await adapter.send(parsed.senderId, result as GatewayOutboundMessage)
|
|
193
|
+
}
|
|
194
|
+
return { ok: true }
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (allMiddleware.length > 0) {
|
|
198
|
+
return await runMiddleware(services, wire, allMiddleware, exec)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return await exec()
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Creates the GET handler for webhook verification challenges.
|
|
207
|
+
* Passes query parameters to the adapter's verifyWebhook method.
|
|
208
|
+
*/
|
|
209
|
+
const createWebhookVerifyHandler = (adapter: GatewayAdapter) => {
|
|
210
|
+
return async (
|
|
211
|
+
_services: CoreSingletonServices,
|
|
212
|
+
_data: unknown,
|
|
213
|
+
wire: PikkuWire
|
|
214
|
+
) => {
|
|
215
|
+
if (!adapter.verifyWebhook) {
|
|
216
|
+
return { error: 'Verification not supported' }
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const query = wire.http?.request?.query()
|
|
220
|
+
const result = await adapter.verifyWebhook(query, wire.http?.request)
|
|
221
|
+
if (result.verified) {
|
|
222
|
+
return result.response
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return { error: 'Verification failed' }
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ---------------------------------------------------------------------------
|
|
230
|
+
// WebSocket gateway — client connects via WebSocket
|
|
231
|
+
// ---------------------------------------------------------------------------
|
|
232
|
+
|
|
233
|
+
const wireWebsocketGateway = (config: CoreGateway): void => {
|
|
234
|
+
const { name, route, adapter } = config
|
|
235
|
+
if (!route) {
|
|
236
|
+
throw new Error(`WebSocket gateway '${name}' requires a route`)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Store gateway config — the channel runner will reference this
|
|
240
|
+
pikkuState(null, 'gateway', 'gateways').set(config.name, config)
|
|
241
|
+
|
|
242
|
+
const channelsMeta = pikkuState(null, 'channel', 'meta')
|
|
243
|
+
const channels = pikkuState(null, 'channel', 'channels')
|
|
244
|
+
|
|
245
|
+
const messageFuncId = `gateway__${name}__message`
|
|
246
|
+
const connectFuncId = `gateway__${name}__connect`
|
|
247
|
+
|
|
248
|
+
// Add function metadata
|
|
249
|
+
const funcMeta = pikkuState(null, 'function', 'meta')
|
|
250
|
+
funcMeta[messageFuncId] = {
|
|
251
|
+
pikkuFuncId: messageFuncId,
|
|
252
|
+
inputSchemaName: null,
|
|
253
|
+
outputSchemaName: null,
|
|
254
|
+
sessionless: true,
|
|
255
|
+
}
|
|
256
|
+
funcMeta[connectFuncId] = {
|
|
257
|
+
pikkuFuncId: connectFuncId,
|
|
258
|
+
inputSchemaName: null,
|
|
259
|
+
outputSchemaName: null,
|
|
260
|
+
sessionless: true,
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Add channel metadata
|
|
264
|
+
channelsMeta[name] = {
|
|
265
|
+
name,
|
|
266
|
+
route,
|
|
267
|
+
gateway: true,
|
|
268
|
+
connect: { pikkuFuncId: connectFuncId },
|
|
269
|
+
message: { pikkuFuncId: messageFuncId },
|
|
270
|
+
} as any
|
|
271
|
+
|
|
272
|
+
const userFuncConfig = config.func as {
|
|
273
|
+
func: Function
|
|
274
|
+
middleware?: CorePikkuMiddleware[]
|
|
275
|
+
}
|
|
276
|
+
const userMiddleware = config.middleware as CorePikkuMiddleware[] | undefined
|
|
277
|
+
|
|
278
|
+
// Register onConnect
|
|
279
|
+
addFunction(connectFuncId, {
|
|
280
|
+
auth: false,
|
|
281
|
+
func: async (_services: any, _data: unknown, wire: PikkuWire) => {
|
|
282
|
+
;(wire as any).gateway = {
|
|
283
|
+
gatewayName: name,
|
|
284
|
+
senderId: '',
|
|
285
|
+
platform: adapter.name,
|
|
286
|
+
send: async (msg: GatewayOutboundMessage) => {
|
|
287
|
+
wire.channel?.send(msg)
|
|
288
|
+
},
|
|
289
|
+
} satisfies PikkuGateway
|
|
290
|
+
},
|
|
291
|
+
} as any)
|
|
292
|
+
|
|
293
|
+
// Register onMessage
|
|
294
|
+
addFunction(messageFuncId, {
|
|
295
|
+
auth: false,
|
|
296
|
+
func: async (services: any, data: unknown, wire: PikkuWire) => {
|
|
297
|
+
const parsed = adapter.parse(data)
|
|
298
|
+
if (!parsed) return
|
|
299
|
+
|
|
300
|
+
const gateway: PikkuGateway = {
|
|
301
|
+
gatewayName: name,
|
|
302
|
+
senderId: parsed.senderId,
|
|
303
|
+
platform: adapter.name,
|
|
304
|
+
send: async (msg: GatewayOutboundMessage) => {
|
|
305
|
+
wire.channel?.send(msg)
|
|
306
|
+
},
|
|
307
|
+
}
|
|
308
|
+
;(wire as any).gateway = gateway
|
|
309
|
+
|
|
310
|
+
const allMiddleware: CorePikkuMiddleware[] = [
|
|
311
|
+
...(userMiddleware || []),
|
|
312
|
+
...(userFuncConfig.middleware || []),
|
|
313
|
+
]
|
|
314
|
+
|
|
315
|
+
const exec = async () => {
|
|
316
|
+
const result = await userFuncConfig.func(services, parsed, wire)
|
|
317
|
+
if (
|
|
318
|
+
result &&
|
|
319
|
+
(result.text || result.richContent || result.attachments)
|
|
320
|
+
) {
|
|
321
|
+
wire.channel?.send(result)
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (allMiddleware.length > 0) {
|
|
326
|
+
await runMiddleware(services, wire, allMiddleware, exec)
|
|
327
|
+
} else {
|
|
328
|
+
await exec()
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
} as any)
|
|
332
|
+
|
|
333
|
+
// Store channel config
|
|
334
|
+
channels.set(name, {
|
|
335
|
+
name,
|
|
336
|
+
route,
|
|
337
|
+
auth: false,
|
|
338
|
+
onConnect: { func: async () => {} },
|
|
339
|
+
onMessage: { func: async () => {} },
|
|
340
|
+
} as any)
|
|
341
|
+
|
|
342
|
+
// Force router to re-initialize
|
|
343
|
+
httpRouter.reset()
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// ---------------------------------------------------------------------------
|
|
347
|
+
// Listener gateway — standalone event loop, no route
|
|
348
|
+
// ---------------------------------------------------------------------------
|
|
349
|
+
|
|
350
|
+
const wireListenerGateway = (config: CoreGateway): void => {
|
|
351
|
+
// Listener gateways don't register routes.
|
|
352
|
+
// They are started manually and call into the gateway's func directly.
|
|
353
|
+
// The gateway config is already stored in pikkuState.
|
|
354
|
+
pikkuState(null, 'gateway', 'gateways').set(config.name, config)
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Create the message handler callback for a listener gateway.
|
|
359
|
+
*
|
|
360
|
+
* Returns a function `(rawData: unknown) => Promise<void>` that can be
|
|
361
|
+
* passed to `adapter.init()`. Used by `LocalGatewayService` (and any
|
|
362
|
+
* other GatewayService implementation) to wire up listener gateways.
|
|
363
|
+
*
|
|
364
|
+
* @param name - Gateway name (for wire.gateway metadata)
|
|
365
|
+
* @param config - The gateway configuration (adapter, func, middleware)
|
|
366
|
+
* @param singletonServices - Singleton services to pass to handler/middleware
|
|
367
|
+
*/
|
|
368
|
+
export const createListenerMessageHandler = (
|
|
369
|
+
name: string,
|
|
370
|
+
config: CoreGateway,
|
|
371
|
+
singletonServices: CoreSingletonServices
|
|
372
|
+
): ((rawData: unknown) => Promise<void>) => {
|
|
373
|
+
const { adapter } = config
|
|
374
|
+
const userFuncConfig = config.func as {
|
|
375
|
+
func: Function
|
|
376
|
+
middleware?: CorePikkuMiddleware[]
|
|
377
|
+
}
|
|
378
|
+
const userMiddleware = config.middleware as CorePikkuMiddleware[] | undefined
|
|
379
|
+
|
|
380
|
+
return async (rawData: unknown): Promise<void> => {
|
|
381
|
+
const parsed = adapter.parse(rawData)
|
|
382
|
+
if (!parsed) return
|
|
383
|
+
|
|
384
|
+
const wire: PikkuWire = {}
|
|
385
|
+
const gateway: PikkuGateway = {
|
|
386
|
+
gatewayName: name,
|
|
387
|
+
senderId: parsed.senderId,
|
|
388
|
+
platform: adapter.name,
|
|
389
|
+
send: (msg: GatewayOutboundMessage) => adapter.send(parsed.senderId, msg),
|
|
390
|
+
}
|
|
391
|
+
;(wire as any).gateway = gateway
|
|
392
|
+
|
|
393
|
+
const allMiddleware: CorePikkuMiddleware[] = [
|
|
394
|
+
...(userMiddleware || []),
|
|
395
|
+
...(userFuncConfig.middleware || []),
|
|
396
|
+
]
|
|
397
|
+
|
|
398
|
+
const exec = async () => {
|
|
399
|
+
const result = await userFuncConfig.func(singletonServices, parsed, wire)
|
|
400
|
+
if (result && (result.text || result.richContent || result.attachments)) {
|
|
401
|
+
await adapter.send(parsed.senderId, result as GatewayOutboundMessage)
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (allMiddleware.length > 0) {
|
|
406
|
+
await runMiddleware(singletonServices, wire, allMiddleware, exec)
|
|
407
|
+
} else {
|
|
408
|
+
await exec()
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CommonWireMeta,
|
|
3
|
+
CorePikkuMiddleware,
|
|
4
|
+
CorePikkuMiddlewareGroup,
|
|
5
|
+
} from '../../types/core.types.js'
|
|
6
|
+
import type {
|
|
7
|
+
CorePikkuFunctionConfig,
|
|
8
|
+
CorePermissionGroup,
|
|
9
|
+
CorePikkuPermission,
|
|
10
|
+
} from '../../function/functions.types.js'
|
|
11
|
+
import type { PikkuHTTPRequest } from '../http/http.types.js'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Attachment in gateway messages (images, files, etc.)
|
|
15
|
+
*/
|
|
16
|
+
export interface GatewayAttachment {
|
|
17
|
+
type: string
|
|
18
|
+
url?: string
|
|
19
|
+
data?: ArrayBuffer | Uint8Array
|
|
20
|
+
mimeType?: string
|
|
21
|
+
filename?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Normalized inbound message from any platform
|
|
26
|
+
*/
|
|
27
|
+
export interface GatewayInboundMessage {
|
|
28
|
+
/** Platform-specific sender identifier (phone number, Slack user ID, etc.) */
|
|
29
|
+
senderId: string
|
|
30
|
+
/** Message text content */
|
|
31
|
+
text: string
|
|
32
|
+
/** Original platform-specific payload */
|
|
33
|
+
raw: unknown
|
|
34
|
+
/** Optional file/media attachments */
|
|
35
|
+
attachments?: GatewayAttachment[]
|
|
36
|
+
/** Optional platform-specific metadata */
|
|
37
|
+
metadata?: Record<string, unknown>
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Outbound message to send back via the platform
|
|
42
|
+
*/
|
|
43
|
+
export interface GatewayOutboundMessage {
|
|
44
|
+
/** Plain text response */
|
|
45
|
+
text?: string
|
|
46
|
+
/** Platform-specific rich content (Slack blocks, WhatsApp templates, etc.) */
|
|
47
|
+
richContent?: Record<string, unknown>
|
|
48
|
+
/** Optional file/media attachments */
|
|
49
|
+
attachments?: GatewayAttachment[]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Result of webhook verification (e.g., WhatsApp GET challenge, Slack url_verification)
|
|
54
|
+
*/
|
|
55
|
+
export type WebhookVerificationResult =
|
|
56
|
+
| { verified: true; response: unknown }
|
|
57
|
+
| { verified: false }
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Gateway adapter interface — implemented by platform-specific addon packages.
|
|
61
|
+
*
|
|
62
|
+
* Two responsibilities:
|
|
63
|
+
* 1. Parse incoming platform-specific format into normalized GatewayInboundMessage
|
|
64
|
+
* 2. Send outgoing GatewayOutboundMessage via the platform's API
|
|
65
|
+
*/
|
|
66
|
+
export interface GatewayAdapter {
|
|
67
|
+
/** Platform name (e.g., 'whatsapp', 'slack', 'telegram') */
|
|
68
|
+
name: string
|
|
69
|
+
/** Parse platform-specific payload into normalized message. Return null to ignore (e.g., delivery receipts). */
|
|
70
|
+
parse(data: unknown): GatewayInboundMessage | null
|
|
71
|
+
/** Send a message to a specific sender via the platform's API */
|
|
72
|
+
send(senderId: string, message: GatewayOutboundMessage): Promise<void>
|
|
73
|
+
/** Initialize the adapter (connect to platform, start listening).
|
|
74
|
+
* Called by GatewayService.start() — the adapter should call onMessage for each incoming event. */
|
|
75
|
+
init(onMessage: (data: unknown) => Promise<void>): Promise<void>
|
|
76
|
+
/** Tear down the adapter (disconnect, release resources). */
|
|
77
|
+
close(): Promise<void>
|
|
78
|
+
/** Handle webhook verification challenges (only for webhook type).
|
|
79
|
+
* Receives the data (body or query params) and the Pikku HTTP request for additional inspection. */
|
|
80
|
+
verifyWebhook?(
|
|
81
|
+
data: unknown,
|
|
82
|
+
request?: PikkuHTTPRequest
|
|
83
|
+
): WebhookVerificationResult | Promise<WebhookVerificationResult>
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The gateway wire object available on wire.gateway inside handler functions and middleware
|
|
88
|
+
*/
|
|
89
|
+
export interface PikkuGateway {
|
|
90
|
+
/** Name of this gateway instance */
|
|
91
|
+
gatewayName: string
|
|
92
|
+
/** Platform-specific sender identifier */
|
|
93
|
+
senderId: string
|
|
94
|
+
/** Platform name from adapter.name */
|
|
95
|
+
platform: string
|
|
96
|
+
/** Send a proactive message to the sender */
|
|
97
|
+
send(msg: GatewayOutboundMessage): Promise<void>
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Gateway transport type:
|
|
102
|
+
* - 'webhook': Platform POSTs to us (WhatsApp Cloud API, Slack Events API, Telegram webhooks)
|
|
103
|
+
* - 'websocket': Client connects via WebSocket (WebChat, browser widget)
|
|
104
|
+
* - 'listener': Standalone event loop, no route (Baileys, Signal CLI, Matrix sync)
|
|
105
|
+
*/
|
|
106
|
+
export type GatewayTransportType = 'webhook' | 'websocket' | 'listener'
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Core gateway configuration for wireGateway()
|
|
110
|
+
*/
|
|
111
|
+
export type CoreGateway<
|
|
112
|
+
PikkuFunctionConfig = CorePikkuFunctionConfig<any, any>,
|
|
113
|
+
PikkuPermission extends CorePikkuPermission = CorePikkuPermission,
|
|
114
|
+
PikkuMiddleware extends CorePikkuMiddleware = CorePikkuMiddleware,
|
|
115
|
+
> = {
|
|
116
|
+
/** Unique name for this gateway */
|
|
117
|
+
name: string
|
|
118
|
+
/** Transport type */
|
|
119
|
+
type: GatewayTransportType
|
|
120
|
+
/** HTTP route for webhook/websocket types */
|
|
121
|
+
route?: string
|
|
122
|
+
/** The gateway adapter (parse inbound, send outbound) */
|
|
123
|
+
adapter: GatewayAdapter
|
|
124
|
+
/** The handler function that processes parsed messages */
|
|
125
|
+
func: PikkuFunctionConfig
|
|
126
|
+
/** Optional middleware chain (e.g., auth) */
|
|
127
|
+
middleware?: CorePikkuMiddlewareGroup<any, any>
|
|
128
|
+
/** Optional permissions */
|
|
129
|
+
permissions?: CorePermissionGroup | PikkuPermission[]
|
|
130
|
+
/** Optional tags for categorization */
|
|
131
|
+
tags?: string[]
|
|
132
|
+
/** Whether authentication is required (default: true) */
|
|
133
|
+
auth?: boolean
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Metadata for a registered gateway, stored in state
|
|
138
|
+
*/
|
|
139
|
+
export type GatewayMeta = CommonWireMeta & {
|
|
140
|
+
name: string
|
|
141
|
+
type: GatewayTransportType
|
|
142
|
+
route?: string
|
|
143
|
+
gateway: true
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* All gateway metadata keyed by name
|
|
148
|
+
*/
|
|
149
|
+
export type GatewaysMeta = Record<string, GatewayMeta>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { wireGateway, createListenerMessageHandler } from './gateway-runner.js'
|
|
2
|
+
export type {
|
|
3
|
+
GatewayAdapter,
|
|
4
|
+
GatewayAttachment,
|
|
5
|
+
GatewayInboundMessage,
|
|
6
|
+
GatewayOutboundMessage,
|
|
7
|
+
GatewayMeta,
|
|
8
|
+
GatewaysMeta,
|
|
9
|
+
GatewayTransportType,
|
|
10
|
+
CoreGateway,
|
|
11
|
+
PikkuGateway,
|
|
12
|
+
WebhookVerificationResult,
|
|
13
|
+
} from './gateway.types.js'
|