@oomol-lab/open-flow 0.1.0-beta.74 → 0.1.0-beta.75
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 +1 -0
- package/dist/browser/{ContentIcon-CUQHjQIw.js → ContentIcon-FCpKiwjp.js} +2 -1
- package/dist/browser/{FlowCanvas-Dtf8_cQp.js → FlowCanvas-dzRBnlUT.js} +1 -1
- package/dist/browser/{createResourceDialog-DI9pL-Yr.js → createResourceDialog-BeYDdUl-.js} +2 -2
- package/dist/browser/{dialog-BYJGvItc.js → dialog-ZtpsFbl4.js} +284 -13
- package/dist/browser/{exportSvg-BOoy7-Db.js → exportSvg-DqXw1yf-.js} +2 -2
- package/dist/browser/{flowChanges-RL-H4CjD.js → flowChanges-b_5UobFj.js} +285 -44
- package/dist/browser/{flowWorkspace-BkDsETj0.js → flowWorkspace-ItojZbO4.js} +414 -53
- package/dist/browser/preview.css +181 -176
- package/dist/browser/preview.js +955 -182
- package/dist/browser/workbench.css +181 -176
- package/dist/browser/workbench.js +12 -6
- package/dist/browser/{workbenchSelect-BcWPWGzw.js → workbenchSelect-M441Lz0d.js} +1 -1
- package/dist/command/index.js +36 -36
- package/dist/common/authoring.d.ts +4 -4
- package/dist/common/authoringExamples.d.ts +3 -2
- package/dist/common/authoringSchema.d.ts +67 -4
- package/dist/common/control-api-conformance.js +58 -7
- package/dist/common/control-api.js +58 -7
- package/dist/common/control-requests.d.ts +3 -3
- package/dist/common/control-requests.js +234 -62
- package/dist/common/engine-contract.d.ts +1 -0
- package/dist/common/flow-authoring.js +56 -7
- package/dist/common/flow-change.d.ts +1 -1
- package/dist/common/flow-change.js +56 -7
- package/dist/common/flow-encoding.js +55 -7
- package/dist/common/flow-graph.d.ts +2 -1
- package/dist/common/flow-retry.d.ts +1 -1
- package/dist/common/flow-semantics.js +810 -577
- package/dist/common/http-request.d.ts +156 -0
- package/dist/common/http-request.js +5254 -0
- package/dist/common/mcp.d.ts +3 -3
- package/dist/common/mcp.js +58 -7
- package/dist/common/run-events.js +11 -9
- package/dist/common/runtime-contract.js +3 -0
- package/dist/common/scheduler.d.ts +1 -1
- package/dist/common/scheduler.js +70 -21
- package/dist/common/webhook-response.d.ts +0 -2
- package/dist/common/webhook-trigger.js +12 -11
- package/package.json +5 -1
- package/skills/open-flow/references/flow-authoring.md +1 -0
- package/skills/open-flow/references/flow-n8n-conversion.md +19 -15
- package/src/control/common/api.ts +1 -0
- package/src/control/common/authoring.ts +14 -3
- package/src/control/common/authoringExamples.ts +8 -0
- package/src/control/common/authoringSchema.ts +30 -24
- package/src/execution/common/engineContract.ts +4 -0
- package/src/execution/common/events.ts +3 -0
- package/src/execution/common/node-execution.ts +4 -2
- package/src/execution/common/scheduler.ts +1 -0
- package/src/flow/common/change.ts +3 -2
- package/src/flow/common/changeSchema.ts +12 -3
- package/src/flow/common/graph.ts +10 -4
- package/src/flow/common/retry.ts +1 -1
- package/src/flow/common/semantics.ts +60 -3
- package/src/flow/common/webhookResponse.ts +3 -7
- package/src/{openapi → http}/common/authBindings.ts +3 -3
- package/src/http/common/headers.ts +5 -0
- package/src/http/common/httpRequest.ts +251 -0
- package/src/trigger/common/webhook.ts +4 -3
package/dist/common/mcp.d.ts
CHANGED
|
@@ -161,7 +161,7 @@ export declare const mcpTools: {
|
|
|
161
161
|
value: {
|
|
162
162
|
revision?: string | undefined;
|
|
163
163
|
query: string;
|
|
164
|
-
type?: "agent" | "approval" | "code" | "condition" | "connector" | "cron" | "decision" | "error" | "integration" | "llm" | "openapi" | "outputs" | "poll" | "start" | "value" | "wait" | "webhook" | "webhook-response" | "workflow" | undefined;
|
|
164
|
+
type?: "agent" | "approval" | "code" | "condition" | "connector" | "cron" | "decision" | "error" | "http-request" | "integration" | "llm" | "openapi" | "outputs" | "poll" | "start" | "value" | "wait" | "webhook" | "webhook-response" | "workflow" | undefined;
|
|
165
165
|
offset: number;
|
|
166
166
|
limit: number;
|
|
167
167
|
flowId: string;
|
|
@@ -193,7 +193,7 @@ export declare const mcpTools: {
|
|
|
193
193
|
};
|
|
194
194
|
validate(value: unknown): {
|
|
195
195
|
value: {
|
|
196
|
-
type?: "agent" | "approval" | "code" | "condition" | "connector" | "cron" | "decision" | "error" | "integration" | "llm" | "openapi" | "outputs" | "poll" | "start" | "value" | "wait" | "webhook" | "webhook-response" | "workflow" | undefined;
|
|
196
|
+
type?: "agent" | "approval" | "code" | "condition" | "connector" | "cron" | "decision" | "error" | "http-request" | "integration" | "llm" | "openapi" | "outputs" | "poll" | "start" | "value" | "wait" | "webhook" | "webhook-response" | "workflow" | undefined;
|
|
197
197
|
action?: string | undefined;
|
|
198
198
|
flowId?: string | undefined;
|
|
199
199
|
};
|
|
@@ -321,7 +321,7 @@ export declare const mcpTools: {
|
|
|
321
321
|
edits: ({
|
|
322
322
|
op: "node.add";
|
|
323
323
|
as: string;
|
|
324
|
-
type: "agent" | "approval" | "code" | "condition" | "connector" | "cron" | "decision" | "error" | "integration" | "llm" | "openapi" | "outputs" | "poll" | "start" | "value" | "wait" | "webhook" | "webhook-response" | "workflow";
|
|
324
|
+
type: "agent" | "approval" | "code" | "condition" | "connector" | "cron" | "decision" | "error" | "http-request" | "integration" | "llm" | "openapi" | "outputs" | "poll" | "start" | "value" | "wait" | "webhook" | "webhook-response" | "workflow";
|
|
325
325
|
name: string;
|
|
326
326
|
onError?: "error" | "stop" | undefined;
|
|
327
327
|
retry?: {
|
package/dist/common/mcp.js
CHANGED
|
@@ -8693,12 +8693,12 @@ function isSchemaKeyword(value) {
|
|
|
8693
8693
|
/** Shared by decision entry points, the editor, and durable decision storage. */
|
|
8694
8694
|
var waitCommentSchema = string$1().trim().refine((value) => [...value].length <= 2e3, "Wait comment must contain at most 2,000 Unicode code points.").nullable().optional();
|
|
8695
8695
|
//#endregion
|
|
8696
|
-
//#region src/
|
|
8696
|
+
//#region src/http/common/authBindings.ts
|
|
8697
8697
|
/** Never allow credentials to enter a Revision as literal node inputs. */
|
|
8698
|
-
function
|
|
8698
|
+
function assertHttpAuthBindings(document) {
|
|
8699
8699
|
for (const node of Object.values(document.graph.nodes)) {
|
|
8700
|
-
if (node.kind != "task" || !("executor" in node.task) || node.task.executor.kind
|
|
8701
|
-
for (const [handle, mapping] of Object.entries(node.inputs)) if (handle.startsWith("auth.") && mapping.kind == "value") throw new TypeError("
|
|
8700
|
+
if (node.kind != "task" || !("executor" in node.task) || !["openapi", "http-request"].includes(node.task.executor.kind)) continue;
|
|
8701
|
+
for (const [handle, mapping] of Object.entries(node.inputs)) if (handle.startsWith("auth.") && mapping.kind == "value") throw new TypeError("HTTP authentication accepts deployment variables and upstream outputs only.");
|
|
8702
8702
|
}
|
|
8703
8703
|
}
|
|
8704
8704
|
//#endregion
|
|
@@ -8791,6 +8791,53 @@ function answerSchema(question) {
|
|
|
8791
8791
|
required: Object.keys(properties)
|
|
8792
8792
|
};
|
|
8793
8793
|
}
|
|
8794
|
+
//#endregion
|
|
8795
|
+
//#region src/http/common/httpRequest.ts
|
|
8796
|
+
var parameterSchema = strictObject({
|
|
8797
|
+
mode: _enum(["fields", "object"]).default("fields"),
|
|
8798
|
+
names: array(string$1()).default([])
|
|
8799
|
+
});
|
|
8800
|
+
var httpRequestConfigSchema = strictObject({
|
|
8801
|
+
method: _enum([
|
|
8802
|
+
"GET",
|
|
8803
|
+
"POST",
|
|
8804
|
+
"PUT",
|
|
8805
|
+
"PATCH",
|
|
8806
|
+
"DELETE",
|
|
8807
|
+
"HEAD",
|
|
8808
|
+
"OPTIONS"
|
|
8809
|
+
]).default("GET"),
|
|
8810
|
+
query: parameterSchema.default({
|
|
8811
|
+
mode: "fields",
|
|
8812
|
+
names: []
|
|
8813
|
+
}),
|
|
8814
|
+
headers: parameterSchema.default({
|
|
8815
|
+
mode: "fields",
|
|
8816
|
+
names: []
|
|
8817
|
+
}),
|
|
8818
|
+
bodyFormat: _enum([
|
|
8819
|
+
"none",
|
|
8820
|
+
"json",
|
|
8821
|
+
"text",
|
|
8822
|
+
"form"
|
|
8823
|
+
]).default("none"),
|
|
8824
|
+
authentication: union([
|
|
8825
|
+
strictObject({ type: literal("none") }),
|
|
8826
|
+
strictObject({ type: _enum(["bearer", "basic"]) }),
|
|
8827
|
+
strictObject({
|
|
8828
|
+
type: literal("apiKey"),
|
|
8829
|
+
name: string$1(),
|
|
8830
|
+
in: _enum(["header", "query"])
|
|
8831
|
+
})
|
|
8832
|
+
]).default({ type: "none" }),
|
|
8833
|
+
responseFormat: _enum([
|
|
8834
|
+
"auto",
|
|
8835
|
+
"json",
|
|
8836
|
+
"text"
|
|
8837
|
+
]).default("auto"),
|
|
8838
|
+
statusPolicy: _enum(["success", "all"]).default("success")
|
|
8839
|
+
});
|
|
8840
|
+
var httpRequestExecutorSchema = httpRequestConfigSchema.extend({ kind: literal("http-request") });
|
|
8794
8841
|
function checkJsonDepth(value, depth = 0) {
|
|
8795
8842
|
if (depth > 64) throw new TypeError("Value exceeds the maximum JSON depth.");
|
|
8796
8843
|
if (value != null && typeof value == "object") for (const child of Object.values(value)) checkJsonDepth(child, depth + 1);
|
|
@@ -9070,7 +9117,8 @@ var managedFields = object({
|
|
|
9070
9117
|
kind: literal("workflow"),
|
|
9071
9118
|
flowId: text,
|
|
9072
9119
|
revisionId: text
|
|
9073
|
-
})
|
|
9120
|
+
}),
|
|
9121
|
+
httpRequestExecutorSchema
|
|
9074
9122
|
])
|
|
9075
9123
|
});
|
|
9076
9124
|
var managed = managedFields.transform(limitDecisionTask);
|
|
@@ -9370,7 +9418,7 @@ function decodeRevisionContent(value) {
|
|
|
9370
9418
|
checkJsonDepth(value);
|
|
9371
9419
|
const content = revision.parse(value);
|
|
9372
9420
|
checkModelVersion(content);
|
|
9373
|
-
|
|
9421
|
+
assertHttpAuthBindings(content.document);
|
|
9374
9422
|
return content;
|
|
9375
9423
|
}
|
|
9376
9424
|
var shapes = {
|
|
@@ -9540,6 +9588,7 @@ array(union([...new Map(Object.entries(shapes).map(([kind, shape]) => [kind, sha
|
|
|
9540
9588
|
...shape
|
|
9541
9589
|
})])).values()])).min(1);
|
|
9542
9590
|
function checkModelVersion(content) {
|
|
9591
|
+
if (content.modelVersion < 11 && Object.values(content.document.graph.nodes).some((candidateNode) => candidateNode.kind == "task" && "executor" in candidateNode.task && candidateNode.task.executor.kind == "http-request")) throw new TypeError("HTTP Request requires Flow model version 11.");
|
|
9543
9592
|
if (content.modelVersion < 11 && Object.values(content.document.graph.nodes).some((candidateNode) => candidateNode.kind == "task" && ("moduleId" in candidateNode.task ? candidateNode.task.capabilities?.some((item) => item.kind == "workflow") : candidateNode.task.executor.kind == "agent" && candidateNode.task.executor.tools.some((item) => item.kind == "workflow")))) throw new TypeError("Workflow tools require Flow model version 11.");
|
|
9544
9593
|
if (content.modelVersion < 10 && Object.values(content.document.graph.nodes).some((candidateNode) => candidateNode.kind == "webhook" && (candidateNode.options?.responseBody != null || candidateNode.options?.responseStatusCodeOutput != null || candidateNode.options?.responseHeadersSource != null || candidateNode.options?.responseHeaderOutputs != null))) throw new TypeError("Dynamic Webhook responses require Flow model version 10.");
|
|
9545
9594
|
if (content.modelVersion < 9 && Object.values(content.document.graph.nodes).some((candidateNode) => "retry" in candidateNode && candidateNode.retry != null)) throw new TypeError("Node retries require Flow model version 9.");
|
|
@@ -9612,6 +9661,7 @@ var type = _enum([
|
|
|
9612
9661
|
"llm",
|
|
9613
9662
|
"decision",
|
|
9614
9663
|
"openapi",
|
|
9664
|
+
"http-request",
|
|
9615
9665
|
"condition",
|
|
9616
9666
|
"value",
|
|
9617
9667
|
"wait",
|
|
@@ -9737,6 +9787,7 @@ var authoringConfigurations = {
|
|
|
9737
9787
|
}),
|
|
9738
9788
|
"connector": authoringDefinitionSchemas.connector.omit({ outputMode: true }).strict(),
|
|
9739
9789
|
"openapi": authoringOpenApiSchema,
|
|
9790
|
+
"http-request": httpRequestConfigSchema,
|
|
9740
9791
|
"agent": authoringDefinitionSchemas.agent.omit({
|
|
9741
9792
|
prompt: true,
|
|
9742
9793
|
tools: true
|
|
@@ -11855,7 +11906,7 @@ function decodeRunEvent(value) {
|
|
|
11855
11906
|
switch (kind) {
|
|
11856
11907
|
case "node.started": {
|
|
11857
11908
|
const nodeKind = payload.nodeKind;
|
|
11858
|
-
if (nodeKind !== void 0 && nodeKind !== "approval" && nodeKind !== "condition" && nodeKind !== "connector" && nodeKind !== "javascript" && nodeKind !== "decision" && nodeKind !== "openapi" && nodeKind !== "llm" && nodeKind !== "agent" && nodeKind !== "value" && nodeKind !== "outputs" && nodeKind !== "webhook-response" && nodeKind !== "workflow" && nodeKind !== "wait") return invalidResponse();
|
|
11909
|
+
if (nodeKind !== void 0 && nodeKind !== "approval" && nodeKind !== "condition" && nodeKind !== "connector" && nodeKind !== "javascript" && nodeKind !== "decision" && nodeKind !== "openapi" && nodeKind !== "http-request" && nodeKind !== "llm" && nodeKind !== "agent" && nodeKind !== "value" && nodeKind !== "outputs" && nodeKind !== "webhook-response" && nodeKind !== "workflow" && nodeKind !== "wait") return invalidResponse();
|
|
11859
11910
|
return {
|
|
11860
11911
|
...base,
|
|
11861
11912
|
kind,
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
//#region src/http/common/headers.ts
|
|
2
|
+
/** Header names are case-insensitive; never silently combine duplicate business fields. */
|
|
3
|
+
function httpHeaderNameIssue(name, names) {
|
|
4
|
+
if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name)) return "invalid";
|
|
5
|
+
if (names.some((candidate) => candidate.toLowerCase() === name.toLowerCase())) return "duplicate";
|
|
6
|
+
}
|
|
7
|
+
//#endregion
|
|
1
8
|
//#region src/flow/common/webhookResponse.ts
|
|
2
9
|
function webhookResponseAllowsBody(status) {
|
|
3
10
|
return status !== 204 && status !== 205 && status !== 304;
|
|
4
11
|
}
|
|
5
|
-
|
|
12
|
+
({
|
|
6
13
|
jsonSchema: {
|
|
7
14
|
type: "object",
|
|
8
15
|
additionalProperties: { type: [
|
|
@@ -12,13 +19,7 @@ var webhookHeadersPort = {
|
|
|
12
19
|
] }
|
|
13
20
|
},
|
|
14
21
|
nullable: true
|
|
15
|
-
};
|
|
16
|
-
/** Header names are case-insensitive; never silently combine duplicate business fields. */
|
|
17
|
-
function webhookHeaderNameIssue(name, names) {
|
|
18
|
-
if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name)) return "invalid";
|
|
19
|
-
if (names.some((candidate) => candidate.toLowerCase() === name.toLowerCase())) return "duplicate";
|
|
20
|
-
}
|
|
21
|
-
webhookHeadersPort.jsonSchema;
|
|
22
|
+
}).jsonSchema;
|
|
22
23
|
function validWebhookStatus(value) {
|
|
23
24
|
return typeof value === "number" && Number.isInteger(value) && value >= 200 && value <= 599 && webhookResponseAllowsBody(value);
|
|
24
25
|
}
|
|
@@ -28,7 +29,7 @@ function webhookHeaderValues(source) {
|
|
|
28
29
|
const headers = {};
|
|
29
30
|
const names = [];
|
|
30
31
|
for (const [name, value] of Object.entries(source)) {
|
|
31
|
-
if (
|
|
32
|
+
if (httpHeaderNameIssue(name, names) != null) return;
|
|
32
33
|
if (typeof value !== "string" && typeof value !== "boolean" && !(typeof value === "number" && Number.isFinite(value))) return;
|
|
33
34
|
const text = String(value);
|
|
34
35
|
if ([...text].some((character) => character.charCodeAt(0) > 255 || /[\0\r\n]/.test(character))) return;
|
|
@@ -52,6 +53,7 @@ var nodeKinds = /* @__PURE__ */ new Set([
|
|
|
52
53
|
"decision",
|
|
53
54
|
"javascript",
|
|
54
55
|
"openapi",
|
|
56
|
+
"http-request",
|
|
55
57
|
"llm",
|
|
56
58
|
"value",
|
|
57
59
|
"wait",
|
|
@@ -27,6 +27,7 @@ var currentEngineContract = "open-flow-engine/v8";
|
|
|
27
27
|
var nodejsEngineContract = "open-flow-engine/v8/nodejs-compat-v1";
|
|
28
28
|
var currentContract = {
|
|
29
29
|
workflowTools: true,
|
|
30
|
+
httpRequests: true,
|
|
30
31
|
waitAutoContinue: true,
|
|
31
32
|
webhookResponses: true,
|
|
32
33
|
platformExports: /* @__PURE__ */ new Set(["engineContract", "identity"]),
|
|
@@ -73,11 +74,13 @@ function findEngineContract(contract) {
|
|
|
73
74
|
if (contract == "open-flow-engine/v7" || contract == "open-flow-engine/v7/nodejs-compat-v1") return {
|
|
74
75
|
...contract.endsWith("/nodejs-compat-v1") ? nodejsContract : currentContract,
|
|
75
76
|
workflowTools: false,
|
|
77
|
+
httpRequests: false,
|
|
76
78
|
platformSource: `export const engineContract = ${JSON.stringify(contract)}\nexport function identity(value) { return value }\n`
|
|
77
79
|
};
|
|
78
80
|
if (contract == "open-flow-engine/v6" || contract == "open-flow-engine/v6/nodejs-compat-v1") return {
|
|
79
81
|
...contract == "open-flow-engine/v6" ? currentContract : nodejsContract,
|
|
80
82
|
workflowTools: false,
|
|
83
|
+
httpRequests: false,
|
|
81
84
|
waitAutoContinue: false,
|
|
82
85
|
webhookResponses: false,
|
|
83
86
|
platformSource: `export const engineContract = ${JSON.stringify(contract)}
|
|
@@ -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' | 'llm' | 'value' | 'wait' | 'outputs' | 'workflow' | 'webhook-response';
|
|
36
|
+
readonly nodeKind: 'agent' | 'approval' | 'condition' | 'connector' | 'decision' | 'javascript' | 'openapi' | 'http-request' | 'llm' | 'value' | 'wait' | 'outputs' | 'workflow' | 'webhook-response';
|
|
37
37
|
readonly nodeTitle?: string;
|
|
38
38
|
readonly runId: string;
|
|
39
39
|
readonly type: 'node.started';
|
package/dist/common/scheduler.js
CHANGED
|
@@ -9472,11 +9472,65 @@ function answerSchema(question) {
|
|
|
9472
9472
|
};
|
|
9473
9473
|
}
|
|
9474
9474
|
//#endregion
|
|
9475
|
+
//#region src/http/common/headers.ts
|
|
9476
|
+
/** Header names are case-insensitive; never silently combine duplicate business fields. */
|
|
9477
|
+
function httpHeaderNameIssue(name, names) {
|
|
9478
|
+
if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name)) return "invalid";
|
|
9479
|
+
if (names.some((candidate) => candidate.toLowerCase() === name.toLowerCase())) return "duplicate";
|
|
9480
|
+
}
|
|
9481
|
+
//#endregion
|
|
9482
|
+
//#region src/http/common/httpRequest.ts
|
|
9483
|
+
var parameterSchema = strictObject({
|
|
9484
|
+
mode: _enum(["fields", "object"]).default("fields"),
|
|
9485
|
+
names: array(string$1()).default([])
|
|
9486
|
+
});
|
|
9487
|
+
var httpRequestExecutorSchema = strictObject({
|
|
9488
|
+
method: _enum([
|
|
9489
|
+
"GET",
|
|
9490
|
+
"POST",
|
|
9491
|
+
"PUT",
|
|
9492
|
+
"PATCH",
|
|
9493
|
+
"DELETE",
|
|
9494
|
+
"HEAD",
|
|
9495
|
+
"OPTIONS"
|
|
9496
|
+
]).default("GET"),
|
|
9497
|
+
query: parameterSchema.default({
|
|
9498
|
+
mode: "fields",
|
|
9499
|
+
names: []
|
|
9500
|
+
}),
|
|
9501
|
+
headers: parameterSchema.default({
|
|
9502
|
+
mode: "fields",
|
|
9503
|
+
names: []
|
|
9504
|
+
}),
|
|
9505
|
+
bodyFormat: _enum([
|
|
9506
|
+
"none",
|
|
9507
|
+
"json",
|
|
9508
|
+
"text",
|
|
9509
|
+
"form"
|
|
9510
|
+
]).default("none"),
|
|
9511
|
+
authentication: union([
|
|
9512
|
+
strictObject({ type: literal("none") }),
|
|
9513
|
+
strictObject({ type: _enum(["bearer", "basic"]) }),
|
|
9514
|
+
strictObject({
|
|
9515
|
+
type: literal("apiKey"),
|
|
9516
|
+
name: string$1(),
|
|
9517
|
+
in: _enum(["header", "query"])
|
|
9518
|
+
})
|
|
9519
|
+
]).default({ type: "none" }),
|
|
9520
|
+
responseFormat: _enum([
|
|
9521
|
+
"auto",
|
|
9522
|
+
"json",
|
|
9523
|
+
"text"
|
|
9524
|
+
]).default("auto"),
|
|
9525
|
+
statusPolicy: _enum(["success", "all"]).default("success")
|
|
9526
|
+
}).extend({ kind: literal("http-request") });
|
|
9527
|
+
//#endregion
|
|
9475
9528
|
//#region src/flow/common/retry.ts
|
|
9476
9529
|
var retryNodeTypes = [
|
|
9477
9530
|
"code",
|
|
9478
9531
|
"connector",
|
|
9479
9532
|
"openapi",
|
|
9533
|
+
"http-request",
|
|
9480
9534
|
"llm",
|
|
9481
9535
|
"decision"
|
|
9482
9536
|
];
|
|
@@ -9565,22 +9619,6 @@ var webhookResponseTimeout = {
|
|
|
9565
9619
|
function webhookResponseAllowsBody(status) {
|
|
9566
9620
|
return status !== 204 && status !== 205 && status !== 304;
|
|
9567
9621
|
}
|
|
9568
|
-
var webhookHeadersPort = {
|
|
9569
|
-
jsonSchema: {
|
|
9570
|
-
type: "object",
|
|
9571
|
-
additionalProperties: { type: [
|
|
9572
|
-
"string",
|
|
9573
|
-
"number",
|
|
9574
|
-
"boolean"
|
|
9575
|
-
] }
|
|
9576
|
-
},
|
|
9577
|
-
nullable: true
|
|
9578
|
-
};
|
|
9579
|
-
/** Header names are case-insensitive; never silently combine duplicate business fields. */
|
|
9580
|
-
function webhookHeaderNameIssue(name, names) {
|
|
9581
|
-
if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name)) return "invalid";
|
|
9582
|
-
if (names.some((candidate) => candidate.toLowerCase() === name.toLowerCase())) return "duplicate";
|
|
9583
|
-
}
|
|
9584
9622
|
/** Fixed inputs belong to the node, not author-supplied definitions. */
|
|
9585
9623
|
var webhookResponseInputs = [
|
|
9586
9624
|
{
|
|
@@ -9597,7 +9635,17 @@ var webhookResponseInputs = [
|
|
|
9597
9635
|
},
|
|
9598
9636
|
{
|
|
9599
9637
|
handle: "headers",
|
|
9600
|
-
jsonSchema:
|
|
9638
|
+
jsonSchema: {
|
|
9639
|
+
jsonSchema: {
|
|
9640
|
+
type: "object",
|
|
9641
|
+
additionalProperties: { type: [
|
|
9642
|
+
"string",
|
|
9643
|
+
"number",
|
|
9644
|
+
"boolean"
|
|
9645
|
+
] }
|
|
9646
|
+
},
|
|
9647
|
+
nullable: true
|
|
9648
|
+
}.jsonSchema,
|
|
9601
9649
|
nullable: false,
|
|
9602
9650
|
value: {}
|
|
9603
9651
|
}
|
|
@@ -9611,7 +9659,7 @@ function webhookHeaderValues(source) {
|
|
|
9611
9659
|
const headers = {};
|
|
9612
9660
|
const names = [];
|
|
9613
9661
|
for (const [name, value] of Object.entries(source)) {
|
|
9614
|
-
if (
|
|
9662
|
+
if (httpHeaderNameIssue(name, names) != null) return;
|
|
9615
9663
|
if (typeof value !== "string" && typeof value !== "boolean" && !(typeof value === "number" && Number.isFinite(value))) return;
|
|
9616
9664
|
const text = String(value);
|
|
9617
9665
|
if ([...text].some((character) => character.charCodeAt(0) > 255 || /[\0\r\n]/.test(character))) return;
|
|
@@ -9822,7 +9870,8 @@ var managedFields = object({
|
|
|
9822
9870
|
kind: literal("workflow"),
|
|
9823
9871
|
flowId: text,
|
|
9824
9872
|
revisionId: text
|
|
9825
|
-
})
|
|
9873
|
+
}),
|
|
9874
|
+
httpRequestExecutorSchema
|
|
9826
9875
|
])
|
|
9827
9876
|
});
|
|
9828
9877
|
var managed = managedFields.transform(limitDecisionTask);
|
|
@@ -11458,7 +11507,7 @@ function executeNode(context, input) {
|
|
|
11458
11507
|
const kind = nodeKind(node);
|
|
11459
11508
|
const title = nodeTitle(node);
|
|
11460
11509
|
const projectedInputs = Object.fromEntries(Object.entries(nodeInputs).filter(([handle]) => {
|
|
11461
|
-
if (node.kind == "task" && "executor" in node.task && node.task
|
|
11510
|
+
if (node.kind == "task" && "executor" in node.task && ["openapi", "http-request"].includes(node.task.executor.kind) && handle.startsWith("auth.")) return false;
|
|
11462
11511
|
const mapping = nodeInputMappings(node)[handle];
|
|
11463
11512
|
return mapping?.kind != "sources" || mapping.sources.every((source) => source.kind != "binding");
|
|
11464
11513
|
}));
|
|
@@ -11509,7 +11558,7 @@ function executeNode(context, input) {
|
|
|
11509
11558
|
input: Object.fromEntries(Object.entries(nodeInputs).filter(([handle]) => {
|
|
11510
11559
|
if (additional.has(handle)) return false;
|
|
11511
11560
|
const task = "executor" in node.task ? node.task : void 0;
|
|
11512
|
-
if (task
|
|
11561
|
+
if (task == null || !["openapi", "http-request"].includes(task.executor.kind)) return true;
|
|
11513
11562
|
const mapping = node.inputs[handle];
|
|
11514
11563
|
return mapping?.kind != "unset" && (mapping != null || task.inputs.some((port) => "handle" in port && port.handle == handle && port.value !== void 0));
|
|
11515
11564
|
})),
|
|
@@ -27,8 +27,6 @@ export declare const webhookHeadersPort: {
|
|
|
27
27
|
readonly nullable: true;
|
|
28
28
|
};
|
|
29
29
|
export declare function workflowOutputsPort(outputs: readonly Port[]): PortDefinition;
|
|
30
|
-
/** Header names are case-insensitive; never silently combine duplicate business fields. */
|
|
31
|
-
export declare function webhookHeaderNameIssue(name: string, names: readonly string[]): 'invalid' | 'duplicate' | undefined;
|
|
32
30
|
/** Fixed inputs belong to the node, not author-supplied definitions. */
|
|
33
31
|
export declare const webhookResponseInputs: readonly InputPort[];
|
|
34
32
|
export interface WebhookResponseSnapshot {
|
|
@@ -91,6 +91,13 @@ function isJsonObject(value) {
|
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
//#endregion
|
|
94
|
+
//#region src/http/common/headers.ts
|
|
95
|
+
/** Header names are case-insensitive; never silently combine duplicate business fields. */
|
|
96
|
+
function httpHeaderNameIssue(name, names) {
|
|
97
|
+
if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name)) return "invalid";
|
|
98
|
+
if (names.some((candidate) => candidate.toLowerCase() === name.toLowerCase())) return "duplicate";
|
|
99
|
+
}
|
|
100
|
+
//#endregion
|
|
94
101
|
//#region src/flow/common/webhookResponse.ts
|
|
95
102
|
var webhookResponseTimeout = {
|
|
96
103
|
defaultMs: 3e4,
|
|
@@ -100,7 +107,7 @@ var webhookResponseTimeout = {
|
|
|
100
107
|
function webhookResponseAllowsBody(status) {
|
|
101
108
|
return status !== 204 && status !== 205 && status !== 304;
|
|
102
109
|
}
|
|
103
|
-
|
|
110
|
+
({
|
|
104
111
|
jsonSchema: {
|
|
105
112
|
type: "object",
|
|
106
113
|
additionalProperties: { type: [
|
|
@@ -110,13 +117,7 @@ var webhookHeadersPort = {
|
|
|
110
117
|
] }
|
|
111
118
|
},
|
|
112
119
|
nullable: true
|
|
113
|
-
};
|
|
114
|
-
/** Header names are case-insensitive; never silently combine duplicate business fields. */
|
|
115
|
-
function webhookHeaderNameIssue(name, names) {
|
|
116
|
-
if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name)) return "invalid";
|
|
117
|
-
if (names.some((candidate) => candidate.toLowerCase() === name.toLowerCase())) return "duplicate";
|
|
118
|
-
}
|
|
119
|
-
webhookHeadersPort.jsonSchema;
|
|
120
|
+
}).jsonSchema;
|
|
120
121
|
function validWebhookStatus(value) {
|
|
121
122
|
return typeof value === "number" && Number.isInteger(value) && value >= 200 && value <= 599 && webhookResponseAllowsBody(value);
|
|
122
123
|
}
|
|
@@ -126,7 +127,7 @@ function webhookHeaderValues(source) {
|
|
|
126
127
|
const headers = {};
|
|
127
128
|
const names = [];
|
|
128
129
|
for (const [name, value] of Object.entries(source)) {
|
|
129
|
-
if (
|
|
130
|
+
if (httpHeaderNameIssue(name, names) != null) return;
|
|
130
131
|
if (typeof value !== "string" && typeof value !== "boolean" && !(typeof value === "number" && Number.isFinite(value))) return;
|
|
131
132
|
const text = String(value);
|
|
132
133
|
if ([...text].some((character) => character.charCodeAt(0) > 255 || /[\0\r\n]/.test(character))) return;
|
|
@@ -2929,7 +2930,7 @@ function webhookResponseHeaders(result, options) {
|
|
|
2929
2930
|
if (!isJsonObject(source)) return;
|
|
2930
2931
|
const values = /* @__PURE__ */ new Map();
|
|
2931
2932
|
for (const [name, value] of Object.entries(source)) {
|
|
2932
|
-
if (
|
|
2933
|
+
if (httpHeaderNameIssue(name, [...values.values()].map((entry) => entry.name)) != null) return;
|
|
2933
2934
|
values.set(name.toLowerCase(), {
|
|
2934
2935
|
name,
|
|
2935
2936
|
value
|
|
@@ -2938,7 +2939,7 @@ function webhookResponseHeaders(result, options) {
|
|
|
2938
2939
|
if (whole == null) {
|
|
2939
2940
|
const names = [];
|
|
2940
2941
|
for (const [name, output] of Object.entries(options.responseHeaderOutputs ?? {})) {
|
|
2941
|
-
if (
|
|
2942
|
+
if (httpHeaderNameIssue(name, names) != null) return;
|
|
2942
2943
|
names.push(name);
|
|
2943
2944
|
values.set(name.toLowerCase(), {
|
|
2944
2945
|
name,
|
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.75",
|
|
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
|
+
"./http-request": {
|
|
29
|
+
"types": "./dist/common/http-request.d.ts",
|
|
30
|
+
"import": "./dist/common/http-request.js"
|
|
31
|
+
},
|
|
28
32
|
"./openapi": {
|
|
29
33
|
"types": "./dist/common/openapi.d.ts",
|
|
30
34
|
"import": "./dist/common/openapi.js"
|
|
@@ -298,6 +298,7 @@ For Connector Tasks, independent Code Actions, Agent tools and notifications, an
|
|
|
298
298
|
| Manual, HTTP or scheduled entry | `start`, `webhook`, `cron`: workflow `inputDefs`, HTTP body fields, or schedule. |
|
|
299
299
|
| Workflow failure handler | `error`: published upstream Flows' automatic Run failures; not a node-level catch or a handler for manual Run failures. |
|
|
300
300
|
| Call another workflow | `workflow`: select an owned published Flow ID; inputs and outputs derive from its saved contract. Use `node.refresh` to update it. |
|
|
301
|
+
| Single HTTP request | `http-request`: method, parameter modes, authentication, body and response configuration; URL and request values use public inputs. |
|
|
301
302
|
| Fixed HTTP operation | `openapi`: an OpenAPI source, path and method; resolved authentication choices and ports appear in node details. |
|
|
302
303
|
| Reusable constants | `value`: named JSON values and optional output constraints. |
|
|
303
304
|
| Durable interaction | `wait`, `approval`: prompt and named inputs; resume through the Run's Wait ID. Wait additionally supports optional `config.autoContinue` with `{kind:"after",durationMs:86400000}` or `{kind:"at",at:"2026-10-25T10:00:00+08:00"}`. |
|
|
@@ -13,17 +13,17 @@ publishing and enabling remain separate actions within the user's authorization.
|
|
|
13
13
|
|
|
14
14
|
## Choose a representation
|
|
15
15
|
|
|
16
|
-
| Source behavior | Open Flow building blocks
|
|
17
|
-
| --------------------------------------------- |
|
|
18
|
-
| Scheduled, webhook or provider event | Cron, Webhook, Poll or Integration Trigger
|
|
19
|
-
| Provider read or write | Connector
|
|
20
|
-
| Field shaping, filtering, aggregation or join | Code with named input/output fields
|
|
21
|
-
| IF or Switch | Condition and named execution branches
|
|
22
|
-
| Prompt plus model and output parser | LLM with template, inputs and `resultSchema`
|
|
23
|
-
| Agent with tools or memory | Agent where its current contract fits
|
|
24
|
-
| HTTP request |
|
|
25
|
-
| Human response or durable pause | Wait or Approval
|
|
26
|
-
| Workflow failure handling | Error Trigger where its scope fits
|
|
16
|
+
| Source behavior | Open Flow building blocks | What determines a good fit |
|
|
17
|
+
| --------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------ |
|
|
18
|
+
| Scheduled, webhook or provider event | Cron, Webhook, Poll or Integration Trigger | Schedule/timezone, event payload, delivery and response behavior. |
|
|
19
|
+
| Provider read or write | Connector | Exact operation, input/output shape, account and number of calls. |
|
|
20
|
+
| Field shaping, filtering, aggregation or join | Code with named input/output fields | Explicit collection representation, ordering, keys and empty-input behavior. |
|
|
21
|
+
| IF or Switch | Condition and named execution branches | Predicate, first/all matches, fallback route and data sent onward. |
|
|
22
|
+
| Prompt plus model and output parser | LLM with template, inputs and `resultSchema` | Supported model, result shape and any parser repair behavior. |
|
|
23
|
+
| Agent with tools or memory | Agent where its current contract fits | Tool permissions, state lifetime and how context is supplied. |
|
|
24
|
+
| HTTP request | `http-request`, or `openapi` for a documented operation | Method, parameters, auth, response format and error behavior. |
|
|
25
|
+
| Human response or durable pause | Wait or Approval | Who resumes it, payload, timing, notification and external response lifecycle. |
|
|
26
|
+
| Workflow failure handling | Error Trigger where its scope fits | Which failures trigger it and what failure data it receives. |
|
|
27
27
|
|
|
28
28
|
Use `schema TYPE` and provider discovery for the actual deployment. A node's
|
|
29
29
|
name alone does not establish equivalence. Provider operations can differ even
|
|
@@ -120,10 +120,14 @@ inside URLs, headers, expressions or Code; keep those out of Drafts and reports.
|
|
|
120
120
|
Use supported credential bindings for the selected operation. A missing account
|
|
121
121
|
can leave a Draft incomplete without implying that the action itself is absent.
|
|
122
122
|
|
|
123
|
-
For HTTP Request conversion, `schema
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
123
|
+
For HTTP Request conversion, `schema http-request` describes a single request's
|
|
124
|
+
method, parameter modes, body, authentication and response handling. Bind `url`,
|
|
125
|
+
request parameters and body through public inputs. Authentication inputs require
|
|
126
|
+
variables or upstream outputs. Query arrays become repeated parameters; response
|
|
127
|
+
arrays remain one value. The first version does not follow redirects or support
|
|
128
|
+
pagination, files, OAuth or direct Agent tools. Check these requirements before
|
|
129
|
+
calling the conversion equivalent. Use `schema openapi` when an actual operation
|
|
130
|
+
contract is available; do not invent an API specification to make a request.
|
|
127
131
|
|
|
128
132
|
A provider event may expose an ID where the source expected a full record. A
|
|
129
133
|
Connector read can supply the missing data if it preserves the desired behavior.
|
|
@@ -675,6 +675,7 @@ export function decodeRunEvent(value: unknown) {
|
|
|
675
675
|
nodeKind !== 'javascript' &&
|
|
676
676
|
nodeKind !== 'decision' &&
|
|
677
677
|
nodeKind !== 'openapi' &&
|
|
678
|
+
nodeKind !== 'http-request' &&
|
|
678
679
|
nodeKind !== 'llm' &&
|
|
679
680
|
nodeKind !== 'agent' &&
|
|
680
681
|
nodeKind !== 'value' &&
|
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
renameFlowOutputSources,
|
|
43
43
|
} from '../../flow/common/nodeChanges.ts'
|
|
44
44
|
import { retryConfigurationSchema } from '../../flow/common/retry.ts'
|
|
45
|
+
import { httpRequestTask, httpRequestConfigSchema, httpRequestInputs } from '../../http/common/httpRequest.ts'
|
|
45
46
|
import { withWorkflowTask, workflowTask, workflowSelectionTask, workflowCapability, workflowAgentTool } from '../../workflow/common/workflow.ts'
|
|
46
47
|
import { conditionConfigurationView, prepareConditionConfiguration } from './authoringCondition.ts'
|
|
47
48
|
import { authoringDiagnosticField } from './authoringDiagnosticFields.ts'
|
|
@@ -145,7 +146,10 @@ export function authoringNode(content: RevisionContent, ref: string) {
|
|
|
145
146
|
),
|
|
146
147
|
}
|
|
147
148
|
else if (task.executor.kind == 'workflow') config = { flowId: task.executor.flowId }
|
|
148
|
-
else if (task.executor.kind == '
|
|
149
|
+
else if (task.executor.kind == 'http-request') {
|
|
150
|
+
const { kind: _kind, ...settings } = task.executor
|
|
151
|
+
config = settings
|
|
152
|
+
} else if (task.executor.kind == 'openapi') {
|
|
149
153
|
config = { ...openApiConfigView(task.executor) }
|
|
150
154
|
}
|
|
151
155
|
} else if (node.kind == 'task' && 'moduleId' in node.task) {
|
|
@@ -442,7 +446,8 @@ export async function compileAuthoring(base: RevisionContent, request: Authoring
|
|
|
442
446
|
const timing = record(record(patch.config).autoContinue)
|
|
443
447
|
if (Object.hasOwn(timing, 'kind') && timing.kind != record(record(view.config).autoContinue).kind) record(update.config).autoContinue = timing
|
|
444
448
|
}
|
|
445
|
-
if (type == 'openapi' && Object.hasOwn(record(patch.config), 'authentication'))
|
|
449
|
+
if ((type == 'openapi' || type == 'http-request') && Object.hasOwn(record(patch.config), 'authentication'))
|
|
450
|
+
record(update.config).authentication = record(patch.config).authentication
|
|
446
451
|
for (const path of clear) clearPath(update, path)
|
|
447
452
|
if (typeof update.name != 'string') fail('node.name-required', 'Node name cannot be cleared.')
|
|
448
453
|
const config = authoringConfigurations[type].parse(update.config) as Record<string, unknown>
|
|
@@ -516,7 +521,10 @@ export async function compileAuthoring(base: RevisionContent, request: Authoring
|
|
|
516
521
|
task = { ...before, name: String(update.name), executor: { ...executor, ...config } } as ManagedTaskDefinition
|
|
517
522
|
} else task = await connectorTask(config, String(update.name))
|
|
518
523
|
} else if (type == 'decision') task = decisionTask(config.questions as Parameters<typeof decisionTask>[0], String(update.name))
|
|
519
|
-
else if (type == '
|
|
524
|
+
else if (type == 'http-request') {
|
|
525
|
+
task = httpRequestTask(httpRequestConfigSchema.parse(config), String(update.name), before)
|
|
526
|
+
next = { ...next, inputs: httpRequestInputs(node.inputs, before, task) } as GraphNode
|
|
527
|
+
} else if (type == 'openapi') {
|
|
520
528
|
task = await prepareOpenApiTask(config as AuthoringOpenApiConfig, before, (url) => host.openapi(url), String(update.name), {
|
|
521
529
|
authentication: explicit('authentication'),
|
|
522
530
|
serverUrl: explicit('serverUrl'),
|
|
@@ -766,6 +774,9 @@ export async function compileAuthoring(base: RevisionContent, request: Authoring
|
|
|
766
774
|
case 'connector':
|
|
767
775
|
changes = createManagedTask(identity, await connectorTask(config, name))
|
|
768
776
|
break
|
|
777
|
+
case 'http-request':
|
|
778
|
+
changes = createManagedTask(identity, httpRequestTask(httpRequestConfigSchema.parse(config), name))
|
|
779
|
+
break
|
|
769
780
|
case 'openapi':
|
|
770
781
|
changes = createManagedTask(
|
|
771
782
|
identity,
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
createValue,
|
|
12
12
|
createWait,
|
|
13
13
|
} from '../../flow/common/nodeChanges.ts'
|
|
14
|
+
import { httpRequestTask } from '../../http/common/httpRequest.ts'
|
|
14
15
|
import { openApiTask, selectOperation } from '../../openapi/common/openapi.ts'
|
|
15
16
|
|
|
16
17
|
const descriptions = {
|
|
@@ -22,6 +23,7 @@ const descriptions = {
|
|
|
22
23
|
'poll': 'Gmail polling by key; replace CONNECTION_ID with an active Gmail connection.',
|
|
23
24
|
'integration': 'Telegram webhook integration by key; replace CONNECTION_ID with an active Telegram connection.',
|
|
24
25
|
'decision': 'AI Decision evaluates named questions against target and returns a complete answer object per output. Configure target before running.',
|
|
26
|
+
'http-request': 'A single HTTP request. Bind url and request inputs; outputs are body, statusCode and headers.',
|
|
25
27
|
'openapi': 'A fixed JSON API operation. Replace the example document and URL before running.',
|
|
26
28
|
'connector':
|
|
27
29
|
'A Connector Task and its node. Replace ACTION_ID, CONNECTION_ID and port definitions using connector_get / connector show. Set connectionId to an empty string to defer account configuration; execution will require an account.',
|
|
@@ -55,6 +57,12 @@ export function authoringExample(name: string): { version: 1; operations: readon
|
|
|
55
57
|
{ name: 'needs_support', type: 'noul', instructions: 'Does the customer need support?' },
|
|
56
58
|
])
|
|
57
59
|
break
|
|
60
|
+
case 'http-request':
|
|
61
|
+
operations = [
|
|
62
|
+
...createManagedTask({ nodeId: 'http' }, httpRequestTask()),
|
|
63
|
+
{ kind: 'graph.node.input.set', nodeId: 'http', handle: 'url', value: { kind: 'value', value: 'https://example.com' } },
|
|
64
|
+
]
|
|
65
|
+
break
|
|
58
66
|
case 'openapi': {
|
|
59
67
|
const document = {
|
|
60
68
|
openapi: '3.1.0',
|