@pikku/core 0.12.4 → 0.12.5
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 +11 -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/in-memory-workflow-service.js +7 -11
- 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/workflow/pikku-workflow-service.js +7 -8
- package/package.json +3 -2
- 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 +76 -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 +502 -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 +95 -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 +1179 -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 +274 -0
- package/tsconfig.json +14 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/lcov.info +0 -18891
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
import type { CoreServices, PikkuWire } from '../../types/core.types.js'
|
|
2
|
+
import type { SessionService } from '../../services/user-session-service.js'
|
|
3
|
+
import type { CoreUserSession } from '../../types/core.types.js'
|
|
4
|
+
import { runPikkuFunc } from '../../function/function-runner.js'
|
|
5
|
+
import { pikkuState } from '../../pikku-state.js'
|
|
6
|
+
import { ForbiddenError } from '../../errors/errors.js'
|
|
7
|
+
import { PikkuError, addError } from '../../errors/error-handler.js'
|
|
8
|
+
import type { PikkuRPC, ResolvedFunction } from './rpc-types.js'
|
|
9
|
+
import { parseVersionedId } from '../../version.js'
|
|
10
|
+
import { encryptJSON } from '../../crypto-utils.js'
|
|
11
|
+
|
|
12
|
+
export class RPCNotFoundError extends PikkuError {
|
|
13
|
+
public readonly rpcName: string
|
|
14
|
+
constructor(rpcName: string) {
|
|
15
|
+
super(`RPC function not found: ${rpcName}`)
|
|
16
|
+
this.rpcName = rpcName
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
addError(RPCNotFoundError, {
|
|
20
|
+
status: 404,
|
|
21
|
+
mcpCode: -32601,
|
|
22
|
+
message: 'RPC function not found.',
|
|
23
|
+
})
|
|
24
|
+
import type { AIAgentInput } from '../ai-agent/ai-agent.types.js'
|
|
25
|
+
import type { AIStreamChannel } from '../ai-agent/ai-agent.types.js'
|
|
26
|
+
import type { StreamAIAgentOptions } from '../ai-agent/ai-agent-prepare.js'
|
|
27
|
+
import { runAIAgent, resumeAIAgentSync } from '../ai-agent/ai-agent-runner.js'
|
|
28
|
+
import { streamAIAgent, resumeAIAgent } from '../ai-agent/ai-agent-stream.js'
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Resolve a namespaced function reference to package and function names
|
|
32
|
+
* Uses pikkuState to look up the namespace -> package mapping
|
|
33
|
+
*/
|
|
34
|
+
export const resolveNamespace = (
|
|
35
|
+
namespacedFunction: string
|
|
36
|
+
): ResolvedFunction | null => {
|
|
37
|
+
const colonIndex = namespacedFunction.indexOf(':')
|
|
38
|
+
if (colonIndex === -1) {
|
|
39
|
+
return null
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const namespace = namespacedFunction.substring(0, colonIndex)
|
|
43
|
+
const functionName = namespacedFunction.substring(colonIndex + 1)
|
|
44
|
+
|
|
45
|
+
const addons = pikkuState(null, 'addons', 'packages')
|
|
46
|
+
const pkgConfig = addons.get(namespace)
|
|
47
|
+
if (!pkgConfig) {
|
|
48
|
+
return null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
package: pkgConfig.package,
|
|
53
|
+
function: functionName,
|
|
54
|
+
addonConfig: pkgConfig,
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const getPikkuFunctionName = (rpcName: string): string => {
|
|
59
|
+
const rpc = pikkuState(null, 'rpc', 'meta')
|
|
60
|
+
let rpcMeta = rpc[rpcName]
|
|
61
|
+
if (!rpcMeta) {
|
|
62
|
+
const { baseName, version } = parseVersionedId(rpcName)
|
|
63
|
+
if (version !== null) {
|
|
64
|
+
rpcMeta = rpc[baseName]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (!rpcMeta) {
|
|
68
|
+
throw new RPCNotFoundError(rpcName)
|
|
69
|
+
}
|
|
70
|
+
return rpcMeta
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Context-aware RPC client for use within services
|
|
74
|
+
export class ContextAwareRPCService {
|
|
75
|
+
constructor(
|
|
76
|
+
private services: CoreServices,
|
|
77
|
+
private wire: PikkuWire,
|
|
78
|
+
private options: {
|
|
79
|
+
requiresAuth?: boolean
|
|
80
|
+
sessionService?: SessionService<CoreUserSession>
|
|
81
|
+
}
|
|
82
|
+
) {}
|
|
83
|
+
|
|
84
|
+
public async rpcExposed(funcName: string, data: any): Promise<any> {
|
|
85
|
+
let functionMeta: any
|
|
86
|
+
if (funcName.includes(':')) {
|
|
87
|
+
const resolved = resolveNamespace(funcName)
|
|
88
|
+
if (resolved) {
|
|
89
|
+
functionMeta = pikkuState(resolved.package, 'function', 'meta')[
|
|
90
|
+
resolved.function
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
functionMeta = pikkuState(null, 'function', 'meta')[funcName]
|
|
95
|
+
}
|
|
96
|
+
if (!functionMeta) {
|
|
97
|
+
throw new RPCNotFoundError(funcName)
|
|
98
|
+
}
|
|
99
|
+
if (!functionMeta.expose) {
|
|
100
|
+
throw new ForbiddenError()
|
|
101
|
+
}
|
|
102
|
+
return await this.rpc(funcName, data)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public async rpc<In = any, Out = any>(
|
|
106
|
+
funcName: string,
|
|
107
|
+
data: In
|
|
108
|
+
): Promise<Out> {
|
|
109
|
+
const rpcDepth = this.wire.rpc?.depth || 0
|
|
110
|
+
const updatedWire: PikkuWire = {
|
|
111
|
+
...this.wire,
|
|
112
|
+
rpc: this.wire.rpc
|
|
113
|
+
? {
|
|
114
|
+
...this.wire.rpc,
|
|
115
|
+
depth: rpcDepth + 1,
|
|
116
|
+
global: false,
|
|
117
|
+
}
|
|
118
|
+
: undefined,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Check if it's a namespaced function call (e.g., 'stripe:createCharge')
|
|
122
|
+
if (funcName.includes(':')) {
|
|
123
|
+
return this.invokeAddonFunction<In, Out>(funcName, data, updatedWire)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Main package function
|
|
127
|
+
return runPikkuFunc<In, Out>(
|
|
128
|
+
'rpc',
|
|
129
|
+
funcName,
|
|
130
|
+
getPikkuFunctionName(funcName),
|
|
131
|
+
{
|
|
132
|
+
auth: this.options.requiresAuth,
|
|
133
|
+
singletonServices: this.services,
|
|
134
|
+
data: () => data,
|
|
135
|
+
wire: updatedWire,
|
|
136
|
+
}
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Invoke a function from an addon package
|
|
142
|
+
* Addon packages register their functions in pikkuState under their package name.
|
|
143
|
+
* The function is executed using the parent services (shared singleton services).
|
|
144
|
+
* @private
|
|
145
|
+
*/
|
|
146
|
+
private async invokeAddonFunction<In = any, Out = any>(
|
|
147
|
+
namespacedFunction: string,
|
|
148
|
+
data: In,
|
|
149
|
+
wire: PikkuWire
|
|
150
|
+
): Promise<Out> {
|
|
151
|
+
// Resolve namespace to package name
|
|
152
|
+
const resolved = resolveNamespace(namespacedFunction)
|
|
153
|
+
if (!resolved) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`Unknown namespace in function reference: ${namespacedFunction}. ` +
|
|
156
|
+
`Make sure the package is registered in addons config.`
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Get the function meta from the addon package
|
|
161
|
+
// Addon packages use function meta, not RPC meta
|
|
162
|
+
const addonFunctionMeta = pikkuState(resolved.package, 'function', 'meta')
|
|
163
|
+
const funcMeta = addonFunctionMeta[resolved.function]
|
|
164
|
+
if (!funcMeta) {
|
|
165
|
+
throw new Error(
|
|
166
|
+
`Function '${resolved.function}' not found in package '${resolved.package}'. ` +
|
|
167
|
+
`Available functions: ${Object.keys(addonFunctionMeta).join(', ') || '(none)'}`
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
const funcName = funcMeta.pikkuFuncId || resolved.function
|
|
171
|
+
|
|
172
|
+
const auth = resolved.addonConfig?.auth ?? this.options.requiresAuth
|
|
173
|
+
const tags = [
|
|
174
|
+
...(resolved.addonConfig?.tags ?? []),
|
|
175
|
+
...(funcMeta.tags ?? []),
|
|
176
|
+
]
|
|
177
|
+
|
|
178
|
+
// Execute the function using runPikkuFunc with the addon package's state
|
|
179
|
+
// We use the parent services (this.services) since addon packages share services
|
|
180
|
+
// Pass the function's tags so tag-based middleware/permissions are applied
|
|
181
|
+
return runPikkuFunc<In, Out>('rpc', namespacedFunction, funcName, {
|
|
182
|
+
auth,
|
|
183
|
+
singletonServices: this.services,
|
|
184
|
+
data: () => data,
|
|
185
|
+
wire,
|
|
186
|
+
packageName: resolved.package,
|
|
187
|
+
tags,
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
public async rpcWithWire<In = any, Out = any>(
|
|
192
|
+
rpcName: string,
|
|
193
|
+
data: In,
|
|
194
|
+
wire: PikkuWire
|
|
195
|
+
): Promise<Out> {
|
|
196
|
+
const rpcDepth = this.wire.rpc?.depth || 0
|
|
197
|
+
const mergedWire: PikkuWire = {
|
|
198
|
+
...this.wire,
|
|
199
|
+
...wire,
|
|
200
|
+
rpc: this.wire.rpc
|
|
201
|
+
? {
|
|
202
|
+
...this.wire.rpc,
|
|
203
|
+
depth: rpcDepth + 1,
|
|
204
|
+
global: false,
|
|
205
|
+
}
|
|
206
|
+
: undefined,
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (rpcName.includes(':')) {
|
|
210
|
+
return this.invokeAddonFunction<In, Out>(rpcName, data, mergedWire)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return runPikkuFunc<In, Out>(
|
|
214
|
+
'rpc',
|
|
215
|
+
rpcName,
|
|
216
|
+
getPikkuFunctionName(rpcName),
|
|
217
|
+
{
|
|
218
|
+
auth: this.options.requiresAuth,
|
|
219
|
+
singletonServices: this.services,
|
|
220
|
+
data: () => data,
|
|
221
|
+
wire: mergedWire,
|
|
222
|
+
}
|
|
223
|
+
)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
public async startWorkflow<In = any>(
|
|
227
|
+
workflowName: string,
|
|
228
|
+
input: In,
|
|
229
|
+
options?: {
|
|
230
|
+
startNode?: string
|
|
231
|
+
wire?: { type: string; id?: string; parentRunId?: string }
|
|
232
|
+
}
|
|
233
|
+
): Promise<{ runId: string }> {
|
|
234
|
+
if (!this.services.workflowService) {
|
|
235
|
+
throw new Error('WorkflowService service not available')
|
|
236
|
+
}
|
|
237
|
+
const parentRunId = this.wire.workflowStep?.runId ?? this.wire.graph?.runId
|
|
238
|
+
const wire = options?.wire ?? {
|
|
239
|
+
type: this.wire.wireType ?? 'unknown',
|
|
240
|
+
id: this.wire.wireId,
|
|
241
|
+
...(parentRunId ? { parentRunId } : {}),
|
|
242
|
+
}
|
|
243
|
+
return this.services.workflowService.startWorkflow(
|
|
244
|
+
workflowName,
|
|
245
|
+
input,
|
|
246
|
+
wire,
|
|
247
|
+
this,
|
|
248
|
+
options
|
|
249
|
+
)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
public get agent() {
|
|
253
|
+
return {
|
|
254
|
+
run: async (agentName: string, input: AIAgentInput) => {
|
|
255
|
+
const result = await runAIAgent(agentName, input, {
|
|
256
|
+
sessionService: this.options.sessionService,
|
|
257
|
+
})
|
|
258
|
+
return {
|
|
259
|
+
runId: result.runId,
|
|
260
|
+
result: result.object ?? result.text,
|
|
261
|
+
usage: result.usage,
|
|
262
|
+
...(result.status === 'suspended' && {
|
|
263
|
+
status: 'suspended' as const,
|
|
264
|
+
pendingApprovals: result.pendingApprovals,
|
|
265
|
+
}),
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
stream: async (
|
|
269
|
+
agentName: string,
|
|
270
|
+
input: {
|
|
271
|
+
message: string
|
|
272
|
+
threadId: string
|
|
273
|
+
resourceId: string
|
|
274
|
+
model?: string
|
|
275
|
+
temperature?: number
|
|
276
|
+
},
|
|
277
|
+
options?: StreamAIAgentOptions
|
|
278
|
+
) => {
|
|
279
|
+
const channel = this.wire.channel as unknown as AIStreamChannel
|
|
280
|
+
if (!channel) throw new Error('No channel available for streaming')
|
|
281
|
+
await streamAIAgent(
|
|
282
|
+
agentName,
|
|
283
|
+
input,
|
|
284
|
+
channel,
|
|
285
|
+
{ sessionService: this.options.sessionService },
|
|
286
|
+
undefined,
|
|
287
|
+
options
|
|
288
|
+
)
|
|
289
|
+
},
|
|
290
|
+
resume: async (
|
|
291
|
+
runId: string,
|
|
292
|
+
input: { toolCallId: string; approved: boolean },
|
|
293
|
+
options?: StreamAIAgentOptions
|
|
294
|
+
) => {
|
|
295
|
+
const channel = this.wire.channel as unknown as AIStreamChannel
|
|
296
|
+
if (!channel) throw new Error('No channel available for streaming')
|
|
297
|
+
await resumeAIAgent(
|
|
298
|
+
{ runId, ...input },
|
|
299
|
+
channel,
|
|
300
|
+
{ sessionService: this.options.sessionService },
|
|
301
|
+
options
|
|
302
|
+
)
|
|
303
|
+
},
|
|
304
|
+
approve: async (
|
|
305
|
+
runId: string,
|
|
306
|
+
approvals: { toolCallId: string; approved: boolean }[],
|
|
307
|
+
expectedAgentName?: string
|
|
308
|
+
) => {
|
|
309
|
+
const result = await resumeAIAgentSync(runId, approvals, {
|
|
310
|
+
sessionService: this.options.sessionService,
|
|
311
|
+
}, expectedAgentName)
|
|
312
|
+
return {
|
|
313
|
+
runId: result.runId,
|
|
314
|
+
result: result.object ?? result.text,
|
|
315
|
+
usage: result.usage,
|
|
316
|
+
...(result.status === 'suspended' && {
|
|
317
|
+
status: 'suspended' as const,
|
|
318
|
+
pendingApprovals: result.pendingApprovals,
|
|
319
|
+
}),
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
public async remote<In = any, Out = any>(
|
|
326
|
+
funcName: string,
|
|
327
|
+
data: In
|
|
328
|
+
): Promise<Out> {
|
|
329
|
+
let endpoint: string | undefined
|
|
330
|
+
const headers: Record<string, string> = {
|
|
331
|
+
'Content-Type': 'application/json',
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const colonIndex = funcName.indexOf(':')
|
|
335
|
+
if (colonIndex !== -1) {
|
|
336
|
+
const namespace = funcName.substring(0, colonIndex)
|
|
337
|
+
const addons = pikkuState(null, 'addons', 'packages')
|
|
338
|
+
const pkgConfig = addons.get(namespace)
|
|
339
|
+
endpoint = pkgConfig?.rpcEndpoint
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (!endpoint && this.services.deploymentService) {
|
|
343
|
+
const deployments =
|
|
344
|
+
await this.services.deploymentService.findFunction(funcName)
|
|
345
|
+
if (deployments.length > 0) {
|
|
346
|
+
endpoint = deployments[0].endpoint
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (!endpoint) {
|
|
351
|
+
throw new Error(
|
|
352
|
+
`No endpoint configured for remote RPC: ${funcName}. ` +
|
|
353
|
+
`Configure rpcEndpoint in addons config or set up a DeploymentService.`
|
|
354
|
+
)
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
let secret: string | undefined
|
|
358
|
+
if (this.services.secrets) {
|
|
359
|
+
try {
|
|
360
|
+
secret = await this.services.secrets.getSecret('PIKKU_REMOTE_SECRET')
|
|
361
|
+
} catch {}
|
|
362
|
+
}
|
|
363
|
+
if (secret) {
|
|
364
|
+
if (!this.services.jwt) {
|
|
365
|
+
throw new Error(
|
|
366
|
+
'PIKKU_REMOTE_SECRET is set but JWT service is not available'
|
|
367
|
+
)
|
|
368
|
+
}
|
|
369
|
+
const session =
|
|
370
|
+
this.wire.getSession && typeof this.wire.getSession === 'function'
|
|
371
|
+
? await this.wire.getSession()
|
|
372
|
+
: (this.wire as any).session
|
|
373
|
+
const sessionEnc = session
|
|
374
|
+
? await encryptJSON(secret, { session })
|
|
375
|
+
: undefined
|
|
376
|
+
const token = await this.services.jwt.encode(
|
|
377
|
+
{ value: 5, unit: 'minute' },
|
|
378
|
+
{
|
|
379
|
+
aud: 'pikku-remote',
|
|
380
|
+
fn: funcName,
|
|
381
|
+
iat: Date.now(),
|
|
382
|
+
session: sessionEnc,
|
|
383
|
+
}
|
|
384
|
+
)
|
|
385
|
+
headers.Authorization = `Bearer ${token}`
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const url = `${endpoint}/rpc/${encodeURIComponent(funcName)}`
|
|
389
|
+
const timeoutMs = 30_000
|
|
390
|
+
const response = await fetch(url, {
|
|
391
|
+
method: 'POST',
|
|
392
|
+
headers,
|
|
393
|
+
body: JSON.stringify({ data }),
|
|
394
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
if (!response.ok) {
|
|
398
|
+
let errorBody: string
|
|
399
|
+
try {
|
|
400
|
+
errorBody = JSON.stringify(await response.json())
|
|
401
|
+
} catch {
|
|
402
|
+
errorBody = await response.text()
|
|
403
|
+
}
|
|
404
|
+
throw new Error(
|
|
405
|
+
`Remote RPC call failed: ${response.status} ${response.statusText}. ${errorBody}`
|
|
406
|
+
)
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
try {
|
|
410
|
+
return (await response.json()) as Out
|
|
411
|
+
} catch {
|
|
412
|
+
const text = await response.text()
|
|
413
|
+
throw new Error(
|
|
414
|
+
`Remote RPC call returned non-JSON response: ${response.status} ${response.statusText}. ${text}`
|
|
415
|
+
)
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// RPC Service class for the global interface
|
|
421
|
+
export class PikkuRPCService<
|
|
422
|
+
Services extends CoreServices,
|
|
423
|
+
TypedRPC = PikkuRPC,
|
|
424
|
+
> {
|
|
425
|
+
// Convenience function for initializing
|
|
426
|
+
getContextRPCService(
|
|
427
|
+
services: Services,
|
|
428
|
+
wire: PikkuWire,
|
|
429
|
+
requiresAuthOrOptions?:
|
|
430
|
+
| boolean
|
|
431
|
+
| {
|
|
432
|
+
requiresAuth?: boolean
|
|
433
|
+
sessionService?: SessionService<CoreUserSession>
|
|
434
|
+
}
|
|
435
|
+
| undefined,
|
|
436
|
+
depth: number = 0
|
|
437
|
+
): TypedRPC {
|
|
438
|
+
const options =
|
|
439
|
+
typeof requiresAuthOrOptions === 'object' &&
|
|
440
|
+
requiresAuthOrOptions !== null
|
|
441
|
+
? requiresAuthOrOptions
|
|
442
|
+
: { requiresAuth: requiresAuthOrOptions }
|
|
443
|
+
const serviceRPC = new ContextAwareRPCService(services, wire, options)
|
|
444
|
+
return {
|
|
445
|
+
depth,
|
|
446
|
+
global: false,
|
|
447
|
+
invoke: serviceRPC.rpc.bind(serviceRPC),
|
|
448
|
+
remote: serviceRPC.remote.bind(serviceRPC),
|
|
449
|
+
exposed: serviceRPC.rpcExposed.bind(serviceRPC),
|
|
450
|
+
startWorkflow: serviceRPC.startWorkflow.bind(serviceRPC),
|
|
451
|
+
get agent() {
|
|
452
|
+
return serviceRPC.agent
|
|
453
|
+
},
|
|
454
|
+
rpcWithWire: serviceRPC.rpcWithWire.bind(serviceRPC),
|
|
455
|
+
} as any
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Create a singleton instance
|
|
460
|
+
export const rpcService = new PikkuRPCService()
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export type PikkuRPC<
|
|
2
|
+
Invoke extends Function = any,
|
|
3
|
+
Remote extends Function = any,
|
|
4
|
+
startWorkflow extends Function = any,
|
|
5
|
+
AgentRun extends Function = any,
|
|
6
|
+
AgentStream extends Function = any,
|
|
7
|
+
> = {
|
|
8
|
+
depth: number
|
|
9
|
+
global: boolean
|
|
10
|
+
invoke: Invoke
|
|
11
|
+
remote: Remote
|
|
12
|
+
exposed: (name: string, data: any) => Promise<any>
|
|
13
|
+
startWorkflow: startWorkflow
|
|
14
|
+
agent: {
|
|
15
|
+
run: AgentRun
|
|
16
|
+
stream: AgentStream
|
|
17
|
+
resume: (
|
|
18
|
+
runId: string,
|
|
19
|
+
input: { toolCallId: string; approved: boolean },
|
|
20
|
+
options?: any
|
|
21
|
+
) => Promise<void>
|
|
22
|
+
approve: (
|
|
23
|
+
runId: string,
|
|
24
|
+
approvals: { toolCallId: string; approved: boolean }[],
|
|
25
|
+
expectedAgentName?: string
|
|
26
|
+
) => Promise<any>
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type RPCMeta = {
|
|
31
|
+
pikkuFuncId: string
|
|
32
|
+
expose: boolean
|
|
33
|
+
remote?: boolean
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Type for RPC handlers
|
|
38
|
+
*/
|
|
39
|
+
export interface RPCHandler<Input, Output> {
|
|
40
|
+
input: Input
|
|
41
|
+
output: Output
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Resolved function reference from namespace
|
|
46
|
+
*/
|
|
47
|
+
export interface ResolvedFunction {
|
|
48
|
+
package: string
|
|
49
|
+
function: string
|
|
50
|
+
addonConfig: {
|
|
51
|
+
package: string
|
|
52
|
+
auth?: boolean
|
|
53
|
+
tags?: string[]
|
|
54
|
+
rpcEndpoint?: string
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { pikkuState } from '../../pikku-state.js'
|
|
2
|
+
|
|
3
|
+
export type WireAddonConfig = {
|
|
4
|
+
name: string
|
|
5
|
+
package: string
|
|
6
|
+
rpcEndpoint?: string
|
|
7
|
+
auth?: boolean
|
|
8
|
+
mcp?: boolean
|
|
9
|
+
tags?: string[]
|
|
10
|
+
secretOverrides?: Record<string, string>
|
|
11
|
+
variableOverrides?: Record<string, string>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const wireAddon = (config: WireAddonConfig): void => {
|
|
15
|
+
pikkuState(null, 'addons', 'packages').set(config.name, {
|
|
16
|
+
package: config.package,
|
|
17
|
+
rpcEndpoint: config.rpcEndpoint,
|
|
18
|
+
auth: config.auth,
|
|
19
|
+
tags: config.tags,
|
|
20
|
+
})
|
|
21
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export {
|
|
2
|
+
runScheduledTask,
|
|
3
|
+
wireScheduler,
|
|
4
|
+
getScheduledTasks,
|
|
5
|
+
} from './scheduler-runner.js'
|
|
6
|
+
export { logSchedulers } from './log-schedulers.js'
|
|
7
|
+
export type {
|
|
8
|
+
CoreScheduledTask,
|
|
9
|
+
ScheduledTasksMeta,
|
|
10
|
+
PikkuScheduledTask,
|
|
11
|
+
} from './scheduler.types.js'
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { pikkuState } from '../../pikku-state.js'
|
|
2
|
+
import type { Logger } from '../../services/index.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Logs all the loaded scheduled tasks.
|
|
6
|
+
* @param logger - A logger for logging information.
|
|
7
|
+
*/
|
|
8
|
+
export const logSchedulers = (logger: Logger) => {
|
|
9
|
+
const scheduledTasks = pikkuState(null, 'scheduler', 'tasks')
|
|
10
|
+
if (scheduledTasks.size === 0) {
|
|
11
|
+
logger.info('No scheduled tasks added')
|
|
12
|
+
return
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let scheduledTasksMessage = 'Scheduled tasks:'
|
|
16
|
+
scheduledTasks.forEach(({ schedule }, name) => {
|
|
17
|
+
scheduledTasksMessage += `\n\t- ${name} -> ${schedule}`
|
|
18
|
+
})
|
|
19
|
+
logger.info(scheduledTasksMessage)
|
|
20
|
+
}
|