@pikku/core 0.12.63 → 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 +147 -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 +9 -34
- 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 +16 -39
- 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,7 +1,9 @@
|
|
|
1
1
|
import { runPikkuFunc } from '../../function/function-runner.js';
|
|
2
|
+
import { addonInstanceForNamespace } from './addon-runner.js';
|
|
2
3
|
import { pikkuState } from '../../pikku-state.js';
|
|
3
4
|
import { PikkuError, addError } from '../../errors/error-handler.js';
|
|
4
5
|
import { parseVersionedId } from '../../version.js';
|
|
6
|
+
import { resolveRemoteAddonToken } from './remote-addon-auth.js';
|
|
5
7
|
export class RPCNotFoundError extends PikkuError {
|
|
6
8
|
rpcName;
|
|
7
9
|
constructor(rpcName) {
|
|
@@ -14,8 +16,31 @@ addError(RPCNotFoundError, {
|
|
|
14
16
|
mcpCode: -32601,
|
|
15
17
|
message: 'RPC function not found.',
|
|
16
18
|
});
|
|
19
|
+
/** A `wireRemoteAddon` namespace is missing a usable `serverUrl`. */
|
|
20
|
+
export class RemoteAddonConfigError extends PikkuError {
|
|
21
|
+
constructor(namespace, detail) {
|
|
22
|
+
super(`Remote addon '${namespace}' is misconfigured: ${detail}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
addError(RemoteAddonConfigError, {
|
|
26
|
+
status: 500,
|
|
27
|
+
message: 'Remote addon is misconfigured.',
|
|
28
|
+
});
|
|
29
|
+
/** The hosted addon returned a non-2xx response for a remote RPC. */
|
|
30
|
+
export class RemoteAddonRequestError extends PikkuError {
|
|
31
|
+
httpStatus;
|
|
32
|
+
constructor(namespace, fnName, status, detail) {
|
|
33
|
+
super(`Remote addon '${namespace}:${fnName}' returned ${status}${detail ? `: ${detail}` : ''}`);
|
|
34
|
+
this.httpStatus = status;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
addError(RemoteAddonRequestError, {
|
|
38
|
+
status: 502,
|
|
39
|
+
message: 'Remote addon request failed.',
|
|
40
|
+
});
|
|
17
41
|
import { runAIAgent, resumeAIAgentSync } from '../ai-agent/ai-agent-runner.js';
|
|
18
42
|
import { streamAIAgent, resumeAIAgent } from '../ai-agent/ai-agent-stream.js';
|
|
43
|
+
import { wrapChannelWithAGUI } from '../ai-agent/ai-agent-agui.js';
|
|
19
44
|
/**
|
|
20
45
|
* Resolve a namespaced function reference to package and function names
|
|
21
46
|
* Uses pikkuState to look up the namespace -> package mapping
|
|
@@ -130,12 +155,16 @@ export class ContextAwareRPCService {
|
|
|
130
155
|
// 'namespace:func' boundary via invokeAddonFunction.
|
|
131
156
|
try {
|
|
132
157
|
const resolved = resolvePikkuFunction(funcName, this.packageName);
|
|
158
|
+
const addonInstance = resolved.packageName
|
|
159
|
+
? addonInstanceForNamespace(this.wire.addonNamespace, resolved.packageName)
|
|
160
|
+
: undefined;
|
|
133
161
|
return await runPikkuFunc('rpc', funcName, resolved.pikkuFuncId, {
|
|
134
162
|
auth: this.options.requiresAuth,
|
|
135
163
|
singletonServices: this.services,
|
|
136
164
|
data: () => data,
|
|
137
165
|
wire: updatedWire,
|
|
138
166
|
packageName: resolved.packageName,
|
|
167
|
+
addonInstance,
|
|
139
168
|
});
|
|
140
169
|
}
|
|
141
170
|
catch (e) {
|
|
@@ -163,6 +192,12 @@ export class ContextAwareRPCService {
|
|
|
163
192
|
if (!resolved) {
|
|
164
193
|
throw new RPCNotFoundError(namespacedFunction);
|
|
165
194
|
}
|
|
195
|
+
const namespace = namespacedFunction.slice(0, namespacedFunction.indexOf(':'));
|
|
196
|
+
// wireRemoteAddon: the addon ships as a devDependency (types only) and its
|
|
197
|
+
// handlers run on the host — dispatch over HTTP, not through local meta.
|
|
198
|
+
if (resolved.addonConfig?.remote) {
|
|
199
|
+
return this.invokeRemoteAddonFunction(namespace, resolved.function, data);
|
|
200
|
+
}
|
|
166
201
|
// Get the function meta from the addon package
|
|
167
202
|
// Addon packages use function meta, not RPC meta
|
|
168
203
|
const addonFunctionMeta = pikkuState(resolved.package, 'function', 'meta');
|
|
@@ -176,6 +211,14 @@ export class ContextAwareRPCService {
|
|
|
176
211
|
...(resolved.addonConfig?.tags ?? []),
|
|
177
212
|
...(funcMeta.tags ?? []),
|
|
178
213
|
];
|
|
214
|
+
// The namespace is the consumer-facing wireAddon name; it selects the
|
|
215
|
+
// per-instance singleton services and secret/variable/credential overrides.
|
|
216
|
+
const addonInstance = {
|
|
217
|
+
namespace,
|
|
218
|
+
secretOverrides: resolved.addonConfig?.secretOverrides,
|
|
219
|
+
variableOverrides: resolved.addonConfig?.variableOverrides,
|
|
220
|
+
credentialOverrides: resolved.addonConfig?.credentialOverrides,
|
|
221
|
+
};
|
|
179
222
|
// Execute the function using runPikkuFunc with the addon package's state
|
|
180
223
|
// We use the parent services (this.services) since addon packages share services
|
|
181
224
|
// Pass the function's tags so tag-based middleware/permissions are applied
|
|
@@ -186,7 +229,56 @@ export class ContextAwareRPCService {
|
|
|
186
229
|
wire,
|
|
187
230
|
packageName: resolved.package,
|
|
188
231
|
tags,
|
|
232
|
+
addonInstance,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Dispatch a `wireRemoteAddon` RPC over HTTP to the hosting service.
|
|
237
|
+
*
|
|
238
|
+
* The consumer sends the addon's own function name (not the namespaced form)
|
|
239
|
+
* to the host's `/remote/rpc/:rpcName` endpoint, authenticating as a client
|
|
240
|
+
* with the token bound in `wireRemoteAddon({ auth })`. The addon's handlers
|
|
241
|
+
* live on the host, so there is no local function meta to resolve.
|
|
242
|
+
*/
|
|
243
|
+
async invokeRemoteAddonFunction(namespace, fnName, data) {
|
|
244
|
+
const cfg = pikkuState(null, 'addons', 'packages').get(namespace);
|
|
245
|
+
if (!cfg?.remote) {
|
|
246
|
+
throw new RPCNotFoundError(`${namespace}:${fnName}`);
|
|
247
|
+
}
|
|
248
|
+
const serverUrl = typeof cfg.serverUrl === 'function'
|
|
249
|
+
? await cfg.serverUrl(this.services)
|
|
250
|
+
: cfg.serverUrl;
|
|
251
|
+
if (!serverUrl) {
|
|
252
|
+
throw new RemoteAddonConfigError(namespace, 'serverUrl resolved empty');
|
|
253
|
+
}
|
|
254
|
+
const remoteFn = cfg.remoteName ? cfg.remoteName(fnName) : fnName;
|
|
255
|
+
const token = await resolveRemoteAddonToken(cfg.remoteAuth, this.services, this.wire, namespace);
|
|
256
|
+
const headers = {
|
|
257
|
+
'content-type': 'application/json',
|
|
258
|
+
accept: 'application/json',
|
|
259
|
+
};
|
|
260
|
+
if (token) {
|
|
261
|
+
headers.authorization = `Bearer ${token}`;
|
|
262
|
+
}
|
|
263
|
+
if (this.wire.traceId) {
|
|
264
|
+
headers['x-trace-id'] = this.wire.traceId;
|
|
265
|
+
}
|
|
266
|
+
const base = serverUrl.replace(/\/+$/, '');
|
|
267
|
+
const res = await fetch(`${base}/remote/rpc/${encodeURIComponent(remoteFn)}`, {
|
|
268
|
+
method: 'POST',
|
|
269
|
+
headers,
|
|
270
|
+
body: JSON.stringify({ rpcName: remoteFn, data }),
|
|
189
271
|
});
|
|
272
|
+
if (!res.ok) {
|
|
273
|
+
// Best-effort body read to enrich the thrown error (mirrors postRpc).
|
|
274
|
+
const detail = (await res.text().catch(() => '')).slice(0, 300);
|
|
275
|
+
throw new RemoteAddonRequestError(namespace, remoteFn, res.status, detail);
|
|
276
|
+
}
|
|
277
|
+
if (res.status === 204) {
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
const text = await res.text();
|
|
281
|
+
return (text ? JSON.parse(text) : undefined);
|
|
190
282
|
}
|
|
191
283
|
async rpcWithWire(rpcName, data, wire) {
|
|
192
284
|
const mergedWire = {
|
|
@@ -198,12 +290,16 @@ export class ContextAwareRPCService {
|
|
|
198
290
|
}
|
|
199
291
|
try {
|
|
200
292
|
const resolved = resolvePikkuFunction(rpcName, this.packageName);
|
|
293
|
+
const addonInstance = resolved.packageName
|
|
294
|
+
? addonInstanceForNamespace(this.wire.addonNamespace, resolved.packageName)
|
|
295
|
+
: undefined;
|
|
201
296
|
return await runPikkuFunc('rpc', rpcName, resolved.pikkuFuncId, {
|
|
202
297
|
auth: this.options.requiresAuth,
|
|
203
298
|
singletonServices: this.services,
|
|
204
299
|
data: () => data,
|
|
205
300
|
wire: mergedWire,
|
|
206
301
|
packageName: resolved.packageName,
|
|
302
|
+
addonInstance,
|
|
207
303
|
});
|
|
208
304
|
}
|
|
209
305
|
catch (e) {
|
|
@@ -250,16 +346,26 @@ export class ContextAwareRPCService {
|
|
|
250
346
|
const channel = this.wire.channel;
|
|
251
347
|
if (!channel)
|
|
252
348
|
throw new Error('No channel available for streaming');
|
|
253
|
-
|
|
349
|
+
let currentRunId;
|
|
350
|
+
await streamAIAgent(agentName, input, wrapChannelWithAGUI(channel, {
|
|
351
|
+
threadId: input.threadId,
|
|
352
|
+
getRunId: () => currentRunId,
|
|
353
|
+
}), {
|
|
254
354
|
sessionService: this.options.sessionService,
|
|
255
355
|
getCredential: this.wire.getCredential?.bind(this.wire),
|
|
256
|
-
}, undefined,
|
|
356
|
+
}, undefined, {
|
|
357
|
+
...options,
|
|
358
|
+
onRunCreated: (runId) => {
|
|
359
|
+
currentRunId = runId;
|
|
360
|
+
options?.onRunCreated?.(runId);
|
|
361
|
+
},
|
|
362
|
+
});
|
|
257
363
|
},
|
|
258
364
|
resume: async (runId, input, options) => {
|
|
259
365
|
const channel = this.wire.channel;
|
|
260
366
|
if (!channel)
|
|
261
367
|
throw new Error('No channel available for streaming');
|
|
262
|
-
await resumeAIAgent({ runId, ...input }, channel, {
|
|
368
|
+
await resumeAIAgent({ runId, ...input }, wrapChannelWithAGUI(channel, { runId }), {
|
|
263
369
|
sessionService: this.options.sessionService,
|
|
264
370
|
getCredential: this.wire.getCredential?.bind(this.wire),
|
|
265
371
|
}, options);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type PikkuRPC<Invoke extends
|
|
1
|
+
export type PikkuRPC<Invoke extends (...args: any[]) => any = (...args: any[]) => any, Remote extends (...args: any[]) => any = (...args: any[]) => any, startWorkflow extends (...args: any[]) => any = (...args: any[]) => any, AgentRun extends (...args: any[]) => any = (...args: any[]) => any, AgentStream extends (...args: any[]) => any = (...args: any[]) => any> = {
|
|
2
2
|
depth: number;
|
|
3
3
|
global: boolean;
|
|
4
4
|
invoke: Invoke;
|
|
@@ -41,5 +41,19 @@ export interface ResolvedFunction {
|
|
|
41
41
|
auth?: boolean;
|
|
42
42
|
tags?: string[];
|
|
43
43
|
rpcEndpoint?: string;
|
|
44
|
+
secretOverrides?: Record<string, string>;
|
|
45
|
+
variableOverrides?: Record<string, string>;
|
|
46
|
+
credentialOverrides?: Record<string, string>;
|
|
47
|
+
/** Set by `wireRemoteAddon`: dispatch this namespace's RPCs over HTTP */
|
|
48
|
+
remote?: boolean;
|
|
49
|
+
serverUrl?: string | ((services: any) => string | Promise<string>);
|
|
50
|
+
remoteAuth?: {
|
|
51
|
+
credentialId: string;
|
|
52
|
+
} | {
|
|
53
|
+
secretId: string;
|
|
54
|
+
} | {
|
|
55
|
+
resolve: (services: any, wire: any) => string | Promise<string>;
|
|
56
|
+
};
|
|
57
|
+
remoteName?: (fn: string) => string;
|
|
44
58
|
};
|
|
45
59
|
}
|
|
@@ -5,5 +5,14 @@ export const wireAddon = (config) => {
|
|
|
5
5
|
rpcEndpoint: config.rpcEndpoint,
|
|
6
6
|
auth: config.auth,
|
|
7
7
|
tags: config.tags,
|
|
8
|
+
...(config.secretOverrides
|
|
9
|
+
? { secretOverrides: config.secretOverrides }
|
|
10
|
+
: {}),
|
|
11
|
+
...(config.variableOverrides
|
|
12
|
+
? { variableOverrides: config.variableOverrides }
|
|
13
|
+
: {}),
|
|
14
|
+
...(config.credentialOverrides
|
|
15
|
+
? { credentialOverrides: config.credentialOverrides }
|
|
16
|
+
: {}),
|
|
8
17
|
});
|
|
9
18
|
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { CoreServices, PikkuWire } from '../../types/core.types.js';
|
|
2
|
+
/**
|
|
3
|
+
* How the consumer supplies the token the hosted addon's auth expects.
|
|
4
|
+
*
|
|
5
|
+
* This is a CLIENT authenticating to a hosted library — NOT pikku's trusted
|
|
6
|
+
* machine-to-machine mesh (which uses `PIKKU_REMOTE_SECRET`). The consumer binds
|
|
7
|
+
* the addon's declared auth requirement to a local source:
|
|
8
|
+
* - `credentialId` — per-user credential, resolved via `wire.getCredential(id)`
|
|
9
|
+
* - `secretId` — platform key, resolved via the secrets service
|
|
10
|
+
* - `resolve` — custom escape hatch
|
|
11
|
+
* Omit entirely when the addon declares its remote surface is public.
|
|
12
|
+
*
|
|
13
|
+
* The value is sent `Authorization: Bearer <token>` by default; a non-default
|
|
14
|
+
* header is declared once by the addon (its meta), never chosen here.
|
|
15
|
+
*/
|
|
16
|
+
export type RemoteAddonAuth = {
|
|
17
|
+
credentialId: string;
|
|
18
|
+
} | {
|
|
19
|
+
secretId: string;
|
|
20
|
+
} | {
|
|
21
|
+
resolve: (services: CoreServices, wire: PikkuWire) => string | Promise<string>;
|
|
22
|
+
};
|
|
23
|
+
export type WireRemoteAddonConfig = {
|
|
24
|
+
/** Consumer-facing namespace, e.g. `registry` → `rpc('registry:getOpenApi')` */
|
|
25
|
+
name: string;
|
|
26
|
+
/**
|
|
27
|
+
* The addon package. Installed as a **devDependency** (types only — its
|
|
28
|
+
* handlers run on the host); `pikku verify` enforces this.
|
|
29
|
+
*/
|
|
30
|
+
package: string;
|
|
31
|
+
/** Base URL of the host serving the addon's remote surface. */
|
|
32
|
+
serverUrl: string | ((services: CoreServices) => string | Promise<string>);
|
|
33
|
+
/** Bind the addon's declared auth to a local source. Omit if the surface is public. */
|
|
34
|
+
auth?: RemoteAddonAuth;
|
|
35
|
+
/** Map a consumer-facing fn name → the remote fn name, when they differ (rare). */
|
|
36
|
+
remoteName?: (fn: string) => string;
|
|
37
|
+
tags?: string[];
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Consume a hosted addon's `remote: true` RPCs transparently over HTTP.
|
|
41
|
+
*
|
|
42
|
+
* Unlike `wireAddon` (which bundles the addon's functions in-process, a
|
|
43
|
+
* production dependency), `wireRemoteAddon` dispatches `rpc('name:fn')` to the
|
|
44
|
+
* host at `serverUrl`, fully typed — the addon ships as a devDependency (types
|
|
45
|
+
* only). See {@link RemoteAddonAuth} for how auth is bound.
|
|
46
|
+
*/
|
|
47
|
+
export declare const wireRemoteAddon: (config: WireRemoteAddonConfig) => void;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { pikkuState } from '../../pikku-state.js';
|
|
2
|
+
/**
|
|
3
|
+
* Consume a hosted addon's `remote: true` RPCs transparently over HTTP.
|
|
4
|
+
*
|
|
5
|
+
* Unlike `wireAddon` (which bundles the addon's functions in-process, a
|
|
6
|
+
* production dependency), `wireRemoteAddon` dispatches `rpc('name:fn')` to the
|
|
7
|
+
* host at `serverUrl`, fully typed — the addon ships as a devDependency (types
|
|
8
|
+
* only). See {@link RemoteAddonAuth} for how auth is bound.
|
|
9
|
+
*/
|
|
10
|
+
export const wireRemoteAddon = (config) => {
|
|
11
|
+
pikkuState(null, 'addons', 'packages').set(config.name, {
|
|
12
|
+
package: config.package,
|
|
13
|
+
tags: config.tags,
|
|
14
|
+
remote: true,
|
|
15
|
+
serverUrl: config.serverUrl,
|
|
16
|
+
...(config.auth ? { remoteAuth: config.auth } : {}),
|
|
17
|
+
...(config.remoteName ? { remoteName: config.remoteName } : {}),
|
|
18
|
+
});
|
|
19
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { wireScope } from './wire-scope.js';
|
|
2
|
+
export { flattenScopeDefinitions, validateAndBuildScopeDefinitionsMeta, } from './validate-scope-definitions.js';
|
|
3
|
+
export type { CoreScopes, CoreScopeNode, FlatScope, ScopeDefinitionMeta, ScopeDefinitionsMeta, ScopeDefinitions, ScopeNodeMeta, } from './scope.types.js';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A node within a scope tree. Every node is itself a grantable scope — given
|
|
3
|
+
* `admin` containing `invoices` containing `create`, all three of `admin`,
|
|
4
|
+
* `admin:invoices` and `admin:invoices:create` are valid scope ids.
|
|
5
|
+
*/
|
|
6
|
+
export type CoreScopeNode = {
|
|
7
|
+
/** Short human-readable name, e.g. "Administration". */
|
|
8
|
+
displayName?: string;
|
|
9
|
+
/** Longer-form description, surfaced in the console when granting. */
|
|
10
|
+
description?: string;
|
|
11
|
+
/** Nested scopes, keyed by their segment. */
|
|
12
|
+
scopes?: Record<string, CoreScopeNode>;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Scope trees to declare, keyed by their root segment. A root is just a node —
|
|
16
|
+
* it is named by its key, exactly like every node beneath it. A key must not
|
|
17
|
+
* contain `:` or be `*`.
|
|
18
|
+
*/
|
|
19
|
+
export type CoreScopes = Record<string, CoreScopeNode>;
|
|
20
|
+
export type ScopeNodeMeta = {
|
|
21
|
+
displayName?: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
scopes?: Record<string, ScopeNodeMeta>;
|
|
24
|
+
};
|
|
25
|
+
export type ScopeDefinitionMeta = {
|
|
26
|
+
name: string;
|
|
27
|
+
displayName?: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
scopes?: Record<string, ScopeNodeMeta>;
|
|
30
|
+
sourceFile?: string;
|
|
31
|
+
};
|
|
32
|
+
export type ScopeDefinitions = ScopeDefinitionMeta[];
|
|
33
|
+
export type ScopeDefinitionsMeta = Record<string, ScopeDefinitionMeta>;
|
|
34
|
+
/** A single scope, flattened out of the declared tree. */
|
|
35
|
+
export type FlatScope = {
|
|
36
|
+
/** Colon-delimited id, e.g. `admin:invoices:create`. */
|
|
37
|
+
id: string;
|
|
38
|
+
description?: string;
|
|
39
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { FlatScope, ScopeDefinitions, ScopeDefinitionsMeta } from './scope.types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Flattens declared scope trees into the full list of grantable scope ids,
|
|
4
|
+
* depth-first. Every node is emitted, including intermediate ones.
|
|
5
|
+
*
|
|
6
|
+
* Used by codegen to build the `ScopeId` union, and by a ScopeService to sync
|
|
7
|
+
* the declared set into its store.
|
|
8
|
+
*/
|
|
9
|
+
export declare const flattenScopeDefinitions: (definitions: ScopeDefinitions) => FlatScope[];
|
|
10
|
+
/**
|
|
11
|
+
* Validates declared scopes and keys them by name.
|
|
12
|
+
*
|
|
13
|
+
* Definitions sharing a name must be identical; a conflicting redeclaration is
|
|
14
|
+
* a hard error naming both source files.
|
|
15
|
+
*/
|
|
16
|
+
export declare function validateAndBuildScopeDefinitionsMeta(definitions: ScopeDefinitions): ScopeDefinitionsMeta;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const SEPARATOR = ':';
|
|
2
|
+
const WILDCARD = '*';
|
|
3
|
+
const assertSegment = (segment, scopeName) => {
|
|
4
|
+
if (segment.includes(SEPARATOR)) {
|
|
5
|
+
throw new Error(`Scope segment '${segment}' in '${scopeName}' contains the '${SEPARATOR}' separator. ` +
|
|
6
|
+
`Nest scopes with the 'scopes' property instead of embedding '${SEPARATOR}' in a name.`);
|
|
7
|
+
}
|
|
8
|
+
if (segment === WILDCARD) {
|
|
9
|
+
throw new Error(`Scope segment '${segment}' in '${scopeName}' is the wildcard. ` +
|
|
10
|
+
`'${WILDCARD}' is reserved for granting a scope and its descendants, and cannot be declared.`);
|
|
11
|
+
}
|
|
12
|
+
if (segment.length === 0) {
|
|
13
|
+
throw new Error(`Scope '${scopeName}' contains an empty segment.`);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const assertNodesValid = (nodes, scopeName) => {
|
|
17
|
+
for (const [segment, node] of Object.entries(nodes ?? {})) {
|
|
18
|
+
assertSegment(segment, scopeName);
|
|
19
|
+
assertNodesValid(node.scopes, scopeName);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const flattenNodes = (nodes, prefix, out) => {
|
|
23
|
+
for (const [segment, node] of Object.entries(nodes ?? {})) {
|
|
24
|
+
const id = `${prefix}${SEPARATOR}${segment}`;
|
|
25
|
+
out.push({ id, description: node.description });
|
|
26
|
+
flattenNodes(node.scopes, id, out);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Flattens declared scope trees into the full list of grantable scope ids,
|
|
31
|
+
* depth-first. Every node is emitted, including intermediate ones.
|
|
32
|
+
*
|
|
33
|
+
* Used by codegen to build the `ScopeId` union, and by a ScopeService to sync
|
|
34
|
+
* the declared set into its store.
|
|
35
|
+
*/
|
|
36
|
+
export const flattenScopeDefinitions = (definitions) => {
|
|
37
|
+
const out = [];
|
|
38
|
+
for (const def of definitions) {
|
|
39
|
+
out.push({ id: def.name, description: def.description });
|
|
40
|
+
flattenNodes(def.scopes, def.name, out);
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Validates declared scopes and keys them by name.
|
|
46
|
+
*
|
|
47
|
+
* Definitions sharing a name must be identical; a conflicting redeclaration is
|
|
48
|
+
* a hard error naming both source files.
|
|
49
|
+
*/
|
|
50
|
+
export function validateAndBuildScopeDefinitionsMeta(definitions) {
|
|
51
|
+
const meta = {};
|
|
52
|
+
for (const def of definitions) {
|
|
53
|
+
assertSegment(def.name, def.name);
|
|
54
|
+
assertNodesValid(def.scopes, def.name);
|
|
55
|
+
const existing = meta[def.name];
|
|
56
|
+
if (existing) {
|
|
57
|
+
const sameShape = JSON.stringify(existing.scopes ?? {}) ===
|
|
58
|
+
JSON.stringify(def.scopes ?? {});
|
|
59
|
+
if (!sameShape) {
|
|
60
|
+
throw new Error(`Scope '${def.name}' is declared with different nested scopes.\n` +
|
|
61
|
+
` First declaration: ${existing.sourceFile ?? 'unknown'}\n` +
|
|
62
|
+
` Second declaration: ${def.sourceFile ?? 'unknown'}\n` +
|
|
63
|
+
`Scopes sharing a name must declare the same tree.`);
|
|
64
|
+
}
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
meta[def.name] = {
|
|
68
|
+
name: def.name,
|
|
69
|
+
displayName: def.displayName,
|
|
70
|
+
description: def.description,
|
|
71
|
+
scopes: def.scopes,
|
|
72
|
+
sourceFile: def.sourceFile,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return meta;
|
|
76
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { CoreScopes } from './scope.types.js';
|
|
2
|
+
/**
|
|
3
|
+
* No-op function for declaring scopes.
|
|
4
|
+
* This exists purely for TypeScript type checking and will be tree-shaken.
|
|
5
|
+
* The CLI extracts metadata via AST parsing and generates a `ScopeId` union,
|
|
6
|
+
* so a function referencing an undeclared scope fails the build.
|
|
7
|
+
*
|
|
8
|
+
* Scopes are keyed by segment at every level: a scope is named by its key, and
|
|
9
|
+
* its value describes it. Every node is grantable — the declaration below
|
|
10
|
+
* yields `admin`, `admin:invoices`, `admin:invoices:create`,
|
|
11
|
+
* `admin:invoices:void` and `billing`.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* wireScope({
|
|
16
|
+
* admin: {
|
|
17
|
+
* displayName: 'Administration',
|
|
18
|
+
* description: 'Administrative access',
|
|
19
|
+
* scopes: {
|
|
20
|
+
* invoices: {
|
|
21
|
+
* description: 'Invoice management',
|
|
22
|
+
* scopes: {
|
|
23
|
+
* create: { description: 'Create invoices' },
|
|
24
|
+
* void: { description: 'Void invoices' },
|
|
25
|
+
* },
|
|
26
|
+
* },
|
|
27
|
+
* },
|
|
28
|
+
* },
|
|
29
|
+
* billing: {},
|
|
30
|
+
* })
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare const wireScope: (_config: CoreScopes) => void;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* No-op function for declaring scopes.
|
|
3
|
+
* This exists purely for TypeScript type checking and will be tree-shaken.
|
|
4
|
+
* The CLI extracts metadata via AST parsing and generates a `ScopeId` union,
|
|
5
|
+
* so a function referencing an undeclared scope fails the build.
|
|
6
|
+
*
|
|
7
|
+
* Scopes are keyed by segment at every level: a scope is named by its key, and
|
|
8
|
+
* its value describes it. Every node is grantable — the declaration below
|
|
9
|
+
* yields `admin`, `admin:invoices`, `admin:invoices:create`,
|
|
10
|
+
* `admin:invoices:void` and `billing`.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* wireScope({
|
|
15
|
+
* admin: {
|
|
16
|
+
* displayName: 'Administration',
|
|
17
|
+
* description: 'Administrative access',
|
|
18
|
+
* scopes: {
|
|
19
|
+
* invoices: {
|
|
20
|
+
* description: 'Invoice management',
|
|
21
|
+
* scopes: {
|
|
22
|
+
* create: { description: 'Create invoices' },
|
|
23
|
+
* void: { description: 'Void invoices' },
|
|
24
|
+
* },
|
|
25
|
+
* },
|
|
26
|
+
* },
|
|
27
|
+
* },
|
|
28
|
+
* billing: {},
|
|
29
|
+
* })
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export const wireScope = (_config) => { };
|
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
* DSL (Domain Specific Language) workflow exports
|
|
3
3
|
*/
|
|
4
4
|
export { addWorkflow } from './workflow-runner.js';
|
|
5
|
-
export type { WorkflowStepOptions, WorkflowWireDoRPC, WorkflowWireDoInline, WorkflowWireSleep, WorkflowWireSuspend, InputSource, OutputBinding, RpcStepMeta, SimpleCondition, Condition, BranchStepMeta, ParallelGroupStepMeta, FanoutStepMeta, ReturnStepMeta, InlineStepMeta, SleepStepMeta, CancelStepMeta, SuspendStepMeta, SwitchCaseMeta, SwitchStepMeta, FilterStepMeta, ArrayPredicateStepMeta, WorkflowStepMeta, WorkflowStepWire, PikkuWorkflowWire, } from './workflow-dsl.types.js';
|
|
5
|
+
export type { WorkflowStepOptions, WorkflowWireDoRPC, WorkflowWireDoInline, WorkflowWireSleep, WorkflowWireSuspend, WorkflowWireApproval, WorkflowApprovalOptions, ApprovalOutcome, InputSource, OutputBinding, RpcStepMeta, SimpleCondition, Condition, BranchStepMeta, ParallelGroupStepMeta, FanoutStepMeta, ReturnStepMeta, InlineStepMeta, SleepStepMeta, CancelStepMeta, SuspendStepMeta, ApprovalStepMeta, SwitchCaseMeta, SwitchStepMeta, FilterStepMeta, ArrayPredicateStepMeta, WorkflowStepMeta, WorkflowStepWire, PikkuWorkflowWire, } from './workflow-dsl.types.js';
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* DSL (Domain Specific Language) workflow types
|
|
3
3
|
* These types define the step-based workflow format extracted by the inspector
|
|
4
4
|
*/
|
|
5
|
+
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
5
6
|
import type { WorkflowRun } from '../workflow.types.js';
|
|
6
7
|
import type { ScenarioActor } from '../../../services/scenario-actors-service.js';
|
|
7
8
|
/**
|
|
@@ -64,6 +65,43 @@ export type WorkflowWireSleep = (stepName: string, duration: string) => Promise<
|
|
|
64
65
|
* loops, like dynamic `do()` step names.
|
|
65
66
|
*/
|
|
66
67
|
export type WorkflowWireSuspend = (reason: string) => Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Options for workflow.approval().
|
|
70
|
+
*/
|
|
71
|
+
export interface WorkflowApprovalOptions<TSchema extends StandardSchemaV1 = StandardSchemaV1> {
|
|
72
|
+
/**
|
|
73
|
+
* Schema the decision payload is validated against. This is a VALUE, not a
|
|
74
|
+
* type generic: the payload arrives from an untrusted caller over the approve
|
|
75
|
+
* wire, and a generic is erased at compile time — it would validate nothing.
|
|
76
|
+
* Any standard-schema library (zod, valibot, arktype) satisfies this.
|
|
77
|
+
*/
|
|
78
|
+
schema: TSchema;
|
|
79
|
+
/**
|
|
80
|
+
* Give up waiting after this long (e.g. '3d'), yielding `{ status: 'expired' }`
|
|
81
|
+
* instead of waiting forever. Evaluated on replay from a recorded deadline, so
|
|
82
|
+
* the answer is correct even if the wake-up timer is never delivered.
|
|
83
|
+
*/
|
|
84
|
+
expiry?: string | number;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The result of an approval gate. A union rather than a throw so that callers
|
|
88
|
+
* must handle the deadline case, and so "skip it and carry on" stays trivial.
|
|
89
|
+
* `decided` means a human answered — whether that answer was yes or no is
|
|
90
|
+
* carried in `data` and is the application's business, not the framework's.
|
|
91
|
+
*/
|
|
92
|
+
export type ApprovalOutcome<T> = {
|
|
93
|
+
status: 'decided';
|
|
94
|
+
data: T;
|
|
95
|
+
} | {
|
|
96
|
+
status: 'expired';
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Type signature for workflow.approval() - used by inspector.
|
|
100
|
+
* Like {@link WorkflowWireSuspend}, `reason` is the approval point's stable
|
|
101
|
+
* durable identity. Unlike suspend, the gate stays closed until a decision is
|
|
102
|
+
* recorded against it, and the decision is handed back to the caller.
|
|
103
|
+
*/
|
|
104
|
+
export type WorkflowWireApproval = <TSchema extends StandardSchemaV1>(reason: string, options: WorkflowApprovalOptions<TSchema>) => Promise<ApprovalOutcome<StandardSchemaV1.InferOutput<TSchema>>>;
|
|
67
105
|
/**
|
|
68
106
|
* Input source for step arguments in DSL workflows
|
|
69
107
|
*/
|
|
@@ -272,6 +310,18 @@ export interface SuspendStepMeta {
|
|
|
272
310
|
/** Reason string passed to workflow.suspend() — becomes the durable step key */
|
|
273
311
|
reason: string;
|
|
274
312
|
}
|
|
313
|
+
/**
|
|
314
|
+
* Approval step metadata (workflow.approval())
|
|
315
|
+
*/
|
|
316
|
+
export interface ApprovalStepMeta {
|
|
317
|
+
type: 'approval';
|
|
318
|
+
/** Reason string passed to workflow.approval() — becomes the durable step key */
|
|
319
|
+
reason: string;
|
|
320
|
+
/** Output variable name (if assigned) */
|
|
321
|
+
outputVar?: string;
|
|
322
|
+
/** Expiry duration, when one was given */
|
|
323
|
+
expiry?: string | number;
|
|
324
|
+
}
|
|
275
325
|
/**
|
|
276
326
|
* Filter step metadata (array.filter)
|
|
277
327
|
*/
|
|
@@ -305,7 +355,7 @@ export interface ArrayPredicateStepMeta {
|
|
|
305
355
|
/**
|
|
306
356
|
* Workflow step metadata (extracted by inspector)
|
|
307
357
|
*/
|
|
308
|
-
export type WorkflowStepMeta = RpcStepMeta | BranchStepMeta | ParallelGroupStepMeta | FanoutStepMeta | ReturnStepMeta | InlineStepMeta | SleepStepMeta | CancelStepMeta | SuspendStepMeta | SwitchStepMeta | FilterStepMeta | ArrayPredicateStepMeta | SetStepMeta;
|
|
358
|
+
export type WorkflowStepMeta = RpcStepMeta | BranchStepMeta | ParallelGroupStepMeta | FanoutStepMeta | ReturnStepMeta | InlineStepMeta | SleepStepMeta | CancelStepMeta | SuspendStepMeta | ApprovalStepMeta | SwitchStepMeta | FilterStepMeta | ArrayPredicateStepMeta | SetStepMeta;
|
|
309
359
|
/**
|
|
310
360
|
* Workflow step wire context for RPC functions
|
|
311
361
|
* Provides step-level metadata including retry attempt tracking
|
|
@@ -355,6 +405,8 @@ export interface PikkuWorkflowWire {
|
|
|
355
405
|
sleep: WorkflowWireSleep;
|
|
356
406
|
/** Suspend workflow until explicitly resumed */
|
|
357
407
|
suspend: WorkflowWireSuspend;
|
|
408
|
+
/** Suspend workflow until a human records a decision against this gate */
|
|
409
|
+
approval: WorkflowWireApproval;
|
|
358
410
|
}
|
|
359
411
|
export interface PikkuScenarioWire extends PikkuWorkflowWire {
|
|
360
412
|
/**
|
|
@@ -93,6 +93,8 @@ type GraphNodeConfigMap<FuncMap extends Record<string, string>, RPCMap extends R
|
|
|
93
93
|
onError?: Extract<keyof FuncMap, string> | Extract<keyof FuncMap, string>[];
|
|
94
94
|
retries?: number;
|
|
95
95
|
retryDelay?: string | number;
|
|
96
|
+
/** Free-text node documentation. Non-semantic — excluded from graphHash. */
|
|
97
|
+
notes?: string;
|
|
96
98
|
};
|
|
97
99
|
};
|
|
98
100
|
/**
|
|
@@ -246,6 +246,9 @@ function resolveTemplate(template, nodeResults) {
|
|
|
246
246
|
}
|
|
247
247
|
function resolveValue(value, nodeResults) {
|
|
248
248
|
if (isDataRef(value)) {
|
|
249
|
+
if (value.$ref === '$item') {
|
|
250
|
+
return value;
|
|
251
|
+
}
|
|
249
252
|
const source = nodeResults[value.$ref];
|
|
250
253
|
return value.path ? getValueAtPath(source, value.path) : source;
|
|
251
254
|
}
|
|
@@ -5,11 +5,17 @@ export interface PikkuWorkflowGraphConfig<FuncMap extends Record<string, string>
|
|
|
5
5
|
tags?: string[];
|
|
6
6
|
nodes: FuncMap;
|
|
7
7
|
config?: T;
|
|
8
|
+
/**
|
|
9
|
+
* Graph-level free-text notes (e.g. imported sticky notes). Non-semantic:
|
|
10
|
+
* excluded from the graph topology hash.
|
|
11
|
+
*/
|
|
12
|
+
notes?: string[];
|
|
8
13
|
}
|
|
9
14
|
export interface PikkuWorkflowGraphResult {
|
|
10
15
|
__type: 'pikkuWorkflowGraph';
|
|
11
16
|
name?: string;
|
|
12
17
|
description?: string;
|
|
13
18
|
tags?: string[];
|
|
19
|
+
notes?: string[];
|
|
14
20
|
}
|
|
15
21
|
export declare function pikkuWorkflowGraph<const FuncMap extends Record<string, string>>(config: PikkuWorkflowGraphConfig<FuncMap, any>): PikkuWorkflowGraphResult;
|