@codemation/core-nodes 0.10.1 → 0.12.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 +125 -0
- package/dist/index.cjs +273 -108
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +212 -71
- package/dist/index.d.ts +213 -72
- package/dist/index.js +273 -105
- package/dist/index.js.map +1 -1
- package/dist/metadata.json +1 -1
- package/package.json +3 -2
- package/src/chatModels/CodemationChatModelConfig.ts +9 -21
- package/src/chatModels/CodemationChatModelFactory.ts +12 -9
- package/src/chatModels/OpenAIChatModelFactory.ts +3 -2
- package/src/http/HttpBodyBuilder.ts +9 -0
- package/src/http/httpRequest.types.ts +10 -1
- package/src/index.ts +1 -1
- package/src/nodes/AIAgentConfig.ts +28 -0
- package/src/nodes/AIAgentNode.ts +84 -17
- package/src/nodes/AgentBinaryContentFactory.ts +74 -0
- package/src/nodes/CallbackNodeFactory.ts +9 -6
- package/src/nodes/CronTriggerFactory.ts +6 -2
- package/src/nodes/DeferredMetaToolStrategy.ts +8 -2
- package/src/nodes/ManualTriggerFactory.ts +15 -11
- package/src/nodes/WebhookTriggerFactory.ts +9 -2
- package/src/nodes/aggregate.ts +9 -2
- package/src/nodes/assertion.ts +3 -0
- package/src/nodes/filter.ts +9 -2
- package/src/nodes/httpRequest.ts +7 -2
- package/src/nodes/if.ts +9 -2
- package/src/nodes/isTestRun.ts +6 -2
- package/src/nodes/mapData.ts +4 -2
- package/src/nodes/merge.ts +9 -2
- package/src/nodes/noOp.ts +9 -2
- package/src/nodes/nodeOptions.types.ts +12 -0
- package/src/nodes/split.ts +9 -2
- package/src/nodes/subWorkflow.ts +9 -2
- package/src/nodes/switch.ts +7 -1
- package/src/nodes/wait.ts +9 -2
- package/src/workflowAuthoring/WorkflowChatModelFactory.types.ts +8 -2
- package/src/chatModels/ManagedModelFetcher.ts +0 -23
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { ToolSet } from "ai";
|
|
2
|
-
import { jsonSchema } from "ai";
|
|
3
2
|
import { z } from "zod";
|
|
4
3
|
import type { BM25Index } from "./BM25Index";
|
|
5
4
|
import type {
|
|
@@ -37,6 +36,12 @@ export class DeferredMetaToolStrategy implements ToolLoadingStrategy {
|
|
|
37
36
|
private mcpEntries: McpToolEntry[] = [];
|
|
38
37
|
private toolsByServerId = new Map<string, Map<string, ToolSet[string]>>();
|
|
39
38
|
private foundToolIds = new Set<string>();
|
|
39
|
+
/**
|
|
40
|
+
* `jsonSchema` from the `ai` SDK, loaded lazily in {@link initialize} so the SDK
|
|
41
|
+
* (~28MB RSS) stays off the boot path. `initialize` always runs before the sync
|
|
42
|
+
* `getToolsForTurn` → `buildFindToolsDefinition` path, so this is set before use.
|
|
43
|
+
*/
|
|
44
|
+
private jsonSchema!: typeof import("ai").jsonSchema;
|
|
40
45
|
|
|
41
46
|
constructor(
|
|
42
47
|
private readonly bm25: BM25Index,
|
|
@@ -44,6 +49,7 @@ export class DeferredMetaToolStrategy implements ToolLoadingStrategy {
|
|
|
44
49
|
) {}
|
|
45
50
|
|
|
46
51
|
async initialize(input: ToolLoadingStrategyInitInput): Promise<void> {
|
|
52
|
+
this.jsonSchema = (await import("ai")).jsonSchema;
|
|
47
53
|
this.nodeBackedTools = { ...input.nodeBackedTools };
|
|
48
54
|
|
|
49
55
|
const pinnedIds = input.pinnedMcpTools ?? [];
|
|
@@ -194,7 +200,7 @@ export class DeferredMetaToolStrategy implements ToolLoadingStrategy {
|
|
|
194
200
|
"After this call, the tools listed in the result will be callable on your very next turn. " +
|
|
195
201
|
"Use this when you need a capability not visible in your current tool list. " +
|
|
196
202
|
"Do not attempt to call a tool name you have not seen yet — use find_tools to discover it first.",
|
|
197
|
-
inputSchema: jsonSchema(inputSchemaRecord),
|
|
203
|
+
inputSchema: this.jsonSchema(inputSchemaRecord),
|
|
198
204
|
} as unknown as ToolSet[string];
|
|
199
205
|
}
|
|
200
206
|
}
|
|
@@ -3,6 +3,7 @@ import type { Items, NodeInspectorSummaryRow, TriggerNodeConfig, TypeToken } fro
|
|
|
3
3
|
import { ItemsInputNormalizer } from "@codemation/core";
|
|
4
4
|
|
|
5
5
|
import { ManualTriggerNode } from "./ManualTriggerNode";
|
|
6
|
+
import type { NodeBaseOptions } from "./nodeOptions.types";
|
|
6
7
|
|
|
7
8
|
type ManualTriggerDefaultValue<TOutputJson> = Items<TOutputJson> | ReadonlyArray<TOutputJson> | TOutputJson;
|
|
8
9
|
|
|
@@ -13,18 +14,28 @@ export class ManualTrigger<TOutputJson = unknown> implements TriggerNodeConfig<T
|
|
|
13
14
|
readonly icon = "lucide:play" as const;
|
|
14
15
|
readonly defaultItems?: Items<TOutputJson>;
|
|
15
16
|
readonly id?: string;
|
|
17
|
+
readonly description?: string;
|
|
16
18
|
/** Manual runs often emit an empty batch; still schedule downstream by default. */
|
|
17
19
|
readonly continueWhenEmptyOutput = true as const;
|
|
18
20
|
|
|
19
|
-
constructor(name?: string,
|
|
20
|
-
constructor(
|
|
21
|
+
constructor(name?: string, idOrOptions?: string | NodeBaseOptions);
|
|
22
|
+
constructor(
|
|
23
|
+
name: string,
|
|
24
|
+
defaultItems: ManualTriggerDefaultValue<TOutputJson> | undefined,
|
|
25
|
+
idOrOptions?: string | NodeBaseOptions,
|
|
26
|
+
);
|
|
21
27
|
constructor(
|
|
22
28
|
public readonly name: string = "Manual trigger",
|
|
23
29
|
defaultItemsOrId?: ManualTriggerDefaultValue<TOutputJson> | string,
|
|
24
|
-
|
|
30
|
+
idOrOptions?: string | NodeBaseOptions,
|
|
25
31
|
) {
|
|
32
|
+
// Position 2 keeps its existing string-vs-object meaning (string→id, object/array→default items).
|
|
33
|
+
// Options (id/description) live in the trailing slot, or position 2 when it's a bare string id.
|
|
26
34
|
this.defaultItems = ManualTrigger.resolveDefaultItems(defaultItemsOrId);
|
|
27
|
-
|
|
35
|
+
const trailing = idOrOptions ?? (typeof defaultItemsOrId === "string" ? defaultItemsOrId : undefined);
|
|
36
|
+
const options = typeof trailing === "string" ? { id: trailing } : (trailing ?? {});
|
|
37
|
+
this.id = options.id;
|
|
38
|
+
this.description = options.description;
|
|
28
39
|
}
|
|
29
40
|
|
|
30
41
|
private static resolveDefaultItems<TOutputJson>(
|
|
@@ -36,13 +47,6 @@ export class ManualTrigger<TOutputJson = unknown> implements TriggerNodeConfig<T
|
|
|
36
47
|
return this.itemsInputNormalizer.normalize(value) as Items<TOutputJson>;
|
|
37
48
|
}
|
|
38
49
|
|
|
39
|
-
private static resolveId<TOutputJson>(
|
|
40
|
-
value: ManualTriggerDefaultValue<TOutputJson> | string | undefined,
|
|
41
|
-
id: string | undefined,
|
|
42
|
-
): string | undefined {
|
|
43
|
-
return typeof value === "string" ? value : id;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
50
|
inspectorSummary(): ReadonlyArray<NodeInspectorSummaryRow> {
|
|
47
51
|
const rows: NodeInspectorSummaryRow[] = [{ label: "Trigger", value: "manual" }];
|
|
48
52
|
if (this.defaultItems && this.defaultItems.length > 0) {
|
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
} from "@codemation/core";
|
|
9
9
|
import type { ZodType } from "zod";
|
|
10
10
|
import { WebhookTriggerNode } from "./webhookTriggerNode";
|
|
11
|
+
import type { NodeBaseOptions } from "./nodeOptions.types";
|
|
11
12
|
|
|
12
13
|
type WebhookInputSchema = ZodType<any, any, any>;
|
|
13
14
|
type WebhookTriggerHandler<TConfig extends WebhookTrigger<any> = WebhookTrigger<any>> = (
|
|
@@ -21,6 +22,8 @@ export class WebhookTrigger<
|
|
|
21
22
|
readonly kind = "trigger" as const;
|
|
22
23
|
readonly type: TypeToken<unknown> = WebhookTriggerNode;
|
|
23
24
|
readonly icon = "lucide:globe";
|
|
25
|
+
readonly id?: string;
|
|
26
|
+
readonly description?: string;
|
|
24
27
|
|
|
25
28
|
constructor(
|
|
26
29
|
public readonly name: string,
|
|
@@ -32,8 +35,12 @@ export class WebhookTrigger<
|
|
|
32
35
|
public readonly handler: WebhookTriggerHandler<
|
|
33
36
|
WebhookTrigger<TSchema>
|
|
34
37
|
> = WebhookTrigger.defaultHandler as WebhookTriggerHandler<WebhookTrigger<TSchema>>,
|
|
35
|
-
|
|
36
|
-
) {
|
|
38
|
+
idOrOptions?: string | NodeBaseOptions,
|
|
39
|
+
) {
|
|
40
|
+
const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
|
|
41
|
+
this.id = options?.id;
|
|
42
|
+
this.description = options?.description;
|
|
43
|
+
}
|
|
37
44
|
|
|
38
45
|
get endpointKey(): string {
|
|
39
46
|
return this.args.endpointKey;
|
package/src/nodes/aggregate.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
TypeToken,
|
|
7
7
|
} from "@codemation/core";
|
|
8
8
|
import { AggregateNode } from "./AggregateNode";
|
|
9
|
+
import type { NodeBaseOptions } from "./nodeOptions.types";
|
|
9
10
|
|
|
10
11
|
export class Aggregate<TIn = unknown, TOut = unknown> implements RunnableNodeConfig<TIn, TOut> {
|
|
11
12
|
readonly kind = "node" as const;
|
|
@@ -13,6 +14,8 @@ export class Aggregate<TIn = unknown, TOut = unknown> implements RunnableNodeCon
|
|
|
13
14
|
readonly execution = { hint: "local" } as const;
|
|
14
15
|
readonly keepBinaries = true as const;
|
|
15
16
|
readonly icon = "builtin:aggregate-rows" as const;
|
|
17
|
+
readonly id?: string;
|
|
18
|
+
readonly description?: string;
|
|
16
19
|
|
|
17
20
|
constructor(
|
|
18
21
|
public readonly name: string,
|
|
@@ -20,8 +23,12 @@ export class Aggregate<TIn = unknown, TOut = unknown> implements RunnableNodeCon
|
|
|
20
23
|
items: Items<TIn>,
|
|
21
24
|
ctx: NodeExecutionContext<Aggregate<TIn, TOut>>,
|
|
22
25
|
) => TOut | Promise<TOut>,
|
|
23
|
-
|
|
24
|
-
) {
|
|
26
|
+
idOrOptions?: string | NodeBaseOptions,
|
|
27
|
+
) {
|
|
28
|
+
const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
|
|
29
|
+
this.id = options?.id;
|
|
30
|
+
this.description = options?.description;
|
|
31
|
+
}
|
|
25
32
|
|
|
26
33
|
inspectorSummary(): ReadonlyArray<NodeInspectorSummaryRow> | undefined {
|
|
27
34
|
const fnName = this.aggregate.name;
|
package/src/nodes/assertion.ts
CHANGED
|
@@ -13,6 +13,7 @@ export interface AssertionOptions<TInputJson> {
|
|
|
13
13
|
readonly name?: string;
|
|
14
14
|
readonly id?: string;
|
|
15
15
|
readonly icon?: string;
|
|
16
|
+
readonly description?: string;
|
|
16
17
|
/**
|
|
17
18
|
* Author callback. Returns one or more {@link AssertionResult}s per input item. Each becomes
|
|
18
19
|
* one emitted output item — useful for per-row reporting in the Tests tab. Return `[]` to
|
|
@@ -35,6 +36,7 @@ export class Assertion<TInputJson = unknown> implements RunnableNodeConfig<TInpu
|
|
|
35
36
|
readonly icon: string;
|
|
36
37
|
readonly name: string;
|
|
37
38
|
readonly id?: string;
|
|
39
|
+
readonly description?: string;
|
|
38
40
|
readonly emitsAssertions = true as const;
|
|
39
41
|
readonly assertions: AssertionOptions<TInputJson>["assertions"];
|
|
40
42
|
|
|
@@ -42,6 +44,7 @@ export class Assertion<TInputJson = unknown> implements RunnableNodeConfig<TInpu
|
|
|
42
44
|
this.name = options.name ?? "Assertion";
|
|
43
45
|
this.id = options.id;
|
|
44
46
|
this.icon = options.icon ?? "lucide:check-circle";
|
|
47
|
+
this.description = options.description;
|
|
45
48
|
this.assertions = options.assertions;
|
|
46
49
|
}
|
|
47
50
|
|
package/src/nodes/filter.ts
CHANGED
|
@@ -7,12 +7,15 @@ import type {
|
|
|
7
7
|
TypeToken,
|
|
8
8
|
} from "@codemation/core";
|
|
9
9
|
import { FilterNode } from "./FilterNode";
|
|
10
|
+
import type { NodeBaseOptions } from "./nodeOptions.types";
|
|
10
11
|
|
|
11
12
|
export class Filter<TIn = unknown> implements RunnableNodeConfig<TIn, TIn> {
|
|
12
13
|
readonly kind = "node" as const;
|
|
13
14
|
readonly type: TypeToken<unknown> = FilterNode;
|
|
14
15
|
readonly execution = { hint: "local" } as const;
|
|
15
16
|
readonly icon = "lucide:filter" as const;
|
|
17
|
+
readonly id?: string;
|
|
18
|
+
readonly description?: string;
|
|
16
19
|
|
|
17
20
|
constructor(
|
|
18
21
|
public readonly name: string,
|
|
@@ -22,8 +25,12 @@ export class Filter<TIn = unknown> implements RunnableNodeConfig<TIn, TIn> {
|
|
|
22
25
|
items: Items<TIn>,
|
|
23
26
|
ctx: NodeExecutionContext<Filter<TIn>>,
|
|
24
27
|
) => boolean,
|
|
25
|
-
|
|
26
|
-
) {
|
|
28
|
+
idOrOptions?: string | NodeBaseOptions,
|
|
29
|
+
) {
|
|
30
|
+
const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
|
|
31
|
+
this.id = options?.id;
|
|
32
|
+
this.description = options?.description;
|
|
33
|
+
}
|
|
27
34
|
|
|
28
35
|
inspectorSummary(): ReadonlyArray<NodeInspectorSummaryRow> | undefined {
|
|
29
36
|
const fnName = this.predicate.name;
|
package/src/nodes/httpRequest.ts
CHANGED
|
@@ -62,6 +62,7 @@ export class HttpRequest<
|
|
|
62
62
|
readonly type: TypeToken<unknown> = HttpRequestNode;
|
|
63
63
|
readonly execution = { hint: "local" } as const;
|
|
64
64
|
readonly icon = "lucide:globe" as const;
|
|
65
|
+
readonly description?: string;
|
|
65
66
|
|
|
66
67
|
constructor(
|
|
67
68
|
public readonly name: string,
|
|
@@ -79,7 +80,7 @@ export class HttpRequest<
|
|
|
79
80
|
headers?: Readonly<Record<string, string>>;
|
|
80
81
|
/** Query parameters to append to the URL. */
|
|
81
82
|
query?: Readonly<Record<string, string>>;
|
|
82
|
-
/** Request body specification. For
|
|
83
|
+
/** Request body specification. For `kind:"json"`, pass the object directly in `body.data` — it is JSON-encoded exactly once, so never a pre-stringified string. */
|
|
83
84
|
body?: HttpBodySpec;
|
|
84
85
|
/**
|
|
85
86
|
* Credential slot.
|
|
@@ -136,9 +137,13 @@ export class HttpRequest<
|
|
|
136
137
|
*/
|
|
137
138
|
allowedOutboundHosts?: ReadonlyArray<string>;
|
|
138
139
|
id?: string;
|
|
140
|
+
/** Plain-language explanation surfaced in the node sidebar. */
|
|
141
|
+
description?: string;
|
|
139
142
|
}> = {},
|
|
140
143
|
public readonly retryPolicy: RetryPolicySpec = RetryPolicy.defaultForHttp,
|
|
141
|
-
) {
|
|
144
|
+
) {
|
|
145
|
+
this.description = args.description;
|
|
146
|
+
}
|
|
142
147
|
|
|
143
148
|
get id(): string | undefined {
|
|
144
149
|
return this.args.id;
|
package/src/nodes/if.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
TypeToken,
|
|
8
8
|
} from "@codemation/core";
|
|
9
9
|
import { IfNode } from "./IfNode";
|
|
10
|
+
import type { NodeBaseOptions } from "./nodeOptions.types";
|
|
10
11
|
|
|
11
12
|
export class If<TInputJson = unknown> implements RunnableNodeConfig<TInputJson, TInputJson> {
|
|
12
13
|
readonly kind = "node" as const;
|
|
@@ -14,6 +15,8 @@ export class If<TInputJson = unknown> implements RunnableNodeConfig<TInputJson,
|
|
|
14
15
|
readonly execution = { hint: "local" } as const;
|
|
15
16
|
readonly icon = "lucide:split@rot=90" as const;
|
|
16
17
|
readonly declaredOutputPorts = ["true", "false"] as const;
|
|
18
|
+
readonly id?: string;
|
|
19
|
+
readonly description?: string;
|
|
17
20
|
constructor(
|
|
18
21
|
public readonly name: string,
|
|
19
22
|
public readonly predicate: (
|
|
@@ -22,8 +25,12 @@ export class If<TInputJson = unknown> implements RunnableNodeConfig<TInputJson,
|
|
|
22
25
|
items: Items<TInputJson>,
|
|
23
26
|
ctx: NodeExecutionContext<If<TInputJson>>,
|
|
24
27
|
) => boolean,
|
|
25
|
-
|
|
26
|
-
) {
|
|
28
|
+
idOrOptions?: string | NodeBaseOptions,
|
|
29
|
+
) {
|
|
30
|
+
const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
|
|
31
|
+
this.id = options?.id;
|
|
32
|
+
this.description = options?.description;
|
|
33
|
+
}
|
|
27
34
|
|
|
28
35
|
inspectorSummary(): ReadonlyArray<NodeInspectorSummaryRow> | undefined {
|
|
29
36
|
const fnName = this.predicate.name;
|
package/src/nodes/isTestRun.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RunnableNodeConfig, TypeToken } from "@codemation/core";
|
|
2
2
|
|
|
3
3
|
import { IsTestRunNode } from "./IsTestRunNode";
|
|
4
|
+
import type { NodeBaseOptions } from "./nodeOptions.types";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Branches per-item on whether the current run is a test run. Output ports: `true`, `false`.
|
|
@@ -14,10 +15,13 @@ export class IsTestRun<TInputJson = unknown> implements RunnableNodeConfig<TInpu
|
|
|
14
15
|
readonly declaredOutputPorts = ["true", "false"] as const;
|
|
15
16
|
readonly name: string;
|
|
16
17
|
readonly id?: string;
|
|
18
|
+
readonly description?: string;
|
|
17
19
|
|
|
18
|
-
constructor(name: string = "Is test run?",
|
|
20
|
+
constructor(name: string = "Is test run?", idOrOptions?: string | NodeBaseOptions) {
|
|
19
21
|
this.name = name;
|
|
20
|
-
|
|
22
|
+
const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
|
|
23
|
+
this.id = options?.id;
|
|
24
|
+
this.description = options?.description;
|
|
21
25
|
}
|
|
22
26
|
}
|
|
23
27
|
|
package/src/nodes/mapData.ts
CHANGED
|
@@ -6,9 +6,9 @@ import type {
|
|
|
6
6
|
TypeToken,
|
|
7
7
|
} from "@codemation/core";
|
|
8
8
|
import { MapDataNode } from "./MapDataNode";
|
|
9
|
+
import type { NodeBaseOptions } from "./nodeOptions.types";
|
|
9
10
|
|
|
10
|
-
export interface MapDataOptions {
|
|
11
|
-
readonly id?: string;
|
|
11
|
+
export interface MapDataOptions extends NodeBaseOptions {
|
|
12
12
|
readonly keepBinaries?: boolean;
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -23,6 +23,7 @@ export class MapData<TInputJson = unknown, TOutputJson = unknown> implements Run
|
|
|
23
23
|
readonly continueWhenEmptyOutput = true as const;
|
|
24
24
|
readonly icon = "lucide:square-pen" as const;
|
|
25
25
|
readonly keepBinaries: boolean;
|
|
26
|
+
readonly description?: string;
|
|
26
27
|
|
|
27
28
|
constructor(
|
|
28
29
|
public readonly name: string,
|
|
@@ -33,6 +34,7 @@ export class MapData<TInputJson = unknown, TOutputJson = unknown> implements Run
|
|
|
33
34
|
private readonly options: MapDataOptions = {},
|
|
34
35
|
) {
|
|
35
36
|
this.keepBinaries = options.keepBinaries ?? true;
|
|
37
|
+
this.description = options.description;
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
get id(): string | undefined {
|
package/src/nodes/merge.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { InputPortKey, NodeInspectorSummaryRow, RunnableNodeConfig, TypeToken } from "@codemation/core";
|
|
2
2
|
import { MergeNode } from "./MergeNode";
|
|
3
|
+
import type { NodeBaseOptions } from "./nodeOptions.types";
|
|
3
4
|
|
|
4
5
|
export type MergeMode = "passThrough" | "append" | "mergeByPosition";
|
|
5
6
|
|
|
@@ -10,6 +11,8 @@ export class Merge<TInputJson = unknown, TOutputJson = TInputJson> implements Ru
|
|
|
10
11
|
readonly kind = "node" as const;
|
|
11
12
|
readonly type: TypeToken<unknown> = MergeNode;
|
|
12
13
|
readonly icon = "lucide:merge@rot=90" as const;
|
|
14
|
+
readonly id?: string;
|
|
15
|
+
readonly description?: string;
|
|
13
16
|
|
|
14
17
|
constructor(
|
|
15
18
|
public readonly name: string,
|
|
@@ -21,8 +24,12 @@ export class Merge<TInputJson = unknown, TOutputJson = TInputJson> implements Ru
|
|
|
21
24
|
*/
|
|
22
25
|
prefer?: ReadonlyArray<InputPortKey>;
|
|
23
26
|
}> = { mode: "passThrough" },
|
|
24
|
-
|
|
25
|
-
) {
|
|
27
|
+
idOrOptions?: string | NodeBaseOptions,
|
|
28
|
+
) {
|
|
29
|
+
const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
|
|
30
|
+
this.id = options?.id;
|
|
31
|
+
this.description = options?.description;
|
|
32
|
+
}
|
|
26
33
|
|
|
27
34
|
inspectorSummary(): ReadonlyArray<NodeInspectorSummaryRow> {
|
|
28
35
|
const rows: NodeInspectorSummaryRow[] = [{ label: "Mode", value: this.cfg.mode }];
|
package/src/nodes/noOp.ts
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
import type { RunnableNodeConfig, TypeToken } from "@codemation/core";
|
|
2
2
|
import { NoOpNode } from "./NoOpNode";
|
|
3
|
+
import type { NodeBaseOptions } from "./nodeOptions.types";
|
|
3
4
|
|
|
4
5
|
export class NoOp<TItemJson = unknown> implements RunnableNodeConfig<TItemJson, TItemJson> {
|
|
5
6
|
readonly kind = "node" as const;
|
|
6
7
|
readonly type: TypeToken<unknown> = NoOpNode;
|
|
7
8
|
readonly execution = { hint: "local" } as const;
|
|
8
9
|
readonly icon = "lucide:circle-dashed" as const;
|
|
10
|
+
readonly id?: string;
|
|
11
|
+
readonly description?: string;
|
|
9
12
|
|
|
10
13
|
constructor(
|
|
11
14
|
public readonly name: string = "NoOp",
|
|
12
|
-
|
|
13
|
-
) {
|
|
15
|
+
idOrOptions?: string | NodeBaseOptions,
|
|
16
|
+
) {
|
|
17
|
+
const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
|
|
18
|
+
this.id = options?.id;
|
|
19
|
+
this.description = options?.description;
|
|
20
|
+
}
|
|
14
21
|
}
|
|
15
22
|
|
|
16
23
|
export { NoOpNode } from "./NoOpNode";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options shared by every authorable built-in node: a stable `id` and a plain-language
|
|
3
|
+
* `description` (the non-technical "what does this node do" line surfaced in the node sidebar).
|
|
4
|
+
*
|
|
5
|
+
* `description` is a first-class config option — passed inline in the node's options, exactly like
|
|
6
|
+
* `id` — and is threaded onto the config instance so it flows into the persisted workflow snapshot
|
|
7
|
+
* the host / canvas mappers read. Node-specific option types extend this.
|
|
8
|
+
*/
|
|
9
|
+
export interface NodeBaseOptions {
|
|
10
|
+
readonly id?: string;
|
|
11
|
+
readonly description?: string;
|
|
12
|
+
}
|
package/src/nodes/split.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
TypeToken,
|
|
7
7
|
} from "@codemation/core";
|
|
8
8
|
import { SplitNode } from "./SplitNode";
|
|
9
|
+
import type { NodeBaseOptions } from "./nodeOptions.types";
|
|
9
10
|
|
|
10
11
|
export class Split<TIn = unknown, TElem = unknown> implements RunnableNodeConfig<TIn, TElem> {
|
|
11
12
|
readonly kind = "node" as const;
|
|
@@ -18,12 +19,18 @@ export class Split<TIn = unknown, TElem = unknown> implements RunnableNodeConfig
|
|
|
18
19
|
*/
|
|
19
20
|
readonly continueWhenEmptyOutput = true as const;
|
|
20
21
|
readonly icon = "builtin:split-rows" as const;
|
|
22
|
+
readonly id?: string;
|
|
23
|
+
readonly description?: string;
|
|
21
24
|
|
|
22
25
|
constructor(
|
|
23
26
|
public readonly name: string,
|
|
24
27
|
public readonly getElements: (item: Item<TIn>, ctx: NodeExecutionContext<Split<TIn, TElem>>) => readonly TElem[],
|
|
25
|
-
|
|
26
|
-
) {
|
|
28
|
+
idOrOptions?: string | NodeBaseOptions,
|
|
29
|
+
) {
|
|
30
|
+
const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
|
|
31
|
+
this.id = options?.id;
|
|
32
|
+
this.description = options?.description;
|
|
33
|
+
}
|
|
27
34
|
|
|
28
35
|
inspectorSummary(): ReadonlyArray<NodeInspectorSummaryRow> | undefined {
|
|
29
36
|
const fnName = this.getElements.name;
|
package/src/nodes/subWorkflow.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
UpstreamRefPlaceholder,
|
|
7
7
|
} from "@codemation/core";
|
|
8
8
|
import { SubWorkflowNode } from "./SubWorkflowNode";
|
|
9
|
+
import type { NodeBaseOptions } from "./nodeOptions.types";
|
|
9
10
|
|
|
10
11
|
export class SubWorkflow<TInputJson = unknown, TOutputJson = unknown> implements RunnableNodeConfig<
|
|
11
12
|
TInputJson,
|
|
@@ -14,13 +15,19 @@ export class SubWorkflow<TInputJson = unknown, TOutputJson = unknown> implements
|
|
|
14
15
|
readonly kind = "node" as const;
|
|
15
16
|
readonly type: TypeToken<unknown> = SubWorkflowNode;
|
|
16
17
|
readonly icon = "lucide:workflow";
|
|
18
|
+
readonly id?: string;
|
|
19
|
+
readonly description?: string;
|
|
17
20
|
constructor(
|
|
18
21
|
public readonly name: string,
|
|
19
22
|
public readonly workflowId: string,
|
|
20
23
|
public upstreamRefs?: Array<{ nodeId: NodeId } | UpstreamRefPlaceholder>,
|
|
21
24
|
public readonly startAt?: NodeId,
|
|
22
|
-
|
|
23
|
-
) {
|
|
25
|
+
idOrOptions?: string | NodeBaseOptions,
|
|
26
|
+
) {
|
|
27
|
+
const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
|
|
28
|
+
this.id = options?.id;
|
|
29
|
+
this.description = options?.description;
|
|
30
|
+
}
|
|
24
31
|
|
|
25
32
|
inspectorSummary(): ReadonlyArray<NodeInspectorSummaryRow> {
|
|
26
33
|
const rows: NodeInspectorSummaryRow[] = [{ label: "Workflow", value: this.workflowId }];
|
package/src/nodes/switch.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
TypeToken,
|
|
8
8
|
} from "@codemation/core";
|
|
9
9
|
import { SwitchNode } from "./SwitchNode";
|
|
10
|
+
import type { NodeBaseOptions } from "./nodeOptions.types";
|
|
10
11
|
|
|
11
12
|
export type SwitchCaseKeyResolver<TInputJson = unknown> = (
|
|
12
13
|
item: Item<TInputJson>,
|
|
@@ -21,6 +22,8 @@ export class Switch<TInputJson = unknown> implements RunnableNodeConfig<TInputJs
|
|
|
21
22
|
readonly execution = { hint: "local" } as const;
|
|
22
23
|
readonly icon = "lucide:git-branch-plus" as const;
|
|
23
24
|
readonly declaredOutputPorts: ReadonlyArray<string>;
|
|
25
|
+
readonly id?: string;
|
|
26
|
+
readonly description?: string;
|
|
24
27
|
|
|
25
28
|
constructor(
|
|
26
29
|
public readonly name: string,
|
|
@@ -29,9 +32,12 @@ export class Switch<TInputJson = unknown> implements RunnableNodeConfig<TInputJs
|
|
|
29
32
|
defaultCase: string;
|
|
30
33
|
resolveCaseKey: SwitchCaseKeyResolver<TInputJson>;
|
|
31
34
|
}>,
|
|
32
|
-
|
|
35
|
+
idOrOptions?: string | NodeBaseOptions,
|
|
33
36
|
) {
|
|
34
37
|
this.declaredOutputPorts = [...new Set([...cfg.cases, cfg.defaultCase])].sort();
|
|
38
|
+
const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
|
|
39
|
+
this.id = options?.id;
|
|
40
|
+
this.description = options?.description;
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
inspectorSummary(): ReadonlyArray<NodeInspectorSummaryRow> {
|
package/src/nodes/wait.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { NodeInspectorSummaryRow, RunnableNodeConfig, TypeToken } from "@codemation/core";
|
|
2
2
|
|
|
3
3
|
import { WaitNode } from "./WaitNode";
|
|
4
|
+
import type { NodeBaseOptions } from "./nodeOptions.types";
|
|
4
5
|
|
|
5
6
|
export class Wait<TItemJson = unknown> implements RunnableNodeConfig<TItemJson, TItemJson> {
|
|
6
7
|
readonly kind = "node" as const;
|
|
@@ -9,12 +10,18 @@ export class Wait<TItemJson = unknown> implements RunnableNodeConfig<TItemJson,
|
|
|
9
10
|
/** Pass-through empty batches should still advance to downstream nodes. */
|
|
10
11
|
readonly continueWhenEmptyOutput = true as const;
|
|
11
12
|
readonly icon = "lucide:hourglass" as const;
|
|
13
|
+
readonly id?: string;
|
|
14
|
+
readonly description?: string;
|
|
12
15
|
|
|
13
16
|
constructor(
|
|
14
17
|
public readonly name: string,
|
|
15
18
|
public readonly milliseconds: number,
|
|
16
|
-
|
|
17
|
-
) {
|
|
19
|
+
idOrOptions?: string | NodeBaseOptions,
|
|
20
|
+
) {
|
|
21
|
+
const options = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
|
|
22
|
+
this.id = options?.id;
|
|
23
|
+
this.description = options?.description;
|
|
24
|
+
}
|
|
18
25
|
|
|
19
26
|
inspectorSummary(): ReadonlyArray<NodeInspectorSummaryRow> {
|
|
20
27
|
const seconds = this.milliseconds / 1000;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { ChatModelConfig } from "@codemation/core";
|
|
2
2
|
import { OpenAIChatModelConfig } from "../chatModels/openAiChatModelConfig";
|
|
3
|
-
import { CodemationChatModelConfig } from "../chatModels/CodemationChatModelConfig";
|
|
3
|
+
import { CodemationChatModelConfig, type ManagedComplexity } from "../chatModels/CodemationChatModelConfig";
|
|
4
|
+
|
|
5
|
+
const VALID_COMPLEXITY: ReadonlySet<string> = new Set(["low", "medium", "high", "xhigh"]);
|
|
4
6
|
|
|
5
7
|
export class WorkflowChatModelFactory {
|
|
6
8
|
static create(model: string | ChatModelConfig): ChatModelConfig {
|
|
@@ -9,7 +11,11 @@ export class WorkflowChatModelFactory {
|
|
|
9
11
|
}
|
|
10
12
|
const [provider, resolvedModel] = model.includes(":") ? model.split(":", 2) : ["openai", model];
|
|
11
13
|
if (provider === "codemation-managed") {
|
|
12
|
-
|
|
14
|
+
const complexity = resolvedModel ?? "medium";
|
|
15
|
+
if (!VALID_COMPLEXITY.has(complexity)) {
|
|
16
|
+
throw new Error(`Invalid managed complexity "${complexity}". Must be one of: low, medium, high, xhigh.`);
|
|
17
|
+
}
|
|
18
|
+
return new CodemationChatModelConfig("Codemation Managed", complexity as ManagedComplexity);
|
|
13
19
|
}
|
|
14
20
|
if (provider !== "openai") {
|
|
15
21
|
throw new Error(`Unsupported workflow().agent() model provider "${provider}".`);
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { ManagedModelDto } from "./CodemationChatModelConfig";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Fetches the active platform-managed model allowlist from the CP.
|
|
5
|
-
* Reads CONTROL_PLANE_URL from the workspace process env.
|
|
6
|
-
* Returns an empty array if the env var is absent or the fetch fails.
|
|
7
|
-
* Cache the result per session — the allowlist changes infrequently.
|
|
8
|
-
*/
|
|
9
|
-
export class ManagedModelFetcher {
|
|
10
|
-
async fetch(): Promise<ManagedModelDto[]> {
|
|
11
|
-
// eslint-disable-next-line no-restricted-properties -- CONTROL_PLANE_URL is injected by the provisioner; this class is the justified boundary.
|
|
12
|
-
const cpUrl = process.env["CONTROL_PLANE_URL"];
|
|
13
|
-
if (!cpUrl) return [];
|
|
14
|
-
|
|
15
|
-
try {
|
|
16
|
-
const res = await globalThis.fetch(`${cpUrl}/api/llm/managed-models`);
|
|
17
|
-
if (!res.ok) return [];
|
|
18
|
-
return (await res.json()) as ManagedModelDto[];
|
|
19
|
-
} catch {
|
|
20
|
-
return [];
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|