@pikku/core 0.12.18 → 0.12.20
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 +76 -14
- package/dist/function/function-runner.js +53 -3
- package/dist/function/functions.types.d.ts +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/services/content-service.d.ts +66 -37
- package/dist/services/in-memory-workflow-service.d.ts +5 -2
- package/dist/services/in-memory-workflow-service.js +3 -1
- package/dist/services/index.d.ts +1 -1
- package/dist/services/local-content.d.ts +10 -12
- package/dist/services/local-content.js +46 -37
- package/dist/services/workflow-service.d.ts +5 -2
- package/dist/wirings/cli/channel/cli-channel-runner.js +4 -2
- package/dist/wirings/cli/cli-runner.js +4 -2
- package/dist/wirings/rpc/rpc-runner.d.ts +3 -2
- package/dist/wirings/rpc/rpc-runner.js +32 -8
- package/dist/wirings/workflow/graph/graph-runner.js +4 -1
- package/dist/wirings/workflow/index.d.ts +1 -1
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +5 -2
- package/dist/wirings/workflow/pikku-workflow-service.js +6 -1
- package/dist/wirings/workflow/workflow.types.d.ts +14 -0
- package/package.json +1 -1
- package/src/function/function-runner.ts +68 -3
- package/src/function/functions.types.ts +5 -14
- package/src/index.ts +10 -1
- package/src/services/content-service.ts +79 -51
- package/src/services/in-memory-workflow-service.test.ts +21 -0
- package/src/services/in-memory-workflow-service.ts +8 -1
- package/src/services/index.ts +10 -1
- package/src/services/local-content.ts +68 -52
- package/src/services/workflow-service.ts +6 -1
- package/src/wirings/cli/channel/cli-channel-runner.ts +4 -2
- package/src/wirings/cli/cli-runner.ts +4 -2
- package/src/wirings/rpc/rpc-runner.ts +45 -18
- package/src/wirings/workflow/graph/graph-runner.ts +5 -1
- package/src/wirings/workflow/index.ts +1 -0
- package/src/wirings/workflow/pikku-workflow-service.ts +13 -2
- package/src/wirings/workflow/workflow.types.ts +15 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { pikkuState } from '../../../pikku-state.js';
|
|
2
2
|
import { generateCommandHelp, parseCLIArguments } from '../command-parser.js';
|
|
3
3
|
/**
|
|
4
|
-
* Default JSON renderer for CLI output
|
|
4
|
+
* Default JSON renderer for CLI output. Emits single-line NDJSON so
|
|
5
|
+
* the output stays machine-parseable when the CLI is run with
|
|
6
|
+
* `--output json`; pretty-printing here would break NDJSON consumers.
|
|
5
7
|
*/
|
|
6
8
|
const defaultJSONRenderer = (_services, data) => {
|
|
7
|
-
console.log(JSON.stringify(data
|
|
9
|
+
console.log(JSON.stringify(data));
|
|
8
10
|
};
|
|
9
11
|
/**
|
|
10
12
|
* Execute a CLI program via WebSocket channel
|
|
@@ -17,10 +17,12 @@ export class CLIError extends Error {
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
|
-
* Default JSON renderer for CLI output
|
|
20
|
+
* Default JSON renderer for CLI output. Emits single-line NDJSON so
|
|
21
|
+
* the output stays machine-parseable when the CLI is run with
|
|
22
|
+
* `--output json`; pretty-printing here would break NDJSON consumers.
|
|
21
23
|
*/
|
|
22
24
|
const defaultJSONRenderer = (_services, data) => {
|
|
23
|
-
console.log(JSON.stringify(data
|
|
25
|
+
console.log(JSON.stringify(data));
|
|
24
26
|
};
|
|
25
27
|
/**
|
|
26
28
|
* Registers a CLI command tree and all its functions
|
|
@@ -18,10 +18,11 @@ export declare class ContextAwareRPCService {
|
|
|
18
18
|
private services;
|
|
19
19
|
private wire;
|
|
20
20
|
private options;
|
|
21
|
+
private packageName;
|
|
21
22
|
constructor(services: CoreServices, wire: PikkuWire, options: {
|
|
22
23
|
requiresAuth?: boolean;
|
|
23
24
|
sessionService?: SessionService<CoreUserSession>;
|
|
24
|
-
});
|
|
25
|
+
}, packageName?: string | null);
|
|
25
26
|
rpcExposed(funcName: string, data: any): Promise<any>;
|
|
26
27
|
rpc<In = any, Out = any>(funcName: string, data: In): Promise<Out>;
|
|
27
28
|
/**
|
|
@@ -96,7 +97,7 @@ export declare class PikkuRPCService<Services extends CoreServices, TypedRPC = P
|
|
|
96
97
|
getContextRPCService(services: Services, wire: PikkuWire, requiresAuthOrOptions?: boolean | {
|
|
97
98
|
requiresAuth?: boolean;
|
|
98
99
|
sessionService?: SessionService<CoreUserSession>;
|
|
99
|
-
} | undefined, depth?: number): TypedRPC;
|
|
100
|
+
} | undefined, depth?: number, packageName?: string | null): TypedRPC;
|
|
100
101
|
}
|
|
101
102
|
export declare const rpcService: PikkuRPCService<import("../../types/core.types.js").CoreSingletonServices<{
|
|
102
103
|
logLevel?: import("../../services/logger.js").LogLevel;
|
|
@@ -39,7 +39,22 @@ export const resolveNamespace = (namespacedFunction) => {
|
|
|
39
39
|
addonConfig: pkgConfig,
|
|
40
40
|
};
|
|
41
41
|
};
|
|
42
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Resolve a bare (non-namespaced) RPC name to its pikkuFuncId, preferring
|
|
44
|
+
* the caller's addon package when provided. Returns the function name plus
|
|
45
|
+
* the package scope that resolved it (null = root), so callers can thread
|
|
46
|
+
* the scope into runPikkuFunc without a second lookup.
|
|
47
|
+
*/
|
|
48
|
+
const resolvePikkuFunction = (rpcName, packageName = null) => {
|
|
49
|
+
// Addon-scoped calls: try the caller's package function meta first.
|
|
50
|
+
// (RPC meta only lives in root; addon functions are registered under their package.)
|
|
51
|
+
if (packageName) {
|
|
52
|
+
const pkgFunctions = pikkuState(packageName, 'function', 'meta');
|
|
53
|
+
const pkgMeta = pkgFunctions?.[rpcName];
|
|
54
|
+
if (pkgMeta) {
|
|
55
|
+
return { pikkuFuncId: pkgMeta.pikkuFuncId || rpcName, packageName };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
43
58
|
const rpc = pikkuState(null, 'rpc', 'meta');
|
|
44
59
|
let rpcMeta = rpc[rpcName];
|
|
45
60
|
if (!rpcMeta) {
|
|
@@ -51,17 +66,19 @@ const getPikkuFunctionName = (rpcName) => {
|
|
|
51
66
|
if (!rpcMeta) {
|
|
52
67
|
throw new RPCNotFoundError(rpcName);
|
|
53
68
|
}
|
|
54
|
-
return rpcMeta;
|
|
69
|
+
return { pikkuFuncId: rpcMeta, packageName: null };
|
|
55
70
|
};
|
|
56
71
|
// Context-aware RPC client for use within services
|
|
57
72
|
export class ContextAwareRPCService {
|
|
58
73
|
services;
|
|
59
74
|
wire;
|
|
60
75
|
options;
|
|
61
|
-
|
|
76
|
+
packageName;
|
|
77
|
+
constructor(services, wire, options, packageName = null) {
|
|
62
78
|
this.services = services;
|
|
63
79
|
this.wire = wire;
|
|
64
80
|
this.options = options;
|
|
81
|
+
this.packageName = packageName;
|
|
65
82
|
}
|
|
66
83
|
async rpcExposed(funcName, data) {
|
|
67
84
|
let functionMeta;
|
|
@@ -105,13 +122,18 @@ export class ContextAwareRPCService {
|
|
|
105
122
|
// Not an addon — fall through to local lookup
|
|
106
123
|
}
|
|
107
124
|
}
|
|
108
|
-
//
|
|
125
|
+
// Bare name: resolve via caller's package scope first (if any), then root.
|
|
126
|
+
// Note: intra-addon bare calls do NOT re-apply the addon's external
|
|
127
|
+
// addonConfig.auth/tags — those gates are only applied on the external
|
|
128
|
+
// 'namespace:func' boundary via invokeAddonFunction.
|
|
109
129
|
try {
|
|
110
|
-
|
|
130
|
+
const resolved = resolvePikkuFunction(funcName, this.packageName);
|
|
131
|
+
return await runPikkuFunc('rpc', funcName, resolved.pikkuFuncId, {
|
|
111
132
|
auth: this.options.requiresAuth,
|
|
112
133
|
singletonServices: this.services,
|
|
113
134
|
data: () => data,
|
|
114
135
|
wire: updatedWire,
|
|
136
|
+
packageName: resolved.packageName,
|
|
115
137
|
});
|
|
116
138
|
}
|
|
117
139
|
catch (e) {
|
|
@@ -181,11 +203,13 @@ export class ContextAwareRPCService {
|
|
|
181
203
|
return this.invokeAddonFunction(rpcName, data, mergedWire);
|
|
182
204
|
}
|
|
183
205
|
try {
|
|
184
|
-
|
|
206
|
+
const resolved = resolvePikkuFunction(rpcName, this.packageName);
|
|
207
|
+
return await runPikkuFunc('rpc', rpcName, resolved.pikkuFuncId, {
|
|
185
208
|
auth: this.options.requiresAuth,
|
|
186
209
|
singletonServices: this.services,
|
|
187
210
|
data: () => data,
|
|
188
211
|
wire: mergedWire,
|
|
212
|
+
packageName: resolved.packageName,
|
|
189
213
|
});
|
|
190
214
|
}
|
|
191
215
|
catch (e) {
|
|
@@ -269,12 +293,12 @@ export class ContextAwareRPCService {
|
|
|
269
293
|
// RPC Service class for the global interface
|
|
270
294
|
export class PikkuRPCService {
|
|
271
295
|
// Convenience function for initializing
|
|
272
|
-
getContextRPCService(services, wire, requiresAuthOrOptions, depth = 0) {
|
|
296
|
+
getContextRPCService(services, wire, requiresAuthOrOptions, depth = 0, packageName = null) {
|
|
273
297
|
const options = typeof requiresAuthOrOptions === 'object' &&
|
|
274
298
|
requiresAuthOrOptions !== null
|
|
275
299
|
? requiresAuthOrOptions
|
|
276
300
|
: { requiresAuth: requiresAuthOrOptions };
|
|
277
|
-
const serviceRPC = new ContextAwareRPCService(services, wire, options);
|
|
301
|
+
const serviceRPC = new ContextAwareRPCService(services, wire, options, packageName);
|
|
278
302
|
return {
|
|
279
303
|
depth,
|
|
280
304
|
global: false,
|
|
@@ -580,7 +580,10 @@ export async function runWorkflowGraph(workflowService, graphName, triggerInput,
|
|
|
580
580
|
if (!meta.graphHash) {
|
|
581
581
|
throw new Error(`Workflow graph '${graphName}': missing graphHash in meta`);
|
|
582
582
|
}
|
|
583
|
-
const runId = await workflowService.createRun(graphName, triggerInput, inline ?? false, meta.graphHash, wire ?? { type: 'unknown' }
|
|
583
|
+
const runId = await workflowService.createRun(graphName, triggerInput, inline ?? false, meta.graphHash, wire ?? { type: 'unknown' }, {
|
|
584
|
+
deterministic: meta.deterministic,
|
|
585
|
+
plannedSteps: meta.plannedSteps,
|
|
586
|
+
});
|
|
584
587
|
if (inline) {
|
|
585
588
|
workflowService.registerInlineRun(runId);
|
|
586
589
|
}
|
|
@@ -8,5 +8,5 @@ export { pikkuWorkflowGraph, type PikkuWorkflowGraphConfig, type PikkuWorkflowGr
|
|
|
8
8
|
export { validateWorkflowWiring, computeEntryNodeIds, } from './graph/graph-validation.js';
|
|
9
9
|
export { pikkuWorkflowWorkerFunc, pikkuWorkflowOrchestratorFunc, pikkuWorkflowSleeperFunc, } from './workflow-queue-workers.js';
|
|
10
10
|
export type { WorkflowStepInput as WorkflowStepQueueInput, PikkuWorkflowOrchestratorInput, PikkuWorkflowSleeperInput, } from './workflow-queue-workers.js';
|
|
11
|
-
export type { WorkflowService, WorkflowServiceConfig, WorkflowRunWire, WorkflowStatus, WorkflowVersionStatus, StepStatus, WorkflowRun, WorkflowRunStatus, StepState, WorkflowRunService, CoreWorkflow, PikkuWorkflow, ContextVariable, WorkflowContext, WorkflowsMeta, WorkflowRuntimeMeta, WorkflowsRuntimeMeta, WorkflowStepInput, WorkflowOrchestratorInput, WorkflowSleeperInput, } from './workflow.types.js';
|
|
11
|
+
export type { WorkflowService, WorkflowServiceConfig, WorkflowPlannedStep, WorkflowRunWire, WorkflowStatus, WorkflowVersionStatus, StepStatus, WorkflowRun, WorkflowRunStatus, StepState, WorkflowRunService, CoreWorkflow, PikkuWorkflow, ContextVariable, WorkflowContext, WorkflowsMeta, WorkflowRuntimeMeta, WorkflowsRuntimeMeta, WorkflowStepInput, WorkflowOrchestratorInput, WorkflowSleeperInput, } from './workflow.types.js';
|
|
12
12
|
export type { WorkflowStepOptions, WorkflowWireDoRPC, WorkflowWireDoInline, WorkflowWireSleep, WorkflowWireSuspend, InputSource, OutputBinding, RpcStepMeta, SimpleCondition, Condition, BranchCase, BranchStepMeta, ParallelGroupStepMeta, FanoutStepMeta, ReturnStepMeta, InlineStepMeta, SleepStepMeta, CancelStepMeta, SetStepMeta, SwitchCaseMeta, SwitchStepMeta, FilterStepMeta, ArrayPredicateStepMeta, WorkflowStepMeta, WorkflowStepWire, PikkuWorkflowWire, } from './workflow.types.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SerializedError } from '../../types/core.types.js';
|
|
2
|
-
import type { PikkuWorkflowWire, StepState, WorkflowRun, WorkflowRunStatus, WorkflowRunWire, WorkflowStatus, WorkflowVersionStatus, WorkflowStepOptions } from './workflow.types.js';
|
|
2
|
+
import type { PikkuWorkflowWire, StepState, WorkflowPlannedStep, WorkflowRun, WorkflowRunStatus, WorkflowRunWire, WorkflowStatus, WorkflowVersionStatus, WorkflowStepOptions } from './workflow.types.js';
|
|
3
3
|
import type { WorkflowService } from '../../services/workflow-service.js';
|
|
4
4
|
import { PikkuError } from '../../errors/error-handler.js';
|
|
5
5
|
/**
|
|
@@ -62,7 +62,10 @@ export declare abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
62
62
|
*/
|
|
63
63
|
unregisterInlineRun(runId: string): void;
|
|
64
64
|
registerWorkflowVersions(): Promise<void>;
|
|
65
|
-
abstract createRun(workflowName: string, input: any, inline: boolean, graphHash: string, wire: WorkflowRunWire
|
|
65
|
+
abstract createRun(workflowName: string, input: any, inline: boolean, graphHash: string, wire: WorkflowRunWire, options?: {
|
|
66
|
+
deterministic?: boolean;
|
|
67
|
+
plannedSteps?: WorkflowPlannedStep[];
|
|
68
|
+
}): Promise<string>;
|
|
66
69
|
/**
|
|
67
70
|
* Get a workflow run by ID
|
|
68
71
|
* @param id - Run ID
|
|
@@ -231,6 +231,8 @@ export class PikkuWorkflowService {
|
|
|
231
231
|
status: run.status,
|
|
232
232
|
startedAt: run.createdAt,
|
|
233
233
|
completedAt: terminalStatuses.has(run.status) ? run.updatedAt : undefined,
|
|
234
|
+
deterministic: run.deterministic,
|
|
235
|
+
plannedSteps: run.plannedSteps,
|
|
234
236
|
steps,
|
|
235
237
|
output: run.status === 'completed' ? run.output : undefined,
|
|
236
238
|
error: run.error
|
|
@@ -326,7 +328,10 @@ export class PikkuWorkflowService {
|
|
|
326
328
|
const shouldInline = options?.inline ||
|
|
327
329
|
workflowMeta.inline ||
|
|
328
330
|
!getSingletonServices()?.queueService;
|
|
329
|
-
const runId = await this.createRun(name, input, shouldInline, workflowMeta.graphHash, wire
|
|
331
|
+
const runId = await this.createRun(name, input, shouldInline, workflowMeta.graphHash, wire, {
|
|
332
|
+
deterministic: workflowMeta.deterministic,
|
|
333
|
+
plannedSteps: workflowMeta.plannedSteps,
|
|
334
|
+
});
|
|
330
335
|
if (shouldInline) {
|
|
331
336
|
this.inlineRuns.add(runId);
|
|
332
337
|
try {
|
|
@@ -18,6 +18,10 @@ export interface WorkflowServiceConfig {
|
|
|
18
18
|
stepWorkerQueueName: string;
|
|
19
19
|
sleeperRPCName: string;
|
|
20
20
|
}
|
|
21
|
+
export interface WorkflowPlannedStep {
|
|
22
|
+
/** Human-readable step label for UI timeline */
|
|
23
|
+
stepName: string;
|
|
24
|
+
}
|
|
21
25
|
/**
|
|
22
26
|
* Workflow run status
|
|
23
27
|
*/
|
|
@@ -50,6 +54,10 @@ export interface WorkflowRun {
|
|
|
50
54
|
inline?: boolean;
|
|
51
55
|
/** Graph hash of the workflow definition at run creation time */
|
|
52
56
|
graphHash?: string;
|
|
57
|
+
/** True when the workflow has a static, pre-computable step timeline */
|
|
58
|
+
deterministic?: boolean;
|
|
59
|
+
/** Static planned steps snapshot captured at run start */
|
|
60
|
+
plannedSteps?: WorkflowPlannedStep[];
|
|
53
61
|
/** Wire origin info (how this run was started) */
|
|
54
62
|
wire: WorkflowRunWire;
|
|
55
63
|
/** Creation timestamp */
|
|
@@ -95,6 +103,8 @@ export interface WorkflowRunStatus {
|
|
|
95
103
|
status: WorkflowStatus;
|
|
96
104
|
startedAt: Date;
|
|
97
105
|
completedAt?: Date;
|
|
106
|
+
deterministic?: boolean;
|
|
107
|
+
plannedSteps?: WorkflowPlannedStep[];
|
|
98
108
|
steps: Array<{
|
|
99
109
|
name: string;
|
|
100
110
|
status: StepStatus;
|
|
@@ -211,6 +221,10 @@ export interface WorkflowRuntimeMeta {
|
|
|
211
221
|
entryNodeIds?: string[];
|
|
212
222
|
/** Hash of graph topology (nodes, edges, input mappings) */
|
|
213
223
|
graphHash?: string;
|
|
224
|
+
/** True when the workflow has a static, pre-computable step timeline */
|
|
225
|
+
deterministic?: boolean;
|
|
226
|
+
/** Static planned steps metadata for deterministic workflows */
|
|
227
|
+
plannedSteps?: WorkflowPlannedStep[];
|
|
214
228
|
}
|
|
215
229
|
/**
|
|
216
230
|
* Unified workflow runtime metadata map
|
package/package.json
CHANGED
|
@@ -72,6 +72,54 @@ async function resolveSession(
|
|
|
72
72
|
* @param parentServices - The parent/caller's singleton services (used as base)
|
|
73
73
|
* @returns The package's singleton services
|
|
74
74
|
*/
|
|
75
|
+
/**
|
|
76
|
+
* Find the consumer-defined namespace (from wireAddon) for a given addon package.
|
|
77
|
+
* Returns null if the package isn't registered as an addon.
|
|
78
|
+
*/
|
|
79
|
+
const findAddonNamespaceForPackage = (packageName: string): string | null => {
|
|
80
|
+
const addons = pikkuState(null, 'addons', 'packages')
|
|
81
|
+
if (!addons) return null
|
|
82
|
+
for (const [namespace, cfg] of addons.entries()) {
|
|
83
|
+
if (cfg?.package === packageName) return namespace
|
|
84
|
+
}
|
|
85
|
+
return null
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Wrap a workflow service so that bare workflow names passed from inside an
|
|
90
|
+
* addon function are auto-prefixed with the addon's consumer-facing namespace.
|
|
91
|
+
* Without this, `runToCompletion('myWorkflow')` from inside an addon misses
|
|
92
|
+
* the workflow registered under the addon's package scope and throws
|
|
93
|
+
* WorkflowNotFoundError — forcing addons to hardcode their consumer-defined
|
|
94
|
+
* namespace, which couples the addon to its caller.
|
|
95
|
+
*
|
|
96
|
+
* Explicit `'ns:name'` and bare names that already exist in root meta are
|
|
97
|
+
* unaffected; only bare names that would otherwise miss resolution get
|
|
98
|
+
* prefixed.
|
|
99
|
+
*/
|
|
100
|
+
const wrapWorkflowServiceForPackage = <T extends object>(
|
|
101
|
+
service: T,
|
|
102
|
+
packageName: string
|
|
103
|
+
): T => {
|
|
104
|
+
return new Proxy(service, {
|
|
105
|
+
get(target, prop, receiver) {
|
|
106
|
+
if (prop === 'startWorkflow' || prop === 'runToCompletion') {
|
|
107
|
+
const original = Reflect.get(target, prop, receiver) as Function
|
|
108
|
+
return function (this: any, name: string, ...rest: any[]) {
|
|
109
|
+
if (typeof name === 'string' && !name.includes(':')) {
|
|
110
|
+
const namespace = findAddonNamespaceForPackage(packageName)
|
|
111
|
+
if (namespace) {
|
|
112
|
+
name = `${namespace}:${name}`
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return original.call(this, name, ...rest)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return Reflect.get(target, prop, receiver)
|
|
119
|
+
},
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
75
123
|
const getOrCreatePackageSingletonServices = async (
|
|
76
124
|
packageName: string,
|
|
77
125
|
parentServices: CoreSingletonServices
|
|
@@ -101,6 +149,18 @@ const getOrCreatePackageSingletonServices = async (
|
|
|
101
149
|
parentServices
|
|
102
150
|
)
|
|
103
151
|
|
|
152
|
+
// Wrap workflowService so that bare names used inside the addon's functions
|
|
153
|
+
// resolve to workflows registered under the addon's package scope.
|
|
154
|
+
if (
|
|
155
|
+
packageServices.workflowService &&
|
|
156
|
+
typeof packageServices.workflowService === 'object'
|
|
157
|
+
) {
|
|
158
|
+
packageServices.workflowService = wrapWorkflowServiceForPackage(
|
|
159
|
+
packageServices.workflowService as object,
|
|
160
|
+
packageName
|
|
161
|
+
) as typeof packageServices.workflowService
|
|
162
|
+
}
|
|
163
|
+
|
|
104
164
|
// Cache the services
|
|
105
165
|
pikkuState(packageName, 'package', 'singletonServices', packageServices)
|
|
106
166
|
|
|
@@ -378,11 +438,16 @@ export const runPikkuFunc = async <In = any, Out = any>(
|
|
|
378
438
|
wireServices && Object.keys(wireServices).length > 0
|
|
379
439
|
? { ...resolvedSingletonServices, ...wireServices }
|
|
380
440
|
: resolvedSingletonServices
|
|
441
|
+
const callerPackageName = packageName
|
|
381
442
|
Object.defineProperty(resolvedWire, 'rpc', {
|
|
382
443
|
get() {
|
|
383
|
-
const rpc = rpcService.getContextRPCService(
|
|
384
|
-
|
|
385
|
-
|
|
444
|
+
const rpc = rpcService.getContextRPCService(
|
|
445
|
+
services,
|
|
446
|
+
resolvedWire,
|
|
447
|
+
{ sessionService },
|
|
448
|
+
0,
|
|
449
|
+
callerPackageName
|
|
450
|
+
)
|
|
386
451
|
Object.defineProperty(resolvedWire, 'rpc', {
|
|
387
452
|
value: rpc,
|
|
388
453
|
writable: true,
|
|
@@ -74,15 +74,8 @@ export type CorePikkuFunctionSessionless<
|
|
|
74
74
|
export type CorePikkuPermission<
|
|
75
75
|
In = any,
|
|
76
76
|
Services extends CoreSingletonServices = CoreServices,
|
|
77
|
-
Wire extends PikkuWire<
|
|
78
|
-
In,
|
|
79
|
-
never,
|
|
80
|
-
false,
|
|
81
|
-
CoreUserSession,
|
|
82
|
-
any,
|
|
83
|
-
never,
|
|
84
|
-
never
|
|
85
|
-
> = PikkuWire<In, never, false, CoreUserSession, never, never, never>,
|
|
77
|
+
Wire extends PikkuWire<In, never, false, CoreUserSession, any, never, never> =
|
|
78
|
+
PikkuWire<In, never, false, CoreUserSession, never, never, never>,
|
|
86
79
|
> = (services: Services, data: In, wire: Wire) => Promise<boolean>
|
|
87
80
|
|
|
88
81
|
/**
|
|
@@ -269,11 +262,8 @@ export type CorePikkuFunctionConfig<
|
|
|
269
262
|
PikkuFunction extends
|
|
270
263
|
| CorePikkuFunction<any, any, any, any, any>
|
|
271
264
|
| CorePikkuFunctionSessionless<any, any, any, any, any>,
|
|
272
|
-
PikkuPermission extends CorePikkuPermission<
|
|
273
|
-
any
|
|
274
|
-
any,
|
|
275
|
-
any
|
|
276
|
-
> = CorePikkuPermission<any>,
|
|
265
|
+
PikkuPermission extends CorePikkuPermission<any, any, any> =
|
|
266
|
+
CorePikkuPermission<any>,
|
|
277
267
|
PikkuMiddleware extends CorePikkuMiddleware<any, any> = CorePikkuMiddleware<
|
|
278
268
|
any,
|
|
279
269
|
any
|
|
@@ -292,6 +282,7 @@ export type CorePikkuFunctionConfig<
|
|
|
292
282
|
remote?: boolean
|
|
293
283
|
mcp?: boolean
|
|
294
284
|
readonly?: boolean
|
|
285
|
+
deploy?: 'serverless' | 'server' | 'auto'
|
|
295
286
|
approvalRequired?: boolean
|
|
296
287
|
approvalDescription?: any
|
|
297
288
|
func: PikkuFunction
|
package/src/index.ts
CHANGED
|
@@ -98,7 +98,16 @@ export type { QueueService } from './wirings/queue/queue.types.js'
|
|
|
98
98
|
export type { JWTService } from './services/jwt-service.js'
|
|
99
99
|
export type { SecretService } from './services/secret-service.js'
|
|
100
100
|
export type { VariablesService } from './services/variables-service.js'
|
|
101
|
-
export type {
|
|
101
|
+
export type {
|
|
102
|
+
ContentService,
|
|
103
|
+
SignContentKeyArgs,
|
|
104
|
+
SignURLArgs,
|
|
105
|
+
GetUploadURLArgs,
|
|
106
|
+
UploadURLResult,
|
|
107
|
+
BucketKeyArgs,
|
|
108
|
+
WriteFileArgs,
|
|
109
|
+
CopyFileArgs,
|
|
110
|
+
} from './services/content-service.js'
|
|
102
111
|
export type { DeploymentService } from './services/deployment-service.js'
|
|
103
112
|
export type { WorkflowService } from './services/workflow-service.js'
|
|
104
113
|
export type { GatewayService } from './services/gateway-service.js'
|
|
@@ -1,81 +1,109 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Arguments for signing a content key into a time-limited URL.
|
|
3
|
+
*/
|
|
4
|
+
export interface SignContentKeyArgs<TBucket extends string = string> {
|
|
5
|
+
bucket: TBucket
|
|
6
|
+
contentKey: string
|
|
7
|
+
dateLessThan: Date
|
|
8
|
+
dateGreaterThan?: Date
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Arguments for signing an arbitrary URL.
|
|
13
|
+
*/
|
|
14
|
+
export interface SignURLArgs {
|
|
15
|
+
url: string
|
|
16
|
+
dateLessThan: Date
|
|
17
|
+
dateGreaterThan?: Date
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Arguments for minting a presigned upload URL.
|
|
22
|
+
*/
|
|
23
|
+
export interface GetUploadURLArgs<TBucket extends string = string> {
|
|
24
|
+
bucket: TBucket
|
|
25
|
+
fileKey: string
|
|
26
|
+
contentType: string
|
|
27
|
+
size?: number
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Result of minting a presigned upload URL.
|
|
32
|
+
*/
|
|
33
|
+
export interface UploadURLResult {
|
|
34
|
+
uploadUrl: string
|
|
35
|
+
assetKey: string
|
|
36
|
+
uploadHeaders?: Record<string, string>
|
|
37
|
+
uploadMethod?: 'PUT' | 'POST'
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Arguments for an operation that targets a single object by key.
|
|
42
|
+
*/
|
|
43
|
+
export interface BucketKeyArgs<TBucket extends string = string> {
|
|
44
|
+
bucket: TBucket
|
|
45
|
+
key: string
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Arguments for writing a stream to storage.
|
|
50
|
+
*/
|
|
51
|
+
export interface WriteFileArgs<
|
|
52
|
+
TBucket extends string = string,
|
|
53
|
+
> extends BucketKeyArgs<TBucket> {
|
|
54
|
+
stream: ReadableStream | NodeJS.ReadableStream
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Arguments for copying a local file into storage.
|
|
59
|
+
*/
|
|
60
|
+
export interface CopyFileArgs<
|
|
61
|
+
TBucket extends string = string,
|
|
62
|
+
> extends BucketKeyArgs<TBucket> {
|
|
63
|
+
fromAbsolutePath: string
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface ContentService<TBucket extends string = string> {
|
|
2
67
|
/**
|
|
3
68
|
* Signs a content key to generate a secure, time-limited access URL.
|
|
4
|
-
* @param contentKey - The key representing the content object.
|
|
5
|
-
* @param dateLessThan - The expiration time for the signed URL.
|
|
6
|
-
* @param dateGreaterThan - (Optional) Start time before which access is denied.
|
|
7
69
|
*/
|
|
8
|
-
signContentKey(
|
|
9
|
-
contentKey: string,
|
|
10
|
-
dateLessThan: Date,
|
|
11
|
-
dateGreaterThan?: Date
|
|
12
|
-
): Promise<string>
|
|
70
|
+
signContentKey(args: SignContentKeyArgs<TBucket>): Promise<string>
|
|
13
71
|
|
|
14
72
|
/**
|
|
15
73
|
* Signs an arbitrary URL to generate a secure, time-limited access URL.
|
|
16
|
-
* @param url - The full URL that needs signing.
|
|
17
|
-
* @param dateLessThan - The expiration time for the signed URL.
|
|
18
|
-
* @param dateGreaterThan - (Optional) Start time before which access is denied.
|
|
19
74
|
*/
|
|
20
|
-
signURL(
|
|
21
|
-
url: string,
|
|
22
|
-
dateLessThan: Date,
|
|
23
|
-
dateGreaterThan?: Date
|
|
24
|
-
): Promise<string>
|
|
75
|
+
signURL(args: SignURLArgs): Promise<string>
|
|
25
76
|
|
|
26
77
|
/**
|
|
27
78
|
* Generates a signed URL for uploading a file directly to storage.
|
|
28
|
-
*
|
|
29
|
-
* @param contentType - The MIME type of the file.
|
|
30
|
-
* @returns A signed upload URL and the finalized asset key.
|
|
79
|
+
* Bucket policy (size limits, MIME allowlist) is enforced by the implementation.
|
|
31
80
|
*/
|
|
32
|
-
getUploadURL(
|
|
33
|
-
fileKey: string,
|
|
34
|
-
contentType: string
|
|
35
|
-
): Promise<{
|
|
36
|
-
uploadUrl: string
|
|
37
|
-
assetKey: string
|
|
38
|
-
uploadHeaders?: Record<string, string>
|
|
39
|
-
uploadMethod?: 'PUT' | 'POST'
|
|
40
|
-
}>
|
|
81
|
+
getUploadURL(args: GetUploadURLArgs<TBucket>): Promise<UploadURLResult>
|
|
41
82
|
|
|
42
83
|
/**
|
|
43
84
|
* Deletes a file from the storage backend.
|
|
44
|
-
* @param fileName - The name or key of the file to delete.
|
|
45
|
-
* @returns A boolean indicating success.
|
|
46
85
|
*/
|
|
47
|
-
deleteFile(
|
|
86
|
+
deleteFile(args: BucketKeyArgs<TBucket>): Promise<boolean>
|
|
48
87
|
|
|
49
88
|
/**
|
|
50
|
-
* Uploads a file stream to storage under
|
|
51
|
-
* @param assetKey - The key where the file will be saved.
|
|
52
|
-
* @param stream - A readable stream of the file contents.
|
|
53
|
-
* @returns A boolean indicating success.
|
|
89
|
+
* Uploads a file stream to storage under the specified bucket + key.
|
|
54
90
|
*/
|
|
55
|
-
writeFile(
|
|
56
|
-
assetKey: string,
|
|
57
|
-
stream: ReadableStream | NodeJS.ReadableStream
|
|
58
|
-
): Promise<boolean>
|
|
91
|
+
writeFile(args: WriteFileArgs<TBucket>): Promise<boolean>
|
|
59
92
|
|
|
60
93
|
/**
|
|
61
|
-
* Copies a file from a local absolute path into storage
|
|
62
|
-
* @param assetKey - The destination key.
|
|
63
|
-
* @param fromAbsolutePath - The local absolute file path.
|
|
64
|
-
* @returns A boolean indicating success.
|
|
94
|
+
* Copies a file from a local absolute path into storage.
|
|
65
95
|
*/
|
|
66
|
-
copyFile(
|
|
96
|
+
copyFile(args: CopyFileArgs<TBucket>): Promise<boolean>
|
|
67
97
|
|
|
68
98
|
/**
|
|
69
99
|
* Reads a file from storage as a readable stream.
|
|
70
|
-
* @param assetKey - The key of the file to read.
|
|
71
|
-
* @returns A readable file stream.
|
|
72
100
|
*/
|
|
73
|
-
readFile(
|
|
101
|
+
readFile(
|
|
102
|
+
args: BucketKeyArgs<TBucket>
|
|
103
|
+
): Promise<ReadableStream | NodeJS.ReadableStream>
|
|
74
104
|
|
|
75
105
|
/**
|
|
76
106
|
* Reads an entire file from storage into a Buffer.
|
|
77
|
-
* @param assetKey - The key of the file to read.
|
|
78
|
-
* @returns The file contents as a Buffer.
|
|
79
107
|
*/
|
|
80
|
-
readFileAsBuffer(
|
|
108
|
+
readFileAsBuffer(args: BucketKeyArgs<TBucket>): Promise<Buffer>
|
|
81
109
|
}
|
|
@@ -46,6 +46,27 @@ describe('InMemoryWorkflowService', () => {
|
|
|
46
46
|
const state = await service.getRunState(runId)
|
|
47
47
|
assert.deepStrictEqual(state, {})
|
|
48
48
|
})
|
|
49
|
+
|
|
50
|
+
test('should persist deterministic planned steps metadata', async () => {
|
|
51
|
+
const runId = await service.createRun(
|
|
52
|
+
'wf',
|
|
53
|
+
{},
|
|
54
|
+
true,
|
|
55
|
+
'h',
|
|
56
|
+
{} as any,
|
|
57
|
+
{
|
|
58
|
+
deterministic: true,
|
|
59
|
+
plannedSteps: [{ stepName: 'Step 1' }, { stepName: 'Step 2' }],
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
const run = await service.getRun(runId)
|
|
64
|
+
assert.strictEqual(run?.deterministic, true)
|
|
65
|
+
assert.deepStrictEqual(run?.plannedSteps, [
|
|
66
|
+
{ stepName: 'Step 1' },
|
|
67
|
+
{ stepName: 'Step 2' },
|
|
68
|
+
])
|
|
69
|
+
})
|
|
49
70
|
})
|
|
50
71
|
|
|
51
72
|
describe('getRun', () => {
|
|
@@ -2,6 +2,7 @@ import { randomUUID } from 'crypto'
|
|
|
2
2
|
import { PikkuWorkflowService } from '../wirings/workflow/pikku-workflow-service.js'
|
|
3
3
|
import type { SerializedError } from '../types/core.types.js'
|
|
4
4
|
import type {
|
|
5
|
+
WorkflowPlannedStep,
|
|
5
6
|
WorkflowRun,
|
|
6
7
|
WorkflowRunService,
|
|
7
8
|
WorkflowRunWire,
|
|
@@ -55,7 +56,11 @@ export class InMemoryWorkflowService
|
|
|
55
56
|
input: any,
|
|
56
57
|
inline: boolean,
|
|
57
58
|
graphHash: string,
|
|
58
|
-
wire: WorkflowRunWire
|
|
59
|
+
wire: WorkflowRunWire,
|
|
60
|
+
options?: {
|
|
61
|
+
deterministic?: boolean
|
|
62
|
+
plannedSteps?: WorkflowPlannedStep[]
|
|
63
|
+
}
|
|
59
64
|
): Promise<string> {
|
|
60
65
|
const runId = randomUUID()
|
|
61
66
|
const now = new Date()
|
|
@@ -67,6 +72,8 @@ export class InMemoryWorkflowService
|
|
|
67
72
|
input,
|
|
68
73
|
inline,
|
|
69
74
|
graphHash,
|
|
75
|
+
deterministic: options?.deterministic,
|
|
76
|
+
plannedSteps: options?.plannedSteps,
|
|
70
77
|
wire,
|
|
71
78
|
createdAt: now,
|
|
72
79
|
updatedAt: now,
|
package/src/services/index.ts
CHANGED
|
@@ -25,7 +25,16 @@ export { InMemoryQueueService } from './in-memory-queue-service.js'
|
|
|
25
25
|
export { InMemoryTriggerService } from './in-memory-trigger-service.js'
|
|
26
26
|
export { InMemoryAIRunStateService } from './in-memory-ai-run-state-service.js'
|
|
27
27
|
export { LocalGatewayService } from './local-gateway-service.js'
|
|
28
|
-
export type {
|
|
28
|
+
export type {
|
|
29
|
+
ContentService,
|
|
30
|
+
SignContentKeyArgs,
|
|
31
|
+
SignURLArgs,
|
|
32
|
+
GetUploadURLArgs,
|
|
33
|
+
UploadURLResult,
|
|
34
|
+
BucketKeyArgs,
|
|
35
|
+
WriteFileArgs,
|
|
36
|
+
CopyFileArgs,
|
|
37
|
+
} from './content-service.js'
|
|
29
38
|
export type { JWTService } from './jwt-service.js'
|
|
30
39
|
export type { Logger } from './logger.js'
|
|
31
40
|
export type { SecretService } from './secret-service.js'
|