@uipath/maestro-builder-sdk 6.60.0 → 6.61.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/README.md +10 -12
- package/dist/api-index.md +5 -4
- package/dist/check-cli.js +8 -7
- package/dist/check.d.ts +11 -0
- package/dist/check.js +192 -783
- package/dist/cli-library.d.ts +2 -1
- package/dist/cli-library.js +7 -2
- package/dist/cli-result.d.ts +14 -0
- package/dist/cli-result.js +18 -1
- package/dist/compile-cli.js +2 -2
- package/dist/core/compile-refusal.d.ts +7 -1
- package/dist/core/compile-refusal.js +9 -1
- package/dist/core/connector-checks.d.ts +8 -30
- package/dist/core/connector-checks.js +11 -60
- package/dist/core/connector-inputs.js +9 -1
- package/dist/core/event-checks.d.ts +15 -2
- package/dist/core/event-checks.js +28 -13
- package/dist/core/hitl-routing.d.ts +10 -1
- package/dist/core/hitl-routing.js +20 -5
- package/dist/core/lookups.d.ts +13 -0
- package/dist/core/lookups.js +26 -0
- package/dist/core/refusals.d.ts +2 -0
- package/dist/core/refusals.js +5 -1
- package/dist/flow-node-type.d.ts +35 -21
- package/dist/flow-parallel-hitl.d.ts +105 -0
- package/dist/flow-parallel-hitl.js +214 -0
- package/dist/flow-sdk.d.ts +4 -4
- package/dist/flow-sdk.js +2 -2
- package/dist/flow-terminate-names.d.ts +2 -2
- package/dist/flow-terminate-names.js +2 -2
- package/dist/merge-compile.js +2 -1
- package/dist/nodes/agent/flow/check.js +13 -88
- package/dist/nodes/agent/flow/inline-agent.js +41 -14
- package/dist/nodes/connector/flow/node-type.js +45 -18
- package/dist/nodes/document/flow/ixp.d.ts +1 -1
- package/dist/nodes/document/flow/ixp.js +22 -26
- package/dist/nodes/hitl/flow/check.js +4 -11
- package/dist/nodes/hitl/flow/node-type.js +16 -7
- package/dist/nodes/http/flow/check.js +5 -31
- package/dist/nodes/http/flow/node-type.js +7 -4
- package/dist/nodes/queue/flow/check.d.ts +4 -3
- package/dist/nodes/queue/flow/check.js +7 -6
- package/dist/nodes/queue/flow/node-type.js +1 -1
- package/dist/nodes/transform/flow/check.d.ts +2 -0
- package/dist/nodes/transform/flow/check.js +3 -6
- package/dist/nodes/transform/flow/node-type.js +6 -6
- package/dist/raw-node.js +8 -8
- package/dist/serialize.d.ts +38 -3
- package/dist/serialize.js +757 -322
- package/package.json +2 -2
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Expr } from '../../../core/expr.js';
|
|
2
|
-
import { derivedResourceNodeId } from '../../../core/agent-resource-identity.js';
|
|
3
2
|
import { GUID_RE } from '../../../core/binding-messages.js';
|
|
4
3
|
import { prepareCommand } from '../../../core/cli-spelling.js';
|
|
5
4
|
import { nearestName } from '../../../core/edit-distance.js';
|
|
@@ -372,24 +371,7 @@ export function checkInlineAgent(inputs, ctx) {
|
|
|
372
371
|
+ `run-dependent search needs.`);
|
|
373
372
|
}
|
|
374
373
|
}
|
|
375
|
-
//
|
|
376
|
-
// (`…context.index.<slug>.<id>`), and the platform resolves the index by it.
|
|
377
|
-
// A blank, padded or non-GUID id builds a type no tenant serves, and nothing
|
|
378
|
-
// downstream says so: the flow compiles, merges and validates, and the agent
|
|
379
|
-
// runs ungrounded. So it is an error, not a warning. The id is tested as
|
|
380
|
-
// written, not trimmed, because serialize emits it as written.
|
|
381
|
-
for (const cx of contexts) {
|
|
382
|
-
const id = typeof cx?.id === 'string' ? cx.id : '';
|
|
383
|
-
if (!GUID_RE.test(id)) {
|
|
384
|
-
at('AGENT_CONTEXT_NO_ID', `Inline agent step "${step}" grounds on context index "${cx?.name ?? ''}" with `
|
|
385
|
-
+ (id.trim() === '' ? 'no `id`'
|
|
386
|
-
: GUID_RE.test(id.trim()) ? `\`id\` ${JSON.stringify(cx?.id)}, which has whitespace around the GUID`
|
|
387
|
-
: `\`id\` ${JSON.stringify(cx?.id)}, which is not a GUID`) + '. '
|
|
388
|
-
+ `The id is the index's key: it is the tail of the resource node's type and how the runtime finds `
|
|
389
|
-
+ `the index, so without a real one the agent runs ungrounded and \`validate\` does not notice. `
|
|
390
|
-
+ `\`uip solution resources list --kind Index --source remote --search "<name>"\` returns it as \`Key\`.`);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
374
|
+
// A context index whose `id` is not a GUID is the serializer's AGENT_CONTEXT_NO_ID.
|
|
393
375
|
// An index outside the deployment folder needs its folder named. The resource
|
|
394
376
|
// file carries no `indexId`, so `indexName` + `folderPath` is the whole locator,
|
|
395
377
|
// and `@uipath/tool-agent`'s `createIndexBinding` leaves the folder off the
|
|
@@ -425,67 +407,23 @@ export function checkInlineAgent(inputs, ctx) {
|
|
|
425
407
|
}
|
|
426
408
|
// Where each resource node sits (`nodeId`, `source`): decompile writes them
|
|
427
409
|
// for a node the canvas placed, and compile writes them as given. A blank or
|
|
428
|
-
//
|
|
429
|
-
//
|
|
430
|
-
//
|
|
410
|
+
// taken `nodeId` and a `source` two of them share are the serializer's
|
|
411
|
+
// INLINE_AGENT_RESOURCE_NODE_ID / INLINE_AGENT_RESOURCE_SOURCE_DUP; a `source`
|
|
412
|
+
// that is not a uuid names a directory the platform does not expect (the
|
|
413
|
+
// agent's own `source` has the same rule).
|
|
431
414
|
const asList = (v) => (v === undefined ? [] : Array.isArray(v) ? v : [v]);
|
|
432
415
|
const placed = [
|
|
433
|
-
...asList(inputs?.context).map((r) => ({ what: `context index "${r?.name ?? ''}"`,
|
|
434
|
-
...asList(inputs?.tools).map((r) => ({ what: `${r?.kind ?? ''} tool`,
|
|
435
|
-
...asList(inputs?.escalation).map((r) => ({ what: `escalation "${r?.name ?? ''}"`,
|
|
416
|
+
...asList(inputs?.context).map((r) => ({ what: `context index "${r?.name ?? ''}"`, source: r?.source })),
|
|
417
|
+
...asList(inputs?.tools).map((r) => ({ what: `${r?.kind ?? ''} tool`, source: r?.source })),
|
|
418
|
+
...asList(inputs?.escalation).map((r) => ({ what: `escalation "${r?.name ?? ''}"`, source: r?.source })),
|
|
436
419
|
];
|
|
437
|
-
// The ids compile derives for the resources with no `nodeId`: the n-th one of
|
|
438
|
-
// a kind gets `<step><Kind>`, then `<step><Kind>2`, ... An explicit id equal to
|
|
439
|
-
// one of them makes compile refuse the flow, or rename the derived node.
|
|
440
|
-
for (const kind of ['Context', 'Tool', 'Escalation']) {
|
|
441
|
-
const derived = placed.filter((r) => r.kind === kind && r.nodeId === undefined);
|
|
442
|
-
derived.forEach((r, n) => {
|
|
443
|
-
const id = derivedResourceNodeId(step, kind, n);
|
|
444
|
-
ctx.resources.derivedNodeId(id, `the id compile derives for the ${r.what} of inline agent step "${step}"`);
|
|
445
|
-
});
|
|
446
|
-
}
|
|
447
|
-
/** Explicit `source` -> the resource on THIS agent that has it: two would write one resource.json. */
|
|
448
|
-
const sources = new Map();
|
|
449
420
|
for (const r of placed) {
|
|
450
|
-
if (r.nodeId !== undefined) {
|
|
451
|
-
const id = typeof r.nodeId === 'string' ? r.nodeId : '';
|
|
452
|
-
let other;
|
|
453
|
-
if (id.trim() === '') {
|
|
454
|
-
at('INLINE_AGENT_RESOURCE_NODE_ID', `Inline agent step "${step}" gives its ${r.what} the \`nodeId\` ${JSON.stringify(r.nodeId)}, which is `
|
|
455
|
-
+ `not a node id. Omit \`nodeId\` to derive one, or give the id the node has in the flow.`);
|
|
456
|
-
}
|
|
457
|
-
else if ((other = ctx.resources.claimNodeId(id, r.what)) !== undefined) {
|
|
458
|
-
at('INLINE_AGENT_RESOURCE_NODE_ID', `Inline agent step "${step}" gives its ${r.what} the \`nodeId\` "${id}", which the ${other.what} `
|
|
459
|
-
+ `(on step "${other.step}") already has. Node ids are unique in a flow.`);
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
if (r.source === undefined)
|
|
463
|
-
continue;
|
|
464
421
|
// Not trimmed: compile writes `source` as given, into `inputs.source` and the directory name.
|
|
465
|
-
if (!GUID_RE.test(String(r.source))) {
|
|
422
|
+
if (r.source !== undefined && !GUID_RE.test(String(r.source))) {
|
|
466
423
|
at('INLINE_AGENT_RESOURCE_SOURCE_NOT_GUID', `Inline agent step "${step}" gives its ${r.what} the \`source\` ${JSON.stringify(r.source)}, which is not `
|
|
467
424
|
+ `a uuid${/^\s|\s$/.test(String(r.source)) ? ' (it has spaces around it)' : ''}. \`source\` is the resource's `
|
|
468
425
|
+ `directory under the agent's own (\`resources/<source>/resource.json\`) and the node's \`inputs.source\`. `
|
|
469
426
|
+ 'Omit it and one is derived from the flow id, the step and the resource.');
|
|
470
|
-
continue;
|
|
471
|
-
}
|
|
472
|
-
const key = String(r.source).toLowerCase();
|
|
473
|
-
const first = sources.get(key);
|
|
474
|
-
if (first !== undefined) {
|
|
475
|
-
at('INLINE_AGENT_RESOURCE_SOURCE_DUP', `Inline agent step "${step}" gives its ${first} and its ${r.what} the same \`source\` "${String(r.source)}". `
|
|
476
|
-
+ 'Both would write `resources/<source>/resource.json` under this agent, and the second file would replace '
|
|
477
|
-
+ 'the first. Give each resource its own `source`, or omit `source` to derive one.');
|
|
478
|
-
}
|
|
479
|
-
else {
|
|
480
|
-
sources.set(key, r.what);
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
if (typeof inputs?.source === 'string' && GUID_RE.test(inputs.source.trim())) {
|
|
484
|
-
const other = ctx.resources.claimFolder(inputs.source.trim());
|
|
485
|
-
if (other !== undefined) {
|
|
486
|
-
at('INLINE_AGENT_RESOURCE_SOURCE_DUP', `Inline agent steps "${other}" and "${step}" have the same \`source\` "${inputs.source}". Both would write `
|
|
487
|
-
+ '`<source>/agent.json`, and the second file would replace the first. Give each agent its own `source`, '
|
|
488
|
-
+ 'or omit it to derive one.');
|
|
489
427
|
}
|
|
490
428
|
}
|
|
491
429
|
if (inputs?.source !== undefined && !GUID_RE.test(String(inputs.source).trim())) {
|
|
@@ -665,17 +603,11 @@ export function checkInlineAgent(inputs, ctx) {
|
|
|
665
603
|
// tools happens at compile (where the library is), not here.
|
|
666
604
|
const toolRefs = inputs?.tools === undefined ? []
|
|
667
605
|
: Array.isArray(inputs.tools) ? inputs.tools : [inputs.tools];
|
|
668
|
-
const BUILTIN_TOOLS = ['analyzefiles', 'summarize', 'batchtransform'];
|
|
669
606
|
for (const t of toolRefs) {
|
|
670
607
|
const kind = t?.kind;
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
+ `The platform ships exactly three: ${BUILTIN_TOOLS.map((b) => `'${b}'`).join(', ')} — the node type is a `
|
|
675
|
-
+ `fixed string, so any other name is a node the tenant has never heard of.`, `{ kind: 'builtin', tool: 'summarize' }`);
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
else if (kind === 'connector') {
|
|
608
|
+
// A builtin tool other than the three, and a kind no tool has, are the
|
|
609
|
+
// serializer's INLINE_AGENT_TOOL_UNKNOWN_BUILTIN / INLINE_AGENT_TOOL_UNKNOWN_KIND.
|
|
610
|
+
if (kind === 'connector') {
|
|
679
611
|
const c = t;
|
|
680
612
|
if (blank(c.connector) || blank(c.operation)) {
|
|
681
613
|
at('INLINE_AGENT_TOOL_NO_OPERATION', `Inline agent step "${step}" declares a connector tool without ${blank(c.connector) ? 'a `connector`' : 'an `operation`'}. `
|
|
@@ -696,9 +628,7 @@ export function checkInlineAgent(inputs, ctx) {
|
|
|
696
628
|
+ `Name the bindings.json labels the same way \`connector()\` takes them — \`${prepareCommand(key, op)}\` `
|
|
697
629
|
+ `finds the connection and writes both entries.`, `{ kind: 'connector', connector: '${key}', operation: '${op}', connection: '<connection label>', folder: '<folder label>' }`);
|
|
698
630
|
}
|
|
699
|
-
|
|
700
|
-
ctx.bindingLabels(`Inline agent step "${step}"'s connector tool`, labels, undefined, blank(c.connector) ? undefined : { key: String(c.connector), ...(blank(c.operation) ? {} : { action: String(c.operation) }) });
|
|
701
|
-
}
|
|
631
|
+
// Labels it has are the serializer's to judge against bindings.json (BINDING_*).
|
|
702
632
|
}
|
|
703
633
|
else if (kind === 'ixp') {
|
|
704
634
|
const x = t;
|
|
@@ -756,11 +686,6 @@ export function checkInlineAgent(inputs, ctx) {
|
|
|
756
686
|
+ `tool's contract allows GET, POST, PUT, PATCH, DELETE — anything else the runtime refuses at call time.`);
|
|
757
687
|
}
|
|
758
688
|
}
|
|
759
|
-
else {
|
|
760
|
-
at('INLINE_AGENT_TOOL_UNKNOWN_KIND', `Inline agent step "${step}" declares a tool with kind ${JSON.stringify(kind)}. Valid kinds: 'builtin', `
|
|
761
|
-
+ `'connector', 'process', 'agent', 'api', 'flow', 'maestro', 'function', 'ixp', 'mcp', 'a2a', `
|
|
762
|
-
+ `'clientside', 'httpRequest'.`);
|
|
763
|
-
}
|
|
764
689
|
}
|
|
765
690
|
const escalations = inputs?.escalation === undefined ? []
|
|
766
691
|
: Array.isArray(inputs.escalation) ? inputs.escalation : [inputs.escalation];
|
|
@@ -118,7 +118,7 @@ export function serializeInlineAgent(s, ctx) {
|
|
|
118
118
|
source,
|
|
119
119
|
name: step,
|
|
120
120
|
});
|
|
121
|
-
ctx.sidecar({ path: `${source}/agent.json`, json: agentJson });
|
|
121
|
+
ctx.sidecar({ path: `${source}/agent.json`, json: agentJson }, s.source !== undefined ? `inline agent step "${step}"` : undefined);
|
|
122
122
|
// The schema's property TYPES need this scope's finished variables, which do
|
|
123
123
|
// not exist yet: the `properties` object is filled in place when the scope
|
|
124
124
|
// closes.
|
|
@@ -131,12 +131,21 @@ export function serializeInlineAgent(s, ctx) {
|
|
|
131
131
|
// is the direction both definitions declare.
|
|
132
132
|
const contexts = s.context === undefined ? [] : Array.isArray(s.context) ? s.context : [s.context];
|
|
133
133
|
for (const cx of contexts) {
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
//
|
|
134
|
+
// The index id is half of the resource node's TYPE
|
|
135
|
+
// (`…context.index.<slug>.<id>`), and the platform resolves the index by it.
|
|
136
|
+
// A blank, padded or non-GUID id builds a type no tenant serves, and nothing
|
|
137
|
+
// downstream says so: the flow compiles, merges and validates, and the agent
|
|
138
|
+
// runs ungrounded. The id is tested as written, not trimmed, because it is
|
|
139
|
+
// emitted as written.
|
|
137
140
|
if (typeof cx?.id !== 'string' || !GUID_RE.test(cx.id)) {
|
|
138
|
-
|
|
139
|
-
|
|
141
|
+
const id = typeof cx?.id === 'string' ? cx.id : '';
|
|
142
|
+
throw new CompileRefusal('AGENT_CONTEXT_NO_ID', `Inline agent step "${step}" grounds on context index "${cx?.name ?? ''}" with `
|
|
143
|
+
+ (id.trim() === '' ? 'no `id`'
|
|
144
|
+
: GUID_RE.test(id.trim()) ? `\`id\` ${JSON.stringify(cx?.id)}, which has whitespace around the GUID`
|
|
145
|
+
: `\`id\` ${JSON.stringify(cx?.id)}, which is not a GUID`) + '. '
|
|
146
|
+
+ `The id is the index's key: it is the tail of the resource node's type and how the runtime finds `
|
|
147
|
+
+ `the index, so without a real one the agent runs ungrounded and \`validate\` does not notice. `
|
|
148
|
+
+ `\`uip solution resources list --kind Index --source remote --search "<name>"\` returns it as \`Key\`.`);
|
|
140
149
|
}
|
|
141
150
|
const cdef = contextIndexDef({ name: cx.name, id: cx.id });
|
|
142
151
|
// `<step>Context`, deduped for a second index on the same agent, unless the
|
|
@@ -188,7 +197,7 @@ export function serializeInlineAgent(s, ctx) {
|
|
|
188
197
|
ctx.sidecar({
|
|
189
198
|
path: `${source}/resources/${contextResourceId}/resource.json`,
|
|
190
199
|
json: contextResourceJson(cx, { id: contextResourceId, query: contextQuery }),
|
|
191
|
-
}, cx.source !== undefined);
|
|
200
|
+
}, cx.source !== undefined ? `inline agent step "${step}"'s context index "${cx.name ?? ''}"` : undefined);
|
|
192
201
|
}
|
|
193
202
|
// TOOLS — one resource node per tool, joined to the agent's `tool` handle; the
|
|
194
203
|
// same artifact shape as context (no incoming control flow, no output, never
|
|
@@ -200,6 +209,9 @@ export function serializeInlineAgent(s, ctx) {
|
|
|
200
209
|
// — derived, so a recompile is not a diff.
|
|
201
210
|
const toolRefs = s.tools === undefined ? [] : Array.isArray(s.tools) ? s.tools : [s.tools];
|
|
202
211
|
for (const tr of toolRefs) {
|
|
212
|
+
// An entry that is no object (null, a hole, a string) has no kind at all.
|
|
213
|
+
if (tr === null || typeof tr !== 'object')
|
|
214
|
+
throw unknownToolKind(step, undefined);
|
|
203
215
|
let tdef;
|
|
204
216
|
let label;
|
|
205
217
|
let extraInputs = {};
|
|
@@ -207,8 +219,11 @@ export function serializeInlineAgent(s, ctx) {
|
|
|
207
219
|
if (tr.kind === 'builtin') {
|
|
208
220
|
const bdef = ownEntry(BUILTIN_TOOL_DEFS, tr.tool);
|
|
209
221
|
tdef = bdef === undefined ? undefined : jsonClone(bdef);
|
|
210
|
-
if (!tdef)
|
|
211
|
-
throw new CompileRefusal('INLINE_AGENT_TOOL_UNKNOWN_BUILTIN', `
|
|
222
|
+
if (!tdef) {
|
|
223
|
+
throw new CompileRefusal('INLINE_AGENT_TOOL_UNKNOWN_BUILTIN', `Inline agent step "${step}" declares a builtin tool ${JSON.stringify(tr.tool)}. `
|
|
224
|
+
+ `The platform ships exactly three: ${Object.keys(BUILTIN_TOOL_DEFS).map((b) => `'${b}'`).join(', ')} — the node `
|
|
225
|
+
+ `type is a fixed string, so any other name is a node the tenant has never heard of.`, `{ kind: 'builtin', tool: 'summarize' }`);
|
|
226
|
+
}
|
|
212
227
|
label = tdef.display.label;
|
|
213
228
|
// The designer-placed node carries the def's `inputDefaults` with the
|
|
214
229
|
// author's values over them — the context rule, kept per builtin (each def
|
|
@@ -242,8 +257,14 @@ export function serializeInlineAgent(s, ctx) {
|
|
|
242
257
|
extraInputs = jsonClone(tdef.inputDefaults);
|
|
243
258
|
}
|
|
244
259
|
else if (tr.kind === 'connector') {
|
|
245
|
-
if (!ctx.library)
|
|
260
|
+
if (!ctx.library) {
|
|
261
|
+
// Its labels are bindings.json's to judge, as a connector step's are:
|
|
262
|
+
// refused ahead of the library, so a run without one still hears about them.
|
|
263
|
+
const where = `inline agent "${step}"'s connector tool "${tr.connector}/${tr.operation}"`;
|
|
264
|
+
ctx.resolveBinding(tr.connection, 'connection', where, { key: tr.connector, action: tr.operation });
|
|
265
|
+
ctx.resolveBinding(tr.folder, 'folder', where, { key: tr.connector, action: tr.operation });
|
|
246
266
|
throw new CompileRefusal('LIBRARY_REQUIRED', `inline agent "${step}"'s connector tool needs a library (pass --library / set $FLOW_SDK_LIBRARY_JSON).`);
|
|
267
|
+
}
|
|
247
268
|
const rc = ctx.library.resolve(tr.connector, tr.operation, tr.version, tr.object);
|
|
248
269
|
// An entry `prepare` synthesized from Integration Service compiles in BPMN
|
|
249
270
|
// only — for a tool as for a step: `connectorToolDef` reads the same
|
|
@@ -338,7 +359,7 @@ export function serializeInlineAgent(s, ctx) {
|
|
|
338
359
|
else {
|
|
339
360
|
const meta = ownEntry(PROCESS_TOOL_META, tr.kind);
|
|
340
361
|
if (!meta)
|
|
341
|
-
throw
|
|
362
|
+
throw unknownToolKind(step, tr.kind);
|
|
342
363
|
tdef = processToolDef(tr.kind, tr);
|
|
343
364
|
label = tr.name;
|
|
344
365
|
// The definition's `model.context` names `<bindings.name>` /
|
|
@@ -377,7 +398,7 @@ export function serializeInlineAgent(s, ctx) {
|
|
|
377
398
|
? httpRequestToolResourceJson(label, toolInputs, { id: toolResourceId, canvasNodeId: tid })
|
|
378
399
|
: toolResourceJson(tr, { id: toolResourceId });
|
|
379
400
|
if (toolResource !== undefined) {
|
|
380
|
-
ctx.sidecar({ path: `${source}/resources/${toolResourceId}/resource.json`, json: toolResource }, tr.source !== undefined);
|
|
401
|
+
ctx.sidecar({ path: `${source}/resources/${toolResourceId}/resource.json`, json: toolResource }, tr.source !== undefined ? `inline agent step "${step}"'s ${tr.kind} tool` : undefined);
|
|
381
402
|
}
|
|
382
403
|
}
|
|
383
404
|
// ESCALATION — the agent's TOP handle; an Action Center app task a human
|
|
@@ -443,7 +464,7 @@ export function serializeInlineAgent(s, ctx) {
|
|
|
443
464
|
ctx.sidecar({
|
|
444
465
|
path: `${source}/resources/${qsource}/resource.json`,
|
|
445
466
|
json: escalationResourceJson(esc, { id: qsource, channelId, quickFormSchema: schema }),
|
|
446
|
-
}, esc.source !== undefined);
|
|
467
|
+
}, esc.source !== undefined ? `inline agent step "${step}"'s escalation "${esc.name ?? ''}"` : undefined);
|
|
447
468
|
continue;
|
|
448
469
|
}
|
|
449
470
|
const app = esc.app;
|
|
@@ -486,7 +507,7 @@ export function serializeInlineAgent(s, ctx) {
|
|
|
486
507
|
ctx.sidecar({
|
|
487
508
|
path: `${source}/resources/${esource}/resource.json`,
|
|
488
509
|
json: escalationResourceJson(esc, { id: esource, channelId }),
|
|
489
|
-
}, esc.source !== undefined);
|
|
510
|
+
}, esc.source !== undefined ? `inline agent step "${step}"'s escalation "${esc.name ?? ''}"` : undefined);
|
|
490
511
|
}
|
|
491
512
|
// MEMORY — one episodic-memory resource on the agent's `memory` handle (the
|
|
492
513
|
// 1.4 definition selected above). Same artifact shape as context.
|
|
@@ -525,3 +546,9 @@ export function serializeInlineAgent(s, ctx) {
|
|
|
525
546
|
attach,
|
|
526
547
|
};
|
|
527
548
|
}
|
|
549
|
+
/** INLINE_AGENT_TOOL_UNKNOWN_KIND: a tool whose `kind` names no tool the platform has. */
|
|
550
|
+
function unknownToolKind(step, kind) {
|
|
551
|
+
return new CompileRefusal('INLINE_AGENT_TOOL_UNKNOWN_KIND', `Inline agent step "${step}" declares a tool with kind ${JSON.stringify(kind)}. Valid `
|
|
552
|
+
+ "kinds: 'builtin', 'connector', 'process', 'agent', 'api', 'flow', 'maestro', 'function', 'ixp', 'mcp', 'a2a', "
|
|
553
|
+
+ "'clientside', 'httpRequest'.");
|
|
554
|
+
}
|
|
@@ -65,15 +65,36 @@ function listOutputs(rc, nodeId) {
|
|
|
65
65
|
}
|
|
66
66
|
function serializeConnector(spec, ctx) {
|
|
67
67
|
const step = ctx.step;
|
|
68
|
-
if (!ctx.library)
|
|
68
|
+
if (!ctx.library) {
|
|
69
|
+
// The labels are bindings.json's to judge, not the library's: refused ahead
|
|
70
|
+
// of the library, so a run without one (`check`'s inner loop) still hears
|
|
71
|
+
// about them. With a library they are judged below, as they always were.
|
|
72
|
+
const where = `connector "${step}"`;
|
|
73
|
+
const target = { key: spec.key, action: spec.action };
|
|
74
|
+
const missing = missingBindingLabels(spec);
|
|
75
|
+
if (missing.length > 0 && !ctx.drafting)
|
|
76
|
+
throw new BindingRefusal('BINDING_MISSING', bindingMissingMessage(where, missing, target));
|
|
77
|
+
ctx.resolveBinding(spec.connection, 'connection', where, target);
|
|
78
|
+
ctx.resolveBinding(spec.folder, 'folder', where, target);
|
|
69
79
|
throw new CompileRefusal('LIBRARY_REQUIRED', `connector "${step}" needs a library (pass --library / set $FLOW_SDK_LIBRARY_JSON).`);
|
|
80
|
+
}
|
|
81
|
+
// The library's refusals (the operation, its fields) name the connector, not
|
|
82
|
+
// the step: they say which step as `check` always has.
|
|
83
|
+
const onStep = (refusal) => new CompileRefusal(refusal.code, `Connector step "${step}": ${refusal.message}`, refusal.suggestion);
|
|
70
84
|
let rc;
|
|
71
85
|
try {
|
|
72
86
|
rc = ctx.library.resolve(spec.key, spec.action, spec.version, spec.object);
|
|
73
87
|
}
|
|
74
88
|
catch (error) {
|
|
75
|
-
if (
|
|
89
|
+
if (error instanceof Error && error.message.startsWith('internal SDK error:'))
|
|
76
90
|
throw error;
|
|
91
|
+
// A throw `library.resolve` makes on no purpose is still this step's refusal,
|
|
92
|
+
// under the code `check` has always given it (#1015).
|
|
93
|
+
if (!(error instanceof ConnectorResolveError)) {
|
|
94
|
+
throw onStep(new CompileRefusal('CONNECTOR_RESOLVE_FAILED', error instanceof Error ? error.message : String(error)));
|
|
95
|
+
}
|
|
96
|
+
if (!ctx.drafting || error.code !== 'CONNECTOR_OBJECT_REQUIRED')
|
|
97
|
+
throw onStep(error);
|
|
77
98
|
// A generic operation saved before its object was picked has no definition
|
|
78
99
|
// to emit against. Draft tolerance emits a stable stand-in under the node's
|
|
79
100
|
// id; merge restores the original when it is untouched and refuses it
|
|
@@ -100,11 +121,10 @@ function serializeConnector(spec, ctx) {
|
|
|
100
121
|
// An entry `prepare` synthesized from Integration Service compiles in BPMN only.
|
|
101
122
|
const noDefinition = synthesizedDefinitionRefusal(rc);
|
|
102
123
|
if (noDefinition)
|
|
103
|
-
throw noDefinition;
|
|
124
|
+
throw onStep(noDefinition);
|
|
104
125
|
const where = `connector "${step}"`;
|
|
105
126
|
const target = { key: spec.key, action: spec.action };
|
|
106
|
-
// A connector-bound node needs both labels (#890)
|
|
107
|
-
// (BINDING_MISSING). A draft that names no connection gets no binding rows
|
|
127
|
+
// A connector-bound node needs both labels (#890: BINDING_MISSING). A draft that names no connection gets no binding rows
|
|
108
128
|
// either: there is nothing to bind, and rows with an empty resourceKey would
|
|
109
129
|
// leak into the merged file through merge's bindings union.
|
|
110
130
|
let unbound = false;
|
|
@@ -129,19 +149,26 @@ function serializeConnector(spec, ctx) {
|
|
|
129
149
|
});
|
|
130
150
|
}
|
|
131
151
|
const library = ctx.library;
|
|
132
|
-
const connectorInputs =
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
152
|
+
const connectorInputs = (() => {
|
|
153
|
+
try {
|
|
154
|
+
return buildConnectorInputs(rc, spec.inputs, {
|
|
155
|
+
...(ctx.drafting
|
|
156
|
+
? { onMissingRequired: (m) => ctx.draft(CONNECTOR_INPUT_MISSING, `connector ${rc.nodeType}: ${m}`) }
|
|
157
|
+
: {}),
|
|
158
|
+
dialect: FLOW_EXPR_DIALECT,
|
|
159
|
+
render: (value) => ctx.renderDeep(value),
|
|
160
|
+
resolutions: library.lookupResolutions ?? {},
|
|
161
|
+
// Only for the missing-required hint; a test double that is not a full
|
|
162
|
+
// Library simply gets the plain message.
|
|
163
|
+
lookups: typeof library.lookupSpecs === 'function'
|
|
164
|
+
? library.lookupSpecs(spec.key, spec.action, spec.version, spec.object)
|
|
165
|
+
: {},
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
throw error instanceof CompileRefusal ? onStep(error) : error;
|
|
170
|
+
}
|
|
171
|
+
})();
|
|
145
172
|
const replayInputs = replayInputsOf(connectorInputs);
|
|
146
173
|
const detail = {
|
|
147
174
|
connector: rc.connectorKey,
|
|
@@ -63,15 +63,25 @@ const IXP_DEF = ixpDefinition;
|
|
|
63
63
|
* validate` and Studio Web's property panel refuse that). So the emitter writes
|
|
64
64
|
* the defaults and the node reads them back, rather than the two drifting.
|
|
65
65
|
*/
|
|
66
|
-
export function ixpExtractDef(spec) {
|
|
67
|
-
const parts = parseIxpProjectType(spec.project);
|
|
66
|
+
export function ixpExtractDef(spec, step = 'ixpExtract') {
|
|
67
|
+
const parts = typeof spec.project === 'string' ? parseIxpProjectType(spec.project) : undefined;
|
|
68
68
|
if (!parts) {
|
|
69
|
-
//
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
// Three conditions, three codes, each with its remedy.
|
|
70
|
+
const project = typeof spec.project === 'string' ? spec.project.trim() : '';
|
|
71
|
+
if (project === '') {
|
|
72
|
+
throw new CompileRefusal('IXP_NO_PROJECT', `IxP step "${step}" has no \`project\`. That is the extractor's whole node type, and it carries the `
|
|
73
|
+
+ `project version id and folder key the platform resolves the deployment by. Copy it from the registry `
|
|
74
|
+
+ `— never construct it: three different spellings of its tail exist in deployed flows.`, `project: 'uipath.ixp.<slug>.<projectVersionId>-<folderKey>' // uip maestro flow registry pull --force && uip maestro flow registry search -f "type:startsWith=uipath.ixp"`);
|
|
75
|
+
}
|
|
76
|
+
if (!project.startsWith('uipath.ixp.')) {
|
|
77
|
+
throw new CompileRefusal('IXP_PROJECT_NOT_NODE_TYPE', `IxP step "${step}"'s \`project\` is ${JSON.stringify(spec.project)}, which is not a \`uipath.ixp.*\` node `
|
|
78
|
+
+ `type. This field takes the type verbatim from \`registry search\`, not the project's name or id — the `
|
|
79
|
+
+ `name goes in \`name\` and the slug in \`modelName\`.`);
|
|
80
|
+
}
|
|
81
|
+
throw new CompileRefusal('IXP_PROJECT_TAIL_UNPARSEABLE', `IxP step "${step}"'s \`project\` ends in ${JSON.stringify(project.slice(project.lastIndexOf('.') + 1))}, which is `
|
|
82
|
+
+ `not the \`<projectVersionId>-<folderKey>\` pair of uuids the current registry serves. Two older spellings `
|
|
83
|
+
+ `exist in the corpus — a sanitized folder path, and the literal string "undefined" — and neither resolves `
|
|
84
|
+
+ `today. Re-read the type from \`registry search\` after \`registry pull --force\`.`);
|
|
75
85
|
}
|
|
76
86
|
const { versionId, folderKey } = parts;
|
|
77
87
|
const model = {
|
|
@@ -223,23 +233,9 @@ function checkIxpExtract(inputs, ctx) {
|
|
|
223
233
|
const step = ctx.step;
|
|
224
234
|
const at = (code, message, suggestion) => ctx.report(code, message, suggestion);
|
|
225
235
|
const blank = (v) => v === undefined || v === null || (typeof v === 'string' && v.trim() === '');
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
+ `project version id and folder key the platform resolves the deployment by. Copy it from the registry `
|
|
230
|
-
+ `— never construct it: three different spellings of its tail exist in deployed flows.`, `project: 'uipath.ixp.<slug>.<projectVersionId>-<folderKey>' // uip maestro flow registry pull --force && uip maestro flow registry search -f "type:startsWith=uipath.ixp"`);
|
|
231
|
-
}
|
|
232
|
-
else if (!project.startsWith('uipath.ixp.')) {
|
|
233
|
-
at('IXP_PROJECT_NOT_NODE_TYPE', `IxP step "${step}"'s \`project\` is ${JSON.stringify(inputs.project)}, which is not a \`uipath.ixp.*\` node `
|
|
234
|
-
+ `type. This field takes the type verbatim from \`registry search\`, not the project's name or id — the `
|
|
235
|
-
+ `name goes in \`name\` and the slug in \`modelName\`.`);
|
|
236
|
-
}
|
|
237
|
-
else if (parseIxpProjectType(project) === undefined) {
|
|
238
|
-
at('IXP_PROJECT_TAIL_UNPARSEABLE', `IxP step "${step}"'s \`project\` ends in ${JSON.stringify(project.slice(project.lastIndexOf('.') + 1))}, which is `
|
|
239
|
-
+ `not the \`<projectVersionId>-<folderKey>\` pair of uuids the current registry serves. Two older spellings `
|
|
240
|
-
+ `exist in the corpus — a sanitized folder path, and the literal string "undefined" — and neither resolves `
|
|
241
|
-
+ `today. Re-read the type from \`registry search\` after \`registry pull --force\`.`);
|
|
242
|
-
}
|
|
236
|
+
// A `project` that is missing, not a `uipath.ixp.*` type, or whose tail does
|
|
237
|
+
// not parse is the serializer's IXP_NO_PROJECT / IXP_PROJECT_NOT_NODE_TYPE /
|
|
238
|
+
// IXP_PROJECT_TAIL_UNPARSEABLE.
|
|
243
239
|
if (blank(inputs?.modelName)) {
|
|
244
240
|
at('IXP_NO_MODEL_NAME', `IxP step "${step}" has no \`modelName\`. The definition marks it required (\`minLength: 1\`) and the `
|
|
245
241
|
+ `platform's \`ixp-node\` validator refuses an empty one — it is the project's own name as the tenant `
|
|
@@ -273,7 +269,7 @@ function checkIxpExtract(inputs, ctx) {
|
|
|
273
269
|
* author named, and the fixed output pair.
|
|
274
270
|
*/
|
|
275
271
|
function serializeIxpExtract(s, ctx) {
|
|
276
|
-
const def = ixpExtractDef(s); // throws on an unparseable project
|
|
272
|
+
const def = ixpExtractDef(s, ctx.step); // throws on an unparseable project
|
|
277
273
|
const { versionId } = parseIxpProjectType(s.project); // ixpExtractDef threw if it could not parse
|
|
278
274
|
// The bindings first: two steps on the same project share ONE pair, and the
|
|
279
275
|
// definition's `model.bindings.resourceKey` names the key they hang off.
|
|
@@ -88,15 +88,10 @@ export function checkHitl(inputs, ctx) {
|
|
|
88
88
|
// Per-outcome exits are not a version question: quick-form draws one handle
|
|
89
89
|
// per outcome like the base node, so it may ask for them; the two app-backed
|
|
90
90
|
// variants read their outcomes from the app and leave on `completed`.
|
|
91
|
+
// Asking a variant for either is the serializer's HITL_OUTCOME_PORTS_VARIANT;
|
|
92
|
+
// the rules below assume a task that can have what it asks for.
|
|
91
93
|
if (variant !== undefined
|
|
92
94
|
&& (inputs?.exposeError === true || (inputs?.outcomePorts === true && !hitlDerivesOutcomeHandles(variant)))) {
|
|
93
|
-
at('HITL_OUTCOME_PORTS_VARIANT', inputs?.exposeError === true
|
|
94
|
-
? `Human task "${step}" asks for exposeError, but its variant is "${variant}" — the error output `
|
|
95
|
-
+ `exists only on the base node type's 1.2 definition.`
|
|
96
|
-
: `Human task "${step}" asks for outcomePorts, but its variant is "${variant}", which reads its `
|
|
97
|
-
+ `outcomes from its app and leaves on its single 'completed' exit.`, inputs?.exposeError === true
|
|
98
|
-
? 'Omit `variant`, or drop exposeError.'
|
|
99
|
-
: `Route on out('${step}', 'Action') downstream.`);
|
|
100
95
|
return;
|
|
101
96
|
}
|
|
102
97
|
// `legacyCompleted: true` keeps a pre-#3512 file's `completed` edge (#1197).
|
|
@@ -104,11 +99,9 @@ export function checkHitl(inputs, ctx) {
|
|
|
104
99
|
// rarely what the author wants, and `flow validate` says nothing about it.
|
|
105
100
|
if (inputs?.legacyCompleted === true) {
|
|
106
101
|
const outcomes = Array.isArray(inputs?.outcomes) ? inputs.outcomes : [];
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
+ `${inputs?.exposeError === true ? 'exposeError' : 'outcomePorts'}, which asks for per-outcome exits.`, 'Drop `legacyCompleted`.');
|
|
102
|
+
// With outcomePorts or exposeError too it is the serializer's HITL_LEGACY_COMPLETED_CONFLICT.
|
|
103
|
+
if (inputs?.outcomePorts !== undefined || inputs?.exposeError === true)
|
|
110
104
|
return;
|
|
111
|
-
}
|
|
112
105
|
if (!hitlDerivesOutcomeHandles(variant) || outcomes.length === 0) {
|
|
113
106
|
// Its own code: this flow compiles, so the code is check's alone.
|
|
114
107
|
at('HITL_LEGACY_COMPLETED_NO_EFFECT', `Human task "${step}" sets legacyCompleted: true, but ${outcomes.length === 0 ? 'it has no outcomes'
|
|
@@ -579,16 +579,16 @@ function hitlAt(typeName, version, factory) {
|
|
|
579
579
|
const pinned = ctx.pinnedVersion;
|
|
580
580
|
const exitModel = hitlExitModel(inputs, pinned);
|
|
581
581
|
if (typeName !== BASE && inputs?.exposeError === true) {
|
|
582
|
-
throw new CompileRefusal('HITL_OUTCOME_PORTS_VARIANT', `"${ctx.step}"
|
|
583
|
-
+ `
|
|
582
|
+
throw new CompileRefusal('HITL_OUTCOME_PORTS_VARIANT', `Human task "${ctx.step}" asks for exposeError, but its variant is "${inputs?.variant}" — the error output `
|
|
583
|
+
+ `exists only on the base node type's 1.2 definition.`, 'Omit `variant`, or drop exposeError.');
|
|
584
584
|
}
|
|
585
585
|
if (inputs?.outcomePorts === true && !OUTCOME_HANDLE_NODE_TYPES.has(typeName)) {
|
|
586
|
-
throw new CompileRefusal('HITL_OUTCOME_PORTS_VARIANT', `"${ctx.step}"
|
|
587
|
-
+ `from its app and leaves on its single 'completed' exit
|
|
586
|
+
throw new CompileRefusal('HITL_OUTCOME_PORTS_VARIANT', `Human task "${ctx.step}" asks for outcomePorts, but its variant is "${inputs?.variant}", which reads its `
|
|
587
|
+
+ `outcomes from its app and leaves on its single 'completed' exit.`, `Route on out('${ctx.step}', 'Action') downstream.`);
|
|
588
588
|
}
|
|
589
589
|
if (inputs?.legacyCompleted === true && (inputs?.outcomePorts !== undefined || inputs?.exposeError === true)) {
|
|
590
|
-
throw new CompileRefusal('HITL_LEGACY_COMPLETED_CONFLICT', `"${ctx.step}"
|
|
591
|
-
+ `${inputs?.exposeError === true ? '
|
|
590
|
+
throw new CompileRefusal('HITL_LEGACY_COMPLETED_CONFLICT', `Human task "${ctx.step}" sets legacyCompleted: true, which leaves it on its single 'completed' handle, and also `
|
|
591
|
+
+ `${inputs?.exposeError === true ? 'exposeError' : 'outcomePorts'}, which asks for per-outcome exits.`, 'Drop `legacyCompleted`.');
|
|
592
592
|
}
|
|
593
593
|
if (inputs?.legacyCompleted === true && typeName === BASE && (pinned === '1.1' || pinned === '1.2')) {
|
|
594
594
|
throw new CompileRefusal('HITL_LEGACY_COMPLETED_VERSION', `"${ctx.step}": legacyCompleted: true, but { version: '${pinned}' } pins the task's per-outcome definition, `
|
|
@@ -615,7 +615,16 @@ function hitlAt(typeName, version, factory) {
|
|
|
615
615
|
// Every human-task definition declares one static source handle,
|
|
616
616
|
// `completed`: the real exit on an app-backed task and on one with no
|
|
617
617
|
// outcomes, and the author's explicit choice with `legacyCompleted: true`.
|
|
618
|
-
|
|
618
|
+
const outcomes = Array.isArray(inputs?.outcomes) ? inputs.outcomes : [];
|
|
619
|
+
const noOutcomePorts = {
|
|
620
|
+
why: outcomes.length === 0 ? 'it declares no outcomes'
|
|
621
|
+
: exitModel.legacyCompleted ? 'it sets legacyCompleted: true'
|
|
622
|
+
: `the '${String(inputs?.variant)}' variant reads its outcomes from its app`,
|
|
623
|
+
suggestion: exitModel.legacyCompleted
|
|
624
|
+
? 'drop `legacyCompleted` (and set outcomePorts: true to route each outcome)'
|
|
625
|
+
: `route on out('${ctx.step}', 'Action') downstream`,
|
|
626
|
+
};
|
|
627
|
+
return { ...contribution, port: 'completed', noOutcomePorts, ...(exitModel.legacyCompleted ? { legacyCompleted: true } : {}) };
|
|
619
628
|
},
|
|
620
629
|
deserialize: (node, ctx) => liftHitl(node, typeName, ctx),
|
|
621
630
|
print: printHitl,
|
|
@@ -88,27 +88,9 @@ export function checkHttp(inputs, ctx) {
|
|
|
88
88
|
+ `Integration Service connection or an .onError() handler.`);
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const bindingConfigured = connection !== undefined || folder !== undefined;
|
|
95
|
-
const targetConfigured = targetConnector !== undefined;
|
|
96
|
-
const nonEmpty = (value) => typeof value === 'string' && value.trim().length > 0;
|
|
97
|
-
if (!inputs?.managed && (bindingConfigured || targetConfigured)) {
|
|
98
|
-
ctx.report('HTTP_BINDING_REQUIRES_MANAGED', `Step "${step}" supplies Integration Service connection settings to the standalone HTTP node. `
|
|
99
|
-
+ `Only the managed node can use connection, folder, or targetConnector.`, `managed: true`);
|
|
100
|
-
}
|
|
101
|
-
else if (inputs?.managed && bindingConfigured && (!nonEmpty(connection) || !nonEmpty(folder))) {
|
|
102
|
-
ctx.report('HTTP_MANAGED_BINDING_INCOMPLETE', `Managed HTTP step "${step}" must name both a non-empty \`connection\` and \`folder\`. The platform `
|
|
103
|
-
+ `needs the pair to resolve the Integration Service connection at runtime.`, `connection: 'http', folder: 'shared'`);
|
|
104
|
-
}
|
|
105
|
-
if (inputs?.managed && targetConfigured && !nonEmpty(targetConnector)) {
|
|
106
|
-
ctx.report('HTTP_TARGET_CONNECTOR_EMPTY', `Managed HTTP step "${step}" sets an empty \`targetConnector\`; name its Integration Service connector key.`, `targetConnector: 'uipath-salesforce-slack'`);
|
|
107
|
-
}
|
|
108
|
-
else if (inputs?.managed && targetConfigured && !bindingConfigured) {
|
|
109
|
-
ctx.report('HTTP_TARGET_CONNECTOR_REQUIRES_BINDING', `Managed HTTP step "${step}" sets \`targetConnector\` without a connection and folder. `
|
|
110
|
-
+ `A connector-specific fallback needs the binding pair whose authentication it reuses.`, `connection: 'slack', folder: 'shared'`);
|
|
111
|
-
}
|
|
91
|
+
// Connection settings on the standalone node, an incomplete pair, and an
|
|
92
|
+
// empty or unbound `targetConnector` are the serializer's HTTP_BINDING_* /
|
|
93
|
+
// HTTP_MANAGED_BINDING_INCOMPLETE / HTTP_TARGET_CONNECTOR_*.
|
|
112
94
|
const timeout = inputs?.timeout;
|
|
113
95
|
if (timeout !== undefined) {
|
|
114
96
|
if (typeof timeout !== 'string' || !ISO8601_DURATION.test(timeout.trim())) {
|
|
@@ -138,16 +120,8 @@ export function checkHttp(inputs, ctx) {
|
|
|
138
120
|
export function checkHttpStep(inputs, ctx) {
|
|
139
121
|
const step = ctx.step;
|
|
140
122
|
checkHttp(inputs, ctx);
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
const pair = inputs;
|
|
144
|
-
const filled = (v) => typeof v === 'string' && v.trim() !== '';
|
|
145
|
-
if (pair?.managed && filled(pair.connection) && filled(pair.folder)) {
|
|
146
|
-
const target = pair.targetConnector;
|
|
147
|
-
ctx.bindingLabels(`managed http "${step}"`, pair,
|
|
148
|
-
// The connector its CONNECTION belongs to: the target, not the wrapper.
|
|
149
|
-
{ id: step, connector: typeof target === 'string' && target !== '' ? target : 'uipath-uipath-http' });
|
|
150
|
-
}
|
|
123
|
+
// A managed step's binding labels are the serializer's (BINDING_*), as a
|
|
124
|
+
// connector step's are.
|
|
151
125
|
const seen = new Set();
|
|
152
126
|
for (const b of inputs?.branches ?? []) {
|
|
153
127
|
if (!/^[A-Za-z0-9_-]+$/.test(b.name)) {
|