@uipath/maestro-builder-sdk 6.14.3 → 6.14.5
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 +58 -58
- package/dist/api-members.md +99 -96
- package/dist/bindings-v2.d.ts +26 -0
- package/dist/bindings-v2.js +111 -7
- package/dist/check.js +80 -9
- package/dist/compile-cli.js +14 -8
- package/dist/core/actions.d.ts +19 -0
- package/dist/core/binding-messages.d.ts +16 -1
- package/dist/core/binding-messages.js +21 -0
- package/dist/serialize.d.ts +28 -0
- package/dist/serialize.js +345 -12
- package/lib/README.md +1 -0
- package/lib/library-json.samples/index.json +9 -0
- package/lib/library-json.samples/uipath-uipath-airdk/web-search@1.0.0.json +118 -0
- package/lib/library-json.samples/uipath-uipath-airdk/web-search@1.0.0.v1def.json +258 -0
- package/package.json +2 -2
package/dist/bindings-v2.js
CHANGED
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
* the connection rows.
|
|
43
43
|
*/
|
|
44
44
|
import { existsSync, readFileSync } from 'node:fs';
|
|
45
|
+
import { BindingRefusal, connectionConnectorConflictMessage } from './core/binding-messages.js';
|
|
45
46
|
/** The document version pack emits. */
|
|
46
47
|
const DOCUMENT_VERSION = '2.0';
|
|
47
48
|
/** The per-resource metadata version pack stamps. */
|
|
@@ -56,6 +57,93 @@ function str(value) {
|
|
|
56
57
|
function isConnection(resource) {
|
|
57
58
|
return typeof resource === 'string' && resource.toLowerCase() === 'connection';
|
|
58
59
|
}
|
|
60
|
+
/** The managed-HTTP wrapper's own connector key — never the connection's type when a target is named. */
|
|
61
|
+
const MANAGED_HTTP_CONNECTOR = 'uipath-uipath-http';
|
|
62
|
+
function nodeDetail(node) {
|
|
63
|
+
return isRecord(node.inputs) && isRecord(node.inputs.detail) ? node.inputs.detail : undefined;
|
|
64
|
+
}
|
|
65
|
+
/** The connection a node runs on: `connectionId`, else `connectionResourceId`. */
|
|
66
|
+
function nodeConnectionKey(detail) {
|
|
67
|
+
return str(detail.connectionId) ?? str(detail.connectionResourceId);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The connector a node's CONNECTION belongs to — not the connector the node is.
|
|
71
|
+
*
|
|
72
|
+
* The two differ for managed HTTP: its `detail.connector` is always the wrapper,
|
|
73
|
+
* `uipath-uipath-http`, and the connection it authenticates with is the one
|
|
74
|
+
* `bodyParameters.targetConnector` names (`uipath-google-drive`,
|
|
75
|
+
* `uipath-salesforce-slack`, …). Stamping the wrapper made `uip solution
|
|
76
|
+
* resources refresh` refuse the connection — "Requested type
|
|
77
|
+
* 'uipath-uipath-http'; the resource is registered as type
|
|
78
|
+
* 'uipath-google-drive'" (flow-builder-sdk#866). Every other family's
|
|
79
|
+
* `detail.connector` is its connection's connector.
|
|
80
|
+
*/
|
|
81
|
+
function nodeConnectionConnector(detail) {
|
|
82
|
+
const connector = str(detail.connector);
|
|
83
|
+
if (connector === MANAGED_HTTP_CONNECTOR && isRecord(detail.bodyParameters)) {
|
|
84
|
+
return str(detail.bodyParameters.targetConnector) ?? connector;
|
|
85
|
+
}
|
|
86
|
+
return connector;
|
|
87
|
+
}
|
|
88
|
+
/** Every node of the flow and of its inline subflows — they share one `bindings[]`. */
|
|
89
|
+
function allNodes(flow) {
|
|
90
|
+
const nodes = Array.isArray(flow.nodes) ? flow.nodes.filter(isRecord) : [];
|
|
91
|
+
if (isRecord(flow.subflows)) {
|
|
92
|
+
for (const sub of Object.values(flow.subflows)) {
|
|
93
|
+
if (isRecord(sub) && Array.isArray(sub.nodes))
|
|
94
|
+
nodes.push(...sub.nodes.filter(isRecord));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return nodes;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Which connector each bound connection belongs to, keyed by connection id —
|
|
101
|
+
* decided by EVERY node on the connection, so the answer does not depend on
|
|
102
|
+
* which step comes first.
|
|
103
|
+
*
|
|
104
|
+
* `bindings.json` cannot answer this: `prepare --connection-id` writes a label,
|
|
105
|
+
* a resource key and a kind (`ConnectionId` / `FolderKey`), never the connector
|
|
106
|
+
* (see `core/binding-messages.ts`). The nodes are the only record of it, so
|
|
107
|
+
* all of them must agree. Two that do not — a Drive step and a Slack step on
|
|
108
|
+
* one connection, or managed HTTP whose `targetConnector` is not the connection's
|
|
109
|
+
* connector — are refused with `CONNECTION_CONNECTOR_CONFLICT`: at most one of
|
|
110
|
+
* them can run, and picking the first silently mislabels the solution resource.
|
|
111
|
+
*
|
|
112
|
+
* @throws BindingRefusal when two nodes on one connection name different connectors.
|
|
113
|
+
*/
|
|
114
|
+
export function connectionConnectors(flow) {
|
|
115
|
+
const out = new Map();
|
|
116
|
+
if (!isRecord(flow))
|
|
117
|
+
return out;
|
|
118
|
+
const bound = new Set();
|
|
119
|
+
for (const b of Array.isArray(flow.bindings) ? flow.bindings.filter(isRecord) : []) {
|
|
120
|
+
const key = str(b.resourceKey);
|
|
121
|
+
if (key && isConnection(b.resource) && str(b.propertyAttribute)?.toLowerCase() === 'connectionid')
|
|
122
|
+
bound.add(key);
|
|
123
|
+
}
|
|
124
|
+
const users = new Map();
|
|
125
|
+
for (const node of allNodes(flow)) {
|
|
126
|
+
const detail = nodeDetail(node);
|
|
127
|
+
const key = detail && nodeConnectionKey(detail);
|
|
128
|
+
if (!detail || !key || !bound.has(key))
|
|
129
|
+
continue;
|
|
130
|
+
const connector = nodeConnectionConnector(detail);
|
|
131
|
+
if (!connector)
|
|
132
|
+
continue;
|
|
133
|
+
const byConnector = users.get(key) ?? new Map();
|
|
134
|
+
users.set(key, byConnector);
|
|
135
|
+
const steps = byConnector.get(connector) ?? [];
|
|
136
|
+
steps.push((isRecord(node.display) ? str(node.display.label) : undefined) ?? str(node.id) ?? '?');
|
|
137
|
+
byConnector.set(connector, steps);
|
|
138
|
+
}
|
|
139
|
+
for (const [key, byConnector] of users) {
|
|
140
|
+
if (byConnector.size > 1) {
|
|
141
|
+
throw new BindingRefusal('CONNECTION_CONNECTOR_CONFLICT', connectionConnectorConflictMessage(key, byConnector));
|
|
142
|
+
}
|
|
143
|
+
out.set(key, [...byConnector.keys()][0]);
|
|
144
|
+
}
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
59
147
|
/**
|
|
60
148
|
* The `connection` resources a compiled flow declares — one per distinct
|
|
61
149
|
* connection, in node order, attributed to the FIRST node that uses it.
|
|
@@ -63,6 +151,16 @@ function isConnection(resource) {
|
|
|
63
151
|
* Both halves of that are pack's behaviour, not a choice made here: a flow with
|
|
64
152
|
* two Outlook steps on one connection yields a single resource whose
|
|
65
153
|
* `ActivityName` is the earlier step's.
|
|
154
|
+
*
|
|
155
|
+
* What the resource IS does not follow node order: its `Connector` comes from
|
|
156
|
+
* {@link connectionConnectors} (every node on the connection, managed HTTP read
|
|
157
|
+
* through its `targetConnector`). Pack stamps the same value — measured with
|
|
158
|
+
* `uip maestro flow pack` on managed HTTP → Slack alone, before a native Slack
|
|
159
|
+
* step, and after one: `uipath-salesforce-slack` all three times, where this
|
|
160
|
+
* module used to stamp `uipath-uipath-http` for the first two (#866;
|
|
161
|
+
* uip 1.202.0-dev, 2026-09-30).
|
|
162
|
+
*
|
|
163
|
+
* @throws BindingRefusal when two nodes on one connection name different connectors.
|
|
66
164
|
*/
|
|
67
165
|
export function connectionResources(flow) {
|
|
68
166
|
if (!isRecord(flow))
|
|
@@ -78,25 +176,31 @@ export function connectionResources(flow) {
|
|
|
78
176
|
continue;
|
|
79
177
|
if (str(binding.propertyAttribute)?.toLowerCase() !== 'connectionid')
|
|
80
178
|
continue;
|
|
81
|
-
|
|
82
|
-
byKey.set(key, binding);
|
|
179
|
+
byKey.set(key, [...(byKey.get(key) ?? []), binding]);
|
|
83
180
|
}
|
|
84
181
|
if (byKey.size === 0)
|
|
85
182
|
return [];
|
|
183
|
+
const connectors = connectionConnectors(flow);
|
|
86
184
|
const nodes = Array.isArray(flow.nodes) ? flow.nodes.filter(isRecord) : [];
|
|
87
185
|
const resources = [];
|
|
88
186
|
const seen = new Set();
|
|
89
187
|
for (const node of nodes) {
|
|
90
|
-
const detail =
|
|
188
|
+
const detail = nodeDetail(node);
|
|
91
189
|
if (!detail)
|
|
92
190
|
continue;
|
|
93
|
-
const key =
|
|
191
|
+
const key = nodeConnectionKey(detail);
|
|
94
192
|
if (!key || seen.has(key))
|
|
95
193
|
continue;
|
|
96
|
-
const
|
|
97
|
-
if (!
|
|
194
|
+
const candidates = byKey.get(key);
|
|
195
|
+
if (!candidates)
|
|
98
196
|
continue;
|
|
99
197
|
seen.add(key);
|
|
198
|
+
const connector = connectors.get(key) ?? nodeConnectionConnector(detail) ?? '';
|
|
199
|
+
// The label is the FIRST ConnectionId binding on the connection, even when
|
|
200
|
+
// #865's fix emits one per referencing definition — measured against `uip
|
|
201
|
+
// maestro flow pack` (uip 1.202.0-dev) on both orders of the issue's repro: pack labels
|
|
202
|
+
// the resource after the earlier step's binding, like `ActivityName`.
|
|
203
|
+
const binding = candidates[0];
|
|
100
204
|
const displayLabel = str(binding.name) ?? key;
|
|
101
205
|
const label = isRecord(node.display) ? str(node.display.label) : undefined;
|
|
102
206
|
resources.push({
|
|
@@ -116,7 +220,7 @@ export function connectionResources(flow) {
|
|
|
116
220
|
BindingsVersion: BINDINGS_VERSION,
|
|
117
221
|
DisplayLabel: displayLabel,
|
|
118
222
|
UseConnectionService: 'true',
|
|
119
|
-
Connector:
|
|
223
|
+
Connector: connector,
|
|
120
224
|
},
|
|
121
225
|
});
|
|
122
226
|
}
|
package/dist/check.js
CHANGED
|
@@ -11,7 +11,7 @@ import { isLookupToken, resolvedValue, unresolvedLookupMessage, } from './core/l
|
|
|
11
11
|
import { QUEUE_ITEM_RECORD_SCHEMA } from './core/queue-record.js';
|
|
12
12
|
import { EVENT_FILTER_OPERATORS, readEventFilter, eventFilterProblem } from './event-filters.js';
|
|
13
13
|
import { extractRefs } from './core/expr-check.js';
|
|
14
|
-
import { bindingSelfNameMessage, bindingUndeclaredMessage, undeclaredBinding } from './core/binding-messages.js';
|
|
14
|
+
import { bindingSelfNameMessage, bindingUndeclaredMessage, connectionConnectorConflictMessage, undeclaredBinding } from './core/binding-messages.js';
|
|
15
15
|
import { isAnswerField } from './core/hitl-answers.js';
|
|
16
16
|
import { hitlDerivesOutcomeHandles, hitlExitModel, hitlRoutesPerOutcome } from './core/hitl-routing.js';
|
|
17
17
|
import { childStepLists, declaredExits, hasExits, stopsAtEnd } from './core/step-ports.js';
|
|
@@ -20,7 +20,51 @@ import { compareSchemaVersions, schemaVersionRefusal } from './schema-version.js
|
|
|
20
20
|
import { prepareCommand, resolveFlagArg } from './core/cli-spelling.js';
|
|
21
21
|
import { EVENT_BYOA_UNVERIFIED, byoaUnverified } from './core/byoa.js';
|
|
22
22
|
export function check(built, opts = {}) {
|
|
23
|
-
|
|
23
|
+
const outer = connectionUsage;
|
|
24
|
+
connectionUsage = [];
|
|
25
|
+
try {
|
|
26
|
+
const diags = checkFlow(built, [], opts);
|
|
27
|
+
diags.push(...connectionConnectorConflicts(connectionUsage, opts));
|
|
28
|
+
return diags;
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
connectionUsage = outer;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The connection uses {@link checkBindingLabels} saw during the current
|
|
36
|
+
* `check()` — the flow and every child flow, which share one `bindings[]`.
|
|
37
|
+
* Module state rather than a `CheckOpts` field because `check` is synchronous
|
|
38
|
+
* and this is not an input; `check()` saves and restores it, so a nested call
|
|
39
|
+
* cannot mix two flows.
|
|
40
|
+
*/
|
|
41
|
+
let connectionUsage;
|
|
42
|
+
/**
|
|
43
|
+
* CONNECTION_CONNECTOR_CONFLICT — the rule `serialize` enforces on the emitted
|
|
44
|
+
* document (`connectionConnectors`, bindings-v2.ts), reported here with the same
|
|
45
|
+
* sentence so `check` does not print `✓ no issues` for a source `compile` then
|
|
46
|
+
* refuses. Labels are grouped by what they resolve to, so two labels bound to
|
|
47
|
+
* one connection id are one connection, as they are in the `.flow`.
|
|
48
|
+
*/
|
|
49
|
+
function connectionConnectorConflicts(uses, opts) {
|
|
50
|
+
const byConnection = new Map();
|
|
51
|
+
for (const use of uses) {
|
|
52
|
+
const key = opts.bindings?.resolve(use.label) ?? use.label;
|
|
53
|
+
const byConnector = byConnection.get(key) ?? new Map();
|
|
54
|
+
byConnection.set(key, byConnector);
|
|
55
|
+
byConnector.set(use.connector, [...(byConnector.get(use.connector) ?? []), use.step]);
|
|
56
|
+
}
|
|
57
|
+
const out = [];
|
|
58
|
+
for (const [key, byConnector] of byConnection) {
|
|
59
|
+
if (byConnector.size < 2)
|
|
60
|
+
continue;
|
|
61
|
+
out.push({
|
|
62
|
+
level: 'error', code: 'CONNECTION_CONNECTOR_CONFLICT',
|
|
63
|
+
step: [...byConnector.values()][1][0],
|
|
64
|
+
message: connectionConnectorConflictMessage(key, byConnector),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
24
68
|
}
|
|
25
69
|
/**
|
|
26
70
|
* `ancestors` is the chain of child flows currently being checked, innermost
|
|
@@ -358,7 +402,10 @@ function checkFlow(built, ancestors, opts = {}) {
|
|
|
358
402
|
const filled = (v) => typeof v === 'string' && v.trim() !== '';
|
|
359
403
|
if (pair?.managed && filled(pair.connection) && filled(pair.folder)) {
|
|
360
404
|
const httpStep = s.name;
|
|
361
|
-
|
|
405
|
+
const target = pair.targetConnector;
|
|
406
|
+
checkBindingLabels(`managed http "${httpStep}"`, pair, opts, (code, message) => diags.push({ level: 'error', code, step: httpStep, message }), undefined,
|
|
407
|
+
// The connector its CONNECTION belongs to: the target, not the wrapper.
|
|
408
|
+
{ step: httpStep, connector: typeof target === 'string' && target !== '' ? target : 'uipath-uipath-http' });
|
|
362
409
|
}
|
|
363
410
|
const seen = new Set();
|
|
364
411
|
for (const b of s.spec.inputs.branches ?? []) {
|
|
@@ -450,7 +497,7 @@ function checkFlow(built, ancestors, opts = {}) {
|
|
|
450
497
|
checkAgent(s.name, s.spec.inputs, diags);
|
|
451
498
|
}
|
|
452
499
|
if (s.spec.kind === 'inlineAgent') {
|
|
453
|
-
checkInlineAgent(s.name, s.spec.inputs, diags);
|
|
500
|
+
checkInlineAgent(s.name, s.spec.inputs, diags, opts);
|
|
454
501
|
}
|
|
455
502
|
if (s.spec.kind === 'queueItem')
|
|
456
503
|
checkQueueItem(s.name, s.spec.inputs, diags);
|
|
@@ -537,7 +584,7 @@ function checkFlow(built, ancestors, opts = {}) {
|
|
|
537
584
|
});
|
|
538
585
|
checkEventSubscription(s.spec.subscription, `waitForEvent "${stepName}"`, push('error'), push('warning'), eventParamsFor(s.spec.subscription, opts));
|
|
539
586
|
checkEventResolution(s.spec.subscription, `waitForEvent "${stepName}"`, 'event', opts, push('error'), push('warning'), eventParamsFor(s.spec.subscription, opts));
|
|
540
|
-
checkBindingLabels(`waitForEvent "${stepName}"`, s.spec.subscription, opts, push('error'), eventTarget(s.spec.subscription));
|
|
587
|
+
checkBindingLabels(`waitForEvent "${stepName}"`, s.spec.subscription, opts, push('error'), eventTarget(s.spec.subscription), { step: stepName, connector: eventTarget(s.spec.subscription)?.key });
|
|
541
588
|
}
|
|
542
589
|
if (s.spec.kind === 'subflow') {
|
|
543
590
|
// Only a real child goes in the map: a missing one is reported by
|
|
@@ -1453,7 +1500,7 @@ function checkConnectorContract(step, spec, opts, diags) {
|
|
|
1453
1500
|
if (inputErr) {
|
|
1454
1501
|
diags.push({ level: 'error', code: 'CONNECTOR_INPUT', step, message: `Connector step "${step}": ${inputErr}` });
|
|
1455
1502
|
}
|
|
1456
|
-
checkBindingLabels(`Connector step "${step}"`, spec, opts, (code, message) => diags.push({ level: 'error', code, step, message }), { key: spec.key, action: spec.action });
|
|
1503
|
+
checkBindingLabels(`Connector step "${step}"`, spec, opts, (code, message) => diags.push({ level: 'error', code, step, message }), { key: spec.key, action: spec.action }, { step, connector: spec.key });
|
|
1457
1504
|
}
|
|
1458
1505
|
/** What `registry prepare` takes for an event subscription: its connector key and curated event name. */
|
|
1459
1506
|
function eventTarget(sub) {
|
|
@@ -1472,7 +1519,13 @@ function eventTarget(sub) {
|
|
|
1472
1519
|
* Silent without a bindings.json, and for a label that resolves to anything
|
|
1473
1520
|
* other than its own name (stub values are compile's CONNECTION_STUB warning).
|
|
1474
1521
|
*/
|
|
1475
|
-
function checkBindingLabels(where, labels, opts, report, target
|
|
1522
|
+
function checkBindingLabels(where, labels, opts, report, target,
|
|
1523
|
+
/** The step, and the connector its connection must belong to, for CONNECTION_CONNECTOR_CONFLICT. */
|
|
1524
|
+
use) {
|
|
1525
|
+
const label = labels?.connection;
|
|
1526
|
+
if (use?.connector && typeof label === 'string' && label !== '') {
|
|
1527
|
+
connectionUsage?.push({ step: use.step, label, connector: use.connector });
|
|
1528
|
+
}
|
|
1476
1529
|
const bindings = opts.bindings;
|
|
1477
1530
|
if (!bindings || !labels)
|
|
1478
1531
|
return;
|
|
@@ -2285,7 +2338,7 @@ function checkTrigger(spec, diags, opts = {}) {
|
|
|
2285
2338
|
const sub = spec.subscription;
|
|
2286
2339
|
checkEventSubscription(sub, 'trigger', at, push('warning'), eventParamsFor(sub, opts));
|
|
2287
2340
|
checkEventResolution(sub, 'trigger', 'trigger', opts, at, push('warning'), eventParamsFor(sub, opts));
|
|
2288
|
-
checkBindingLabels('event trigger', sub, opts, at, eventTarget(sub));
|
|
2341
|
+
checkBindingLabels('event trigger', sub, opts, at, eventTarget(sub), { step: 'trigger', connector: eventTarget(sub)?.key });
|
|
2289
2342
|
return;
|
|
2290
2343
|
}
|
|
2291
2344
|
// manual() is the explicit spelling of the default — nothing to validate.
|
|
@@ -3457,7 +3510,7 @@ export function jsonTextDirective(text, returnKeys) {
|
|
|
3457
3510
|
* ALSO asks for JSON text makes the model pack its whole answer, serialized, into
|
|
3458
3511
|
* one string field. See {@link jsonTextDirective}.
|
|
3459
3512
|
*/
|
|
3460
|
-
function checkInlineAgent(step, inputs, diags) {
|
|
3513
|
+
function checkInlineAgent(step, inputs, diags, opts = {}) {
|
|
3461
3514
|
const at = (code, message, suggestion) => {
|
|
3462
3515
|
diags.push({ level: 'error', code, message, step, ...(suggestion ? { suggestion } : {}) });
|
|
3463
3516
|
};
|
|
@@ -3740,6 +3793,24 @@ function checkInlineAgent(step, inputs, diags) {
|
|
|
3740
3793
|
at('INLINE_AGENT_TOOL_NO_OPERATION', `Inline agent step "${step}" declares a connector tool without ${blank(c.connector) ? 'a `connector`' : 'an `operation`'}. `
|
|
3741
3794
|
+ `Both name the curated-library entry the tool is resolved from — the same pair \`connector(…)\` takes.`, `{ kind: 'connector', connector: 'uipath-atlassian-jira', operation: 'create-issue' }`);
|
|
3742
3795
|
}
|
|
3796
|
+
// A connector tool calls Integration Service on a CONNECTION, exactly as a
|
|
3797
|
+
// connector step does — and before #867 there was no way to give it one, so
|
|
3798
|
+
// the tool compiled with none anywhere (no binding, no resource.json row)
|
|
3799
|
+
// and could only fail at run time. Both halves, as `registry prepare` writes them.
|
|
3800
|
+
const labels = t;
|
|
3801
|
+
const missing = ['connection', 'folder'].filter((k) => blank(labels[k]));
|
|
3802
|
+
if (missing.length > 0) {
|
|
3803
|
+
const key = blank(c.connector) ? '<connector-key>' : String(c.connector);
|
|
3804
|
+
const op = blank(c.operation) ? '<operation>' : String(c.operation);
|
|
3805
|
+
at('INLINE_AGENT_TOOL_CONNECTOR_NO_CONNECTION', `Inline agent step "${step}"'s connector tool "${key}/${op}" has no ${missing.map((m) => `\`${m}\``).join(' or ')}. `
|
|
3806
|
+
+ `The tool calls Integration Service on a tenant connection: without one the agent's tool has no `
|
|
3807
|
+
+ `connection binding, its resource.json carries none, and the solution's Resources never list it. `
|
|
3808
|
+
+ `Name the bindings.json labels the same way \`connector()\` takes them — \`${prepareCommand(key, op)}\` `
|
|
3809
|
+
+ `finds the connection and writes both entries.`, `{ kind: 'connector', connector: '${key}', operation: '${op}', connection: '<connection label>', folder: '<folder label>' }`);
|
|
3810
|
+
}
|
|
3811
|
+
else {
|
|
3812
|
+
checkBindingLabels(`Inline agent step "${step}"'s connector tool`, labels, opts, (code, message) => at(code, message), blank(c.connector) ? undefined : { key: String(c.connector), ...(blank(c.operation) ? {} : { action: String(c.operation) }) });
|
|
3813
|
+
}
|
|
3743
3814
|
}
|
|
3744
3815
|
else if (kind === 'ixp') {
|
|
3745
3816
|
const x = t;
|
package/dist/compile-cli.js
CHANGED
|
@@ -36,20 +36,24 @@ function opt(argv, name) {
|
|
|
36
36
|
return i >= 0 ? argv[i + 1] : undefined;
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
|
-
* The
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
39
|
+
* The tool `type` values `compile` writes. NOT every `$resourceType: 'tool'`:
|
|
40
|
+
* the designer writes kinds the SDK cannot derive into the same directory, so
|
|
41
|
+
* pruning by `$resourceType` alone would delete theirs.
|
|
42
|
+
*
|
|
43
|
+
* `integration` (connector tools) is compile's since #867: compile writes the
|
|
44
|
+
* file under the tool node's own `source` id, the same id and content `uip agent
|
|
45
|
+
* refresh --inline-in-flow` derives from that node, so a file under any other id
|
|
46
|
+
* belongs to a tool node the flow no longer has.
|
|
43
47
|
*/
|
|
44
48
|
const COMPILE_OWNED_TOOL_TYPES = new Set([
|
|
45
|
-
'process', 'api', 'processOrchestration', 'flow', 'agent', 'function',
|
|
49
|
+
'process', 'api', 'processOrchestration', 'flow', 'agent', 'function', 'integration',
|
|
46
50
|
]);
|
|
47
51
|
/**
|
|
48
52
|
* Does `compile` own this resource file — i.e. would this run have written it,
|
|
49
53
|
* so an unwritten one is stale rather than someone else's?
|
|
50
54
|
*
|
|
51
|
-
* Everything it says no to belongs to
|
|
52
|
-
*
|
|
55
|
+
* Everything it says no to belongs to the designer (escalation, mcp, and the
|
|
56
|
+
* kinds the SDK cannot derive).
|
|
53
57
|
*/
|
|
54
58
|
function compileOwnsResource(body) {
|
|
55
59
|
const kind = body?.$resourceType;
|
|
@@ -236,7 +240,9 @@ export async function run(argv) {
|
|
|
236
240
|
// fail on it. Keyed the way refresh keys it — an index by its name.
|
|
237
241
|
const named = body?.indexName ?? body?.name;
|
|
238
242
|
const key = typeof named === 'string' && named !== '' ? named : undefined;
|
|
239
|
-
|
|
243
|
+
// A connector tool's binding is a `connection` row, and `mergeBindingsV2`
|
|
244
|
+
// re-derives every connection row from the flow below — nothing to retract.
|
|
245
|
+
if (key !== undefined && body?.type !== 'integration') {
|
|
240
246
|
// Keyed the way refresh keys it: an index by its name, a deployed-resource
|
|
241
247
|
// tool under `process` whatever its own type says.
|
|
242
248
|
staleBindings.push({ resource: resourceType === 'context' ? 'index' : 'process', key });
|
package/dist/core/actions.d.ts
CHANGED
|
@@ -1806,6 +1806,25 @@ export interface ConnectorToolRef {
|
|
|
1806
1806
|
object?: string;
|
|
1807
1807
|
/** Display label; defaults to the library's own (e.g. `'Create Issue'`). */
|
|
1808
1808
|
name?: string;
|
|
1809
|
+
/**
|
|
1810
|
+
* What the agent is told the tool does — the runtime hands it to the MODEL.
|
|
1811
|
+
* Defaults to the library's own operation description.
|
|
1812
|
+
*/
|
|
1813
|
+
description?: string;
|
|
1814
|
+
/**
|
|
1815
|
+
* The connection's binding NAME in `bindings.json` — the same label
|
|
1816
|
+
* `connector()`'s `connection` option takes (e.g. `'genai'`), resolved to
|
|
1817
|
+
* the tenant connection id at compile. Required: a connector tool with no
|
|
1818
|
+
* connection has nothing to call Integration Service with, and `check`
|
|
1819
|
+
* reports `INLINE_AGENT_TOOL_CONNECTOR_NO_CONNECTION`.
|
|
1820
|
+
*/
|
|
1821
|
+
connection?: string;
|
|
1822
|
+
/**
|
|
1823
|
+
* The connection's folder binding NAME in `bindings.json` (a `FolderKey`
|
|
1824
|
+
* entry) — the same label `connector()`'s `folder` option takes. Required
|
|
1825
|
+
* with `connection`.
|
|
1826
|
+
*/
|
|
1827
|
+
folder?: string;
|
|
1809
1828
|
}
|
|
1810
1829
|
/**
|
|
1811
1830
|
* The tool kinds that wrap a DEPLOYED tenant resource. `kind` picks the
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/** A tenant resource key — an Integration Service connection id or an Orchestrator folder key — is a GUID. */
|
|
2
2
|
export declare const GUID_RE: RegExp;
|
|
3
3
|
/** The diagnostic codes a label refusal carries, in `check` and on the error `serialize` throws. */
|
|
4
|
-
export type BindingRefusalCode = 'BINDING_SELF_NAME' | 'BINDING_UNDECLARED'
|
|
4
|
+
export type BindingRefusalCode = 'BINDING_SELF_NAME' | 'BINDING_UNDECLARED'
|
|
5
|
+
/** An emitted definition's `<bindings.NAME>` placeholder has no `bindings[]` entry named NAME (#865). */
|
|
6
|
+
| 'BINDING_PLACEHOLDER_UNRESOLVED'
|
|
7
|
+
/** Two steps on one connection disagree on which connector it belongs to (#866). */
|
|
8
|
+
| 'CONNECTION_CONNECTOR_CONFLICT';
|
|
5
9
|
/**
|
|
6
10
|
* What `serialize` throws for a label it refuses. It is an ordinary `Error` whose
|
|
7
11
|
* message is the sentence `check` prints; the `code` lets `compile --no-check`
|
|
@@ -58,3 +62,14 @@ export interface PrepareTarget {
|
|
|
58
62
|
* `prepare` command for this step's own connector and operation.
|
|
59
63
|
*/
|
|
60
64
|
export declare function bindingUndeclaredMessage(where: string, name: string, attribute: 'connection' | 'folder', declared: readonly string[], target?: PrepareTarget): string;
|
|
65
|
+
/**
|
|
66
|
+
* Why compile refuses one connection used by steps of different connectors —
|
|
67
|
+
* shared by `serialize` (which throws it, via `connectionConnectors` in
|
|
68
|
+
* `bindings-v2.ts`) and `check`, so both print the same sentence.
|
|
69
|
+
*
|
|
70
|
+
* `users` maps each connector to the steps that use the connection as that
|
|
71
|
+
* connector. For managed HTTP the connector is its `targetConnector` — the
|
|
72
|
+
* connection it authenticates with — not the `uipath-uipath-http` wrapper
|
|
73
|
+
* (flow-builder-sdk#866).
|
|
74
|
+
*/
|
|
75
|
+
export declare function connectionConnectorConflictMessage(connection: string, users: ReadonlyMap<string, readonly string[]>): string;
|
|
@@ -89,3 +89,24 @@ export function bindingUndeclaredMessage(where, name, attribute, declared, targe
|
|
|
89
89
|
+ `declares the label with a placeholder resourceKey instead, which compiles with a CONNECTION_STUB warning — `
|
|
90
90
|
+ `never an invented GUID.`;
|
|
91
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Why compile refuses one connection used by steps of different connectors —
|
|
94
|
+
* shared by `serialize` (which throws it, via `connectionConnectors` in
|
|
95
|
+
* `bindings-v2.ts`) and `check`, so both print the same sentence.
|
|
96
|
+
*
|
|
97
|
+
* `users` maps each connector to the steps that use the connection as that
|
|
98
|
+
* connector. For managed HTTP the connector is its `targetConnector` — the
|
|
99
|
+
* connection it authenticates with — not the `uipath-uipath-http` wrapper
|
|
100
|
+
* (flow-builder-sdk#866).
|
|
101
|
+
*/
|
|
102
|
+
export function connectionConnectorConflictMessage(connection, users) {
|
|
103
|
+
const who = [...users]
|
|
104
|
+
.map(([connector, steps]) => `${steps.map((s) => `"${s}"`).join(', ')} as ${connector}`)
|
|
105
|
+
.join('; ');
|
|
106
|
+
return `connection "${connection}" is used by steps of different connectors (${who}). An Integration Service `
|
|
107
|
+
+ `connection belongs to exactly one connector, so at most one of these steps can run on it, and the solution `
|
|
108
|
+
+ `resource would be declared as whichever came first. Give each connector its own connection label in `
|
|
109
|
+
+ `bindings.json (\`${prepareCommand('<connector-key>', '<action>')}\` binds one per connector). A managed `
|
|
110
|
+
+ `\`http({ managed: true, … })\` step on another connector's connection must name that connector in `
|
|
111
|
+
+ `\`targetConnector\`.`;
|
|
112
|
+
}
|
package/dist/serialize.d.ts
CHANGED
|
@@ -54,6 +54,34 @@ export declare function serializeWithSidecars(built: BuiltFlow, opts?: Serialize
|
|
|
54
54
|
}>;
|
|
55
55
|
};
|
|
56
56
|
export declare function serialize(built: BuiltFlow, opts?: SerializeOpts): any;
|
|
57
|
+
/**
|
|
58
|
+
* Every `<bindings.NAME>` placeholder a node's definition carries in its
|
|
59
|
+
* `model.context` must resolve to a `bindings[]` entry for THAT node.
|
|
60
|
+
*
|
|
61
|
+
* "Resolve" is Studio Web's rule, not a looser one: flow-v1
|
|
62
|
+
* `matchContextBindings` + `getInstanceBindingKey`
|
|
63
|
+
* (flow-schema/src/packaging/resolve-context.ts) take the entries whose
|
|
64
|
+
* `resource` EQUALS the definition's `model.bindings.resource`, scope them to the
|
|
65
|
+
* node's `inputs.detail.connectionResourceId` when that resource is `connection`
|
|
66
|
+
* (or undeclared), and look the placeholder up there BY NAME. Any miss blocks
|
|
67
|
+
* publish (MST-15010), and one that slips through reaches Studio as the literal
|
|
68
|
+
* placeholder: `invalid GUID <bindings.uipath-salesforce-slack connection>`.
|
|
69
|
+
* `uip maestro flow validate` does not check this.
|
|
70
|
+
*
|
|
71
|
+
* That is how #865 shipped — a per-call-site dedupe that let the first step on a
|
|
72
|
+
* shared connection claim it — and UV-16774 before it (a `Connection` resource
|
|
73
|
+
* spelling). Checking the finished document catches that class for every family
|
|
74
|
+
* and every emission order, whichever call site produced it.
|
|
75
|
+
*
|
|
76
|
+
* It holds for real definitions: of the 5,102 in the local registry index
|
|
77
|
+
* (2026-09-30), 4,999 reference a `<bindings.…>` name and every one of them
|
|
78
|
+
* declares that name in `model.bindings.values`, which is what the emitters
|
|
79
|
+
* close. Agent resource definitions are the one exemption (see below).
|
|
80
|
+
*
|
|
81
|
+
* @throws BindingRefusal `BINDING_PLACEHOLDER_UNRESOLVED`, naming each dangling
|
|
82
|
+
* placeholder and the step that carries it.
|
|
83
|
+
*/
|
|
84
|
+
export declare function assertBindingPlaceholdersResolve(flow: any): void;
|
|
57
85
|
/**
|
|
58
86
|
* What `compile` would refuse these connector inputs with, or `undefined` when it
|
|
59
87
|
* would accept them — the SAME code path `buildConnectorInputs` runs at emit time,
|