@oomol-lab/open-flow 0.1.0-beta.76 → 0.1.0-beta.78
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/README.md +3 -0
- package/dist/browser/{ContentIcon-Cm1hbVzW.js → ContentIcon-CMn0c-kl.js} +99 -6
- package/dist/browser/{FlowCanvas-NzKtla_c.js → FlowCanvas-BQGBcfoL.js} +1709 -613
- package/dist/browser/{createResourceDialog-BgdedQqu.js → createResourceDialog-DCGxfKFM.js} +63 -57
- package/dist/browser/{dialog-aSElFFw2.js → dialog-WzR4CRFH.js} +76 -77
- package/dist/browser/{exportSvg-Dcz4HTo_.js → exportSvg-CYwyYiuH.js} +2 -2
- package/dist/browser/{flowChanges-NZyS7HSp.js → flowChanges-Bz4dbDN_.js} +6875 -11091
- package/dist/browser/{flowWorkspace-Bl0SDehy.js → flowWorkspace-BI6L6a4r.js} +1415 -3037
- package/dist/browser/licenses.md +1 -1
- package/dist/browser/preview.css +619 -462
- package/dist/browser/preview.js +9016 -14956
- package/dist/browser/theme.css +4 -2
- package/dist/browser/{tooltip-3XMK9jkr.js → tooltip-BDcVTNpu.js} +65 -2
- package/dist/browser/{typeScriptSession-C9sBGOKZ.js → typeScriptSession-CRoezJPN.js} +1 -1
- package/dist/browser/ui.css +112 -24
- package/dist/browser/ui.js +3 -2
- package/dist/browser/workbench.css +10020 -9842
- package/dist/browser/workbench.d.ts +5 -2
- package/dist/browser/workbench.js +2382 -2668
- package/dist/browser/{workbenchSelect-DBGP7Y4Z.js → workbenchSelect-DtAd0zc4.js} +2 -2
- package/dist/command/index.js +48 -48
- package/dist/common/authoring.d.ts +4 -4
- package/dist/common/authoringExamples.d.ts +3 -2
- package/dist/common/authoringSchema.d.ts +15 -4
- package/dist/common/control-api-conformance.js +155 -15
- package/dist/common/control-api-errors.d.ts +12 -0
- package/dist/common/control-api.d.ts +39 -2
- package/dist/common/control-api.js +180 -37
- package/dist/common/control-requests.d.ts +23 -3
- package/dist/common/control-requests.js +141 -14
- package/dist/common/engine-contract.d.ts +3 -2
- package/dist/common/flow-authoring.js +47 -7
- package/dist/common/flow-change-schema.d.ts +1 -1
- package/dist/common/flow-change.d.ts +2 -2
- package/dist/common/flow-change.js +48 -8
- package/dist/common/flow-encoding.js +47 -7
- package/dist/common/flow-semantics.js +86 -8
- package/dist/common/flow-state.d.ts +100 -0
- package/dist/common/flow-state.js +5794 -0
- package/dist/common/mcp.d.ts +96 -3
- package/dist/common/mcp.js +158 -13
- package/dist/common/run-events.js +19 -8
- package/dist/common/runtime-contract.d.ts +14 -0
- package/dist/common/runtime-contract.js +124 -17
- package/dist/common/runtime-nodejs-compat.js +11 -12
- package/dist/common/scheduler-node-execution.d.ts +1 -0
- package/dist/common/scheduler.d.ts +2 -1
- package/dist/common/scheduler.js +95 -40
- package/dist/common/state-conformance.d.ts +16 -0
- package/dist/common/tags.d.ts +4 -0
- package/dist/index.d.ts +11 -0
- package/package.json +5 -1
- package/src/control/common/api.ts +101 -11
- package/src/control/common/authoring.ts +7 -1
- package/src/control/common/authoringExamples.ts +9 -0
- package/src/control/common/authoringSchema.ts +18 -12
- package/src/control/common/errors.ts +6 -0
- package/src/control/common/flowDecoders.ts +1 -0
- package/src/control/common/mcp.ts +16 -0
- package/src/control/common/requests.ts +16 -2
- package/src/control/common/tags.ts +20 -0
- package/src/execution/common/events.ts +22 -7
- package/src/execution/common/node-execution.ts +3 -0
- package/src/execution/common/scheduler.ts +29 -13
- package/src/flow/common/change.ts +2 -1
- package/src/flow/common/changeSchema.ts +11 -1
- package/src/flow/common/errorHandling.ts +1 -1
- package/src/flow/common/semantics.ts +27 -1
- package/src/runtime/common/engineContract.ts +6 -3
- package/src/runtime/common/nodejsGuest.generated.json +11 -12
- package/src/runtime/common/runtime.ts +122 -7
- package/src/state/common/conformance.ts +81 -0
- package/src/state/common/state.ts +141 -0
- package/src/types/index.ts +11 -0
|
@@ -63,6 +63,7 @@ export declare function executeNode(context: NodeExecutionContext, input: {
|
|
|
63
63
|
readonly node: ExecutableNode;
|
|
64
64
|
readonly jobId: string;
|
|
65
65
|
readonly nodeInputs: Readonly<Record<string, JsonValue>>;
|
|
66
|
+
readonly launchInputs?: Readonly<Record<string, JsonValue>>;
|
|
66
67
|
readonly flowId: string;
|
|
67
68
|
readonly runId: string;
|
|
68
69
|
readonly graph: Graph;
|
|
@@ -33,7 +33,7 @@ export type SchedulerEvent = {
|
|
|
33
33
|
readonly inputs: Readonly<Record<string, JsonValue>>;
|
|
34
34
|
readonly jobId: string;
|
|
35
35
|
readonly nodeId: string;
|
|
36
|
-
readonly nodeKind: 'agent' | 'approval' | 'condition' | 'connector' | 'decision' | 'javascript' | 'openapi' | 'http-request' | 'llm' | 'value' | 'wait' | 'outputs' | 'workflow' | 'webhook-response';
|
|
36
|
+
readonly nodeKind: 'agent' | 'approval' | 'condition' | 'connector' | 'decision' | 'javascript' | 'openapi' | 'http-request' | 'skip-duplicates' | 'llm' | 'value' | 'wait' | 'outputs' | 'workflow' | 'webhook-response';
|
|
37
37
|
readonly nodeTitle?: string;
|
|
38
38
|
readonly runId: string;
|
|
39
39
|
readonly type: 'node.started';
|
|
@@ -167,6 +167,7 @@ export type RunLaunch = {
|
|
|
167
167
|
} | {
|
|
168
168
|
readonly resume: {
|
|
169
169
|
readonly checkpoint: unknown;
|
|
170
|
+
readonly readyWorkflowRunIds?: readonly string[];
|
|
170
171
|
};
|
|
171
172
|
readonly bindingValues?: never;
|
|
172
173
|
readonly inputs?: never;
|
package/dist/common/scheduler.js
CHANGED
|
@@ -14,37 +14,6 @@ var nanoid$1 = (size = 21) => {
|
|
|
14
14
|
return id;
|
|
15
15
|
};
|
|
16
16
|
//#endregion
|
|
17
|
-
//#region src/flow/common/inputValue.ts
|
|
18
|
-
/** An explicit clear suppresses the default; absent mappings still inherit it. */
|
|
19
|
-
function inputValue(mapping, fallback) {
|
|
20
|
-
if (mapping?.kind === "unset") return void 0;
|
|
21
|
-
return mapping?.kind === "value" ? mapping.value : fallback;
|
|
22
|
-
}
|
|
23
|
-
//#endregion
|
|
24
|
-
//#region ../../node_modules/.bun/dequal@2.0.3/node_modules/dequal/lite/index.mjs
|
|
25
|
-
var has = Object.prototype.hasOwnProperty;
|
|
26
|
-
function dequal(foo, bar) {
|
|
27
|
-
var ctor, len;
|
|
28
|
-
if (foo === bar) return true;
|
|
29
|
-
if (foo && bar && (ctor = foo.constructor) === bar.constructor) {
|
|
30
|
-
if (ctor === Date) return foo.getTime() === bar.getTime();
|
|
31
|
-
if (ctor === RegExp) return foo.toString() === bar.toString();
|
|
32
|
-
if (ctor === Array) {
|
|
33
|
-
if ((len = foo.length) === bar.length) while (len-- && dequal(foo[len], bar[len]));
|
|
34
|
-
return len === -1;
|
|
35
|
-
}
|
|
36
|
-
if (!ctor || typeof foo === "object") {
|
|
37
|
-
len = 0;
|
|
38
|
-
for (ctor in foo) {
|
|
39
|
-
if (has.call(foo, ctor) && ++len && !has.call(bar, ctor)) return false;
|
|
40
|
-
if (!(ctor in bar) || !dequal(foo[ctor], bar[ctor])) return false;
|
|
41
|
-
}
|
|
42
|
-
return Object.keys(bar).length === len;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return foo !== foo && bar !== bar;
|
|
46
|
-
}
|
|
47
|
-
//#endregion
|
|
48
17
|
//#region ../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/core/util.js
|
|
49
18
|
function getEnumValues(entries) {
|
|
50
19
|
const numericValues = Object.values(entries).filter((v) => typeof v === "number");
|
|
@@ -5719,6 +5688,37 @@ function datetime(params) {
|
|
|
5719
5688
|
return /* @__PURE__ */ _isoDateTime(ZodISODateTime, params);
|
|
5720
5689
|
}
|
|
5721
5690
|
//#endregion
|
|
5691
|
+
//#region src/flow/common/inputValue.ts
|
|
5692
|
+
/** An explicit clear suppresses the default; absent mappings still inherit it. */
|
|
5693
|
+
function inputValue(mapping, fallback) {
|
|
5694
|
+
if (mapping?.kind === "unset") return void 0;
|
|
5695
|
+
return mapping?.kind === "value" ? mapping.value : fallback;
|
|
5696
|
+
}
|
|
5697
|
+
//#endregion
|
|
5698
|
+
//#region ../../node_modules/.bun/dequal@2.0.3/node_modules/dequal/lite/index.mjs
|
|
5699
|
+
var has = Object.prototype.hasOwnProperty;
|
|
5700
|
+
function dequal(foo, bar) {
|
|
5701
|
+
var ctor, len;
|
|
5702
|
+
if (foo === bar) return true;
|
|
5703
|
+
if (foo && bar && (ctor = foo.constructor) === bar.constructor) {
|
|
5704
|
+
if (ctor === Date) return foo.getTime() === bar.getTime();
|
|
5705
|
+
if (ctor === RegExp) return foo.toString() === bar.toString();
|
|
5706
|
+
if (ctor === Array) {
|
|
5707
|
+
if ((len = foo.length) === bar.length) while (len-- && dequal(foo[len], bar[len]));
|
|
5708
|
+
return len === -1;
|
|
5709
|
+
}
|
|
5710
|
+
if (!ctor || typeof foo === "object") {
|
|
5711
|
+
len = 0;
|
|
5712
|
+
for (ctor in foo) {
|
|
5713
|
+
if (has.call(foo, ctor) && ++len && !has.call(bar, ctor)) return false;
|
|
5714
|
+
if (!(ctor in bar) || !dequal(foo[ctor], bar[ctor])) return false;
|
|
5715
|
+
}
|
|
5716
|
+
return Object.keys(bar).length === len;
|
|
5717
|
+
}
|
|
5718
|
+
}
|
|
5719
|
+
return foo !== foo && bar !== bar;
|
|
5720
|
+
}
|
|
5721
|
+
//#endregion
|
|
5722
5722
|
//#region ../../node_modules/.bun/@cfworker+json-schema@4.1.1/node_modules/@cfworker/json-schema/dist/esm/deep-compare-strict.js
|
|
5723
5723
|
function deepCompareStrict(a, b) {
|
|
5724
5724
|
const typeofa = typeof a;
|
|
@@ -9535,6 +9535,43 @@ var httpRequestExecutorSchema = strictObject({
|
|
|
9535
9535
|
]).default("auto"),
|
|
9536
9536
|
statusPolicy: _enum(["success", "all"]).default("success")
|
|
9537
9537
|
}).extend({ kind: literal("http-request") });
|
|
9538
|
+
strictObject({
|
|
9539
|
+
scope: _enum(["draft", "live"]),
|
|
9540
|
+
values: boolean().optional()
|
|
9541
|
+
});
|
|
9542
|
+
strictObject({
|
|
9543
|
+
scope: _enum(["draft", "live"]),
|
|
9544
|
+
key: string$1().min(1).refine((key) => new TextEncoder().encode(key).length <= 256).optional()
|
|
9545
|
+
});
|
|
9546
|
+
strictObject({
|
|
9547
|
+
scope: _enum(["draft", "live"]),
|
|
9548
|
+
space: string$1().regex(/^(flow|node:.+)$/)
|
|
9549
|
+
});
|
|
9550
|
+
var skipDuplicatesExecutorSchema = strictObject({ fields: array(string$1().refine((field) => field.length > 0 && field.split(".").every((part) => part.length > 0), "Use a nonempty dot-separated field path.")).refine((fields) => new Set(fields).size === fields.length, "Identity fields must be distinct.").default([]) }).extend({ kind: literal("skip-duplicates") });
|
|
9551
|
+
object({
|
|
9552
|
+
version: literal(1),
|
|
9553
|
+
scope: _enum(["draft", "live"]),
|
|
9554
|
+
limits: object({
|
|
9555
|
+
maxValueBytes: number().positive(),
|
|
9556
|
+
maxValues: number().int().positive(),
|
|
9557
|
+
maxSpaces: number().int().positive(),
|
|
9558
|
+
maxIdentities: number().positive(),
|
|
9559
|
+
maxRunWrites: number().positive()
|
|
9560
|
+
}),
|
|
9561
|
+
values: array(object({
|
|
9562
|
+
key: string$1(),
|
|
9563
|
+
size: number().nonnegative(),
|
|
9564
|
+
updatedAt: number(),
|
|
9565
|
+
value: json$1().optional()
|
|
9566
|
+
})),
|
|
9567
|
+
spaces: array(object({
|
|
9568
|
+
space: string$1(),
|
|
9569
|
+
count: number().nonnegative(),
|
|
9570
|
+
pendingCount: number().nonnegative(),
|
|
9571
|
+
reservedCount: number().nonnegative(),
|
|
9572
|
+
updatedAt: number()
|
|
9573
|
+
}))
|
|
9574
|
+
});
|
|
9538
9575
|
//#endregion
|
|
9539
9576
|
//#region src/flow/common/retry.ts
|
|
9540
9577
|
var retryNodeTypes = [
|
|
@@ -9889,7 +9926,8 @@ var managedFields = object({
|
|
|
9889
9926
|
flowId: text,
|
|
9890
9927
|
revisionId: text
|
|
9891
9928
|
}),
|
|
9892
|
-
httpRequestExecutorSchema
|
|
9929
|
+
httpRequestExecutorSchema,
|
|
9930
|
+
skipDuplicatesExecutorSchema
|
|
9893
9931
|
])
|
|
9894
9932
|
});
|
|
9895
9933
|
var managed = managedFields.transform(limitDecisionTask);
|
|
@@ -10143,7 +10181,8 @@ object({
|
|
|
10143
10181
|
literal(9),
|
|
10144
10182
|
literal(10),
|
|
10145
10183
|
literal(11),
|
|
10146
|
-
literal(12)
|
|
10184
|
+
literal(12),
|
|
10185
|
+
literal(13)
|
|
10147
10186
|
]),
|
|
10148
10187
|
document,
|
|
10149
10188
|
modules: record$1(text, module)
|
|
@@ -10551,7 +10590,7 @@ var nodeErrorSchema = {
|
|
|
10551
10590
|
}
|
|
10552
10591
|
};
|
|
10553
10592
|
function supportsNodeErrorHandling(node) {
|
|
10554
|
-
return supportsNodeRetry(node);
|
|
10593
|
+
return supportsNodeRetry(node) || node.kind === "task" && "executor" in node.task && node.task.executor.kind === "skip-duplicates";
|
|
10555
10594
|
}
|
|
10556
10595
|
function nodeSystemOutputPorts(node) {
|
|
10557
10596
|
return supportsNodeErrorHandling(node) && node.onError == "error" ? { error: {
|
|
@@ -11580,6 +11619,7 @@ function executeNode(context, input) {
|
|
|
11580
11619
|
if (additional.has(handle)) return false;
|
|
11581
11620
|
const task = "executor" in node.task ? node.task : void 0;
|
|
11582
11621
|
if (task == null || !["openapi", "http-request"].includes(task.executor.kind)) return true;
|
|
11622
|
+
if (Object.hasOwn(input.launchInputs ?? {}, handle)) return true;
|
|
11583
11623
|
const mapping = node.inputs[handle];
|
|
11584
11624
|
return mapping?.kind != "unset" && (mapping != null || task.inputs.some((port) => "handle" in port && port.handle == handle && port.value !== void 0));
|
|
11585
11625
|
})),
|
|
@@ -11949,6 +11989,8 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
|
|
|
11949
11989
|
const changes = yield* Queue.unbounded();
|
|
11950
11990
|
let remainingMs = context.remainingMs;
|
|
11951
11991
|
let measuredAt = yield* Clock.currentTimeMillis;
|
|
11992
|
+
let nextCallCheckAt = measuredAt + (resume == null ? 1e3 : 0);
|
|
11993
|
+
let readyWorkflowRunIds = resume?.readyWorkflowRunIds;
|
|
11952
11994
|
let idleSince;
|
|
11953
11995
|
const agents = { ...resume?.checkpoint.agents };
|
|
11954
11996
|
const resolveInputs = (nodeId, node) => resolveNodeInputs(nodeId, node, dependencies, context.bindingValues, launch[nodeId]);
|
|
@@ -11994,6 +12036,7 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
|
|
|
11994
12036
|
node,
|
|
11995
12037
|
jobId,
|
|
11996
12038
|
nodeInputs,
|
|
12039
|
+
launchInputs: launch[nodeId],
|
|
11997
12040
|
resolution,
|
|
11998
12041
|
flowId: target.flowId,
|
|
11999
12042
|
runId,
|
|
@@ -12165,10 +12208,17 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
|
|
|
12165
12208
|
return yield* Effect.failCause(firstCause);
|
|
12166
12209
|
}
|
|
12167
12210
|
if (pendingWaits.size > 0 && context.waits != null) applyResolutions(yield* context.waits.resolutions([...pendingWaits.values()].map((wait) => wait.waitId), false), now);
|
|
12168
|
-
|
|
12169
|
-
|
|
12170
|
-
|
|
12171
|
-
|
|
12211
|
+
if (pendingCalls.size > 0 && now >= nextCallCheckAt) {
|
|
12212
|
+
for (const call of pendingCalls.values()) {
|
|
12213
|
+
if (readyWorkflowRunIds != null && !readyWorkflowRunIds.has(call.childRunId)) continue;
|
|
12214
|
+
yield* observe(call.nodeId, call.jobId, Effect.gen(function* () {
|
|
12215
|
+
const result = yield* execute(call.nodeId, target.graph.nodes[call.nodeId], call.jobId, call.input);
|
|
12216
|
+
yield* settleNode(call.nodeId, call.jobId, result);
|
|
12217
|
+
})).pipe(Effect.catch(() => Effect.void));
|
|
12218
|
+
}
|
|
12219
|
+
readyWorkflowRunIds = void 0;
|
|
12220
|
+
nextCallCheckAt = (yield* Clock.currentTimeMillis) + 1e3;
|
|
12221
|
+
}
|
|
12172
12222
|
const ready = dependencies.ready();
|
|
12173
12223
|
for (const nodeId of ready) executeReady(nodeId);
|
|
12174
12224
|
const running = yield* FiberSet.size(active);
|
|
@@ -12203,7 +12253,7 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
|
|
|
12203
12253
|
const activity = Queue.take(changes).pipe(Effect.as({}));
|
|
12204
12254
|
const decision = pendingWaits.size == 0 || context.waits == null ? Effect.never : context.waits.resolutions([...pendingWaits.values()].map((wait) => wait.waitId), true);
|
|
12205
12255
|
let next = Effect.raceFirst(activity, decision);
|
|
12206
|
-
if (pendingCalls.size > 0) next = Effect.raceFirst(next, Effect.sleep(
|
|
12256
|
+
if (pendingCalls.size > 0) next = Effect.raceFirst(next, Effect.sleep(Math.max(0, nextCallCheckAt - (yield* Clock.currentTimeMillis))).pipe(Effect.as({})));
|
|
12207
12257
|
if (left != null) next = Effect.raceFirst(next, Effect.sleep(Math.max(0, left)).pipe(Effect.as({})));
|
|
12208
12258
|
applyResolutions(yield* next, yield* Clock.currentTimeMillis);
|
|
12209
12259
|
}
|
|
@@ -12241,6 +12291,8 @@ function runFlow(prepared, options) {
|
|
|
12241
12291
|
return Effect.gen(function* () {
|
|
12242
12292
|
if (options.resume != null && (options.inputs != null || options.trigger != null || options.bindingValues != null)) return yield* Effect.fail(/* @__PURE__ */ new Error("A resumed Flow Run cannot accept launch inputs, binding values, or a Trigger seed."));
|
|
12243
12293
|
const decoded = options.resume == null ? void 0 : decodeFlowRunCheckpoint(options.resume.checkpoint);
|
|
12294
|
+
const readyWorkflowRunIds = options.resume?.readyWorkflowRunIds === void 0 ? void 0 : new Set(array(string$1().min(1)).parse(options.resume.readyWorkflowRunIds));
|
|
12295
|
+
if (readyWorkflowRunIds != null && [...readyWorkflowRunIds].some((id) => !decoded?.calls?.some((call) => call.childRunId == id))) return yield* Effect.fail(/* @__PURE__ */ new Error("Ready workflow Run is not present in the checkpoint."));
|
|
12244
12296
|
const outputs = (nodeId, value) => isResolutionNode(prepared.graph.nodes[nodeId]) && value.pending != null ? {
|
|
12245
12297
|
...value,
|
|
12246
12298
|
pending: withWaitNotificationMessage(value.pending)
|
|
@@ -12291,7 +12343,10 @@ function runFlow(prepared, options) {
|
|
|
12291
12343
|
inputDefs: prepared.inputDefs,
|
|
12292
12344
|
outputDefs: prepared.outputDefs,
|
|
12293
12345
|
outputMappings: prepared.outputMappings
|
|
12294
|
-
}, options.runId, options.inputs, trigger, checkpoint == null ? void 0 : {
|
|
12346
|
+
}, options.runId, options.inputs, trigger, checkpoint == null ? void 0 : {
|
|
12347
|
+
checkpoint,
|
|
12348
|
+
readyWorkflowRunIds
|
|
12349
|
+
});
|
|
12295
12350
|
});
|
|
12296
12351
|
}
|
|
12297
12352
|
//#endregion
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { JsonValue } from './flow-model.js';
|
|
2
|
+
import type { SkipDuplicatesConfig } from './flow-state.js';
|
|
3
|
+
export interface StateConformanceHarness {
|
|
4
|
+
readonly maxIdentities: number;
|
|
5
|
+
/** Executes Code on the same initially empty Flow and scope on every call; returns its `result` output. */
|
|
6
|
+
run(source: string): Promise<JsonValue>;
|
|
7
|
+
/** Executes the same Skip Duplicates node across Runs, updating its business config and items. */
|
|
8
|
+
skipDuplicates(config: SkipDuplicatesConfig, items: JsonValue[]): Promise<{
|
|
9
|
+
items: JsonValue[];
|
|
10
|
+
duplicates: JsonValue[];
|
|
11
|
+
}>;
|
|
12
|
+
}
|
|
13
|
+
export declare const stateConformanceCases: readonly {
|
|
14
|
+
name: string;
|
|
15
|
+
verify(harness: StateConformanceHarness): Promise<void>;
|
|
16
|
+
}[];
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FlowQuery, Tag, TagListItem } from './control-api.js';
|
|
2
|
+
export declare function normalizeFlowQuery(query?: FlowQuery): Required<FlowQuery>;
|
|
3
|
+
export declare function decodeTag(value: unknown): Tag;
|
|
4
|
+
export declare function decodeTagListItem(value: unknown): TagListItem;
|
package/dist/index.d.ts
CHANGED
|
@@ -102,6 +102,17 @@ export interface TaskContext<Actions extends object = Record<string, never>, Wor
|
|
|
102
102
|
readonly inputs: Readonly<Record<string, unknown>>;
|
|
103
103
|
readonly runId: string;
|
|
104
104
|
readonly blockId: string;
|
|
105
|
+
readonly state: {
|
|
106
|
+
get(key: string): Promise<JsonValue | null>;
|
|
107
|
+
set(key: string, value: JsonValue): Promise<void>;
|
|
108
|
+
insert(key: string, value: Exclude<JsonValue, null>): Promise<boolean>;
|
|
109
|
+
delete(key: string): Promise<void>;
|
|
110
|
+
list(): Promise<string[]>;
|
|
111
|
+
};
|
|
112
|
+
readonly dedupe: {
|
|
113
|
+
insert(ids: readonly (string | number)[]): Promise<boolean[]>;
|
|
114
|
+
has(ids: readonly (string | number)[]): Promise<boolean[]>;
|
|
115
|
+
};
|
|
105
116
|
readonly artifact: ArtifactCapability;
|
|
106
117
|
/** Reads the selected direct predecessors, sorted by node ID; empty for a graph root. */
|
|
107
118
|
getPrevious(): Promise<readonly PreviousNode[]>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oomol-lab/open-flow",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.78",
|
|
4
4
|
"description": "TypeScript contracts and Browser runtime for Open Flow.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"repository": {
|
|
@@ -25,6 +25,10 @@
|
|
|
25
25
|
"types": "./dist/common/decision.d.ts",
|
|
26
26
|
"import": "./dist/common/decision.js"
|
|
27
27
|
},
|
|
28
|
+
"./flow-state": {
|
|
29
|
+
"types": "./dist/common/flow-state.d.ts",
|
|
30
|
+
"import": "./dist/common/flow-state.js"
|
|
31
|
+
},
|
|
28
32
|
"./http-request": {
|
|
29
33
|
"types": "./dist/common/http-request.d.ts",
|
|
30
34
|
"import": "./dist/common/http-request.js"
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { CanvasCommand } from '../../canvas/common/commands.ts'
|
|
2
|
+
import type { FlowStateView, StateScope } from '../../state/common/state.ts'
|
|
2
3
|
|
|
3
4
|
import { currentEngineContract } from '../../runtime/common/engineContract.ts'
|
|
5
|
+
import { flowStateViewSchema } from '../../state/common/state.ts'
|
|
4
6
|
import { workflowContractSchema, workflowReferencesSchema } from '../../workflow/common/workflow.ts'
|
|
5
7
|
export type { CanvasCommand, CanvasCommandSubscriber } from '../../canvas/common/commands.ts'
|
|
6
8
|
import type { LiveDetails } from './liveDetails.ts'
|
|
@@ -77,6 +79,8 @@ export type { FlowCatalogEvent, FlowChangeEvent } from './flowNotifications.ts'
|
|
|
77
79
|
|
|
78
80
|
import { ApiError } from './errors.ts'
|
|
79
81
|
import { randomId } from './random.ts'
|
|
82
|
+
import { decodeTag, decodeTagListItem, normalizeFlowQuery } from './tags.ts'
|
|
83
|
+
export { normalizeFlowQuery } from './tags.ts'
|
|
80
84
|
|
|
81
85
|
export interface CanvasBroadcast {
|
|
82
86
|
readonly version: 1
|
|
@@ -103,7 +107,29 @@ export interface FlowResources {
|
|
|
103
107
|
}
|
|
104
108
|
}
|
|
105
109
|
|
|
110
|
+
export interface FlowUpdate {
|
|
111
|
+
readonly name?: string
|
|
112
|
+
readonly tagIds?: readonly string[]
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface FlowQuery {
|
|
116
|
+
readonly query?: string
|
|
117
|
+
readonly tagIds?: readonly string[]
|
|
118
|
+
readonly untagged?: boolean
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface Tag {
|
|
122
|
+
readonly tagId: string
|
|
123
|
+
readonly name: string
|
|
124
|
+
readonly version: 1
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface TagListItem extends Tag {
|
|
128
|
+
readonly referenceCount: number
|
|
129
|
+
}
|
|
130
|
+
|
|
106
131
|
export interface Flow {
|
|
132
|
+
readonly tagIds?: readonly string[]
|
|
107
133
|
readonly live?: { readonly enabled: boolean; readonly hasStart: boolean; readonly publicationId: string; readonly revisionId: string }
|
|
108
134
|
|
|
109
135
|
readonly createdAt: string
|
|
@@ -123,6 +149,11 @@ export interface PublicationPresentation {
|
|
|
123
149
|
readonly presentation: Presentation | null
|
|
124
150
|
}
|
|
125
151
|
|
|
152
|
+
export interface FlowCreationOptions {
|
|
153
|
+
readonly tagIds?: readonly string[]
|
|
154
|
+
readonly presentation?: Readonly<Record<string, JsonValue>>
|
|
155
|
+
}
|
|
156
|
+
|
|
126
157
|
export interface Presentation {
|
|
127
158
|
readonly revision: number
|
|
128
159
|
readonly updatedAt: string
|
|
@@ -677,6 +708,7 @@ export function decodeRunEvent(value: unknown) {
|
|
|
677
708
|
nodeKind !== 'decision' &&
|
|
678
709
|
nodeKind !== 'openapi' &&
|
|
679
710
|
nodeKind !== 'http-request' &&
|
|
711
|
+
nodeKind !== 'skip-duplicates' &&
|
|
680
712
|
nodeKind !== 'llm' &&
|
|
681
713
|
nodeKind !== 'agent' &&
|
|
682
714
|
nodeKind !== 'value' &&
|
|
@@ -809,9 +841,13 @@ export class ControlClient {
|
|
|
809
841
|
}
|
|
810
842
|
|
|
811
843
|
async listFlows(
|
|
812
|
-
options: { readonly cursor?: string; readonly includeTotal?: boolean; readonly limit?: number; readonly signal?: AbortSignal } = {},
|
|
844
|
+
options: FlowQuery & { readonly cursor?: string; readonly includeTotal?: boolean; readonly limit?: number; readonly signal?: AbortSignal } = {},
|
|
813
845
|
): Promise<FlowPage> {
|
|
814
846
|
const parameters = new URLSearchParams()
|
|
847
|
+
const filter = normalizeFlowQuery(options)
|
|
848
|
+
if (filter.query) parameters.set('query', filter.query)
|
|
849
|
+
if (filter.tagIds.length) parameters.set('tagIds', filter.tagIds.join(','))
|
|
850
|
+
if (filter.untagged) parameters.set('untagged', 'true')
|
|
815
851
|
if (options.cursor != null) parameters.set('cursor', options.cursor)
|
|
816
852
|
if (options.limit != null) parameters.set('limit', String(options.limit))
|
|
817
853
|
if (options.includeTotal != null) parameters.set('includeTotal', String(options.includeTotal))
|
|
@@ -897,20 +933,35 @@ export class ControlClient {
|
|
|
897
933
|
}
|
|
898
934
|
}
|
|
899
935
|
|
|
900
|
-
async importFlow(
|
|
936
|
+
async importFlow(
|
|
937
|
+
file: WorkflowFile,
|
|
938
|
+
options: { readonly name?: string; readonly teamId?: string; readonly idempotencyKey?: string; readonly tagIds?: readonly string[] } = {},
|
|
939
|
+
): Promise<Flow> {
|
|
901
940
|
return flow(
|
|
902
941
|
await this.request('/v1/flows/import', {
|
|
903
942
|
method: 'POST',
|
|
904
943
|
headers: { 'Idempotency-Key': options.idempotencyKey ?? `flow-${randomId()}` },
|
|
905
|
-
body: JSON.stringify({ file, name: options.name, teamId: options.teamId, version: 1 }),
|
|
944
|
+
body: JSON.stringify({ file, name: options.name, teamId: options.teamId, tagIds: options.tagIds, version: 1 }),
|
|
906
945
|
}),
|
|
907
946
|
)
|
|
908
947
|
}
|
|
909
948
|
|
|
910
|
-
async createFlow(
|
|
949
|
+
async createFlow(
|
|
950
|
+
name: string,
|
|
951
|
+
idempotencyKey = `flow-${randomId()}`,
|
|
952
|
+
teamId?: string,
|
|
953
|
+
tagIds?: readonly string[],
|
|
954
|
+
initialPresentation?: Readonly<Record<string, JsonValue>>,
|
|
955
|
+
): Promise<Flow> {
|
|
911
956
|
return flow(
|
|
912
957
|
await this.request('/v1/flows', {
|
|
913
|
-
body: JSON.stringify({
|
|
958
|
+
body: JSON.stringify({
|
|
959
|
+
name,
|
|
960
|
+
...(teamId == null ? {} : { teamId }),
|
|
961
|
+
...(tagIds == null ? {} : { tagIds }),
|
|
962
|
+
...(initialPresentation == null ? {} : { presentation: initialPresentation }),
|
|
963
|
+
version: 1,
|
|
964
|
+
}),
|
|
914
965
|
headers: { 'idempotency-key': idempotencyKey },
|
|
915
966
|
method: 'POST',
|
|
916
967
|
}),
|
|
@@ -939,13 +990,52 @@ export class ControlClient {
|
|
|
939
990
|
return source.listeners.map(errorListener)
|
|
940
991
|
}
|
|
941
992
|
|
|
942
|
-
async
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
993
|
+
async listTags(signal?: AbortSignal): Promise<{ readonly tags: readonly TagListItem[]; readonly version: 1 }> {
|
|
994
|
+
const source = record(await this.request('/v1/tags', { signal }))
|
|
995
|
+
if (source.version != 1 || !Array.isArray(source.tags)) return invalidResponse()
|
|
996
|
+
return { tags: source.tags.map(decodeTagListItem), version: 1 }
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
async createTag(name: string): Promise<Tag> {
|
|
1000
|
+
return decodeTag(await this.request('/v1/tags', { method: 'POST', body: JSON.stringify({ name, version: 1 }) }))
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
async renameTag(tagId: string, name: string): Promise<Tag> {
|
|
1004
|
+
return decodeTag(await this.request(`/v1/tags/${segment(tagId)}`, { method: 'PATCH', body: JSON.stringify({ name, version: 1 }) }))
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
async deleteTag(tagId: string): Promise<void> {
|
|
1008
|
+
const source = record(await this.request(`/v1/tags/${segment(tagId)}`, { method: 'DELETE' }))
|
|
1009
|
+
if (source.version != 1) invalidResponse()
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
async setFlowTags(flowId: string, tagIds: readonly string[]): Promise<Flow> {
|
|
1013
|
+
return flow(await this.request(`/v1/flows/${segment(flowId)}/tags`, { method: 'PUT', body: JSON.stringify({ tagIds, version: 1 }) }))
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
async updateFlow(flowId: string, changes: FlowUpdate): Promise<Flow> {
|
|
1017
|
+
return flow(await this.request(`/v1/flows/${segment(flowId)}`, { method: 'PATCH', body: JSON.stringify({ ...changes, version: 1 }) }))
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
async getFlowState(flowId: string, scope: StateScope, values = false): Promise<FlowStateView> {
|
|
1021
|
+
const result = flowStateViewSchema.safeParse(await this.request(`/v1/flows/${segment(flowId)}/state?scope=${scope}&values=${values}`))
|
|
1022
|
+
return result.success && result.data.scope === scope ? result.data : invalidResponse()
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
async clearFlowState(flowId: string, scope: StateScope, key?: string): Promise<void> {
|
|
1026
|
+
const source = record(
|
|
1027
|
+
await this.request(`/v1/flows/${segment(flowId)}/state?scope=${scope}${key == null ? '' : `&key=${encodeURIComponent(key)}`}`, { method: 'DELETE' }),
|
|
948
1028
|
)
|
|
1029
|
+
if (source.version !== 1 || source.cleared !== true) invalidResponse()
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
async clearFlowDedupe(flowId: string, scope: StateScope, space: string): Promise<void> {
|
|
1033
|
+
const source = record(await this.request(`/v1/flows/${segment(flowId)}/dedupe?scope=${scope}&space=${encodeURIComponent(space)}`, { method: 'DELETE' }))
|
|
1034
|
+
if (source.version !== 1 || source.cleared !== true) invalidResponse()
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
async renameFlow(flowId: string, name: string): Promise<Flow> {
|
|
1038
|
+
return this.updateFlow(flowId, { name })
|
|
949
1039
|
}
|
|
950
1040
|
|
|
951
1041
|
async deleteFlow(flowId: string): Promise<Flow> {
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
} from '../../flow/common/nodeChanges.ts'
|
|
44
44
|
import { retryConfigurationSchema } from '../../flow/common/retry.ts'
|
|
45
45
|
import { httpRequestTask, httpRequestConfigSchema, httpRequestInputs } from '../../http/common/httpRequest.ts'
|
|
46
|
+
import { skipDuplicatesConfigSchema, skipDuplicatesTask } from '../../state/common/state.ts'
|
|
46
47
|
import { withWorkflowTask, workflowTask, workflowSelectionTask, workflowCapability, workflowAgentTool } from '../../workflow/common/workflow.ts'
|
|
47
48
|
import { conditionConfigurationView, prepareConditionConfiguration } from './authoringCondition.ts'
|
|
48
49
|
import { authoringDiagnosticField } from './authoringDiagnosticFields.ts'
|
|
@@ -146,7 +147,7 @@ export function authoringNode(content: RevisionContent, ref: string) {
|
|
|
146
147
|
),
|
|
147
148
|
}
|
|
148
149
|
else if (task.executor.kind == 'workflow') config = { flowId: task.executor.flowId }
|
|
149
|
-
else if (task.executor.kind == 'http-request') {
|
|
150
|
+
else if (task.executor.kind == 'http-request' || task.executor.kind == 'skip-duplicates') {
|
|
150
151
|
const { kind: _kind, ...settings } = task.executor
|
|
151
152
|
config = settings
|
|
152
153
|
} else if (task.executor.kind == 'openapi') {
|
|
@@ -521,6 +522,8 @@ export async function compileAuthoring(base: RevisionContent, request: Authoring
|
|
|
521
522
|
task = { ...before, name: String(update.name), executor: { ...executor, ...config } } as ManagedTaskDefinition
|
|
522
523
|
} else task = await connectorTask(config, String(update.name))
|
|
523
524
|
} else if (type == 'decision') task = decisionTask(config.questions as Parameters<typeof decisionTask>[0], String(update.name))
|
|
525
|
+
else if (type == 'skip-duplicates')
|
|
526
|
+
task = { ...before, name: String(update.name), executor: { kind: 'skip-duplicates', ...skipDuplicatesConfigSchema.parse(config) } }
|
|
524
527
|
else if (type == 'http-request') {
|
|
525
528
|
task = httpRequestTask(httpRequestConfigSchema.parse(config), String(update.name), before)
|
|
526
529
|
next = { ...next, inputs: httpRequestInputs(node.inputs, before, task) } as GraphNode
|
|
@@ -774,6 +777,9 @@ export async function compileAuthoring(base: RevisionContent, request: Authoring
|
|
|
774
777
|
case 'connector':
|
|
775
778
|
changes = createManagedTask(identity, await connectorTask(config, name))
|
|
776
779
|
break
|
|
780
|
+
case 'skip-duplicates':
|
|
781
|
+
changes = createManagedTask(identity, skipDuplicatesTask(skipDuplicatesConfigSchema.parse(config), name))
|
|
782
|
+
break
|
|
777
783
|
case 'http-request':
|
|
778
784
|
changes = createManagedTask(identity, httpRequestTask(httpRequestConfigSchema.parse(config), name))
|
|
779
785
|
break
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from '../../flow/common/nodeChanges.ts'
|
|
14
14
|
import { httpRequestTask } from '../../http/common/httpRequest.ts'
|
|
15
15
|
import { openApiTask, selectOperation } from '../../openapi/common/openapi.ts'
|
|
16
|
+
import { skipDuplicatesTask } from '../../state/common/state.ts'
|
|
16
17
|
|
|
17
18
|
const descriptions = {
|
|
18
19
|
'start': 'A Start node with a required message parameter. Supply it through trigger.outputs.',
|
|
@@ -23,6 +24,8 @@ const descriptions = {
|
|
|
23
24
|
'poll': 'Gmail polling by key; replace CONNECTION_ID with an active Gmail connection.',
|
|
24
25
|
'integration': 'Telegram webhook integration by key; replace CONNECTION_ID with an active Telegram connection.',
|
|
25
26
|
'decision': 'AI Decision evaluates named questions against target and returns a complete answer object per output. Configure target before running.',
|
|
27
|
+
'skip-duplicates':
|
|
28
|
+
'Skip items already recorded by successful Runs. Configure identity fields; an empty list uses each string or numeric item. History is private to the node and only commits after Run success.',
|
|
26
29
|
'http-request': 'A single HTTP request. Bind url and request inputs; outputs are body, statusCode and headers.',
|
|
27
30
|
'openapi': 'A fixed JSON API operation. Replace the example document and URL before running.',
|
|
28
31
|
'connector':
|
|
@@ -57,6 +60,12 @@ export function authoringExample(name: string): { version: 1; operations: readon
|
|
|
57
60
|
{ name: 'needs_support', type: 'noul', instructions: 'Does the customer need support?' },
|
|
58
61
|
])
|
|
59
62
|
break
|
|
63
|
+
case 'skip-duplicates':
|
|
64
|
+
operations = [
|
|
65
|
+
...createManagedTask({ nodeId: 'skip-duplicates' }, skipDuplicatesTask()),
|
|
66
|
+
{ kind: 'graph.node.input.set', nodeId: 'skip-duplicates', handle: 'items', value: { kind: 'value', value: ['a', 'b', 'a'] } },
|
|
67
|
+
]
|
|
68
|
+
break
|
|
60
69
|
case 'http-request':
|
|
61
70
|
operations = [
|
|
62
71
|
...createManagedTask({ nodeId: 'http' }, httpRequestTask()),
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { retryConfigurationSchema, retryNodeTypes } from '../../flow/common/retry.ts'
|
|
13
13
|
import { httpRequestConfigSchema } from '../../http/common/httpRequest.ts'
|
|
14
14
|
import { currentEngineContract, findEngineContract } from '../../runtime/common/engineContract.ts'
|
|
15
|
+
import { skipDuplicatesConfigSchema } from '../../state/common/state.ts'
|
|
15
16
|
import { authoringLlmConfiguration } from './authoringLlm.ts'
|
|
16
17
|
import { authoringOpenApiSchema } from './authoringOpenApi.ts'
|
|
17
18
|
|
|
@@ -35,6 +36,7 @@ export const authoringTypes = [
|
|
|
35
36
|
'decision',
|
|
36
37
|
'openapi',
|
|
37
38
|
'http-request',
|
|
39
|
+
'skip-duplicates',
|
|
38
40
|
'condition',
|
|
39
41
|
'value',
|
|
40
42
|
'wait',
|
|
@@ -118,6 +120,7 @@ export const authoringConfigurations = {
|
|
|
118
120
|
'connector': definitions.connector.omit({ outputMode: true }).strict(),
|
|
119
121
|
'openapi': authoringOpenApiSchema,
|
|
120
122
|
'http-request': httpRequestConfigSchema,
|
|
123
|
+
'skip-duplicates': skipDuplicatesConfigSchema,
|
|
121
124
|
'agent': definitions.agent
|
|
122
125
|
.omit({ prompt: true, tools: true })
|
|
123
126
|
.extend({
|
|
@@ -283,7 +286,7 @@ export function authoringSchema(kind?: AuthoringType) {
|
|
|
283
286
|
}
|
|
284
287
|
: {
|
|
285
288
|
type: kind,
|
|
286
|
-
onError: retryNodeTypes.some((nodeType) => nodeType == kind) ? { enum: ['stop', 'error'] } : false,
|
|
289
|
+
onError: kind == 'skip-duplicates' || retryNodeTypes.some((nodeType) => nodeType == kind) ? { enum: ['stop', 'error'] } : false,
|
|
287
290
|
retry: retryNodeTypes.some((nodeType) => nodeType == kind) ? z.toJSONSchema(retryConfigurationSchema) : false,
|
|
288
291
|
inputSources: ['poll', 'integration'].includes(kind)
|
|
289
292
|
? ['value', 'unset', 'default']
|
|
@@ -306,17 +309,19 @@ export function authoringSchema(kind?: AuthoringType) {
|
|
|
306
309
|
? 'Configure static parameters with node.add.inputs or input.set. Definitions come from the selected Trigger key; execution output and variable sources are unavailable.'
|
|
307
310
|
: kind == 'llm'
|
|
308
311
|
? 'Configure model settings, message templates and history as defaults. Input bindings with the same names override these defaults; named business inputs can be bound directly. Template strings interpolate {{name}}.'
|
|
309
|
-
: kind == '
|
|
310
|
-
? '
|
|
311
|
-
: kind == '
|
|
312
|
-
? '
|
|
313
|
-
: kind == '
|
|
314
|
-
? 'Select
|
|
315
|
-
: kind == '
|
|
316
|
-
? '
|
|
317
|
-
: kind == '
|
|
318
|
-
?
|
|
319
|
-
:
|
|
312
|
+
: kind == 'skip-duplicates'
|
|
313
|
+
? 'Bind an items array. Configure fields as dot-separated identity paths; an empty list uses each scalar item. Outputs are items and duplicates. History belongs to this node and commits only when the Run completes successfully. Fields accept strings or finite numbers; missing or complex values reject the batch.'
|
|
314
|
+
: kind == 'http-request'
|
|
315
|
+
? 'Configure method, query/headers modes and names, bodyFormat, authentication, responseFormat and statusPolicy. Bind url, query.<name>/header.<name> or whole query/headers objects, body and auth.token (Basic: auth.username/auth.password). Credentials accept variables or upstream outputs only. Outputs: body, statusCode, headers. Authentication is replaced as a complete choice.'
|
|
316
|
+
: kind == 'openapi'
|
|
317
|
+
? 'Select an operation; the tool resolves its ports and authentication. Read authenticationChoices from node details. authentication is replaced as a complete choice. Bind advertised credentials through inputs.'
|
|
318
|
+
: kind == 'workflow'
|
|
319
|
+
? 'Select another workflow with config.flowId. A workflow cannot call itself, including in a disconnected node. Owned unpublished or unavailable targets may be saved as incomplete drafts. Use node.refresh to load the current published interface.'
|
|
320
|
+
: kind == 'agent'
|
|
321
|
+
? 'Tools accept Connector Actions or {kind: "workflow", flowId, name, description, approval, inputs}. Workflow parameter sources use model, input or value. The platform derives the interface. Refresh a workflow tool with node.refresh and tool: its name.'
|
|
322
|
+
: kind == 'code'
|
|
323
|
+
? codeGuidance
|
|
324
|
+
: 'Supply business configuration and input data. Fixed definitions are assembled by the tool. Existing constraints and defaults are preserved on partial updates.',
|
|
320
325
|
example: {
|
|
321
326
|
op: 'node.add',
|
|
322
327
|
as: 'step',
|
|
@@ -330,6 +335,7 @@ export function authoringSchema(kind?: AuthoringType) {
|
|
|
330
335
|
'connector': { config: { action: '<Action ID>', connectionId: '<Connection ID>' } },
|
|
331
336
|
'poll': { config: { key: '<Trigger key>', schedule: [{ type: 'every', unit: 'minute', value: 5 }] } },
|
|
332
337
|
'integration': { config: { key: '<Trigger key>' } },
|
|
338
|
+
'skip-duplicates': { config: { fields: ['id'] }, inputs: { items: { kind: 'value', value: [{ id: 'a' }, { id: 'b' }] } } },
|
|
333
339
|
'http-request': { config: {}, inputs: { url: { kind: 'value', value: 'https://example.com' } } },
|
|
334
340
|
'openapi': { config: { sourceUrl: 'https://example.com/openapi.json', path: '/items', method: 'get' } },
|
|
335
341
|
'code': { code: 'export default ({value}) => ({result:value})', inputs: { value: { kind: 'value', value: 'Hello' } } },
|