@codemation/core 0.3.0 → 0.5.0
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 +23 -0
- package/dist/{EngineRuntimeRegistration.types-Bjeo7Sfq.d.ts → EngineRuntimeRegistration.types-BtTZolK0.d.ts} +2 -2
- package/dist/{EngineWorkflowRunnerService-Dd4yD31l.d.cts → EngineWorkflowRunnerService-Ddl0fekp.d.cts} +2 -2
- package/dist/{InMemoryRunDataFactory-OUzDmAHt.d.cts → InMemoryRunDataFactory-i-u2yngD.d.cts} +11 -3
- package/dist/{RunIntentService-Bkg4oYrM.d.cts → RunIntentService-Cjx-glgz.d.cts} +232 -237
- package/dist/{RunIntentService-BAKikN8h.d.ts → RunIntentService-Dkr4YwN8.d.ts} +313 -259
- package/dist/bootstrap/index.cjs +2 -2
- package/dist/bootstrap/index.d.cts +19 -7
- package/dist/bootstrap/index.d.ts +3 -3
- package/dist/bootstrap/index.js +2 -2
- package/dist/{bootstrap-DwS5S7s9.cjs → bootstrap-DHH2uo-W.cjs} +4 -2
- package/dist/bootstrap-DHH2uo-W.cjs.map +1 -0
- package/dist/{bootstrap-BD6CobHl.js → bootstrap-DbUlOl11.js} +4 -2
- package/dist/bootstrap-DbUlOl11.js.map +1 -0
- package/dist/{index-uCm9l0nw.d.ts → index-B2v4wtys.d.ts} +62 -34
- package/dist/index.cjs +22 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +108 -42
- package/dist/index.d.ts +3 -3
- package/dist/index.js +13 -16
- package/dist/index.js.map +1 -1
- package/dist/{runtime-Cy-3FTI_.js → runtime-BdH94eBR.js} +502 -123
- package/dist/runtime-BdH94eBR.js.map +1 -0
- package/dist/{runtime-ZJUpWmPH.cjs → runtime-feFn8OmG.cjs} +561 -122
- package/dist/runtime-feFn8OmG.cjs.map +1 -0
- package/dist/testing.cjs +40 -36
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +17 -26
- package/dist/testing.d.ts +17 -26
- package/dist/testing.js +40 -36
- package/dist/testing.js.map +1 -1
- package/dist/{workflowActivationPolicy-BzyzXLa_.cjs → workflowActivationPolicy-6V3OJD3N.cjs} +65 -19
- package/dist/workflowActivationPolicy-6V3OJD3N.cjs.map +1 -0
- package/dist/{workflowActivationPolicy-B8HzTk3o.js → workflowActivationPolicy-Td9HTOuD.js} +65 -19
- package/dist/workflowActivationPolicy-Td9HTOuD.js.map +1 -0
- package/package.json +2 -1
- package/src/ai/AgentConfigInspectorFactory.ts +4 -0
- package/src/ai/AgentMessageConfigNormalizerFactory.ts +7 -0
- package/src/ai/AgentToolFactory.ts +2 -2
- package/src/ai/AiHost.ts +11 -10
- package/src/ai/NodeBackedToolConfig.ts +1 -1
- package/src/authoring/defineNode.types.ts +48 -72
- package/src/authoring/index.ts +1 -1
- package/src/bootstrap/runtime/EngineRuntimeRegistrar.ts +8 -0
- package/src/contracts/credentialTypes.ts +9 -0
- package/src/contracts/emitPorts.ts +27 -0
- package/src/contracts/index.ts +3 -0
- package/src/contracts/itemMeta.ts +11 -0
- package/src/contracts/itemValue.ts +147 -0
- package/src/contracts/runtimeTypes.ts +39 -22
- package/src/contracts/workflowTypes.ts +26 -56
- package/src/execution/FanInMergeByOriginMerger.ts +67 -0
- package/src/execution/ItemValueResolver.ts +27 -0
- package/src/execution/NodeActivationRequestComposer.ts +25 -0
- package/src/execution/NodeActivationRequestInputPreparer.ts +57 -25
- package/src/execution/NodeExecutor.ts +199 -30
- package/src/execution/NodeOutputNormalizer.ts +90 -0
- package/src/execution/index.ts +2 -0
- package/src/index.ts +2 -0
- package/src/orchestration/NodeExecutionRequestHandlerService.ts +39 -18
- package/src/orchestration/RunContinuationService.ts +11 -17
- package/src/planning/CurrentStateFrontierPlanner.ts +20 -20
- package/src/planning/RunQueuePlanner.ts +56 -19
- package/src/planning/WorkflowTopologyPlanner.ts +57 -33
- package/src/testing/ItemHarnessNode.ts +4 -10
- package/src/testing/ItemHarnessNodeConfig.ts +7 -16
- package/src/testing/RegistrarEngineTestKitFactory.ts +2 -0
- package/src/testing/SubWorkflowRunnerTestNode.ts +28 -43
- package/src/testing/SwitchHarnessNode.ts +54 -0
- package/src/types/index.ts +3 -0
- package/src/workflow/dsl/ChainCursorResolver.ts +68 -23
- package/src/workflow/dsl/WorkflowBuilder.ts +3 -5
- package/src/workflow/dsl/workflowBuilderTypes.ts +5 -8
- package/src/workflowSnapshots/MissingRuntimeNode.ts +4 -4
- package/src/workflowSnapshots/MissingRuntimeNodeConfig.ts +2 -2
- package/src/workflowSnapshots/WorkflowSnapshotCodec.ts +16 -7
- package/dist/bootstrap-BD6CobHl.js.map +0 -1
- package/dist/bootstrap-DwS5S7s9.cjs.map +0 -1
- package/dist/runtime-Cy-3FTI_.js.map +0 -1
- package/dist/runtime-ZJUpWmPH.cjs.map +0 -1
- package/dist/workflowActivationPolicy-B8HzTk3o.js.map +0 -1
- package/dist/workflowActivationPolicy-BzyzXLa_.cjs.map +0 -1
package/src/ai/AiHost.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { TypeToken } from "../di";
|
|
2
2
|
|
|
3
3
|
import type { CredentialRequirement } from "../contracts/credentialTypes";
|
|
4
|
+
import type { ItemValue } from "../contracts/itemValue";
|
|
4
5
|
|
|
5
6
|
import type {
|
|
6
7
|
Item,
|
|
@@ -91,6 +92,7 @@ export type AgentMessageLine<TInputJson = unknown> = AgentMessageDto | AgentMess
|
|
|
91
92
|
* Use the object form only when you need `buildMessages` to append messages after optional `prompt` lines.
|
|
92
93
|
*/
|
|
93
94
|
export type AgentMessageConfig<TInputJson = unknown> =
|
|
95
|
+
| ItemValue<ReadonlyArray<AgentMessageLine<TInputJson>>, TInputJson>
|
|
94
96
|
| ReadonlyArray<AgentMessageLine<TInputJson>>
|
|
95
97
|
| {
|
|
96
98
|
readonly prompt?: ReadonlyArray<AgentMessageLine<TInputJson>>;
|
|
@@ -150,7 +152,7 @@ export interface ChatModelFactory<TConfig extends ChatModelConfig = ChatModelCon
|
|
|
150
152
|
}
|
|
151
153
|
|
|
152
154
|
export type NodeBackedToolInputMapperArgs<
|
|
153
|
-
TNodeConfig extends RunnableNodeConfig<any, any
|
|
155
|
+
TNodeConfig extends RunnableNodeConfig<any, any>,
|
|
154
156
|
TToolInput = unknown,
|
|
155
157
|
> = Readonly<{
|
|
156
158
|
input: TToolInput;
|
|
@@ -162,7 +164,7 @@ export type NodeBackedToolInputMapperArgs<
|
|
|
162
164
|
}>;
|
|
163
165
|
|
|
164
166
|
export type NodeBackedToolOutputMapperArgs<
|
|
165
|
-
TNodeConfig extends RunnableNodeConfig<any, any
|
|
167
|
+
TNodeConfig extends RunnableNodeConfig<any, any>,
|
|
166
168
|
TToolInput = unknown,
|
|
167
169
|
> = Readonly<{
|
|
168
170
|
input: TToolInput;
|
|
@@ -174,18 +176,18 @@ export type NodeBackedToolOutputMapperArgs<
|
|
|
174
176
|
outputs: NodeOutputs;
|
|
175
177
|
}>;
|
|
176
178
|
|
|
177
|
-
export type NodeBackedToolInputMapper<TNodeConfig extends RunnableNodeConfig<any, any
|
|
179
|
+
export type NodeBackedToolInputMapper<TNodeConfig extends RunnableNodeConfig<any, any>, TToolInput = unknown> = (
|
|
178
180
|
args: NodeBackedToolInputMapperArgs<TNodeConfig, TToolInput>,
|
|
179
181
|
) => Item<RunnableNodeInputJson<TNodeConfig>> | RunnableNodeInputJson<TNodeConfig>;
|
|
180
182
|
|
|
181
183
|
export type NodeBackedToolOutputMapper<
|
|
182
|
-
TNodeConfig extends RunnableNodeConfig<any, any
|
|
184
|
+
TNodeConfig extends RunnableNodeConfig<any, any>,
|
|
183
185
|
TToolInput = unknown,
|
|
184
186
|
TToolOutput = unknown,
|
|
185
187
|
> = (args: NodeBackedToolOutputMapperArgs<TNodeConfig, TToolInput>) => TToolOutput;
|
|
186
188
|
|
|
187
189
|
export type NodeBackedToolConfigOptions<
|
|
188
|
-
TNodeConfig extends RunnableNodeConfig<any, any
|
|
190
|
+
TNodeConfig extends RunnableNodeConfig<any, any>,
|
|
189
191
|
TInputSchema extends ZodSchemaAny,
|
|
190
192
|
TOutputSchema extends ZodSchemaAny,
|
|
191
193
|
> = Readonly<{
|
|
@@ -197,11 +199,10 @@ export type NodeBackedToolConfigOptions<
|
|
|
197
199
|
mapOutput?: NodeBackedToolOutputMapper<TNodeConfig, ZodInput<TInputSchema>, ZodOutput<TOutputSchema>>;
|
|
198
200
|
}>;
|
|
199
201
|
|
|
200
|
-
export interface AgentNodeConfig<
|
|
201
|
-
TInputJson
|
|
202
|
-
TOutputJson
|
|
203
|
-
|
|
204
|
-
> extends RunnableNodeConfig<TInputJson, TOutputJson, TWireJson> {
|
|
202
|
+
export interface AgentNodeConfig<TInputJson = unknown, TOutputJson = unknown> extends RunnableNodeConfig<
|
|
203
|
+
TInputJson,
|
|
204
|
+
TOutputJson
|
|
205
|
+
> {
|
|
205
206
|
readonly messages: AgentMessageConfig<TInputJson>;
|
|
206
207
|
readonly chatModel: ChatModelConfig;
|
|
207
208
|
readonly tools?: ReadonlyArray<ToolConfig>;
|
|
@@ -14,7 +14,7 @@ import type {
|
|
|
14
14
|
} from "./AiHost";
|
|
15
15
|
|
|
16
16
|
export class NodeBackedToolConfig<
|
|
17
|
-
TNodeConfig extends RunnableNodeConfig<any, any
|
|
17
|
+
TNodeConfig extends RunnableNodeConfig<any, any>,
|
|
18
18
|
TInputSchema extends ZodSchemaAny,
|
|
19
19
|
TOutputSchema extends ZodSchemaAny,
|
|
20
20
|
> implements ToolConfig {
|
|
@@ -4,8 +4,8 @@ import type {
|
|
|
4
4
|
CredentialRequirement,
|
|
5
5
|
CredentialTypeId,
|
|
6
6
|
} from "../contracts/credentialTypes";
|
|
7
|
-
import type {
|
|
8
|
-
import type { Item,
|
|
7
|
+
import type { RunnableNode, RunnableNodeExecuteArgs, NodeExecutionContext } from "../contracts/runtimeTypes";
|
|
8
|
+
import type { Item, Items, RunnableNodeConfig } from "../contracts/workflowTypes";
|
|
9
9
|
import type { TypeToken } from "../di";
|
|
10
10
|
import { node as persistedNode } from "../runtime-types/runtimeTypeDecorators.types";
|
|
11
11
|
import type { ZodType } from "zod";
|
|
@@ -56,15 +56,15 @@ export interface DefinedNodeRunContext<
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
|
-
* Arguments for {@link defineNode} `
|
|
59
|
+
* Arguments for {@link defineNode} `execute` (engine `ctx` matches {@link RunnableNode.execute};
|
|
60
60
|
* the second callback parameter adds {@link DefinedNodeRunContext} for credential accessors).
|
|
61
61
|
*/
|
|
62
|
-
export type
|
|
62
|
+
export type DefineNodeExecuteArgs<TConfig extends CredentialJsonRecord, TInputJson> = Readonly<{
|
|
63
63
|
input: TInputJson;
|
|
64
64
|
item: Item;
|
|
65
65
|
itemIndex: number;
|
|
66
66
|
items: Items;
|
|
67
|
-
ctx: NodeExecutionContext<RunnableNodeConfig<TInputJson, unknown
|
|
67
|
+
ctx: NodeExecutionContext<RunnableNodeConfig<TInputJson, unknown> & Readonly<{ config: TConfig }>>;
|
|
68
68
|
}>;
|
|
69
69
|
|
|
70
70
|
export interface DefinedNode<
|
|
@@ -73,20 +73,17 @@ export interface DefinedNode<
|
|
|
73
73
|
TInputJson,
|
|
74
74
|
TOutputJson,
|
|
75
75
|
_TBindings extends DefinedNodeCredentialBindings | undefined = undefined,
|
|
76
|
-
TWireJson = TInputJson,
|
|
77
76
|
> {
|
|
78
77
|
readonly kind: "defined-node";
|
|
79
78
|
readonly key: TKey;
|
|
80
79
|
readonly title: string;
|
|
81
80
|
readonly description?: string;
|
|
82
|
-
create(config: TConfig, name?: string, id?: string): RunnableNodeConfig<TInputJson, TOutputJson
|
|
81
|
+
create(config: TConfig, name?: string, id?: string): RunnableNodeConfig<TInputJson, TOutputJson>;
|
|
83
82
|
register(context: { registerNode<TValue>(token: TypeToken<TValue>, implementation?: TypeToken<TValue>): void }): void;
|
|
84
83
|
}
|
|
85
84
|
|
|
86
85
|
/**
|
|
87
|
-
* Plugin / DSL-friendly node:
|
|
88
|
-
* The engine applies {@link RunnableNodeConfig.mapInput} (if any) + {@link RunnableNodeConfig.inputSchema}
|
|
89
|
-
* before `executeOne`. Use {@link defineBatchNode} for legacy batch `run(items, …)` semantics.
|
|
86
|
+
* Plugin / DSL-friendly node: per-item `execute` with optional {@link RunnableNodeConfig.inputSchema}.
|
|
90
87
|
*/
|
|
91
88
|
export interface DefineNodeOptions<
|
|
92
89
|
TKey extends string,
|
|
@@ -94,7 +91,6 @@ export interface DefineNodeOptions<
|
|
|
94
91
|
TInputJson,
|
|
95
92
|
TOutputJson,
|
|
96
93
|
TBindings extends DefinedNodeCredentialBindings | undefined = undefined,
|
|
97
|
-
TWireJson = TInputJson,
|
|
98
94
|
> {
|
|
99
95
|
readonly key: TKey;
|
|
100
96
|
readonly title: string;
|
|
@@ -109,21 +105,17 @@ export interface DefineNodeOptions<
|
|
|
109
105
|
readonly configSchema?: z.ZodType<TConfig>;
|
|
110
106
|
readonly credentials?: TBindings;
|
|
111
107
|
/**
|
|
112
|
-
* Validates **`input`**
|
|
108
|
+
* Validates **`input`** (engine also accepts `inputSchema` on the node class).
|
|
113
109
|
*/
|
|
114
110
|
readonly inputSchema?: ZodType<TInputJson>;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
*/
|
|
118
|
-
readonly mapInput?: ItemInputMapper<TWireJson, TInputJson>;
|
|
119
|
-
executeOne(
|
|
120
|
-
args: DefineNodeExecuteOneArgs<TConfig, TInputJson, TWireJson>,
|
|
111
|
+
execute(
|
|
112
|
+
args: DefineNodeExecuteArgs<TConfig, TInputJson>,
|
|
121
113
|
context: DefinedNodeRunContext<TConfig, TBindings>,
|
|
122
114
|
): MaybePromise<TOutputJson>;
|
|
123
115
|
}
|
|
124
116
|
|
|
125
117
|
/**
|
|
126
|
-
* Batch-oriented defined node
|
|
118
|
+
* Batch-oriented defined node: `run` receives all item JSON once (last item in activation); emits one output per input row.
|
|
127
119
|
*/
|
|
128
120
|
export interface DefineBatchNodeOptions<
|
|
129
121
|
TKey extends string,
|
|
@@ -197,7 +189,7 @@ const definedNodeCredentialRequirementFactory = {
|
|
|
197
189
|
const definedNodeCredentialAccessorFactory = {
|
|
198
190
|
create<TBindings extends DefinedNodeCredentialBindings | undefined>(
|
|
199
191
|
bindings: TBindings,
|
|
200
|
-
ctx: NodeExecutionContext<RunnableNodeConfig<any, any
|
|
192
|
+
ctx: NodeExecutionContext<RunnableNodeConfig<any, any>>,
|
|
201
193
|
): DefinedNodeCredentialAccessors<TBindings> {
|
|
202
194
|
if (!bindings) {
|
|
203
195
|
return {} as DefinedNodeCredentialAccessors<TBindings>;
|
|
@@ -213,28 +205,20 @@ export function defineNode<
|
|
|
213
205
|
TInputJson,
|
|
214
206
|
TOutputJson,
|
|
215
207
|
TBindings extends DefinedNodeCredentialBindings | undefined = undefined,
|
|
216
|
-
TWireJson = TInputJson,
|
|
217
208
|
>(
|
|
218
|
-
options: DefineNodeOptions<TKey, TConfig, TInputJson, TOutputJson, TBindings
|
|
219
|
-
): DefinedNode<TKey, TConfig, TInputJson, TOutputJson, TBindings
|
|
209
|
+
options: DefineNodeOptions<TKey, TConfig, TInputJson, TOutputJson, TBindings>,
|
|
210
|
+
): DefinedNode<TKey, TConfig, TInputJson, TOutputJson, TBindings> {
|
|
220
211
|
const credentialRequirements = definedNodeCredentialRequirementFactory.create(options.credentials);
|
|
221
|
-
type DefinedRunnableNodeConfigShape = RunnableNodeConfig<TInputJson, TOutputJson
|
|
222
|
-
Readonly<{ config: TConfig }>;
|
|
212
|
+
type DefinedRunnableNodeConfigShape = RunnableNodeConfig<TInputJson, TOutputJson> & Readonly<{ config: TConfig }>;
|
|
223
213
|
|
|
224
|
-
const DefinedNodeRuntime = class implements
|
|
214
|
+
const DefinedNodeRuntime = class implements RunnableNode<DefinedRunnableNodeConfigShape, TInputJson, TOutputJson> {
|
|
225
215
|
readonly kind = "node" as const;
|
|
226
216
|
readonly outputPorts = ["main"] as const;
|
|
227
217
|
readonly inputSchema = options.inputSchema;
|
|
228
218
|
|
|
229
|
-
async
|
|
230
|
-
args: Readonly<
|
|
231
|
-
|
|
232
|
-
item: Item;
|
|
233
|
-
itemIndex: number;
|
|
234
|
-
items: Items;
|
|
235
|
-
ctx: NodeExecutionContext<DefinedRunnableNodeConfigShape>;
|
|
236
|
-
}>,
|
|
237
|
-
): Promise<TOutputJson> {
|
|
219
|
+
async execute(
|
|
220
|
+
args: Readonly<RunnableNodeExecuteArgs<DefinedRunnableNodeConfigShape, TInputJson>>,
|
|
221
|
+
): Promise<unknown> {
|
|
238
222
|
const ctx = args.ctx;
|
|
239
223
|
const context: DefinedNodeRunContext<TConfig, TBindings> = {
|
|
240
224
|
config: ctx.config.config,
|
|
@@ -244,25 +228,24 @@ export function defineNode<
|
|
|
244
228
|
) as DefinedNodeCredentialAccessors<TBindings>,
|
|
245
229
|
execution: ctx as unknown as NodeExecutionContext<RunnableNodeConfig<TConfig, unknown>>,
|
|
246
230
|
};
|
|
247
|
-
const payload:
|
|
231
|
+
const payload: DefineNodeExecuteArgs<TConfig, TInputJson> = {
|
|
248
232
|
input: args.input,
|
|
249
233
|
item: args.item,
|
|
250
234
|
itemIndex: args.itemIndex,
|
|
251
235
|
items: args.items,
|
|
252
236
|
ctx,
|
|
253
237
|
};
|
|
254
|
-
return await options.
|
|
238
|
+
return await options.execute(payload, context);
|
|
255
239
|
}
|
|
256
240
|
};
|
|
257
241
|
|
|
258
242
|
persistedNode({ name: options.key })(DefinedNodeRuntime);
|
|
259
243
|
|
|
260
|
-
const DefinedRunnableNodeConfig = class implements RunnableNodeConfig<TInputJson, TOutputJson
|
|
244
|
+
const DefinedRunnableNodeConfig = class implements RunnableNodeConfig<TInputJson, TOutputJson> {
|
|
261
245
|
readonly kind = "node" as const;
|
|
262
246
|
readonly type: TypeToken<unknown> = DefinedNodeRuntime;
|
|
263
247
|
readonly icon = options.icon;
|
|
264
248
|
readonly inputSchema = options.inputSchema;
|
|
265
|
-
readonly mapInput = options.mapInput;
|
|
266
249
|
|
|
267
250
|
constructor(
|
|
268
251
|
public readonly name: string,
|
|
@@ -275,7 +258,7 @@ export function defineNode<
|
|
|
275
258
|
}
|
|
276
259
|
};
|
|
277
260
|
|
|
278
|
-
const definition: DefinedNode<TKey, TConfig, TInputJson, TOutputJson, TBindings
|
|
261
|
+
const definition: DefinedNode<TKey, TConfig, TInputJson, TOutputJson, TBindings> = {
|
|
279
262
|
kind: "defined-node",
|
|
280
263
|
key: options.key,
|
|
281
264
|
title: options.title,
|
|
@@ -288,9 +271,7 @@ export function defineNode<
|
|
|
288
271
|
},
|
|
289
272
|
};
|
|
290
273
|
|
|
291
|
-
DefinedNodeRegistry.register(
|
|
292
|
-
definition as DefinedNode<string, Record<string, unknown>, unknown, unknown, undefined, unknown>,
|
|
293
|
-
);
|
|
274
|
+
DefinedNodeRegistry.register(definition as DefinedNode<string, Record<string, unknown>, unknown, unknown, undefined>);
|
|
294
275
|
|
|
295
276
|
return definition;
|
|
296
277
|
}
|
|
@@ -303,43 +284,40 @@ export function defineBatchNode<
|
|
|
303
284
|
TBindings extends DefinedNodeCredentialBindings | undefined = undefined,
|
|
304
285
|
>(
|
|
305
286
|
options: DefineBatchNodeOptions<TKey, TConfig, TInputJson, TOutputJson, TBindings>,
|
|
306
|
-
): DefinedNode<TKey, TConfig, TInputJson, TOutputJson, TBindings
|
|
287
|
+
): DefinedNode<TKey, TConfig, TInputJson, TOutputJson, TBindings> {
|
|
307
288
|
const credentialRequirements = definedNodeCredentialRequirementFactory.create(options.credentials);
|
|
308
|
-
type DefinedRunnableNodeConfigShape = RunnableNodeConfig<TInputJson, TOutputJson
|
|
309
|
-
Readonly<{ config: TConfig }>;
|
|
289
|
+
type DefinedRunnableNodeConfigShape = RunnableNodeConfig<TInputJson, TOutputJson> & Readonly<{ config: TConfig }>;
|
|
310
290
|
|
|
311
|
-
const DefinedNodeRuntime = class implements
|
|
291
|
+
const DefinedNodeRuntime = class implements RunnableNode<DefinedRunnableNodeConfigShape, TInputJson, TOutputJson> {
|
|
312
292
|
readonly kind = "node" as const;
|
|
313
293
|
readonly outputPorts = ["main"] as const;
|
|
314
294
|
|
|
315
|
-
async execute(
|
|
295
|
+
async execute(
|
|
296
|
+
args: Readonly<RunnableNodeExecuteArgs<DefinedRunnableNodeConfigShape, TInputJson>>,
|
|
297
|
+
): Promise<unknown> {
|
|
298
|
+
if (args.itemIndex !== args.items.length - 1) {
|
|
299
|
+
return [];
|
|
300
|
+
}
|
|
301
|
+
const ctx = args.ctx;
|
|
302
|
+
const context: DefinedNodeRunContext<TConfig, TBindings> = {
|
|
303
|
+
config: ctx.config.config,
|
|
304
|
+
credentials: definedNodeCredentialAccessorFactory.create(
|
|
305
|
+
options.credentials,
|
|
306
|
+
ctx,
|
|
307
|
+
) as DefinedNodeCredentialAccessors<TBindings>,
|
|
308
|
+
execution: ctx as unknown as NodeExecutionContext<RunnableNodeConfig<TConfig, unknown>>,
|
|
309
|
+
};
|
|
316
310
|
const outputs = await options.run(
|
|
317
|
-
items.map((item) => item.json as TInputJson),
|
|
318
|
-
|
|
319
|
-
config: ctx.config.config,
|
|
320
|
-
credentials: definedNodeCredentialAccessorFactory.create(
|
|
321
|
-
options.credentials,
|
|
322
|
-
ctx,
|
|
323
|
-
) as DefinedNodeCredentialAccessors<TBindings>,
|
|
324
|
-
execution: ctx as unknown as NodeExecutionContext<RunnableNodeConfig<TConfig, unknown>>,
|
|
325
|
-
},
|
|
311
|
+
args.items.map((item) => item.json as TInputJson),
|
|
312
|
+
context,
|
|
326
313
|
);
|
|
327
|
-
|
|
328
|
-
return {
|
|
329
|
-
main: outputs.map((json, index) => {
|
|
330
|
-
const existing = items[index];
|
|
331
|
-
if (!existing) {
|
|
332
|
-
return { json };
|
|
333
|
-
}
|
|
334
|
-
return { ...existing, json };
|
|
335
|
-
}),
|
|
336
|
-
};
|
|
314
|
+
return [...outputs];
|
|
337
315
|
}
|
|
338
316
|
};
|
|
339
317
|
|
|
340
318
|
persistedNode({ name: options.key })(DefinedNodeRuntime);
|
|
341
319
|
|
|
342
|
-
const DefinedRunnableNodeConfig = class implements RunnableNodeConfig<TInputJson, TOutputJson
|
|
320
|
+
const DefinedRunnableNodeConfig = class implements RunnableNodeConfig<TInputJson, TOutputJson> {
|
|
343
321
|
readonly kind = "node" as const;
|
|
344
322
|
readonly type: TypeToken<unknown> = DefinedNodeRuntime;
|
|
345
323
|
readonly icon = options.icon;
|
|
@@ -355,7 +333,7 @@ export function defineBatchNode<
|
|
|
355
333
|
}
|
|
356
334
|
};
|
|
357
335
|
|
|
358
|
-
const definition: DefinedNode<TKey, TConfig, TInputJson, TOutputJson, TBindings
|
|
336
|
+
const definition: DefinedNode<TKey, TConfig, TInputJson, TOutputJson, TBindings> = {
|
|
359
337
|
kind: "defined-node",
|
|
360
338
|
key: options.key,
|
|
361
339
|
title: options.title,
|
|
@@ -368,9 +346,7 @@ export function defineBatchNode<
|
|
|
368
346
|
},
|
|
369
347
|
};
|
|
370
348
|
|
|
371
|
-
DefinedNodeRegistry.register(
|
|
372
|
-
definition as DefinedNode<string, Record<string, unknown>, unknown, unknown, undefined, unknown>,
|
|
373
|
-
);
|
|
349
|
+
DefinedNodeRegistry.register(definition as DefinedNode<string, Record<string, unknown>, unknown, unknown, undefined>);
|
|
374
350
|
|
|
375
351
|
return definition;
|
|
376
352
|
}
|
package/src/authoring/index.ts
CHANGED
|
@@ -6,7 +6,7 @@ export type {
|
|
|
6
6
|
DefinedNodeCredentialBindings,
|
|
7
7
|
DefinedNodeRunContext,
|
|
8
8
|
DefineBatchNodeOptions,
|
|
9
|
-
|
|
9
|
+
DefineNodeExecuteArgs,
|
|
10
10
|
DefineNodeOptions,
|
|
11
11
|
} from "./defineNode.types";
|
|
12
12
|
export { defineBatchNode, defineNode } from "./defineNode.types";
|
|
@@ -4,9 +4,11 @@ import { EngineExecutionLimitsPolicyFactory } from "../../policies/executionLimi
|
|
|
4
4
|
import {
|
|
5
5
|
DefaultAsyncSleeper,
|
|
6
6
|
InProcessRetryRunnerFactory,
|
|
7
|
+
ItemValueResolver,
|
|
7
8
|
NodeExecutor,
|
|
8
9
|
NodeExecutorFactory,
|
|
9
10
|
NodeInstanceFactoryFactory,
|
|
11
|
+
NodeOutputNormalizer,
|
|
10
12
|
} from "../../execution";
|
|
11
13
|
import {
|
|
12
14
|
EngineFactory,
|
|
@@ -38,6 +40,12 @@ export class EngineRuntimeRegistrar {
|
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
private registerSupportFactories(container: DependencyContainer): void {
|
|
43
|
+
if (!container.isRegistered(ItemValueResolver, true)) {
|
|
44
|
+
container.registerSingleton(ItemValueResolver, ItemValueResolver);
|
|
45
|
+
}
|
|
46
|
+
if (!container.isRegistered(NodeOutputNormalizer, true)) {
|
|
47
|
+
container.registerSingleton(NodeOutputNormalizer, NodeOutputNormalizer);
|
|
48
|
+
}
|
|
41
49
|
container.register(EngineExecutionLimitsPolicyFactory, { useClass: EngineExecutionLimitsPolicyFactory });
|
|
42
50
|
container.register(NodeInstanceFactoryFactory, { useClass: NodeInstanceFactoryFactory });
|
|
43
51
|
container.register(DefaultAsyncSleeper, { useClass: DefaultAsyncSleeper });
|
|
@@ -61,11 +61,19 @@ export type OAuth2ProviderFromPublicConfig = Readonly<{
|
|
|
61
61
|
userInfoUrlFieldKey?: string;
|
|
62
62
|
}>;
|
|
63
63
|
|
|
64
|
+
export type CredentialOAuth2ScopesFromPublicConfig = Readonly<{
|
|
65
|
+
presetFieldKey: string;
|
|
66
|
+
presetScopes: Readonly<Record<string, ReadonlyArray<string>>>;
|
|
67
|
+
customPresetKey?: string;
|
|
68
|
+
customScopesFieldKey?: string;
|
|
69
|
+
}>;
|
|
70
|
+
|
|
64
71
|
export type CredentialOAuth2AuthDefinition = Readonly<
|
|
65
72
|
| {
|
|
66
73
|
kind: "oauth2";
|
|
67
74
|
providerId: string;
|
|
68
75
|
scopes: ReadonlyArray<string>;
|
|
76
|
+
scopesFromPublicConfig?: CredentialOAuth2ScopesFromPublicConfig;
|
|
69
77
|
clientIdFieldKey?: string;
|
|
70
78
|
clientSecretFieldKey?: string;
|
|
71
79
|
}
|
|
@@ -73,6 +81,7 @@ export type CredentialOAuth2AuthDefinition = Readonly<
|
|
|
73
81
|
kind: "oauth2";
|
|
74
82
|
providerFromPublicConfig: OAuth2ProviderFromPublicConfig;
|
|
75
83
|
scopes: ReadonlyArray<string>;
|
|
84
|
+
scopesFromPublicConfig?: CredentialOAuth2ScopesFromPublicConfig;
|
|
76
85
|
clientIdFieldKey?: string;
|
|
77
86
|
clientSecretFieldKey?: string;
|
|
78
87
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Items, JsonNonArray, OutputPortKey } from "./workflowTypes";
|
|
2
|
+
|
|
3
|
+
const EMIT_PORTS_BRAND = Symbol.for("codemation.emitPorts");
|
|
4
|
+
|
|
5
|
+
export type PortsEmission = Readonly<{
|
|
6
|
+
readonly [EMIT_PORTS_BRAND]: true;
|
|
7
|
+
readonly ports: Readonly<Partial<Record<OutputPortKey, Items | ReadonlyArray<JsonNonArray>>>>;
|
|
8
|
+
}>;
|
|
9
|
+
|
|
10
|
+
export function emitPorts(
|
|
11
|
+
ports: Readonly<Partial<Record<OutputPortKey, Items | ReadonlyArray<JsonNonArray>>>>,
|
|
12
|
+
): PortsEmission {
|
|
13
|
+
return { [EMIT_PORTS_BRAND]: true, ports };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function isPortsEmission(value: unknown): value is PortsEmission {
|
|
17
|
+
return (
|
|
18
|
+
typeof value === "object" &&
|
|
19
|
+
value !== null &&
|
|
20
|
+
EMIT_PORTS_BRAND in value &&
|
|
21
|
+
(value as Record<symbol, unknown>)[EMIT_PORTS_BRAND] === true
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function isUnbrandedPortsEmissionShape(value: unknown): value is Readonly<{ ports: unknown }> {
|
|
26
|
+
return typeof value === "object" && value !== null && "ports" in value && !isPortsEmission(value);
|
|
27
|
+
}
|
package/src/contracts/index.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export * from "./credentialTypes";
|
|
2
|
+
export * from "./emitPorts";
|
|
2
3
|
export * from "./executionPersistenceContracts";
|
|
4
|
+
export * from "./itemMeta";
|
|
5
|
+
export * from "./itemValue";
|
|
3
6
|
export * from "./runtimeTypes";
|
|
4
7
|
export * from "./runFinishedAtFactory";
|
|
5
8
|
export * from "./runTypes";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Item } from "./workflowTypes";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Reads `meta._cm.originIndex` when present (used for fan-in merge-by-origin and Merge routing).
|
|
5
|
+
*/
|
|
6
|
+
export function getOriginIndexFromItem(item: Item): number | undefined {
|
|
7
|
+
const meta = item.meta as Record<string, unknown> | undefined;
|
|
8
|
+
const cm = meta?._cm as Record<string, unknown> | undefined;
|
|
9
|
+
const v = cm?.originIndex;
|
|
10
|
+
return typeof v === "number" && Number.isFinite(v) ? v : undefined;
|
|
11
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type { NodeExecutionContext } from "./runtimeTypes";
|
|
2
|
+
import type { Item, Items, NodeActivationId, NodeId, RunDataSnapshot, RunId, WorkflowId } from "./workflowTypes";
|
|
3
|
+
|
|
4
|
+
const ITEM_VALUE_BRAND = Symbol.for("codemation.itemValue");
|
|
5
|
+
|
|
6
|
+
export type ItemValueResolvedContext = Readonly<{
|
|
7
|
+
runId: RunId;
|
|
8
|
+
workflowId: WorkflowId;
|
|
9
|
+
nodeId: NodeId;
|
|
10
|
+
activationId: NodeActivationId;
|
|
11
|
+
data: RunDataSnapshot;
|
|
12
|
+
}>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Context aligned with former {@link ItemInputMapperContext} — use **`data`** to read any completed upstream node.
|
|
16
|
+
*/
|
|
17
|
+
export type ItemValueContext = ItemValueResolvedContext;
|
|
18
|
+
|
|
19
|
+
export type ItemValueArgs<TItemJson = unknown> = Readonly<{
|
|
20
|
+
item: Item<TItemJson>;
|
|
21
|
+
itemIndex: number;
|
|
22
|
+
items: Items<TItemJson>;
|
|
23
|
+
ctx: ItemValueContext;
|
|
24
|
+
}>;
|
|
25
|
+
|
|
26
|
+
export type ItemValueCallback<T, TItemJson = unknown> = (args: ItemValueArgs<TItemJson>) => T | Promise<T>;
|
|
27
|
+
|
|
28
|
+
export type ItemValue<T, TItemJson = unknown> = Readonly<{
|
|
29
|
+
readonly [ITEM_VALUE_BRAND]: true;
|
|
30
|
+
readonly fn: ItemValueCallback<T, TItemJson>;
|
|
31
|
+
}>;
|
|
32
|
+
|
|
33
|
+
export function itemValue<T, TItemJson = unknown>(fn: ItemValueCallback<T, TItemJson>): ItemValue<T, TItemJson> {
|
|
34
|
+
return { [ITEM_VALUE_BRAND]: true, fn };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function isItemValue<T, TItemJson = unknown>(value: unknown): value is ItemValue<T, TItemJson> {
|
|
38
|
+
if (typeof value !== "object" || value === null) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
const v = value as Record<PropertyKey, unknown>;
|
|
42
|
+
if (v[ITEM_VALUE_BRAND] === true) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
// Support snapshot-hydrated itemValue wrappers where the symbol brand was lost but the callback survived.
|
|
46
|
+
// Workflow snapshot hydration currently restores function-valued fields (like `fn`) but may drop symbol-keyed brands.
|
|
47
|
+
// We treat the minimal `{ fn: Function }` shape as an itemValue wrapper to keep runnable configs working.
|
|
48
|
+
const keys = Object.keys(v);
|
|
49
|
+
if (keys.length === 1 && keys[0] === "fn" && typeof (v as { fn?: unknown }).fn === "function") {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
// Support legacy module-local Symbol("codemation.itemValue") brands (e.g. duplicate module graphs).
|
|
53
|
+
for (const sym of Object.getOwnPropertySymbols(v)) {
|
|
54
|
+
if (sym.description === "codemation.itemValue" && v[sym] === true) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function containsItemValueInUnknown(value: unknown, seen: WeakSet<object> = new WeakSet()): boolean {
|
|
62
|
+
if (isItemValue(value)) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
if (value === null || typeof value !== "object") {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
if (seen.has(value as object)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
seen.add(value as object);
|
|
72
|
+
if (Array.isArray(value)) {
|
|
73
|
+
return value.some((entry) => containsItemValueInUnknown(entry, seen));
|
|
74
|
+
}
|
|
75
|
+
for (const entry of Object.values(value as Record<string, unknown>)) {
|
|
76
|
+
if (containsItemValueInUnknown(entry, seen)) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Deep-resolves {@link itemValue} leaves. Returns a new graph (does not mutate the original config object).
|
|
85
|
+
*/
|
|
86
|
+
export async function resolveItemValuesInUnknown(
|
|
87
|
+
value: unknown,
|
|
88
|
+
args: ItemValueArgs,
|
|
89
|
+
seen: WeakSet<object> = new WeakSet(),
|
|
90
|
+
): Promise<unknown> {
|
|
91
|
+
if (isItemValue(value)) {
|
|
92
|
+
return await Promise.resolve(value.fn(args));
|
|
93
|
+
}
|
|
94
|
+
if (value === null || typeof value !== "object") {
|
|
95
|
+
return value;
|
|
96
|
+
}
|
|
97
|
+
if (seen.has(value as object)) {
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
100
|
+
seen.add(value as object);
|
|
101
|
+
if (Array.isArray(value)) {
|
|
102
|
+
const out: unknown[] = [];
|
|
103
|
+
for (let i = 0; i < value.length; i++) {
|
|
104
|
+
out.push(await resolveItemValuesInUnknown(value[i], args, seen));
|
|
105
|
+
}
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
const rec = value as Record<string, unknown>;
|
|
109
|
+
const entries = Object.entries(rec);
|
|
110
|
+
const proto = Object.getPrototypeOf(value);
|
|
111
|
+
if (proto !== Object.prototype && proto !== null && entries.length === 0) {
|
|
112
|
+
return value;
|
|
113
|
+
}
|
|
114
|
+
const out = Object.create(proto) as Record<string, unknown>;
|
|
115
|
+
for (const [k, v] of entries) {
|
|
116
|
+
out[k] = await resolveItemValuesInUnknown(v, args, seen);
|
|
117
|
+
}
|
|
118
|
+
return out;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Clones runnable config (best-effort) so per-item {@link itemValue} resolution never mutates shared instances.
|
|
123
|
+
*/
|
|
124
|
+
export async function resolveItemValuesForExecution(
|
|
125
|
+
config: unknown,
|
|
126
|
+
nodeCtx: NodeExecutionContext,
|
|
127
|
+
item: Item,
|
|
128
|
+
itemIndex: number,
|
|
129
|
+
items: Items,
|
|
130
|
+
): Promise<unknown | undefined> {
|
|
131
|
+
const ivArgs: ItemValueArgs = {
|
|
132
|
+
item,
|
|
133
|
+
itemIndex,
|
|
134
|
+
items,
|
|
135
|
+
ctx: {
|
|
136
|
+
runId: nodeCtx.runId,
|
|
137
|
+
workflowId: nodeCtx.workflowId,
|
|
138
|
+
nodeId: nodeCtx.nodeId,
|
|
139
|
+
activationId: nodeCtx.activationId,
|
|
140
|
+
data: nodeCtx.data,
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
if (!containsItemValueInUnknown(config)) {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
return await resolveItemValuesInUnknown(config, ivArgs);
|
|
147
|
+
}
|