@oomol-lab/open-flow 0.1.0-beta.69 → 0.1.0-beta.70
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/dist/browser/{ContentIcon-CsECY5id.js → ContentIcon-CUQHjQIw.js} +16 -8
- package/dist/browser/{FlowCanvas-C5X22xQ7.js → FlowCanvas-CqtUDPGj.js} +11 -26
- package/dist/browser/{createResourceDialog-Di5BBrQv.js → createResourceDialog-BAHYW9yu.js} +3 -3
- package/dist/browser/{dialog-Bjo-r9B0.js → dialog-DcGOzUP2.js} +162 -12
- package/dist/browser/{exportSvg-Cj6Spxx-.js → exportSvg-Dv5Qgrq7.js} +2 -2
- package/dist/browser/flow-authoring.js +95 -12
- package/dist/browser/flow-change-schema.d.ts +24 -1
- package/dist/browser/flow-change.d.ts +33 -4
- package/dist/browser/flow-change.js +96 -13
- package/dist/browser/{flowChanges-Dp-rcEJ6.js → flowChanges-CYyehKer.js} +2593 -2254
- package/dist/browser/{flowWorkspace-CB1QACku.js → flowWorkspace-qYUZPJHR.js} +1114 -392
- package/dist/browser/preview.css +225 -221
- package/dist/browser/preview.js +3781 -2655
- package/dist/browser/{tooltip-CJjYs_2I.js → tooltip-wZJRB9oP.js} +28 -1
- package/dist/browser/ui.css +8 -20
- package/dist/browser/workbench.css +225 -221
- package/dist/browser/workbench.js +31 -10
- package/dist/browser/{workbenchSelect-C3hxUcYm.js → workbenchSelect-D8_vP2p5.js} +2 -2
- package/dist/command/index.js +45 -45
- package/dist/common/authoring.d.ts +3 -3
- package/dist/common/authoringExamples.d.ts +3 -2
- package/dist/common/authoringSchema.d.ts +37 -374
- package/dist/common/control-api-conformance.js +151 -55
- package/dist/common/control-api.d.ts +12 -78
- package/dist/common/control-api.js +162 -39
- package/dist/common/control-requests.d.ts +3 -3
- package/dist/common/control-requests.js +221 -35
- package/dist/common/decision.js +85 -7
- package/dist/common/engine-contract.d.ts +4 -2
- package/dist/common/flow-encoding.js +110 -14
- package/dist/common/flow-graph.d.ts +3 -0
- package/dist/common/flow-semantics.js +273 -14
- package/dist/common/integration-trigger.js +85 -7
- package/dist/common/liveDetails.d.ts +15 -0
- package/dist/common/mcp.d.ts +3 -3
- package/dist/common/mcp.js +134 -38
- package/dist/common/openapi.js +85 -7
- package/dist/common/poll-trigger.js +85 -7
- package/dist/common/run-events.d.ts +2 -0
- package/dist/common/run-events.js +59 -2
- package/dist/common/runtime-contract.js +14 -4
- package/dist/common/scheduler.d.ts +6 -2
- package/dist/common/scheduler.js +178 -11
- package/dist/common/webhook-response.d.ts +43 -0
- package/dist/common/webhook-trigger.d.ts +12 -0
- package/dist/common/webhook-trigger.js +236 -9
- package/dist/common/workflow.js +85 -7
- package/package.json +1 -1
- package/skills/open-flow/references/flow-authoring.md +15 -10
|
@@ -1,3 +1,47 @@
|
|
|
1
|
+
//#region src/flow/common/webhookResponse.ts
|
|
2
|
+
function webhookResponseAllowsBody(status) {
|
|
3
|
+
return status !== 204 && status !== 205 && status !== 304;
|
|
4
|
+
}
|
|
5
|
+
var webhookHeadersPort = {
|
|
6
|
+
jsonSchema: {
|
|
7
|
+
type: "object",
|
|
8
|
+
additionalProperties: { type: [
|
|
9
|
+
"string",
|
|
10
|
+
"number",
|
|
11
|
+
"boolean"
|
|
12
|
+
] }
|
|
13
|
+
},
|
|
14
|
+
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
|
+
function validWebhookStatus(value) {
|
|
23
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 200 && value <= 599 && webhookResponseAllowsBody(value);
|
|
24
|
+
}
|
|
25
|
+
/** Portable validation also runs in the isolated engine, which has no HTTP globals. */
|
|
26
|
+
function webhookHeaderValues(source) {
|
|
27
|
+
if (source == null || typeof source !== "object" || Array.isArray(source)) return;
|
|
28
|
+
const headers = {};
|
|
29
|
+
const names = [];
|
|
30
|
+
for (const [name, value] of Object.entries(source)) {
|
|
31
|
+
if (webhookHeaderNameIssue(name, names) != null) return;
|
|
32
|
+
if (typeof value !== "string" && typeof value !== "boolean" && !(typeof value === "number" && Number.isFinite(value))) return;
|
|
33
|
+
const text = String(value);
|
|
34
|
+
if ([...text].some((character) => character.charCodeAt(0) > 255 || /[\0\r\n]/.test(character))) return;
|
|
35
|
+
names.push(name);
|
|
36
|
+
Object.defineProperty(headers, name, {
|
|
37
|
+
value: text,
|
|
38
|
+
enumerable: true,
|
|
39
|
+
configurable: true
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return headers;
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
1
45
|
//#region src/execution/common/events.ts
|
|
2
46
|
var encoder = new TextEncoder();
|
|
3
47
|
var nodeKinds = /* @__PURE__ */ new Set([
|
|
@@ -12,7 +56,8 @@ var nodeKinds = /* @__PURE__ */ new Set([
|
|
|
12
56
|
"value",
|
|
13
57
|
"wait",
|
|
14
58
|
"outputs",
|
|
15
|
-
"workflow"
|
|
59
|
+
"workflow",
|
|
60
|
+
"webhook-response"
|
|
16
61
|
]);
|
|
17
62
|
function object(value, description) {
|
|
18
63
|
if (value == null || typeof value != "object" || Array.isArray(value)) throw new TypeError(`${description} must be an object.`);
|
|
@@ -167,7 +212,8 @@ function createEventProjector(platformRunId, nodeFailureCodes) {
|
|
|
167
212
|
case "node.completed": return {
|
|
168
213
|
kind: type,
|
|
169
214
|
payload,
|
|
170
|
-
value: object(event.outputs, "Runtime node.completed outputs")
|
|
215
|
+
value: object(event.outputs, "Runtime node.completed outputs"),
|
|
216
|
+
...event.response == null || context.rawRunId !== platformRunId ? {} : { response: responseSnapshot(event.response) }
|
|
171
217
|
};
|
|
172
218
|
case "node.started": {
|
|
173
219
|
jobs.set(JSON.stringify([context.rawRunId, event.jobId]), context.nodeId);
|
|
@@ -234,5 +280,16 @@ function createEventProjector(platformRunId, nodeFailureCodes) {
|
|
|
234
280
|
throw new TypeError(`Runtime event type ${JSON.stringify(type)} is not supported.`);
|
|
235
281
|
};
|
|
236
282
|
}
|
|
283
|
+
function responseSnapshot(value) {
|
|
284
|
+
const source = object(value, "Runtime response");
|
|
285
|
+
const headers = webhookHeaderValues(source.headers);
|
|
286
|
+
if (!validWebhookStatus(source.status) || typeof source.body !== "string" || !["text/plain;charset=UTF-8", "application/json"].includes(String(source.contentType)) || headers == null) throw new TypeError("Invalid runtime response.");
|
|
287
|
+
return {
|
|
288
|
+
status: source.status,
|
|
289
|
+
body: source.body,
|
|
290
|
+
contentType: String(source.contentType),
|
|
291
|
+
headers
|
|
292
|
+
};
|
|
293
|
+
}
|
|
237
294
|
//#endregion
|
|
238
295
|
export { createEventProjector, projectExecutionId, message as sanitizeRuntimeErrorMessage };
|
|
@@ -23,9 +23,11 @@ function dequal(foo, bar) {
|
|
|
23
23
|
}
|
|
24
24
|
//#endregion
|
|
25
25
|
//#region src/execution/common/engineContract.ts
|
|
26
|
-
var currentEngineContract = "open-flow-engine/
|
|
27
|
-
var nodejsEngineContract = "open-flow-engine/
|
|
26
|
+
var currentEngineContract = "open-flow-engine/v7";
|
|
27
|
+
var nodejsEngineContract = "open-flow-engine/v7/nodejs-compat-v1";
|
|
28
28
|
var currentContract = {
|
|
29
|
+
waitAutoContinue: true,
|
|
30
|
+
webhookResponses: true,
|
|
29
31
|
platformExports: /* @__PURE__ */ new Set(["engineContract", "identity"]),
|
|
30
32
|
platformModule: "open-flow:platform",
|
|
31
33
|
platformSource: `export const engineContract = ${JSON.stringify(currentEngineContract)}
|
|
@@ -65,8 +67,16 @@ export function identity(value) { return value }
|
|
|
65
67
|
`
|
|
66
68
|
};
|
|
67
69
|
function findEngineContract(contract) {
|
|
68
|
-
if (contract == "open-flow-engine/
|
|
69
|
-
if (contract == "open-flow-engine/
|
|
70
|
+
if (contract == "open-flow-engine/v7") return currentContract;
|
|
71
|
+
if (contract == "open-flow-engine/v7/nodejs-compat-v1") return nodejsContract;
|
|
72
|
+
if (contract == "open-flow-engine/v6" || contract == "open-flow-engine/v6/nodejs-compat-v1") return {
|
|
73
|
+
...contract == "open-flow-engine/v6" ? currentContract : nodejsContract,
|
|
74
|
+
waitAutoContinue: false,
|
|
75
|
+
webhookResponses: false,
|
|
76
|
+
platformSource: `export const engineContract = ${JSON.stringify(contract)}
|
|
77
|
+
export function identity(value) { return value }
|
|
78
|
+
`
|
|
79
|
+
};
|
|
70
80
|
}
|
|
71
81
|
//#endregion
|
|
72
82
|
//#region src/execution/common/runtime.ts
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import type { ConnectorCapability, JsonValue, WaitAction } from '../browser/flow-change.js';
|
|
1
|
+
import type { ConnectorCapability, JsonValue, WaitAction, WaitAutoContinue } from '../browser/flow-change.js';
|
|
2
2
|
import type { PreparedFlow } from './flow-semantics.js';
|
|
3
|
+
import type { WebhookResponseSnapshot } from './webhook-response.js';
|
|
3
4
|
import type { PreviousNode } from '../index.js';
|
|
4
5
|
import type { AgentCheckpoint } from './runtime-contract.js';
|
|
5
6
|
import * as Effect from 'effect/Effect';
|
|
6
7
|
export declare const withWaitNotificationMessage: (value: JsonValue) => JsonValue;
|
|
7
8
|
export declare const normalizeWaitComment: (value?: string | null) => string | null;
|
|
9
|
+
export declare const waitAutoContinueAt: (config: WaitAutoContinue, waitingSince: number) => number;
|
|
8
10
|
export interface WaitResolution {
|
|
9
11
|
readonly action: WaitAction;
|
|
10
12
|
readonly resolvedAt: string;
|
|
@@ -28,7 +30,7 @@ export type SchedulerEvent = {
|
|
|
28
30
|
readonly inputs: Readonly<Record<string, JsonValue>>;
|
|
29
31
|
readonly jobId: string;
|
|
30
32
|
readonly nodeId: string;
|
|
31
|
-
readonly nodeKind: 'agent' | 'approval' | 'condition' | 'connector' | 'decision' | 'javascript' | 'openapi' | 'llm' | 'value' | 'wait' | 'outputs' | 'workflow';
|
|
33
|
+
readonly nodeKind: 'agent' | 'approval' | 'condition' | 'connector' | 'decision' | 'javascript' | 'openapi' | 'llm' | 'value' | 'wait' | 'outputs' | 'workflow' | 'webhook-response';
|
|
32
34
|
readonly nodeTitle?: string;
|
|
33
35
|
readonly runId: string;
|
|
34
36
|
readonly type: 'node.started';
|
|
@@ -38,6 +40,7 @@ export type SchedulerEvent = {
|
|
|
38
40
|
readonly outputs: Readonly<Record<string, JsonValue>>;
|
|
39
41
|
readonly runId: string;
|
|
40
42
|
readonly systemOutputs?: Readonly<Record<string, JsonValue>>;
|
|
43
|
+
readonly response?: WebhookResponseSnapshot;
|
|
41
44
|
readonly type: 'node.completed';
|
|
42
45
|
} | {
|
|
43
46
|
readonly code: string;
|
|
@@ -86,6 +89,7 @@ export interface PendingWait {
|
|
|
86
89
|
readonly pending?: JsonValue;
|
|
87
90
|
}
|
|
88
91
|
export interface WaitRequest {
|
|
92
|
+
readonly autoContinue?: WaitAutoContinue;
|
|
89
93
|
readonly jobId: string;
|
|
90
94
|
readonly nodeId: string;
|
|
91
95
|
readonly value: JsonValue;
|
package/dist/common/scheduler.js
CHANGED
|
@@ -1035,7 +1035,7 @@ function timeSource(args) {
|
|
|
1035
1035
|
function time$1(args) {
|
|
1036
1036
|
return new RegExp(`^${timeSource(args)}$`);
|
|
1037
1037
|
}
|
|
1038
|
-
function datetime(args) {
|
|
1038
|
+
function datetime$1(args) {
|
|
1039
1039
|
const opts = ["Z"];
|
|
1040
1040
|
if (args.offset) opts.push(`([+-](?:[01]\\d|2[0-3]):[0-5]\\d)`);
|
|
1041
1041
|
const qualified = `${timeSource({
|
|
@@ -1686,7 +1686,7 @@ var $ZodKSUID = /*@__PURE__*/ $constructor("$ZodKSUID", (inst, def) => {
|
|
|
1686
1686
|
$ZodStringFormat.init(inst, def);
|
|
1687
1687
|
});
|
|
1688
1688
|
var $ZodISODateTime = /*@__PURE__*/ $constructor("$ZodISODateTime", (inst, def) => {
|
|
1689
|
-
def.pattern ?? (def.pattern = datetime(def));
|
|
1689
|
+
def.pattern ?? (def.pattern = datetime$1(def));
|
|
1690
1690
|
$ZodStringFormat.init(inst, def);
|
|
1691
1691
|
});
|
|
1692
1692
|
var $ZodISODate = /*@__PURE__*/ $constructor("$ZodISODate", (inst, def) => {
|
|
@@ -5683,6 +5683,11 @@ function preprocess(fn, schema) {
|
|
|
5683
5683
|
});
|
|
5684
5684
|
}
|
|
5685
5685
|
//#endregion
|
|
5686
|
+
//#region ../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/classic/iso.js
|
|
5687
|
+
function datetime(params) {
|
|
5688
|
+
return /* @__PURE__ */ _isoDateTime(ZodISODateTime, params);
|
|
5689
|
+
}
|
|
5690
|
+
//#endregion
|
|
5686
5691
|
//#region ../../node_modules/.bun/@cfworker+json-schema@4.1.1/node_modules/@cfworker/json-schema/dist/esm/deep-compare-strict.js
|
|
5687
5692
|
function deepCompareStrict(a, b) {
|
|
5688
5693
|
const typeofa = typeof a;
|
|
@@ -9506,6 +9511,39 @@ var triggerScheduleSchema = array(union([object({
|
|
|
9506
9511
|
value: number()
|
|
9507
9512
|
})]));
|
|
9508
9513
|
//#endregion
|
|
9514
|
+
//#region src/localization/common/timeZone.ts
|
|
9515
|
+
/** Validate a named timezone using the same runtime data used for date conversion. */
|
|
9516
|
+
function isTimeZone(value) {
|
|
9517
|
+
try {
|
|
9518
|
+
Intl.DateTimeFormat("en", { timeZone: value });
|
|
9519
|
+
return true;
|
|
9520
|
+
} catch {
|
|
9521
|
+
return false;
|
|
9522
|
+
}
|
|
9523
|
+
}
|
|
9524
|
+
//#endregion
|
|
9525
|
+
//#region src/flow/common/wait.ts
|
|
9526
|
+
/** Drafts retain incomplete values; semantic validation gates publication and execution. */
|
|
9527
|
+
var waitAutoContinueDraftSchema = discriminatedUnion("kind", [strictObject({
|
|
9528
|
+
kind: literal("after"),
|
|
9529
|
+
durationMs: number().nullable()
|
|
9530
|
+
}), strictObject({
|
|
9531
|
+
kind: literal("at"),
|
|
9532
|
+
at: string$1(),
|
|
9533
|
+
timezone: string$1().optional()
|
|
9534
|
+
})]);
|
|
9535
|
+
/** Static business configuration; the deployment owns the allowed wait lifetime. */
|
|
9536
|
+
var waitAutoContinueSchema = discriminatedUnion("kind", [waitAutoContinueDraftSchema.options[0].extend({ durationMs: number().int().positive().max(Number.MAX_SAFE_INTEGER) }), waitAutoContinueDraftSchema.options[1].extend({
|
|
9537
|
+
at: datetime({ offset: true }),
|
|
9538
|
+
timezone: string$1().refine(isTimeZone, "Select a valid IANA timezone.").optional()
|
|
9539
|
+
})]);
|
|
9540
|
+
/** Resolve one registration's intent; the deployment separately enforces its lifetime. */
|
|
9541
|
+
function waitAutoContinueAt$1(config, waitingSince) {
|
|
9542
|
+
const timing = waitAutoContinueSchema.parse(config);
|
|
9543
|
+
const at = timing.kind == "after" ? waitingSince + timing.durationMs : Date.parse(timing.at);
|
|
9544
|
+
return Math.max(waitingSince, at);
|
|
9545
|
+
}
|
|
9546
|
+
//#endregion
|
|
9509
9547
|
//#region src/flow/common/webhookMethod.ts
|
|
9510
9548
|
var webhookMethods = [
|
|
9511
9549
|
"GET",
|
|
@@ -9517,6 +9555,84 @@ var webhookMethods = [
|
|
|
9517
9555
|
function webhookSupportsBody(method) {
|
|
9518
9556
|
return method !== "GET";
|
|
9519
9557
|
}
|
|
9558
|
+
//#endregion
|
|
9559
|
+
//#region src/flow/common/webhookResponse.ts
|
|
9560
|
+
var webhookResponseTimeout = {
|
|
9561
|
+
defaultMs: 3e4,
|
|
9562
|
+
minMs: 1e3,
|
|
9563
|
+
maxMs: 6e4
|
|
9564
|
+
};
|
|
9565
|
+
function webhookResponseAllowsBody(status) {
|
|
9566
|
+
return status !== 204 && status !== 205 && status !== 304;
|
|
9567
|
+
}
|
|
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
|
+
/** Fixed inputs belong to the node, not author-supplied definitions. */
|
|
9585
|
+
var webhookResponseInputs = [
|
|
9586
|
+
{
|
|
9587
|
+
handle: "body",
|
|
9588
|
+
jsonSchema: {},
|
|
9589
|
+
nullable: true,
|
|
9590
|
+
value: ""
|
|
9591
|
+
},
|
|
9592
|
+
{
|
|
9593
|
+
handle: "statusCode",
|
|
9594
|
+
jsonSchema: { type: "number" },
|
|
9595
|
+
nullable: false,
|
|
9596
|
+
value: 200
|
|
9597
|
+
},
|
|
9598
|
+
{
|
|
9599
|
+
handle: "headers",
|
|
9600
|
+
jsonSchema: webhookHeadersPort.jsonSchema,
|
|
9601
|
+
nullable: false,
|
|
9602
|
+
value: {}
|
|
9603
|
+
}
|
|
9604
|
+
];
|
|
9605
|
+
function validWebhookStatus(value) {
|
|
9606
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 200 && value <= 599 && webhookResponseAllowsBody(value);
|
|
9607
|
+
}
|
|
9608
|
+
/** Portable validation also runs in the isolated engine, which has no HTTP globals. */
|
|
9609
|
+
function webhookHeaderValues(source) {
|
|
9610
|
+
if (source == null || typeof source !== "object" || Array.isArray(source)) return;
|
|
9611
|
+
const headers = {};
|
|
9612
|
+
const names = [];
|
|
9613
|
+
for (const [name, value] of Object.entries(source)) {
|
|
9614
|
+
if (webhookHeaderNameIssue(name, names) != null) return;
|
|
9615
|
+
if (typeof value !== "string" && typeof value !== "boolean" && !(typeof value === "number" && Number.isFinite(value))) return;
|
|
9616
|
+
const text = String(value);
|
|
9617
|
+
if ([...text].some((character) => character.charCodeAt(0) > 255 || /[\0\r\n]/.test(character))) return;
|
|
9618
|
+
names.push(name);
|
|
9619
|
+
Object.defineProperty(headers, name, {
|
|
9620
|
+
value: text,
|
|
9621
|
+
enumerable: true,
|
|
9622
|
+
configurable: true
|
|
9623
|
+
});
|
|
9624
|
+
}
|
|
9625
|
+
return headers;
|
|
9626
|
+
}
|
|
9627
|
+
function encodeWebhookBody(value) {
|
|
9628
|
+
return typeof value === "string" ? {
|
|
9629
|
+
body: value,
|
|
9630
|
+
contentType: "text/plain;charset=UTF-8"
|
|
9631
|
+
} : {
|
|
9632
|
+
body: JSON.stringify(value),
|
|
9633
|
+
contentType: "application/json"
|
|
9634
|
+
};
|
|
9635
|
+
}
|
|
9520
9636
|
var text = string$1();
|
|
9521
9637
|
var actionConnection = text.describe("Account selection: omit while unconfigured; use an account ID to select it; use \"\" to explicitly defer configuration. Deferred Actions fail if called; deferred Agent tools are excluded from the callable tool set. Not supported by Triggers.");
|
|
9522
9638
|
var json = json$1();
|
|
@@ -9744,9 +9860,24 @@ var webhookOptions = object({
|
|
|
9744
9860
|
allowedMethods: never().optional(),
|
|
9745
9861
|
allowedOrigins: strings.optional(),
|
|
9746
9862
|
noResponseBody: never().optional(),
|
|
9863
|
+
responseBody: discriminatedUnion("kind", [strictObject({
|
|
9864
|
+
kind: literal("workflow-outputs"),
|
|
9865
|
+
output: text.optional(),
|
|
9866
|
+
timeoutMs: number().int().min(webhookResponseTimeout.minMs).max(webhookResponseTimeout.maxMs).optional()
|
|
9867
|
+
}), strictObject({
|
|
9868
|
+
kind: literal("webhook-response"),
|
|
9869
|
+
nodeId: text.optional(),
|
|
9870
|
+
timeoutMs: number().int().min(webhookResponseTimeout.minMs).max(webhookResponseTimeout.maxMs).optional()
|
|
9871
|
+
})]).optional(),
|
|
9747
9872
|
responseData: text.optional(),
|
|
9748
9873
|
responseHeaders: record$1(text, text).optional(),
|
|
9749
|
-
|
|
9874
|
+
responseHeadersSource: strictObject({
|
|
9875
|
+
kind: literal("workflow-outputs"),
|
|
9876
|
+
output: text.optional()
|
|
9877
|
+
}).optional(),
|
|
9878
|
+
responseHeaderOutputs: record$1(text, text).optional(),
|
|
9879
|
+
responseStatusCode: number().int().min(200).max(599).optional(),
|
|
9880
|
+
responseStatusCodeOutput: text.optional()
|
|
9750
9881
|
});
|
|
9751
9882
|
var webhook = {
|
|
9752
9883
|
bodyFields: array(input),
|
|
@@ -9801,6 +9932,12 @@ var base = {
|
|
|
9801
9932
|
onError: _enum(["stop", "error"]).optional()
|
|
9802
9933
|
};
|
|
9803
9934
|
var node = union([
|
|
9935
|
+
strictObject({
|
|
9936
|
+
...base,
|
|
9937
|
+
kind: literal("webhook-response"),
|
|
9938
|
+
retry: never().optional(),
|
|
9939
|
+
onError: never().optional()
|
|
9940
|
+
}),
|
|
9804
9941
|
strictObject({
|
|
9805
9942
|
...base,
|
|
9806
9943
|
kind: literal("outputs"),
|
|
@@ -9833,7 +9970,8 @@ var node = union([
|
|
|
9833
9970
|
strictObject({
|
|
9834
9971
|
...base,
|
|
9835
9972
|
kind: literal("wait"),
|
|
9836
|
-
...wait
|
|
9973
|
+
...wait,
|
|
9974
|
+
autoContinue: waitAutoContinueDraftSchema.optional()
|
|
9837
9975
|
}).omit({ timeoutMs: true }),
|
|
9838
9976
|
object({
|
|
9839
9977
|
...trigger,
|
|
@@ -9920,7 +10058,8 @@ object({
|
|
|
9920
10058
|
literal(6),
|
|
9921
10059
|
literal(7),
|
|
9922
10060
|
literal(8),
|
|
9923
|
-
literal(9)
|
|
10061
|
+
literal(9),
|
|
10062
|
+
literal(10)
|
|
9924
10063
|
]),
|
|
9925
10064
|
document,
|
|
9926
10065
|
modules: record$1(text, module)
|
|
@@ -10045,8 +10184,14 @@ var shapes = {
|
|
|
10045
10184
|
},
|
|
10046
10185
|
"graph.node.resolution.set": {
|
|
10047
10186
|
...at,
|
|
10048
|
-
before:
|
|
10049
|
-
|
|
10187
|
+
before: strictObject({
|
|
10188
|
+
...wait,
|
|
10189
|
+
autoContinue: waitAutoContinueDraftSchema.optional()
|
|
10190
|
+
}),
|
|
10191
|
+
value: strictObject({
|
|
10192
|
+
...wait,
|
|
10193
|
+
autoContinue: waitAutoContinueDraftSchema.optional()
|
|
10194
|
+
})
|
|
10050
10195
|
},
|
|
10051
10196
|
"graph.node.webhook.set": {
|
|
10052
10197
|
...at,
|
|
@@ -10654,6 +10799,7 @@ function isResolutionNode(node) {
|
|
|
10654
10799
|
}
|
|
10655
10800
|
function nodeInputPorts(node) {
|
|
10656
10801
|
switch (node.kind) {
|
|
10802
|
+
case "webhook-response": return portsByHandle(webhookResponseInputs);
|
|
10657
10803
|
case "outputs": return portsByHandle(node.outputDefs);
|
|
10658
10804
|
case "condition": return conditionInputPorts(node);
|
|
10659
10805
|
case "value": return {};
|
|
@@ -10700,7 +10846,8 @@ function resolutionOutputPorts(node) {
|
|
|
10700
10846
|
pattern: "\\S"
|
|
10701
10847
|
},
|
|
10702
10848
|
...Object.fromEntries(actions.map((action) => [`${action}Url`, { type: "string" }])),
|
|
10703
|
-
expiresAt: { type: "string" }
|
|
10849
|
+
expiresAt: { type: "string" },
|
|
10850
|
+
...node.kind == "wait" && node.autoContinue != null ? { autoContinueAt: { type: "string" } } : {}
|
|
10704
10851
|
}),
|
|
10705
10852
|
...Object.fromEntries(actions.map((action) => [action, objectPort({
|
|
10706
10853
|
inputs,
|
|
@@ -10712,6 +10859,7 @@ function resolutionOutputPorts(node) {
|
|
|
10712
10859
|
}
|
|
10713
10860
|
function nodeOutputPorts(node) {
|
|
10714
10861
|
switch (node.kind) {
|
|
10862
|
+
case "webhook-response":
|
|
10715
10863
|
case "outputs":
|
|
10716
10864
|
case "condition": return {};
|
|
10717
10865
|
case "value": return portsByHandle(node.values);
|
|
@@ -11058,6 +11206,7 @@ function withWaitNotificationMessage$1(value) {
|
|
|
11058
11206
|
pageUrl,
|
|
11059
11207
|
message: source.message === void 0 ? [
|
|
11060
11208
|
source.prompt,
|
|
11209
|
+
...typeof source.autoContinueAt == "string" ? [`Automatically continues at: ${source.autoContinueAt}`] : [],
|
|
11061
11210
|
`Expires at: ${source.expiresAt}`,
|
|
11062
11211
|
`Review request: ${pageUrl}`
|
|
11063
11212
|
].join("\n\n") : source.message
|
|
@@ -11067,6 +11216,7 @@ function withWaitNotificationMessage$1(value) {
|
|
|
11067
11216
|
//#region src/execution/common/scheduler.ts
|
|
11068
11217
|
var withWaitNotificationMessage = withWaitNotificationMessage$1;
|
|
11069
11218
|
var normalizeWaitComment = normalizeWaitComment$1;
|
|
11219
|
+
var waitAutoContinueAt = waitAutoContinueAt$1;
|
|
11070
11220
|
var PortValidationError = class extends Error {};
|
|
11071
11221
|
function usesPending(graph, nodeId) {
|
|
11072
11222
|
return graph.edges.some((edge) => edge.source == nodeId && edge.sourceHandle == "pending") || Object.values(graph.nodes).some((node) => !isTriggerNode(node) && Object.values(nodeInputMappings(node)).some((mapping) => mapping.kind == "sources" && mapping.sources.some((source) => source.kind == "node" && source.nodeId == nodeId && source.output == "pending")));
|
|
@@ -11471,7 +11621,7 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
|
|
|
11471
11621
|
const systemFrame = dependencies.systemInputValues(nodeId);
|
|
11472
11622
|
return Object.fromEntries(Object.entries(nodeInputPorts(node)).map(([handle, port]) => [handle, resolveInput(mappings[handle], port, launch[nodeId]?.[handle], `Node "${nodeId}" input "${handle}"`, frame, node.kind == "condition", systemFrame)]));
|
|
11473
11623
|
};
|
|
11474
|
-
const commit = (nodeId, jobId, outputs, releasedHandles = Object.keys(outputs), failure) => Effect.uninterruptible(Effect.gen(function* () {
|
|
11624
|
+
const commit = (nodeId, jobId, outputs, releasedHandles = Object.keys(outputs), failure, response) => Effect.uninterruptible(Effect.gen(function* () {
|
|
11475
11625
|
const node = target.graph.nodes[nodeId];
|
|
11476
11626
|
const systemOutputs = nodeSystemOutputPorts(node).error == null ? void 0 : { error: failure == null ? null : {
|
|
11477
11627
|
code: failure.code,
|
|
@@ -11489,6 +11639,7 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
|
|
|
11489
11639
|
outputs,
|
|
11490
11640
|
runId,
|
|
11491
11641
|
...system,
|
|
11642
|
+
...response == null ? {} : { response },
|
|
11492
11643
|
type: "node.completed"
|
|
11493
11644
|
} : {
|
|
11494
11645
|
...failure,
|
|
@@ -11531,6 +11682,21 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
|
|
|
11531
11682
|
});
|
|
11532
11683
|
let outputs;
|
|
11533
11684
|
switch (node.kind) {
|
|
11685
|
+
case "webhook-response": {
|
|
11686
|
+
const status = nodeInputs.statusCode;
|
|
11687
|
+
if (!validWebhookStatus(status)) return yield* Effect.fail(new NodeValidationError("webhook.response-status-invalid", "Invalid HTTP response status."));
|
|
11688
|
+
const headers = webhookHeaderValues(nodeInputs.headers);
|
|
11689
|
+
if (headers == null) return yield* Effect.fail(new NodeValidationError("webhook.response-headers-invalid", "Invalid HTTP response headers."));
|
|
11690
|
+
return {
|
|
11691
|
+
kind: "completed",
|
|
11692
|
+
outputs: {},
|
|
11693
|
+
response: {
|
|
11694
|
+
...encodeWebhookBody(nodeInputs.body),
|
|
11695
|
+
status,
|
|
11696
|
+
headers
|
|
11697
|
+
}
|
|
11698
|
+
};
|
|
11699
|
+
}
|
|
11534
11700
|
case "outputs":
|
|
11535
11701
|
outputs = nodeInputs;
|
|
11536
11702
|
break;
|
|
@@ -11655,6 +11821,7 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
|
|
|
11655
11821
|
value,
|
|
11656
11822
|
waitId,
|
|
11657
11823
|
notify,
|
|
11824
|
+
...node.kind == "wait" && node.autoContinue != null ? { autoContinue: node.autoContinue } : {},
|
|
11658
11825
|
actions: isResolutionNode(node) ? resolutionActions(node) : ["approve", "reject"],
|
|
11659
11826
|
prompt: isResolutionNode(node) ? node.prompt : JSON.stringify(value, null, 2),
|
|
11660
11827
|
...isResolutionNode(node) ? {} : { notification: agentNotice(node, agents[jobId].input) }
|
|
@@ -11686,7 +11853,7 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
|
|
|
11686
11853
|
pendingCalls.delete(jobId);
|
|
11687
11854
|
if (result.kind == "completed") {
|
|
11688
11855
|
delete agents[jobId];
|
|
11689
|
-
return commit(nodeId, jobId, result.outputs, result.releasedHandles);
|
|
11856
|
+
return commit(nodeId, jobId, result.outputs, result.releasedHandles, void 0, result.response);
|
|
11690
11857
|
}
|
|
11691
11858
|
agents[jobId] = result.agent;
|
|
11692
11859
|
return createWait(nodeId, jobId, result.value);
|
|
@@ -11935,4 +12102,4 @@ function runFlow(prepared, options) {
|
|
|
11935
12102
|
});
|
|
11936
12103
|
}
|
|
11937
12104
|
//#endregion
|
|
11938
|
-
export { PortValidationError, decodeFlowRunCheckpoint, normalizeWaitComment, runFlow, waitRetentionMs, withWaitNotificationMessage };
|
|
12105
|
+
export { PortValidationError, decodeFlowRunCheckpoint, normalizeWaitComment, runFlow, waitAutoContinueAt, waitRetentionMs, withWaitNotificationMessage };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { InputPort, JsonValue, Port, PortDefinition } from '../browser/flow-change.js';
|
|
2
|
+
export declare const webhookResponseTimeout: {
|
|
3
|
+
readonly defaultMs: 30000;
|
|
4
|
+
readonly minMs: 1000;
|
|
5
|
+
readonly maxMs: 60000;
|
|
6
|
+
};
|
|
7
|
+
export declare const webhookStatusCodePort: {
|
|
8
|
+
readonly jsonSchema: {
|
|
9
|
+
readonly type: 'number';
|
|
10
|
+
};
|
|
11
|
+
readonly nullable: true;
|
|
12
|
+
};
|
|
13
|
+
export declare function webhookResponseAllowsBody(status: number): boolean;
|
|
14
|
+
export declare const webhookHeaderValuePort: {
|
|
15
|
+
readonly jsonSchema: {
|
|
16
|
+
readonly type: readonly ['string', 'number', 'boolean'];
|
|
17
|
+
};
|
|
18
|
+
readonly nullable: true;
|
|
19
|
+
};
|
|
20
|
+
export declare const webhookHeadersPort: {
|
|
21
|
+
readonly jsonSchema: {
|
|
22
|
+
readonly type: 'object';
|
|
23
|
+
readonly additionalProperties: {
|
|
24
|
+
readonly type: readonly ['string', 'number', 'boolean'];
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
readonly nullable: true;
|
|
28
|
+
};
|
|
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
|
+
/** Fixed inputs belong to the node, not author-supplied definitions. */
|
|
33
|
+
export declare const webhookResponseInputs: readonly InputPort[];
|
|
34
|
+
export interface WebhookResponseSnapshot {
|
|
35
|
+
readonly body: string;
|
|
36
|
+
readonly contentType: string;
|
|
37
|
+
readonly status: number;
|
|
38
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
39
|
+
}
|
|
40
|
+
export declare function validWebhookStatus(value: unknown): value is number;
|
|
41
|
+
/** Portable validation also runs in the isolated engine, which has no HTTP globals. */
|
|
42
|
+
export declare function webhookHeaderValues(source: unknown): Readonly<Record<string, string>> | undefined;
|
|
43
|
+
export declare function encodeWebhookBody(value: JsonValue): Pick<WebhookResponseSnapshot, 'body' | 'contentType'>;
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import type { JsonValue, WebhookBodyField, WebhookMethod, WebhookOptions } from '../browser/flow-change.js';
|
|
2
2
|
export { webhookSupportsBody } from './trigger-contract.js';
|
|
3
|
+
export { webhookResponseAllowsBody, webhookResponseTimeout } from './webhook-response.js';
|
|
4
|
+
export type { WebhookResponseSnapshot } from './webhook-response.js';
|
|
3
5
|
export declare const maximumWebhookBodyBytes: number;
|
|
6
|
+
/** Serialize only declared workflow business outputs, never node logs or execution metadata. */
|
|
7
|
+
export declare function webhookResponseBody(result: unknown, source: Extract<NonNullable<WebhookOptions['responseBody']>, {
|
|
8
|
+
kind: 'workflow-outputs';
|
|
9
|
+
}>): {
|
|
10
|
+
readonly body: string;
|
|
11
|
+
readonly contentType: string;
|
|
12
|
+
} | undefined;
|
|
13
|
+
export declare function webhookResponseStatusCode(result: unknown, output: string): number | undefined;
|
|
14
|
+
/** Project business headers before the HTTP layer applies its deployment-owned header policy. */
|
|
15
|
+
export declare function webhookResponseHeaders(result: unknown, options: WebhookOptions): Readonly<Record<string, string>> | undefined;
|
|
4
16
|
export declare function webhookEndpointId(url: URL): string | undefined;
|
|
5
17
|
export declare function webhookOccurrenceId(endpointId: string, runtimeVersion: number, key: string | null): Promise<string | undefined>;
|
|
6
18
|
export interface WebhookConformanceFixture {
|