@uipath/maestro-builder-sdk 6.63.0 → 6.64.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-index.md +22 -21
- package/dist/api-members.md +82 -81
- package/dist/cli/commands/flow.js +4 -2
- package/dist/compile-cli.js +55 -45
- package/dist/decompile-cli.js +2 -2
- package/dist/decompile.js +36 -11
- package/dist/flow-node-type.d.ts +9 -0
- package/dist/flow-sdk.d.ts +24 -3
- package/dist/flow-sdk.js +1 -1
- package/dist/nodes/agent/flow/check.js +4 -4
- package/dist/nodes/connector/flow/node-type.js +36 -1
- package/dist/serialize.d.ts +24 -0
- package/dist/serialize.js +114 -4
- package/package.json +2 -2
package/dist/compile-cli.js
CHANGED
|
@@ -10,12 +10,17 @@
|
|
|
10
10
|
* --library defaults to $FLOW_SDK_LIBRARY_JSON; --bindings defaults to
|
|
11
11
|
* .flow-sdk/bindings.json (or a legacy ./bindings.json) when present. Connector actions require a library.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
13
|
+
* `compile` only serializes (docs/CHECK_COMPILE_CONTRACT_DESIGN.md D1, D2). It
|
|
14
|
+
* does not run `check()`'s lint rules: run `flow check` first for those. It
|
|
15
|
+
* stops only when it cannot write a correct `.flow`, a refusal the serializer
|
|
16
|
+
* throws. A draft node (a connector with no connection, an empty required
|
|
17
|
+
* input) the canvas saved is written, with a warning: decompile marks its step
|
|
18
|
+
* `{ draft: true }` and carries its definition in `.definitions([...])`. Any
|
|
19
|
+
* other draft, such as one an agent just wrote (even of an operation the canvas
|
|
20
|
+
* has a draft of), is refused.
|
|
21
|
+
*
|
|
22
|
+
* `--no-check` is a deprecated no-op: it is accepted for one minor version, with
|
|
23
|
+
* a warning, and then removed.
|
|
19
24
|
*/
|
|
20
25
|
import { diagnosticLine, reportDiagnostics, reportResult, thrownDiagnostic } from './cli-result.js';
|
|
21
26
|
import { writeJsonFile } from './core/json-file.js';
|
|
@@ -24,8 +29,7 @@ import { basename, dirname, join, relative, resolve } from 'node:path';
|
|
|
24
29
|
import { resolveFlowFile, loadBuiltFlow } from './load.js';
|
|
25
30
|
import { serializeWithSidecars } from './serialize.js';
|
|
26
31
|
import { compileOwnsResource } from './core/agent-tool-resources.js';
|
|
27
|
-
import {
|
|
28
|
-
import { flowCheckOpts, openCliLibrary } from './cli-library.js';
|
|
32
|
+
import { openCliLibrary } from './cli-library.js';
|
|
29
33
|
import { Bindings } from './bindings.js';
|
|
30
34
|
import { flagValue, runWhenInvokedDirectly } from './cli-run.js';
|
|
31
35
|
import { connectionResources, inlineAgentResources, mergeBindingsV2, refreshInlineAgentRows } from './bindings-v2.js';
|
|
@@ -35,8 +39,8 @@ import { defaultBindingsFile } from './workdir.js';
|
|
|
35
39
|
import { isWithin } from './path-within.js';
|
|
36
40
|
import { flowSources, resourceSourcesOfOtherFlows } from './project-resource-users.js';
|
|
37
41
|
import { fnv1aX4Uuid } from './core/stable-id.js';
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
42
|
+
import { CompileRefusal } from './core/compile-refusal.js';
|
|
43
|
+
import { cliSpelling } from './core/cli-spelling.js';
|
|
40
44
|
/** An agent folder name: a uuid, any case. */
|
|
41
45
|
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
42
46
|
export async function run(argv) {
|
|
@@ -45,7 +49,9 @@ export async function run(argv) {
|
|
|
45
49
|
return 2;
|
|
46
50
|
}
|
|
47
51
|
const flags = new Set(['-o', '--library', '--bindings', '--connectors-local']);
|
|
48
|
-
|
|
52
|
+
if (argv.includes('--no-check')) {
|
|
53
|
+
console.error(`compile: --no-check is deprecated and does nothing: compile no longer runs check. Run \`${cliSpelling()} flow check\` before \`compile\`; the flag will be removed.`);
|
|
54
|
+
}
|
|
49
55
|
const pos = argv.find((a, i) => !flags.has(a) && !flags.has(argv[i - 1]));
|
|
50
56
|
if (!pos) {
|
|
51
57
|
console.error('compile: missing <flow.ts | BaseName>');
|
|
@@ -66,37 +72,20 @@ export async function run(argv) {
|
|
|
66
72
|
// (e.g. HTTP-only) then compile fine even if the connector cache isn't built.
|
|
67
73
|
// A connector action with no library resolves to a clear error in serialize().
|
|
68
74
|
//
|
|
69
|
-
// Loaded BEFORE check() on purpose: one of check's rules (whether a connector
|
|
70
|
-
// event operation declares any event parameters) can only be answered from the
|
|
71
|
-
// library, and answering it from an assumption instead is what made
|
|
72
|
-
// EVENT_NO_SCOPE an error for HTTP Webhook, whose only correct `where` is empty.
|
|
73
75
|
const library = openCliLibrary(argv);
|
|
74
76
|
const bindFile = flagValue(argv, '--bindings') ?? defaultBindingsFile();
|
|
75
77
|
const bindings = new Bindings(bindFile);
|
|
76
78
|
// One list per run; every exit path reports it (reportResult overwrites keys).
|
|
77
79
|
const diagnostics = [];
|
|
78
|
-
if (!noCheck) {
|
|
79
|
-
// Same options as `flow check`, so the two cannot disagree.
|
|
80
|
-
const diags = check(built, flowCheckOpts(library, bindings));
|
|
81
|
-
const errors = diags.filter((d) => d.level === 'error');
|
|
82
|
-
for (const d of diags)
|
|
83
|
-
console.error(` ${diagnosticLine(d)}`);
|
|
84
|
-
diagnostics.push(...diags);
|
|
85
|
-
if (errors.length) {
|
|
86
|
-
reportDiagnostics(diagnostics);
|
|
87
|
-
console.error(`compile: ${errors.length} error(s); not emitting.`);
|
|
88
|
-
return 1;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
80
|
// The serializer's own observations — a connection or folder value that can
|
|
92
|
-
// never be a tenant resource key
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
//
|
|
81
|
+
// never be a tenant resource key, a draft node written from its carried
|
|
82
|
+
// definition — printed in the same `⚠ [CODE]` shape as check()'s. Warnings are
|
|
83
|
+
// never fatal: the .flow is still written, exit stays 0. A binding LABEL the
|
|
84
|
+
// serializer refuses (its own name as the resourceKey, or a label a populated
|
|
85
|
+
// bindings.json does not declare) is an error, not a warning, and so is a
|
|
86
|
+
// BYOA-only event on a connection `prepare` never checked
|
|
87
|
+
// (EVENT_BYOA_UNVERIFIED): printed in the same `✗ [CODE]` shape `check` uses
|
|
88
|
+
// for it. Nothing is written.
|
|
100
89
|
const out = flagValue(argv, '-o') ?? `${built.name || basename(file).replace(/\.flow\.ts$|\.ts$/, '')}.flow`;
|
|
101
90
|
const outDir = dirname(resolve(out));
|
|
102
91
|
// ONE FOLDER, ONE FLOW (#1184). An agent's `source` is the name of the folder
|
|
@@ -147,28 +136,37 @@ export async function run(argv) {
|
|
|
147
136
|
const warnings = [];
|
|
148
137
|
let emitted;
|
|
149
138
|
try {
|
|
150
|
-
emitted = serializeWithSidecars(built, {
|
|
139
|
+
emitted = serializeWithSidecars(built, {
|
|
140
|
+
library, bindings, warn: (w) => warnings.push(w), agentSource,
|
|
141
|
+
carriedDrafts: (d) => warnings.push(draftWarning(d)),
|
|
142
|
+
});
|
|
151
143
|
}
|
|
152
144
|
catch (error) {
|
|
153
145
|
for (const w of warnings)
|
|
154
|
-
console.error(`
|
|
146
|
+
console.error(` ${diagnosticLine(serializeWarningDiagnostic(w))}`);
|
|
155
147
|
diagnostics.push(...warnings.map(serializeWarningDiagnostic));
|
|
156
|
-
if (!(error instanceof
|
|
157
|
-
// Any other throw still ends the run through the
|
|
158
|
-
// `error`); the list is reported first.
|
|
148
|
+
if (!(error instanceof CompileRefusal)) {
|
|
149
|
+
// Any other throw (an internal SDK error) still ends the run through the
|
|
150
|
+
// caller (exit 1, `--json` `error`); the list is reported first.
|
|
159
151
|
diagnostics.push(thrownDiagnostic(error));
|
|
160
152
|
reportDiagnostics(diagnostics);
|
|
161
153
|
throw error;
|
|
162
154
|
}
|
|
163
|
-
|
|
164
|
-
|
|
155
|
+
// A coded refusal: the serializer cannot write a correct `.flow`. Printed in
|
|
156
|
+
// `check`'s `✗ [CODE]` shape, which `check` reports for it too.
|
|
157
|
+
const refused = {
|
|
158
|
+
level: 'error', code: error.code, message: error.message,
|
|
159
|
+
...(error.suggestion !== undefined ? { suggestion: error.suggestion } : {}),
|
|
160
|
+
};
|
|
161
|
+
console.error(` ${diagnosticLine(refused)}`);
|
|
162
|
+
diagnostics.push(refused);
|
|
165
163
|
reportDiagnostics(diagnostics);
|
|
166
164
|
console.error('compile: 1 error(s); not emitting.');
|
|
167
165
|
return 1;
|
|
168
166
|
}
|
|
169
167
|
const { flow: flowJson, sidecars } = emitted;
|
|
170
168
|
for (const w of warnings)
|
|
171
|
-
console.error(`
|
|
169
|
+
console.error(` ${diagnosticLine(serializeWarningDiagnostic(w))}`);
|
|
172
170
|
diagnostics.push(...warnings.map(serializeWarningDiagnostic));
|
|
173
171
|
// An inline agent is TWO artifacts: the node, and the agent's own configuration
|
|
174
172
|
// at `<source>/agent.json` — which is where flow-v1's serializer points the
|
|
@@ -325,7 +323,19 @@ export async function run(argv) {
|
|
|
325
323
|
return 0;
|
|
326
324
|
}
|
|
327
325
|
function serializeWarningDiagnostic(w) {
|
|
328
|
-
return { level: 'warning', code: w.code, message: w.message };
|
|
326
|
+
return { level: 'warning', code: w.code, message: w.message, ...(w.step !== undefined ? { step: w.step } : {}) };
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* A draft node written from its carried definition, as a warning: the node is
|
|
330
|
+
* in the `.flow` as the canvas saved it, unfinished. For instance
|
|
331
|
+
* `⚠ [BINDING_MISSING] … written as a draft from its carried definition`.
|
|
332
|
+
*/
|
|
333
|
+
function draftWarning(d) {
|
|
334
|
+
return {
|
|
335
|
+
code: String(d.code),
|
|
336
|
+
message: `${d.message} Written as a draft, with the definition the source carries; finish the node before the flow runs.`,
|
|
337
|
+
...(d.step !== undefined ? { step: d.step } : {}),
|
|
338
|
+
};
|
|
329
339
|
}
|
|
330
340
|
runWhenInvokedDirectly(import.meta.url, 'compile', run);
|
|
331
341
|
/** A JSON file's contents, or `undefined` when it is missing or unreadable. */
|
package/dist/decompile-cli.js
CHANGED
|
@@ -159,7 +159,7 @@ if (!existsSync(BASELINE)) {
|
|
|
159
159
|
// ── Step 1: capture the baseline = compile of the PRISTINE decompiled code,
|
|
160
160
|
// BEFORE any edit. Run this once right after decompiling, then edit. ──
|
|
161
161
|
mkdirSync(dirname(BASELINE), { recursive: true });
|
|
162
|
-
run('compile', [TS, '-o', BASELINE
|
|
162
|
+
run('compile', [TS, '-o', BASELINE]);
|
|
163
163
|
console.log('\\n>>> Baseline captured. Now EDIT ' + ${JSON.stringify(basename(tsPath))} + ', then re-run this script to compile + merge.');
|
|
164
164
|
} else {
|
|
165
165
|
// ── Step 2: compile your edits, then merge them back into the original. ──
|
|
@@ -182,7 +182,7 @@ if (!existsSync(BASELINE)) {
|
|
|
182
182
|
process.exit(2);
|
|
183
183
|
}
|
|
184
184
|
mkdirSync(dirname(EDITED), { recursive: true });
|
|
185
|
-
run('compile', [TS, '-o', EDITED
|
|
185
|
+
run('compile', [TS, '-o', EDITED]);
|
|
186
186
|
|
|
187
187
|
// ── DIFF GATE (seam) ──────────────────────────────────────────────────────
|
|
188
188
|
// A future step can diff ORIGINAL vs the about-to-be-written MERGED here and
|
package/dist/decompile.js
CHANGED
|
@@ -298,6 +298,11 @@ function optsArgWith(node, ctx, extra) {
|
|
|
298
298
|
const entries = updates.map((u) => `${key(u.variableId)}: ${emitVal(u.expression, ctx.imp, ctx.inputNames)}`);
|
|
299
299
|
parts.push(`updates: { ${entries.join(', ')} }`);
|
|
300
300
|
}
|
|
301
|
+
// A draft the canvas saved: `compile` writes it back only when the step says
|
|
302
|
+
// so AND the source carries its definition (carriedPairs), so a draft an agent
|
|
303
|
+
// adds of the same operation is still refused (StepOptions.draft).
|
|
304
|
+
if (isCanvasDraft(node))
|
|
305
|
+
parts.push('draft: true');
|
|
301
306
|
return parts.length ? `, { ${parts.join(', ')} }` : '';
|
|
302
307
|
}
|
|
303
308
|
// ─── Node type constants (mirror of serialize.ts NODE_TYPE) ───────────────────
|
|
@@ -957,8 +962,9 @@ function emitStepSpec(node, imp, inputNames, o, graph) {
|
|
|
957
962
|
/**
|
|
958
963
|
* A connector-bound node the file carries UNBOUND: Studio Web saves a draft whose
|
|
959
964
|
* connection was never picked as `connectionId: null`. The source cannot invent
|
|
960
|
-
* labels for it, and `
|
|
961
|
-
* rule `flow validate` applies too: flow-builder-sdk#890)
|
|
965
|
+
* labels for it, and `check` refuses a node without both (BINDING_MISSING, the
|
|
966
|
+
* rule `flow validate` applies too: flow-builder-sdk#890), and so does `compile`
|
|
967
|
+
* unless it is a carried canvas draft (`{ draft: true }`). So the source says so
|
|
962
968
|
* at the node and the run warns once, instead of handing back a file whose first
|
|
963
969
|
* compile fails with no context. Returns the inline marker, or '' when bound.
|
|
964
970
|
*/
|
|
@@ -968,10 +974,12 @@ function unboundMarker(node, labels, key, action) {
|
|
|
968
974
|
return '';
|
|
969
975
|
const names = missing.join(' and ');
|
|
970
976
|
warnOnce(`node ${tsLiteral(node.id)} (${node.type}) has no ${missing.join(' or ')} in the .flow (an unbound draft). `
|
|
971
|
-
+ `The source leaves ${missing.length > 1 ? 'them' : 'it'} out
|
|
972
|
-
|
|
977
|
+
+ `The source leaves ${missing.length > 1 ? 'them' : 'it'} out${isCanvasDraft(node)
|
|
978
|
+
? ' and marks the step `{ draft: true }`: compile writes it as a draft, and check refuses it'
|
|
979
|
+
: ', and compile and check refuse it'} (BINDING_MISSING) until it names both labels: `
|
|
980
|
+
+ `run \`${prepareCommand(key, action ?? '<action>')}\`, then pass `
|
|
973
981
|
+ `\`connection\` and \`folder\`.`);
|
|
974
|
-
return ` /* TODO: bind ${commentText(names)};
|
|
982
|
+
return ` /* TODO: bind ${commentText(names)}; check refuses this node until it has both labels (BINDING_MISSING) */`;
|
|
975
983
|
}
|
|
976
984
|
function emitEventSubscription(node, imp, inputNames) {
|
|
977
985
|
const prefix = node.type.startsWith('uipath.connector.event.') ? 'uipath.connector.event.'
|
|
@@ -1071,11 +1079,27 @@ function unsupportedSpec(node, imp, o) {
|
|
|
1071
1079
|
return `${imp.use('mock')}() /* TODO: unsupported node type ${commentText(node.type)} */`;
|
|
1072
1080
|
}
|
|
1073
1081
|
/**
|
|
1074
|
-
*
|
|
1075
|
-
*
|
|
1076
|
-
* `
|
|
1077
|
-
|
|
1078
|
-
|
|
1082
|
+
* Whether `node` is a draft the canvas saved whose definition the file holds
|
|
1083
|
+
* (its node type's `isDraft`). Decompile carries that definition and marks the
|
|
1084
|
+
* step `{ draft: true }`. For instance a Slack send with `detail.connectionId: null`.
|
|
1085
|
+
*/
|
|
1086
|
+
function isCanvasDraft(node) {
|
|
1087
|
+
const key = `${node.type}@${node.typeVersion}`;
|
|
1088
|
+
return !!node.typeVersion && runDefs.has(key) && registeredImpl(node)?.isDraft?.(node, runDefs.get(key)) === true;
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Every `<type>@<version>` in the file (root and subflows) whose definition the
|
|
1092
|
+
* source must carry, and that the file's `definitions[]` holds — mapped to the
|
|
1093
|
+
* const name decompile hoists the definition under. Two kinds of node need it:
|
|
1094
|
+
*
|
|
1095
|
+
* - a pin this SDK cannot resolve, for a type it ships at some version. For
|
|
1096
|
+
* instance a quick-form node at 1.6 maps
|
|
1097
|
+
* `uipath.human-in-the-loop.quick-form@1.6` → `quickFormV1_6Definition`.
|
|
1098
|
+
* - a DRAFT the canvas saved (its node type's `isDraft`): `compile`
|
|
1099
|
+
* writes a draft node only with the definition the source carries for it
|
|
1100
|
+
* (docs/CHECK_COMPILE_CONTRACT_DESIGN.md D2), so an untouched canvas draft
|
|
1101
|
+
* compiles back. The step is also marked `{ draft: true }`, so a draft an
|
|
1102
|
+
* agent writes, even of the same operation, is refused.
|
|
1079
1103
|
*/
|
|
1080
1104
|
function carriedPairs(flow) {
|
|
1081
1105
|
const out = new Map();
|
|
@@ -1086,7 +1110,8 @@ function carriedPairs(flow) {
|
|
|
1086
1110
|
if (!n.typeVersion || out.has(key) || !runDefs.has(key))
|
|
1087
1111
|
continue;
|
|
1088
1112
|
const shipped = shippedVersions(n.type);
|
|
1089
|
-
|
|
1113
|
+
const unresolvable = shipped.length > 0 && !shipped.includes(n.typeVersion);
|
|
1114
|
+
if (!unresolvable && !isCanvasDraft(n))
|
|
1090
1115
|
continue;
|
|
1091
1116
|
const base = `${camel(String(n.type).split('.').pop() ?? 'node')}V${String(n.typeVersion).replace(/[^A-Za-z0-9]+/g, '_')}Definition`;
|
|
1092
1117
|
let name = base;
|
package/dist/flow-node-type.d.ts
CHANGED
|
@@ -484,6 +484,15 @@ export interface FlowRawNodeAdvice {
|
|
|
484
484
|
}
|
|
485
485
|
/** One Flow node type at one version. */
|
|
486
486
|
export interface FlowNodeType<Spec> extends NodeType<Spec, FlowNodeContribution, FlowContexts> {
|
|
487
|
+
/**
|
|
488
|
+
* Whether a serialized node of this type is a DRAFT the canvas saved: one an
|
|
489
|
+
* ordinary compile refuses only for a draft code (a connector with no
|
|
490
|
+
* connection, an empty required input). Decompile then copies the node's
|
|
491
|
+
* definition from the file into `.definitions([...])`, and `compile` writes
|
|
492
|
+
* the draft back with it (`SerializeOpts.carriedDrafts`). `definition` is the
|
|
493
|
+
* file's own `definitions[]` entry for the node, if any. Absent: never a draft.
|
|
494
|
+
*/
|
|
495
|
+
isDraft?(node: unknown, definition: unknown): boolean;
|
|
487
496
|
/**
|
|
488
497
|
* The node-level `model` every node of this type carries, whichever path emits
|
|
489
498
|
* it — this type's `serialize`, or a `rawNode` of its definition. `'omit'`: none
|
package/dist/flow-sdk.d.ts
CHANGED
|
@@ -444,6 +444,27 @@ export interface NodeOptions {
|
|
|
444
444
|
*/
|
|
445
445
|
label?: string;
|
|
446
446
|
}
|
|
447
|
+
/**
|
|
448
|
+
* The options of `.step()`: the {@link NodeOptions}, plus `draft` for a node the
|
|
449
|
+
* canvas saved unfinished.
|
|
450
|
+
*/
|
|
451
|
+
export interface StepOptions extends NodeOptions {
|
|
452
|
+
/**
|
|
453
|
+
* This node is a DRAFT the canvas saved: a connector with no connection, or
|
|
454
|
+
* with an empty required input. Decompile sets it on such a node, and also
|
|
455
|
+
* carries the node's definition in `.definitions([...])`. `compile` then
|
|
456
|
+
* writes the node with that carried definition and the inputs it has, and
|
|
457
|
+
* prints a warning (`BINDING_MISSING`, `CONNECTOR_INPUT_MISSING`) instead of
|
|
458
|
+
* refusing. Without the flag, or without a carried definition for the node's
|
|
459
|
+
* exact type and version, the draft is refused as usual. `check` reports the
|
|
460
|
+
* draft either way.
|
|
461
|
+
*
|
|
462
|
+
* Do not set it on a node you write: finish the node instead. For instance
|
|
463
|
+
* decompile prints `.step('notify', connector(SLACK, SEND, { ... }), { draft: true })`
|
|
464
|
+
* for a Slack send saved with no connection.
|
|
465
|
+
*/
|
|
466
|
+
draft?: boolean;
|
|
467
|
+
}
|
|
447
468
|
/**
|
|
448
469
|
* The options of `.parallel()`: the join's {@link NodeOptions}, plus the fork
|
|
449
470
|
* node when the flow has one.
|
|
@@ -558,7 +579,7 @@ export type Step = {
|
|
|
558
579
|
kind: 'action';
|
|
559
580
|
name: string;
|
|
560
581
|
spec: FlowActionSpec;
|
|
561
|
-
options?:
|
|
582
|
+
options?: StepOptions;
|
|
562
583
|
} | {
|
|
563
584
|
kind: 'branch';
|
|
564
585
|
name: string;
|
|
@@ -920,10 +941,10 @@ declare class StepList {
|
|
|
920
941
|
* so it must be unique within the flow.
|
|
921
942
|
* @param spec - What the node does, from an action factory.
|
|
922
943
|
* @param options - Node options; `version` selects the exact definition
|
|
923
|
-
* version (see {@link
|
|
944
|
+
* version (see {@link StepOptions}).
|
|
924
945
|
* @returns This builder, so calls chain.
|
|
925
946
|
*/
|
|
926
|
-
step(name: string, spec: FlowActionSpec | FlowAction, options?:
|
|
947
|
+
step(name: string, spec: FlowActionSpec | FlowAction, options?: StepOptions): this;
|
|
927
948
|
/**
|
|
928
949
|
* Add an action node and route EVERY exit it declares, one arm per exit.
|
|
929
950
|
*
|
package/dist/flow-sdk.js
CHANGED
|
@@ -388,7 +388,7 @@ class StepList {
|
|
|
388
388
|
* so it must be unique within the flow.
|
|
389
389
|
* @param spec - What the node does, from an action factory.
|
|
390
390
|
* @param options - Node options; `version` selects the exact definition
|
|
391
|
-
* version (see {@link
|
|
391
|
+
* version (see {@link StepOptions}).
|
|
392
392
|
* @returns This builder, so calls chain.
|
|
393
393
|
*/
|
|
394
394
|
step(name, spec, options) {
|
|
@@ -340,15 +340,15 @@ export function checkInlineAgent(inputs, ctx) {
|
|
|
340
340
|
//
|
|
341
341
|
// An artifact that compiles and can never validate is the worst of the two
|
|
342
342
|
// failures, so this is said here — before the emit — rather than left to a rung.
|
|
343
|
-
// It is an ERROR because there is no spelling that works today
|
|
344
|
-
//
|
|
343
|
+
// It is an ERROR because there is no spelling that works today. `compile` does
|
|
344
|
+
// not run check, so a tenant whose manifest does declare the handle still compiles it.
|
|
345
345
|
if (inputs?.memory !== undefined) {
|
|
346
346
|
at('AGENT_MEMORY_NOT_AUTHORABLE', `Inline agent step "${step}" attaches \`memory\`, which the platform cannot resolve: the tenant's `
|
|
347
347
|
+ `\`uipath.agent.autonomous\` manifest declares no \`memory\` handle (only escalation, context, tool, `
|
|
348
348
|
+ `success, error) and the registry serves no \`uipath.agent.resource.memory.*\` node type. The flow `
|
|
349
349
|
+ `compiles and then fails \`uip maestro flow validate\` as RetryWillNotFix. Drop \`memory\`; for `
|
|
350
350
|
+ `retrieval over a knowledge source use a context index instead (\`tools\`/context grounding). `
|
|
351
|
-
+ `
|
|
351
|
+
+ `On a tenant whose manifest does declare the handle, ignore this finding: \`compile\` does not run check.`);
|
|
352
352
|
}
|
|
353
353
|
// A context `query` is what the RUNTIME searches with, and it is not an
|
|
354
354
|
// expression slot. A plain string is the model-invoked `dynamic` variant: the
|
|
@@ -468,7 +468,7 @@ export function checkInlineAgent(inputs, ctx) {
|
|
|
468
468
|
+ `of the prompt) with one line per field, in the platform's own wording: "${rewrite}" Declare a field \`'object'\` or `
|
|
469
469
|
+ `\`'array'\` if it should carry structure. \`uip maestro flow validate\` accepts this prompt, so `
|
|
470
470
|
+ `nothing later catches it. If this is a false positive (the prompt only mentions JSON as data), `
|
|
471
|
-
+ `compile
|
|
471
|
+
+ `ignore this finding: \`compile\` does not run check.`);
|
|
472
472
|
}
|
|
473
473
|
}
|
|
474
474
|
// The prompts-vs-variables agreement, in both directions. Both prompts are
|
|
@@ -270,6 +270,40 @@ function liftConnector(node, ctx) {
|
|
|
270
270
|
missing,
|
|
271
271
|
};
|
|
272
272
|
}
|
|
273
|
+
/**
|
|
274
|
+
* Whether a connector operation node is a draft the canvas saved, by what the
|
|
275
|
+
* file itself shows: no connection or folder (`BINDING_MISSING`), or a field its
|
|
276
|
+
* own definition marks `required` that the node gives no value
|
|
277
|
+
* (`CONNECTOR_INPUT_MISSING`). For instance a Slack send saved before a
|
|
278
|
+
* connection was picked has `detail.connectionId: null`. An operation with no
|
|
279
|
+
* object picked is not one: `compile` can only stand in for it, so it is never
|
|
280
|
+
* written either way.
|
|
281
|
+
*/
|
|
282
|
+
function isConnectorDraft(node, definition) {
|
|
283
|
+
const detail = (node.inputs?.detail ?? {});
|
|
284
|
+
const label = (v) => (typeof v === 'string' ? v : undefined);
|
|
285
|
+
if (missingBindingLabels({ connection: label(detail.connectionId), folder: label(detail.connectionFolderKey) }).length > 0)
|
|
286
|
+
return true;
|
|
287
|
+
const fields = definition?.inputDefinition?.fields;
|
|
288
|
+
if (!Array.isArray(fields))
|
|
289
|
+
return false;
|
|
290
|
+
const slots = ['bodyParameters', 'queryParameters', 'pathParameters']
|
|
291
|
+
.map((slot) => detail[slot])
|
|
292
|
+
.filter((m) => !!m && typeof m === 'object' && !Array.isArray(m));
|
|
293
|
+
const parts = Array.isArray(detail.multipartParameters) ? detail.multipartParameters : [];
|
|
294
|
+
const given = (name) => parts.some((p) => p?.name === name) || slots.some((m) => {
|
|
295
|
+
if (Object.prototype.hasOwnProperty.call(m, name))
|
|
296
|
+
return true;
|
|
297
|
+
let at = m;
|
|
298
|
+
for (const seg of name.split('.')) {
|
|
299
|
+
if (!at || typeof at !== 'object' || !Object.prototype.hasOwnProperty.call(at, seg))
|
|
300
|
+
return false;
|
|
301
|
+
at = at[seg];
|
|
302
|
+
}
|
|
303
|
+
return true;
|
|
304
|
+
});
|
|
305
|
+
return fields.some((f) => f?.required === true && typeof f.name === 'string' && !given(f.name));
|
|
306
|
+
}
|
|
273
307
|
function printConnector(l, ctx) {
|
|
274
308
|
// A present input whose value is `undefined` (the inputs check accepts one, and
|
|
275
309
|
// serialize keeps it in memory) prints as `undefined`, as it did before the port.
|
|
@@ -284,7 +318,7 @@ function printConnector(l, ctx) {
|
|
|
284
318
|
['object', optional(l.object)],
|
|
285
319
|
],
|
|
286
320
|
...(l.missing.length > 0
|
|
287
|
-
? { comment: `TODO: bind ${l.missing.join(' and ')};
|
|
321
|
+
? { comment: `TODO: bind ${l.missing.join(' and ')}; check refuses this node until it has both labels (BINDING_MISSING)` }
|
|
288
322
|
: {}),
|
|
289
323
|
};
|
|
290
324
|
}
|
|
@@ -302,6 +336,7 @@ const connector10 = {
|
|
|
302
336
|
},
|
|
303
337
|
serialize: (spec, ctx) => serializeConnector(spec, ctx),
|
|
304
338
|
deserialize: (node, ctx) => liftConnector(node, ctx),
|
|
339
|
+
isDraft: (node, definition) => isConnectorDraft(node, definition),
|
|
305
340
|
print: (spec, ctx) => printConnector(spec, ctx),
|
|
306
341
|
check: (spec, ctx) => ctx.connector(spec),
|
|
307
342
|
// Every Expr in the inputs, nested ones included, resolves against the flow's names.
|
package/dist/serialize.d.ts
CHANGED
|
@@ -29,6 +29,8 @@ export type DraftCode = BindingRefusalCode | typeof CONNECTOR_INPUT_MISSING | 'C
|
|
|
29
29
|
export interface DraftFinding {
|
|
30
30
|
/** The emitted node's id. */
|
|
31
31
|
nodeId: string;
|
|
32
|
+
/** The step whose node it is. Set by a carried-drafts compile ({@link SerializeOpts.carriedDrafts}). */
|
|
33
|
+
step?: string;
|
|
32
34
|
/** A {@link DraftCode}, or a node-type package's own code reported through `ctx.draft`. */
|
|
33
35
|
code: DraftCode | string;
|
|
34
36
|
/** The sentence the strict compile refuses with. */
|
|
@@ -71,6 +73,28 @@ export interface SerializeOpts {
|
|
|
71
73
|
* ordinary compile), behaviour is unchanged.
|
|
72
74
|
*/
|
|
73
75
|
draft?: (finding: DraftFinding) => void;
|
|
76
|
+
/**
|
|
77
|
+
* Carried drafts, for `compile` (docs/CHECK_COMPILE_CONTRACT_DESIGN.md D2).
|
|
78
|
+
*
|
|
79
|
+
* A step marked `{ draft: true }` ({@link StepOptions.draft}) that the
|
|
80
|
+
* ordinary compile refuses only for draft findings ({@link DraftCode}: a
|
|
81
|
+
* connector with no connection, an empty required input) is written anyway
|
|
82
|
+
* when the flow CARRIES the definition of the node it emits
|
|
83
|
+
* (`.definitions([...])`). Decompile sets both for a canvas draft node. The
|
|
84
|
+
* node is written with that definition and the inputs it has, and each
|
|
85
|
+
* finding is reported here. A draft with no flag, or no carried definition,
|
|
86
|
+
* or one the node type can only stand in for (a generic operation with no
|
|
87
|
+
* object), is refused exactly as without this option. So is any other
|
|
88
|
+
* refusal. A flow with no draft compiles byte for byte as without it.
|
|
89
|
+
*
|
|
90
|
+
* For instance, a decompiled canvas flow whose Slack send has no `messageToSend`
|
|
91
|
+
* compiles with a `CONNECTOR_INPUT_MISSING` finding here, and a second Slack
|
|
92
|
+
* send written by hand with no `messageToSend` (no flag) is refused, though
|
|
93
|
+
* the flow carries the definition of that operation.
|
|
94
|
+
*
|
|
95
|
+
* Ignored when {@link draft} is given (merge's compile tolerates every draft).
|
|
96
|
+
*/
|
|
97
|
+
carriedDrafts?: (finding: DraftFinding) => void;
|
|
74
98
|
/**
|
|
75
99
|
* Collect mode, for `check` (docs/CHECK_COMPILE_CONTRACT_DESIGN.md §3.5).
|
|
76
100
|
*
|
package/dist/serialize.js
CHANGED
|
@@ -2542,6 +2542,76 @@ function serializeFlow(built, opts) {
|
|
|
2542
2542
|
}
|
|
2543
2543
|
return [{ node: id, port }];
|
|
2544
2544
|
}
|
|
2545
|
+
/**
|
|
2546
|
+
* Run a node type's `serialize`, and in a carried-drafts compile
|
|
2547
|
+
* ({@link SerializeOpts.carriedDrafts}) try a refused `{ draft: true }` step
|
|
2548
|
+
* once more as a draft. The ordinary emission runs first, so a flow with no draft compiles
|
|
2549
|
+
* exactly as it would without the option. When it throws a refusal and the flow
|
|
2550
|
+
* carries any definition, what that emission bound is undone and the node is
|
|
2551
|
+
* emitted again with `drafting` on. That second contribution is returned with
|
|
2552
|
+
* the first refusal as `strict` when it reported a draft finding; the caller
|
|
2553
|
+
* writes it only with a carried definition, and throws `strict` otherwise. A
|
|
2554
|
+
* second emission that throws, or reports no finding, ends in `strict` too, so
|
|
2555
|
+
* a refusal that is not a draft reads as it always has.
|
|
2556
|
+
*/
|
|
2557
|
+
function serializeCarriedDraft(impl, spec, ctx, scope, id, bound, stepOptions, findings, enableDrafting, warnings) {
|
|
2558
|
+
if (!opts.carriedDrafts || opts.draft || opts.collect || carriedDefs.size === 0 || stepOptions?.draft !== true) {
|
|
2559
|
+
return { contribution: impl.serialize(spec, ctx) };
|
|
2560
|
+
}
|
|
2561
|
+
const mark = {
|
|
2562
|
+
resources: new Set(resourceBindings.keys()), sidecars: sidecars.length,
|
|
2563
|
+
agentSchemas: scope.agentSchemas.length, unbound: unboundDrafts.has(id),
|
|
2564
|
+
};
|
|
2565
|
+
// Each emission's warnings are held, so a node emitted twice reports only
|
|
2566
|
+
// the warnings of the emission that is kept (or of the refused one).
|
|
2567
|
+
const flush = (held) => { for (const w of held)
|
|
2568
|
+
opts.warn?.(w); };
|
|
2569
|
+
const first = [];
|
|
2570
|
+
warnings.held = first;
|
|
2571
|
+
try {
|
|
2572
|
+
const contribution = impl.serialize(spec, ctx);
|
|
2573
|
+
warnings.held = undefined;
|
|
2574
|
+
flush(first);
|
|
2575
|
+
return { contribution };
|
|
2576
|
+
}
|
|
2577
|
+
catch (error) {
|
|
2578
|
+
warnings.held = undefined;
|
|
2579
|
+
if (!(error instanceof CompileRefusal)) {
|
|
2580
|
+
flush(first);
|
|
2581
|
+
throw error;
|
|
2582
|
+
}
|
|
2583
|
+
for (const key of [...resourceBindings.keys()])
|
|
2584
|
+
if (!mark.resources.has(key))
|
|
2585
|
+
resourceBindings.delete(key);
|
|
2586
|
+
for (const f of sidecars.splice(mark.sidecars)) {
|
|
2587
|
+
if (!sidecars.some((kept) => kept.path === f.path))
|
|
2588
|
+
explicitSidecars.delete(f.path);
|
|
2589
|
+
}
|
|
2590
|
+
scope.agentSchemas.length = mark.agentSchemas;
|
|
2591
|
+
if (!mark.unbound)
|
|
2592
|
+
unboundDrafts.delete(id);
|
|
2593
|
+
bound.length = 0;
|
|
2594
|
+
enableDrafting();
|
|
2595
|
+
const second = [];
|
|
2596
|
+
warnings.held = second;
|
|
2597
|
+
let contribution;
|
|
2598
|
+
try {
|
|
2599
|
+
contribution = impl.serialize(spec, ctx);
|
|
2600
|
+
}
|
|
2601
|
+
catch {
|
|
2602
|
+
flush(first);
|
|
2603
|
+
throw error;
|
|
2604
|
+
}
|
|
2605
|
+
finally {
|
|
2606
|
+
warnings.held = undefined;
|
|
2607
|
+
}
|
|
2608
|
+
if (findings.length === 0) {
|
|
2609
|
+
flush(first);
|
|
2610
|
+
throw error;
|
|
2611
|
+
}
|
|
2612
|
+
return { contribution, strict: error, strictWarnings: first, draftWarnings: second };
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2545
2615
|
/**
|
|
2546
2616
|
* Lower a step whose kind is a registered node type (flow-node-registry.ts):
|
|
2547
2617
|
* the node type serializes its spec through a context, and this owns the rest —
|
|
@@ -2572,6 +2642,19 @@ function serializeFlow(built, opts) {
|
|
|
2572
2642
|
rendered.add(copy);
|
|
2573
2643
|
return copy;
|
|
2574
2644
|
};
|
|
2645
|
+
// Merge's compile drafts every node; `compile` (carriedDrafts) drafts a node
|
|
2646
|
+
// only on a second try, after the ordinary emission refused it.
|
|
2647
|
+
let drafting = opts.draft !== undefined;
|
|
2648
|
+
const carriedFindings = [];
|
|
2649
|
+
// Where this step's warnings go: `opts.warn`, or held while a carried-drafts
|
|
2650
|
+
// compile does not yet know which emission it keeps (serializeCarriedDraft).
|
|
2651
|
+
const warnings = {};
|
|
2652
|
+
const bindingOpts = opts.warn
|
|
2653
|
+
? { ...opts, warn: (w) => { if (warnings.held)
|
|
2654
|
+
warnings.held.push(w);
|
|
2655
|
+
else
|
|
2656
|
+
opts.warn(w); } }
|
|
2657
|
+
: opts;
|
|
2575
2658
|
const ctx = {
|
|
2576
2659
|
step: step.name,
|
|
2577
2660
|
nodeId: id,
|
|
@@ -2598,7 +2681,7 @@ function serializeFlow(built, opts) {
|
|
|
2598
2681
|
? resolved.carried
|
|
2599
2682
|
: bundledDefinition(typeName, v) ?? carriedFor(typeName, v)),
|
|
2600
2683
|
bindings: resourceBindings,
|
|
2601
|
-
resolveBinding: ((name, attribute, at, target) => resolveBinding(
|
|
2684
|
+
resolveBinding: ((name, attribute, at, target) => resolveBinding(bindingOpts, name, attribute, at, target)),
|
|
2602
2685
|
bindConnection: (c) => {
|
|
2603
2686
|
const { entries } = connectionResourceBindings({
|
|
2604
2687
|
def: { model: c.model },
|
|
@@ -2625,15 +2708,26 @@ function serializeFlow(built, opts) {
|
|
|
2625
2708
|
const out = renderDeep(value, scope.rename, rendered);
|
|
2626
2709
|
return out !== null && typeof out === 'object' ? fileCopy(out) : out;
|
|
2627
2710
|
},
|
|
2628
|
-
drafting
|
|
2711
|
+
get drafting() { return drafting; },
|
|
2629
2712
|
draft: (code, message) => {
|
|
2630
2713
|
if (code === 'BINDING_MISSING')
|
|
2631
2714
|
unboundDrafts.add(id);
|
|
2632
|
-
opts.draft
|
|
2715
|
+
if (opts.draft)
|
|
2716
|
+
opts.draft({ nodeId: id, code: code, message });
|
|
2717
|
+
else
|
|
2718
|
+
carriedFindings.push({ nodeId: id, step: step.name, code: code, message });
|
|
2633
2719
|
},
|
|
2634
2720
|
};
|
|
2635
2721
|
const bound = [];
|
|
2636
|
-
const contribution = impl
|
|
2722
|
+
const { contribution, strict, strictWarnings, draftWarnings } = serializeCarriedDraft(impl, spec, ctx, scope, id, bound, step.options, carriedFindings, () => { drafting = true; }, warnings);
|
|
2723
|
+
// A carried draft refused after all reports the warnings of the refused emission.
|
|
2724
|
+
const refuse = () => {
|
|
2725
|
+
for (const w of strictWarnings ?? [])
|
|
2726
|
+
opts.warn?.(w);
|
|
2727
|
+
throw strict;
|
|
2728
|
+
};
|
|
2729
|
+
if (strict !== undefined && contribution.standIn !== undefined)
|
|
2730
|
+
refuse();
|
|
2637
2731
|
if (contribution.standIn !== undefined) {
|
|
2638
2732
|
// A draft stand-in: the node as the draft has it, with no definition to
|
|
2639
2733
|
// place it against (see FlowNodeContribution.standIn).
|
|
@@ -2677,6 +2771,22 @@ function serializeFlow(built, opts) {
|
|
|
2677
2771
|
const origin = entry.family.package;
|
|
2678
2772
|
def = { ...def, source: { package: origin.name, ...(origin.version !== undefined ? { version: origin.version } : {}) } };
|
|
2679
2773
|
}
|
|
2774
|
+
// A draft `compile` writes: only a step marked `{ draft: true }` (checked in
|
|
2775
|
+
// serializeCarriedDraft) with the definition the flow carries for the node it
|
|
2776
|
+
// emits (decompile's copy of the canvas file's entry), else refused as the
|
|
2777
|
+
// ordinary emission refused it (SerializeOpts.carriedDrafts).
|
|
2778
|
+
if (strict !== undefined) {
|
|
2779
|
+
const carried = typeof def?.nodeType === 'string' && typeof def?.version === 'string'
|
|
2780
|
+
? carriedFor(def.nodeType, def.version)
|
|
2781
|
+
: undefined;
|
|
2782
|
+
if (carried === undefined)
|
|
2783
|
+
refuse();
|
|
2784
|
+
def = carried;
|
|
2785
|
+
for (const w of draftWarnings ?? [])
|
|
2786
|
+
opts.warn?.(w);
|
|
2787
|
+
for (const f of carriedFindings)
|
|
2788
|
+
opts.carriedDrafts?.(f);
|
|
2789
|
+
}
|
|
2680
2790
|
// A type that owns a family of names (`typeNamePrefix`) is placed under the
|
|
2681
2791
|
// name its definition carries — `uipath.core.function.<key>`, not the prefix.
|
|
2682
2792
|
// The bare prefix is allowed: an empty `key` builds it, check refuses that
|