@oxygen-agent/cli 1.696.2 → 1.717.1
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 -1
- package/dist/help.js +2 -0
- package/dist/http-client.js +11 -1
- package/dist/index.js +524 -86
- package/dist/transcript.js +2 -2
- package/node_modules/@oxygen/recipe-sdk/dist/index.d.ts +4 -0
- package/node_modules/@oxygen/shared/dist/billing.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/billing.js +7 -6
- package/node_modules/@oxygen/shared/dist/dnc-identities.d.ts +53 -0
- package/node_modules/@oxygen/shared/dist/dnc-identities.js +175 -0
- package/node_modules/@oxygen/shared/dist/file-import.d.ts +36 -1
- package/node_modules/@oxygen/shared/dist/file-import.js +80 -1
- package/node_modules/@oxygen/shared/dist/image-sniff.d.ts +39 -0
- package/node_modules/@oxygen/shared/dist/image-sniff.js +75 -0
- package/node_modules/@oxygen/shared/dist/import-limits.d.ts +28 -0
- package/node_modules/@oxygen/shared/dist/import-limits.js +30 -0
- package/node_modules/@oxygen/shared/dist/index.d.ts +2 -0
- package/node_modules/@oxygen/shared/dist/index.js +2 -0
- package/node_modules/@oxygen/shared/dist/mailbox-import.d.ts +10 -4
- package/node_modules/@oxygen/shared/dist/mailbox-import.js +18 -10
- package/node_modules/@oxygen/shared/dist/member-columns.d.ts +64 -0
- package/node_modules/@oxygen/shared/dist/member-columns.js +111 -0
- package/node_modules/@oxygen/shared/dist/microsoft-consent-url.js +1 -1
- package/node_modules/@oxygen/shared/dist/object-storage.d.ts +65 -0
- package/node_modules/@oxygen/shared/dist/object-storage.js +100 -0
- package/node_modules/@oxygen/shared/dist/plan-limits.d.ts +2 -1
- package/node_modules/@oxygen/shared/dist/plan-limits.js +12 -2
- package/node_modules/@oxygen/shared/dist/spend-safety.d.ts +9 -0
- package/node_modules/@oxygen/shared/dist/spend-safety.js +10 -0
- package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.js +1 -1
- package/node_modules/@oxygen/shared/package.json +20 -0
- package/node_modules/@oxygen/workflows/dist/graph/lint.js +79 -11
- package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.d.ts +679 -0
- package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.js +80 -1
- package/node_modules/@oxygen/workflows/dist/graph/remap.js +8 -1
- package/node_modules/@oxygen/workflows/dist/graph/types.d.ts +72 -3
- package/node_modules/@oxygen/workflows/dist/graph/types.js +12 -0
- package/node_modules/@oxygen/workflows/dist/index.d.ts +16 -0
- package/node_modules/@oxygen/workflows/dist/index.js +45 -3
- package/node_modules/@oxygen/workflows/dist/portable.d.ts +43 -0
- package/node_modules/@oxygen/workflows/dist/portable.js +319 -0
- package/node_modules/@oxygen/workflows/dist/usage-estimate.js +18 -4
- package/package.json +1 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// full copy of those rules here would drift from the linter that actually runs.
|
|
11
11
|
//
|
|
12
12
|
// Pure data: this module ships to the browser with the rest of the graph barrel.
|
|
13
|
-
import { MAX_WORKFLOW_NODE_RETRY_ATTEMPTS, MAX_WORKFLOW_NODE_RETRY_WAIT_SECONDS, WORKFLOW_GRAPH_COMPILER_VERSION, WORKFLOW_GRAPH_MANIFEST_VERSION, } from "./types.js";
|
|
13
|
+
import { MAX_WORKFLOW_NODE_RETRY_ATTEMPTS, MAX_WORKFLOW_NODE_RETRY_WAIT_SECONDS, PORTABLE_WORKFLOW_DEFINITION_COMPILER_VERSION, PORTABLE_WORKFLOW_DEFINITION_VERSION, WORKFLOW_GRAPH_COMPILER_VERSION, WORKFLOW_GRAPH_MANIFEST_VERSION, } from "./types.js";
|
|
14
14
|
const valueRefSchema = {
|
|
15
15
|
type: "object",
|
|
16
16
|
description: "How a node input is bound at run time. Scope paths are 'trigger.input.x', 'steps.<node_id>.output.y', or 'loop.<loop_node_id>.item.z'.",
|
|
@@ -122,6 +122,15 @@ const nodeSchema = {
|
|
|
122
122
|
description: "tool nodes: parameter name -> value ref.",
|
|
123
123
|
additionalProperties: valueRefSchema,
|
|
124
124
|
},
|
|
125
|
+
payload_source: {
|
|
126
|
+
type: "string",
|
|
127
|
+
description: "Imported v1 tool nodes only: sandboxed payload function preserved until converted to mapped params.",
|
|
128
|
+
},
|
|
129
|
+
legacy_context: {
|
|
130
|
+
type: "string",
|
|
131
|
+
enum: ["v1"],
|
|
132
|
+
description: "Imported v1 source receives the original raw-step-output context shape.",
|
|
133
|
+
},
|
|
125
134
|
when: conditionSchema,
|
|
126
135
|
cases: {
|
|
127
136
|
type: "array",
|
|
@@ -155,6 +164,16 @@ const nodeSchema = {
|
|
|
155
164
|
duration_seconds: { type: "number", exclusiveMinimum: 0, description: "wait nodes: exactly one of duration_seconds or until." },
|
|
156
165
|
until: valueRefSchema,
|
|
157
166
|
run_source: { type: "string", description: "code nodes: sandboxed pure-function source." },
|
|
167
|
+
recipe: {
|
|
168
|
+
type: "object",
|
|
169
|
+
description: "Imported durable-recipe code nodes: the complete executable recipe manifest. Exactly one of run_source or recipe is required.",
|
|
170
|
+
additionalProperties: true,
|
|
171
|
+
},
|
|
172
|
+
configuration: {
|
|
173
|
+
type: "object",
|
|
174
|
+
description: "Imported durable-recipe code nodes: configuration field -> value ref.",
|
|
175
|
+
additionalProperties: valueRefSchema,
|
|
176
|
+
},
|
|
158
177
|
workflow_id: { type: "string", description: "workflow nodes: the child workflow to run." },
|
|
159
178
|
input: { type: "object", description: "workflow nodes: child input name -> value ref.", additionalProperties: valueRefSchema },
|
|
160
179
|
},
|
|
@@ -203,3 +222,63 @@ export const workflowGraphManifestSchema = {
|
|
|
203
222
|
},
|
|
204
223
|
required: ["manifest_version", "workflow", "nodes", "edges", "source_hash", "compiler_version"],
|
|
205
224
|
};
|
|
225
|
+
export const portableWorkflowDefinitionSchema = {
|
|
226
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
227
|
+
title: "OXYGEN Portable Workflow Definition",
|
|
228
|
+
description: "A portable, editable oxygen-workflows-v2 graph. It never contains source-workspace connection UUIDs or standing trigger authority.",
|
|
229
|
+
type: "object",
|
|
230
|
+
additionalProperties: false,
|
|
231
|
+
properties: {
|
|
232
|
+
kind: { const: "oxygen-workflow-definition" },
|
|
233
|
+
definition_version: { const: PORTABLE_WORKFLOW_DEFINITION_VERSION },
|
|
234
|
+
graph: workflowGraphManifestSchema,
|
|
235
|
+
requirements: {
|
|
236
|
+
type: "object",
|
|
237
|
+
additionalProperties: false,
|
|
238
|
+
properties: {
|
|
239
|
+
connections: {
|
|
240
|
+
type: "array",
|
|
241
|
+
items: {
|
|
242
|
+
type: "object",
|
|
243
|
+
additionalProperties: false,
|
|
244
|
+
properties: {
|
|
245
|
+
binding_id: { type: "string", minLength: 1 },
|
|
246
|
+
integration_id: { type: "string", minLength: 1 },
|
|
247
|
+
node_ids: { type: "array", minItems: 1, items: { type: "string", minLength: 1 } },
|
|
248
|
+
account_hint: {
|
|
249
|
+
type: "object",
|
|
250
|
+
additionalProperties: false,
|
|
251
|
+
properties: {
|
|
252
|
+
external_account_id: { type: "string" },
|
|
253
|
+
display_name: { type: "string" },
|
|
254
|
+
auth_scheme: { type: "string" },
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
required: ["binding_id", "integration_id", "node_ids"],
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
required: ["connections"],
|
|
263
|
+
},
|
|
264
|
+
exported_at: { type: "string", format: "date-time" },
|
|
265
|
+
exported_from: {
|
|
266
|
+
type: "object",
|
|
267
|
+
additionalProperties: false,
|
|
268
|
+
properties: {
|
|
269
|
+
workflow_slug: { type: "string" },
|
|
270
|
+
revision_version: { type: "integer", minimum: 1 },
|
|
271
|
+
oxygen_version: { type: "string" },
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
compiler_version: { const: PORTABLE_WORKFLOW_DEFINITION_COMPILER_VERSION },
|
|
275
|
+
},
|
|
276
|
+
required: [
|
|
277
|
+
"kind",
|
|
278
|
+
"definition_version",
|
|
279
|
+
"graph",
|
|
280
|
+
"requirements",
|
|
281
|
+
"exported_at",
|
|
282
|
+
"compiler_version",
|
|
283
|
+
],
|
|
284
|
+
};
|
|
@@ -204,8 +204,15 @@ export function remapNodeRefs(node, ids) {
|
|
|
204
204
|
request: Object.fromEntries(Object.entries(node.request).map(([key, ref]) => [key, remapValueRef(ref, ids)])),
|
|
205
205
|
}),
|
|
206
206
|
};
|
|
207
|
+
case "code":
|
|
208
|
+
return node.configuration === undefined
|
|
209
|
+
? node
|
|
210
|
+
: {
|
|
211
|
+
...node,
|
|
212
|
+
configuration: Object.fromEntries(Object.entries(node.configuration).map(([key, ref]) => [key, remapValueRef(ref, ids)])),
|
|
213
|
+
};
|
|
207
214
|
default:
|
|
208
|
-
// trigger, merge
|
|
215
|
+
// trigger, merge — no bindings. A source code node's JavaScript
|
|
209
216
|
// reading `ctx.steps`, deliberately NOT rewritten: it is arbitrary code, and
|
|
210
217
|
// string-substituting inside it would be a guess.
|
|
211
218
|
return node;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type { JsonSchema, WorkflowMode, WorkflowStatus, WorkflowStepEffect, WorkflowTriggerManifest } from "../index.js";
|
|
1
|
+
import type { JsonSchema, RecipeManifest, WorkflowMode, WorkflowStatus, WorkflowStepEffect, WorkflowTriggerManifest } from "../index.js";
|
|
2
2
|
export declare const WORKFLOW_GRAPH_MANIFEST_VERSION = 2;
|
|
3
3
|
export declare const WORKFLOW_GRAPH_COMPILER_VERSION = "oxygen-workflows-v2";
|
|
4
|
+
export declare const PORTABLE_WORKFLOW_DEFINITION_VERSION = 1;
|
|
5
|
+
export declare const PORTABLE_WORKFLOW_DEFINITION_COMPILER_VERSION = "oxygen-workflow-definition-v1";
|
|
4
6
|
export declare const RESERVED_NODE_IDS: ReadonlySet<string>;
|
|
5
7
|
export declare const LOOP_BODY_HANDLE = "loop_body";
|
|
6
8
|
export declare const LOOP_DONE_HANDLE = "loop_done";
|
|
@@ -144,6 +146,15 @@ export type WorkflowGraphNode =
|
|
|
144
146
|
/** Per-node managed-credit ceiling, same semantics as v1 tool steps. */
|
|
145
147
|
max_credits?: number;
|
|
146
148
|
params: Record<string, WorkflowValueRef>;
|
|
149
|
+
/**
|
|
150
|
+
* Lossless bridge for a v1 code-authored tool step. The canvas cannot infer
|
|
151
|
+
* declarative field bindings from arbitrary JavaScript, so imported steps
|
|
152
|
+
* keep their sandboxed payload function until the author explicitly replaces
|
|
153
|
+
* it with schema-mapped `params` in the inspector.
|
|
154
|
+
*/
|
|
155
|
+
payload_source?: string;
|
|
156
|
+
/** Imported payload functions receive the original v1 `{ input, steps, mode }` shape. */
|
|
157
|
+
legacy_context?: "v1";
|
|
147
158
|
})
|
|
148
159
|
/** Passes execution on only when `when` holds. */
|
|
149
160
|
| (WorkflowGraphNodeBase & {
|
|
@@ -212,10 +223,21 @@ export type WorkflowGraphNode =
|
|
|
212
223
|
reason?: WorkflowValueRef;
|
|
213
224
|
request?: Record<string, WorkflowValueRef>;
|
|
214
225
|
})
|
|
215
|
-
/**
|
|
226
|
+
/**
|
|
227
|
+
* Sandboxed transform source, or an imported durable recipe bundle.
|
|
228
|
+
*
|
|
229
|
+
* A recipe is deliberately ONE executable node: its bundle is the runtime
|
|
230
|
+
* authority, while visual_plan remains inspectable metadata. Turning those
|
|
231
|
+
* descriptive checkpoints into editable graph nodes would make the canvas lie
|
|
232
|
+
* about what deletion or rewiring actually changes.
|
|
233
|
+
*/
|
|
216
234
|
| (WorkflowGraphNodeBase & {
|
|
217
235
|
kind: "code";
|
|
218
|
-
run_source
|
|
236
|
+
run_source?: string;
|
|
237
|
+
recipe?: RecipeManifest;
|
|
238
|
+
configuration?: Record<string, WorkflowValueRef>;
|
|
239
|
+
/** Imported v1 source reads raw step outputs rather than v2 output envelopes. */
|
|
240
|
+
legacy_context?: "v1";
|
|
219
241
|
})
|
|
220
242
|
/** Calls another workflow as a child run. */
|
|
221
243
|
| (WorkflowGraphNodeBase & {
|
|
@@ -259,11 +281,58 @@ export type WorkflowGraphManifest = {
|
|
|
259
281
|
compiler_version: typeof WORKFLOW_GRAPH_COMPILER_VERSION;
|
|
260
282
|
created_at: string;
|
|
261
283
|
};
|
|
284
|
+
/**
|
|
285
|
+
* A connection requirement in a portable workflow definition.
|
|
286
|
+
*
|
|
287
|
+
* Connection UUIDs are organization-local credentials and never travel in the
|
|
288
|
+
* artifact. Only exact source-account pins appear here. Unpinned provider nodes
|
|
289
|
+
* and Oxygen-managed nodes remain absent and keep ordinary workspace-default or
|
|
290
|
+
* managed resolution in the destination.
|
|
291
|
+
*/
|
|
292
|
+
export type PortableWorkflowConnectionRequirement = {
|
|
293
|
+
/** Artifact-local key used by import flags and UI controls. */
|
|
294
|
+
binding_id: string;
|
|
295
|
+
integration_id: string;
|
|
296
|
+
/** Tool nodes that shared this exact source account pin. */
|
|
297
|
+
node_ids: string[];
|
|
298
|
+
/** Display/matching hints only; never a source connection UUID or secret. */
|
|
299
|
+
account_hint?: {
|
|
300
|
+
external_account_id?: string;
|
|
301
|
+
display_name?: string;
|
|
302
|
+
auth_scheme?: string;
|
|
303
|
+
};
|
|
304
|
+
};
|
|
305
|
+
/**
|
|
306
|
+
* Portable, editable representation of one deterministic workflow graph.
|
|
307
|
+
*
|
|
308
|
+
* This is deliberately narrower than a Blueprint (which may also carry tables,
|
|
309
|
+
* prompts, and column grafts) and narrower than AnyWorkflowManifest (which also
|
|
310
|
+
* includes legacy source-authored generations). A portable definition always
|
|
311
|
+
* opens as the same v2 graph the worker executes.
|
|
312
|
+
*/
|
|
313
|
+
export type PortableWorkflowDefinitionV1 = {
|
|
314
|
+
kind: "oxygen-workflow-definition";
|
|
315
|
+
definition_version: typeof PORTABLE_WORKFLOW_DEFINITION_VERSION;
|
|
316
|
+
/** Named `graph`, not `manifest`: old CLIs treat `{manifest}` as directly applyable. */
|
|
317
|
+
graph: WorkflowGraphManifest;
|
|
318
|
+
requirements: {
|
|
319
|
+
connections: PortableWorkflowConnectionRequirement[];
|
|
320
|
+
};
|
|
321
|
+
exported_at: string;
|
|
322
|
+
exported_from?: {
|
|
323
|
+
workflow_slug?: string;
|
|
324
|
+
revision_version?: number;
|
|
325
|
+
oxygen_version?: string;
|
|
326
|
+
};
|
|
327
|
+
compiler_version: typeof PORTABLE_WORKFLOW_DEFINITION_COMPILER_VERSION;
|
|
328
|
+
};
|
|
262
329
|
/**
|
|
263
330
|
* Shape gate only — enough to route a manifest to the v2 code path. Full
|
|
264
331
|
* structural validation is the linter's job, not this guard's.
|
|
265
332
|
*/
|
|
266
333
|
export declare function isWorkflowGraphManifest(value: unknown): value is WorkflowGraphManifest;
|
|
334
|
+
/** Shape gate only; use the portable-definition linter before importing. */
|
|
335
|
+
export declare function isPortableWorkflowDefinitionV1(value: unknown): value is PortableWorkflowDefinitionV1;
|
|
267
336
|
export declare function isTriggerNode(node: WorkflowGraphNode): node is WorkflowGraphNodeOfKind<"trigger">;
|
|
268
337
|
export declare function isToolNode(node: WorkflowGraphNode): node is WorkflowGraphNodeOfKind<"tool">;
|
|
269
338
|
export declare function isSwitchNode(node: WorkflowGraphNode): node is WorkflowGraphNodeOfKind<"switch">;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export const WORKFLOW_GRAPH_MANIFEST_VERSION = 2;
|
|
2
2
|
export const WORKFLOW_GRAPH_COMPILER_VERSION = "oxygen-workflows-v2";
|
|
3
|
+
export const PORTABLE_WORKFLOW_DEFINITION_VERSION = 1;
|
|
4
|
+
export const PORTABLE_WORKFLOW_DEFINITION_COMPILER_VERSION = "oxygen-workflow-definition-v1";
|
|
3
5
|
// Node ids become keys on the runtime scope object (`steps.<id>.output`), exactly
|
|
4
6
|
// as v1 step ids become keys on ctx.steps. The same JavaScript-internals collision
|
|
5
7
|
// applies: a node named `__proto__` would silently lose its output.
|
|
@@ -73,6 +75,16 @@ export function isWorkflowGraphManifest(value) {
|
|
|
73
75
|
&& Array.isArray(value.nodes)
|
|
74
76
|
&& Array.isArray(value.edges);
|
|
75
77
|
}
|
|
78
|
+
/** Shape gate only; use the portable-definition linter before importing. */
|
|
79
|
+
export function isPortableWorkflowDefinitionV1(value) {
|
|
80
|
+
return isRecord(value)
|
|
81
|
+
&& value.kind === "oxygen-workflow-definition"
|
|
82
|
+
&& value.definition_version === PORTABLE_WORKFLOW_DEFINITION_VERSION
|
|
83
|
+
&& value.compiler_version === PORTABLE_WORKFLOW_DEFINITION_COMPILER_VERSION
|
|
84
|
+
&& isWorkflowGraphManifest(value.graph)
|
|
85
|
+
&& isRecord(value.requirements)
|
|
86
|
+
&& Array.isArray(value.requirements.connections);
|
|
87
|
+
}
|
|
76
88
|
function isNodeOfKind(node, kind) {
|
|
77
89
|
return node.kind === kind;
|
|
78
90
|
}
|
|
@@ -5,6 +5,7 @@ export * from "./event-dispatch.js";
|
|
|
5
5
|
export type { CanonicalWorkflowToolEffect } from "./tool-effects.js";
|
|
6
6
|
export * from "./graph/index.js";
|
|
7
7
|
export * from "./graph/lint.js";
|
|
8
|
+
export * from "./portable.js";
|
|
8
9
|
export declare const WORKFLOW_MANIFEST_VERSION = 1;
|
|
9
10
|
export declare const WORKFLOW_COMPILER_VERSION = "oxygen-workflows-v1";
|
|
10
11
|
export declare const DURABLE_RECIPE_COMPILER_VERSION = "oxygen-recipes-v2";
|
|
@@ -146,6 +147,13 @@ export type RecipeManifest = {
|
|
|
146
147
|
};
|
|
147
148
|
trigger?: WorkflowTriggerManifest;
|
|
148
149
|
input_schema?: JsonSchema;
|
|
150
|
+
/**
|
|
151
|
+
* Operator-editable recipe settings, distinct from the per-delivery trigger
|
|
152
|
+
* payload. When present, the canvas renders this schema with the same mapped
|
|
153
|
+
* field controls used by tool/action configuration.
|
|
154
|
+
*/
|
|
155
|
+
configuration_schema?: JsonSchema;
|
|
156
|
+
configuration?: Record<string, unknown>;
|
|
149
157
|
runtime: RecipeRuntime;
|
|
150
158
|
bundle: string;
|
|
151
159
|
bundle_format: "esm";
|
|
@@ -392,6 +400,8 @@ export declare function buildRecipeManifest(input: {
|
|
|
392
400
|
status?: WorkflowStatus;
|
|
393
401
|
trigger?: WorkflowTriggerManifest;
|
|
394
402
|
inputSchema?: JsonSchema;
|
|
403
|
+
configurationSchema?: JsonSchema;
|
|
404
|
+
configuration?: Record<string, unknown>;
|
|
395
405
|
bundle: string;
|
|
396
406
|
toolsUsed: string[];
|
|
397
407
|
visualPlan?: RecipeVisualPlanManifest;
|
|
@@ -399,6 +409,12 @@ export declare function buildRecipeManifest(input: {
|
|
|
399
409
|
sourceHash?: string;
|
|
400
410
|
createdAt?: Date;
|
|
401
411
|
}): RecipeManifest;
|
|
412
|
+
/** Canonical executable identity shared by recipe compilers and repair paths. */
|
|
413
|
+
export declare function hashRecipeSource(input: {
|
|
414
|
+
bundle: string;
|
|
415
|
+
configurationSchema?: JsonSchema;
|
|
416
|
+
configuration?: Record<string, unknown>;
|
|
417
|
+
}): string;
|
|
402
418
|
export declare function validateOptionalMaxCredits(value: unknown, path: string, add: (path: string, code: string, message: string) => void): void;
|
|
403
419
|
export declare function lintWorkflowManifest(// skipcq: JS-R1005
|
|
404
420
|
value: unknown, options?: WorkflowManifestValidationOptions): WorkflowLintResult;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import * as vm from "node:vm"; // skipcq: JS-C1003
|
|
3
3
|
import { isWorkflowGraphManifest } from "./graph/types.js";
|
|
4
|
+
import { lintWorkflowGraphManifest } from "./graph/lint.js";
|
|
4
5
|
export * from "./usage-estimate.js";
|
|
5
6
|
// Pure event-dispatch semantics, shared by the apps/web and apps/worker event
|
|
6
7
|
// dispatchers so the filter gate, idempotency rule, and delivery verdict cannot
|
|
@@ -11,6 +12,7 @@ export * from "./event-dispatch.js";
|
|
|
11
12
|
// scanner below — so it is re-exported here, from the node entry point only.
|
|
12
13
|
export * from "./graph/index.js";
|
|
13
14
|
export * from "./graph/lint.js";
|
|
15
|
+
export * from "./portable.js";
|
|
14
16
|
export const WORKFLOW_MANIFEST_VERSION = 1;
|
|
15
17
|
export const WORKFLOW_COMPILER_VERSION = "oxygen-workflows-v1";
|
|
16
18
|
export const DURABLE_RECIPE_COMPILER_VERSION = "oxygen-recipes-v2";
|
|
@@ -278,7 +280,13 @@ export function buildRecipeManifest(input) {
|
|
|
278
280
|
if (typeof bundle !== "string" || bundle.length === 0) {
|
|
279
281
|
throw new Error("Recipe bundle source is required.");
|
|
280
282
|
}
|
|
281
|
-
const sourceHash = input.sourceHash ??
|
|
283
|
+
const sourceHash = input.sourceHash ?? hashRecipeSource({
|
|
284
|
+
bundle,
|
|
285
|
+
...(input.configurationSchema !== undefined
|
|
286
|
+
? { configurationSchema: input.configurationSchema }
|
|
287
|
+
: {}),
|
|
288
|
+
...(input.configuration !== undefined ? { configuration: input.configuration } : {}),
|
|
289
|
+
});
|
|
282
290
|
const manifest = {
|
|
283
291
|
manifest_version: WORKFLOW_MANIFEST_VERSION,
|
|
284
292
|
workflow: {
|
|
@@ -288,6 +296,8 @@ export function buildRecipeManifest(input) {
|
|
|
288
296
|
},
|
|
289
297
|
...(input.trigger ? { trigger: input.trigger } : {}),
|
|
290
298
|
...(input.inputSchema ? { input_schema: input.inputSchema } : {}),
|
|
299
|
+
...(input.configurationSchema ? { configuration_schema: input.configurationSchema } : {}),
|
|
300
|
+
...(input.configuration ? { configuration: input.configuration } : {}),
|
|
291
301
|
runtime: "durable",
|
|
292
302
|
bundle,
|
|
293
303
|
bundle_format: "esm",
|
|
@@ -300,6 +310,20 @@ export function buildRecipeManifest(input) {
|
|
|
300
310
|
};
|
|
301
311
|
return manifest;
|
|
302
312
|
}
|
|
313
|
+
/** Canonical executable identity shared by recipe compilers and repair paths. */
|
|
314
|
+
export function hashRecipeSource(input) {
|
|
315
|
+
// Preserve the historical bundle-only identity for recipes that have no
|
|
316
|
+
// editable configuration. Configuration-bearing revisions include schema +
|
|
317
|
+
// values because either can change the behavior of the same bundle source.
|
|
318
|
+
if (input.configurationSchema === undefined && input.configuration === undefined) {
|
|
319
|
+
return hashWorkflowSource(input.bundle);
|
|
320
|
+
}
|
|
321
|
+
return hashWorkflowSource(JSON.stringify({
|
|
322
|
+
bundle: input.bundle,
|
|
323
|
+
configuration_schema: input.configurationSchema ?? null,
|
|
324
|
+
configuration: input.configuration ?? null,
|
|
325
|
+
}, workflowJsonReplacer));
|
|
326
|
+
}
|
|
303
327
|
// Shared positive-finite validator for the optional per-run / per-step
|
|
304
328
|
// max_credits caps (Gap 2). `undefined` is valid (no cap); anything else must be
|
|
305
329
|
// a positive finite number, or lint rejects the manifest.
|
|
@@ -314,6 +338,9 @@ export function validateOptionalMaxCredits(value, path, add) {
|
|
|
314
338
|
}
|
|
315
339
|
export function lintWorkflowManifest(// skipcq: JS-R1005
|
|
316
340
|
value, options = {}) {
|
|
341
|
+
if (isWorkflowGraphManifest(value)) {
|
|
342
|
+
return lintWorkflowGraphManifest(value, options);
|
|
343
|
+
}
|
|
317
344
|
if (isRecord(value)
|
|
318
345
|
&& typeof value.compiler_version === "string"
|
|
319
346
|
&& value.compiler_version.startsWith("oxygen-recipes-")) {
|
|
@@ -432,7 +459,7 @@ value, options = {}) {
|
|
|
432
459
|
if (resolveEffect) {
|
|
433
460
|
const canonicalEffect = resolveEffect(step.tool);
|
|
434
461
|
if (canonicalEffect !== null && step.effect !== canonicalEffect) {
|
|
435
|
-
add(`${path}.effect`, "tool_effect_mismatch", `Tool '${step.tool}' has canonical effect '${canonicalEffect}', not '${String(step.effect)}'.`);
|
|
462
|
+
add(`${path}.effect`, "tool_effect_mismatch", `Tool '${step.tool}' has canonical effect '${canonicalEffect}', not '${String(step.effect)}'. Set step.effect to '${canonicalEffect}'.`);
|
|
436
463
|
}
|
|
437
464
|
}
|
|
438
465
|
}
|
|
@@ -504,6 +531,17 @@ value, options = {}) {
|
|
|
504
531
|
}
|
|
505
532
|
if (value.trigger !== undefined)
|
|
506
533
|
validateTrigger(value.trigger, "$.trigger", add);
|
|
534
|
+
if (value.configuration_schema !== undefined && !isRecord(value.configuration_schema)) {
|
|
535
|
+
add("$.configuration_schema", "invalid_configuration_schema", "Recipe configuration_schema must be a JSON Schema object.");
|
|
536
|
+
}
|
|
537
|
+
if (value.configuration !== undefined && !isRecord(value.configuration)) {
|
|
538
|
+
add("$.configuration", "invalid_configuration", "Recipe configuration must be an object.");
|
|
539
|
+
}
|
|
540
|
+
else if (isRecord(value.configuration_schema)) {
|
|
541
|
+
for (const issue of validateJsonSchemaValue(isRecord(value.configuration) ? value.configuration : {}, value.configuration_schema, "$.configuration")) {
|
|
542
|
+
add(issue.path, issue.code, issue.message);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
507
545
|
if (!isNonEmptyString(value.bundle)) {
|
|
508
546
|
add("$.bundle", "missing_bundle", "Recipe bundle is required.");
|
|
509
547
|
}
|
|
@@ -1756,7 +1794,11 @@ export function workflowManifestCanInvokeTool(manifest) {
|
|
|
1756
1794
|
const viaRecipe = Array.isArray(manifest.tools_used)
|
|
1757
1795
|
&& manifest.tools_used.some((toolId) => isNonEmptyString(toolId));
|
|
1758
1796
|
const viaGraph = Array.isArray(manifest.nodes)
|
|
1759
|
-
&& manifest.nodes.some((node) => isRecord(node) && (node.kind === "tool"
|
|
1797
|
+
&& manifest.nodes.some((node) => isRecord(node) && (node.kind === "tool"
|
|
1798
|
+
|| node.kind === "workflow"
|
|
1799
|
+
|| (isRecord(node.recipe)
|
|
1800
|
+
&& Array.isArray(node.recipe.tools_used)
|
|
1801
|
+
&& node.recipe.tools_used.some((toolId) => isNonEmptyString(toolId)))));
|
|
1760
1802
|
const viaSteps = Array.isArray(manifest.steps)
|
|
1761
1803
|
&& manifest.steps.some((step) => isRecord(step) && step.kind === "tool");
|
|
1762
1804
|
return viaRecipe || viaGraph || viaSteps;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type PortableWorkflowDefinitionV1, type WorkflowGraphManifest } from "./graph/types.js";
|
|
2
|
+
export type PortableWorkflowDefinitionIssue = {
|
|
3
|
+
path: string;
|
|
4
|
+
code: string;
|
|
5
|
+
message: string;
|
|
6
|
+
};
|
|
7
|
+
export type PortableWorkflowDefinitionLintResult = {
|
|
8
|
+
ok: boolean;
|
|
9
|
+
issues: PortableWorkflowDefinitionIssue[];
|
|
10
|
+
};
|
|
11
|
+
export declare class PortableWorkflowDefinitionValidationError extends Error {
|
|
12
|
+
readonly issues: PortableWorkflowDefinitionIssue[];
|
|
13
|
+
constructor(issues: PortableWorkflowDefinitionIssue[]);
|
|
14
|
+
}
|
|
15
|
+
type ExportedFrom = NonNullable<PortableWorkflowDefinitionV1["exported_from"]>;
|
|
16
|
+
export type PortableWorkflowSourceConnection = {
|
|
17
|
+
integrationId: string;
|
|
18
|
+
externalAccountId?: string | null;
|
|
19
|
+
displayName?: string | null;
|
|
20
|
+
authScheme?: string | null;
|
|
21
|
+
};
|
|
22
|
+
/** The graph editor and server use this same material rule. */
|
|
23
|
+
export declare function hashPortableWorkflowGraphManifest(manifest: WorkflowGraphManifest): string;
|
|
24
|
+
export declare function lintPortableWorkflowGraph(manifest: unknown): PortableWorkflowDefinitionLintResult;
|
|
25
|
+
export declare function assertPortableWorkflowGraph(manifest: unknown): asserts manifest is WorkflowGraphManifest;
|
|
26
|
+
export declare function lintPortableWorkflowDefinition(value: unknown): PortableWorkflowDefinitionLintResult;
|
|
27
|
+
export declare function assertPortableWorkflowDefinition(value: unknown): asserts value is PortableWorkflowDefinitionV1;
|
|
28
|
+
export declare function buildPortableWorkflowDefinition(input: {
|
|
29
|
+
manifest: unknown;
|
|
30
|
+
exportedAt?: Date;
|
|
31
|
+
exportedFrom?: ExportedFrom;
|
|
32
|
+
/** Source connection metadata keyed by source-only UUID; UUIDs are never serialized. */
|
|
33
|
+
sourceConnections?: Record<string, PortableWorkflowSourceConnection>;
|
|
34
|
+
}): PortableWorkflowDefinitionV1;
|
|
35
|
+
export declare function materializePortableWorkflowDefinition(input: {
|
|
36
|
+
definition: unknown;
|
|
37
|
+
connectionBindings?: Record<string, string>;
|
|
38
|
+
workflowId?: string;
|
|
39
|
+
workflowName?: string;
|
|
40
|
+
webhookTriggerId?: string;
|
|
41
|
+
importedAt?: Date;
|
|
42
|
+
}): WorkflowGraphManifest;
|
|
43
|
+
export {};
|