@uipath/maestro-builder-sdk 6.8.5 → 6.9.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.
@@ -1,12 +1,4 @@
1
- /**
2
- * Reading what `uip … --output json` prints.
3
- *
4
- * The CLI is chatty: an update banner, a tool-version warning and a
5
- * `ManifestClient` diagnostic can precede the JSON document, and the document
6
- * itself is a `{ Result, Data, Message, … }` envelope whose key casing has
7
- * varied between tools. Three Python callers each had their own loop for
8
- * finding the document; this is the one loop, and the one accessor.
9
- */
1
+ import type { UipResult } from "./client.js";
10
2
  /** The shape every `uip` JSON response shares, read case-insensitively. */
11
3
  export interface UipEnvelope {
12
4
  Result?: unknown;
@@ -35,3 +27,46 @@ export declare function isSuccess(envelope: UipEnvelope | undefined): boolean;
35
27
  export declare function dataItems(envelope: UipEnvelope | undefined): unknown[];
36
28
  /** The last `limit` characters of a stream, for a message. */
37
29
  export declare function tail(text: string, limit?: number): string;
30
+ /**
31
+ * Why a `uip` call failed, as text a message can quote after a colon. Never empty.
32
+ *
33
+ * `uip … --output json` prints its FAILURE envelope on stdout, not stderr:
34
+ * `OutputFormatter.error` hands it to `logOutput`, which writes through
35
+ * `sink.writeOut` (uipath-cli `packages/common/src/formatter.ts:1657-1688` and
36
+ * `:654-675`). For example, `registry get` of a node the catalog does not serve
37
+ * exits 1 with this on stdout:
38
+ *
39
+ * ```json
40
+ * { "Result": "Failure", "Message": "Node not found",
41
+ * "Instructions": "No node found with node type: uipath.connector.trigger.…" }
42
+ * ```
43
+ *
44
+ * and, on stderr, nothing, or a `[ManifestClient] fetchDynamicNodes ok: …`
45
+ * diagnostic, or a self-update banner. Callers that quoted stderr printed
46
+ * `registry get failed for <nodeType>: ` with nothing after the colon (2026-09-23
47
+ * eval, `skill-flow-datafabric-trigger-lifecycle`, v2 command 33).
48
+ *
49
+ * So the envelope is read first. The order is:
50
+ *
51
+ * 1. a call the client refused (`OfflineUipClient`): no process ran, and its
52
+ * stderr already says so (`offline: refused to run …`), so that text is kept;
53
+ * 2. a call killed by its timeout: `timed out`, whatever partial output it left;
54
+ * 3. a non-Success envelope on stdout (found past any banner, keys read
55
+ * case-insensitively): `<Message>: <Instructions>`, or whichever is present;
56
+ * 4. the end of stderr, then the end of stdout;
57
+ * 5. `no output`.
58
+ */
59
+ export declare function failureReason(result: UipResult): string;
60
+ /**
61
+ * True when a failed `registry get` answered the CLI's own "not in the catalog"
62
+ * envelope, `Message: "Node not found"` (uipath-cli
63
+ * `packages/maestro-sdk/src/registry/command.ts:595-600`).
64
+ *
65
+ * Only that answer says the node is ABSENT. Every other failure — `Error
66
+ * retrieving node` (`command.ts:586-591`, which carries the thrown error in
67
+ * `Instructions`), a refusal, a timeout, output that is not JSON — says the
68
+ * lookup did not work, which is a different fact with a different remedy. The
69
+ * `Message` is the discriminator because the envelope's `ErrorCode` is the
70
+ * generic `unknown_error` for both (`formatter.ts:1490-1502`).
71
+ */
72
+ export declare function isNodeNotFound(result: UipResult): boolean;
@@ -6,7 +6,12 @@
6
6
  * itself is a `{ Result, Data, Message, … }` envelope whose key casing has
7
7
  * varied between tools. Three Python callers each had their own loop for
8
8
  * finding the document; this is the one loop, and the one accessor.
9
+ *
10
+ * It is also the one place that decides why a call FAILED ({@link failureReason}).
11
+ * Callers outside `registry/uip/` do not read a result's stderr themselves;
12
+ * `tests/uip-stderr-gate.test.ts` holds them to that.
9
13
  */
14
+ import { pyStrip } from "../compat/pystr.js";
10
15
  /**
11
16
  * The first JSON document in `text`, or `undefined`.
12
17
  *
@@ -82,3 +87,78 @@ export function dataItems(envelope) {
82
87
  export function tail(text, limit = 300) {
83
88
  return text.length <= limit ? text : text.slice(text.length - limit);
84
89
  }
90
+ /** A non-empty trimmed string, or `undefined`. */
91
+ function said(value) {
92
+ if (typeof value !== "string")
93
+ return undefined;
94
+ const text = pyStrip(value);
95
+ return text === "" ? undefined : text;
96
+ }
97
+ /**
98
+ * Why a `uip` call failed, as text a message can quote after a colon. Never empty.
99
+ *
100
+ * `uip … --output json` prints its FAILURE envelope on stdout, not stderr:
101
+ * `OutputFormatter.error` hands it to `logOutput`, which writes through
102
+ * `sink.writeOut` (uipath-cli `packages/common/src/formatter.ts:1657-1688` and
103
+ * `:654-675`). For example, `registry get` of a node the catalog does not serve
104
+ * exits 1 with this on stdout:
105
+ *
106
+ * ```json
107
+ * { "Result": "Failure", "Message": "Node not found",
108
+ * "Instructions": "No node found with node type: uipath.connector.trigger.…" }
109
+ * ```
110
+ *
111
+ * and, on stderr, nothing, or a `[ManifestClient] fetchDynamicNodes ok: …`
112
+ * diagnostic, or a self-update banner. Callers that quoted stderr printed
113
+ * `registry get failed for <nodeType>: ` with nothing after the colon (2026-09-23
114
+ * eval, `skill-flow-datafabric-trigger-lifecycle`, v2 command 33).
115
+ *
116
+ * So the envelope is read first. The order is:
117
+ *
118
+ * 1. a call the client refused (`OfflineUipClient`): no process ran, and its
119
+ * stderr already says so (`offline: refused to run …`), so that text is kept;
120
+ * 2. a call killed by its timeout: `timed out`, whatever partial output it left;
121
+ * 3. a non-Success envelope on stdout (found past any banner, keys read
122
+ * case-insensitively): `<Message>: <Instructions>`, or whichever is present;
123
+ * 4. the end of stderr, then the end of stdout;
124
+ * 5. `no output`.
125
+ */
126
+ export function failureReason(result) {
127
+ if (result.refused === true)
128
+ return said(result.stderr) ?? "not run (offline client refused the call)";
129
+ if (result.timedOut === true)
130
+ return "timed out (the call was killed before it answered)";
131
+ const envelope = parseEnvelope(result.stdout);
132
+ if (envelope !== undefined && !isSuccess(envelope)) {
133
+ const parts = [said(getCI(envelope, "Message")), said(getCI(envelope, "Instructions"))].filter((part) => part !== undefined);
134
+ if (parts.length > 0)
135
+ return parts.join(": ");
136
+ }
137
+ const stderr = said(result.stderr);
138
+ if (stderr !== undefined)
139
+ return tail(stderr);
140
+ const stdout = said(result.stdout);
141
+ if (stdout !== undefined)
142
+ return tail(stdout);
143
+ return "no output";
144
+ }
145
+ /**
146
+ * True when a failed `registry get` answered the CLI's own "not in the catalog"
147
+ * envelope, `Message: "Node not found"` (uipath-cli
148
+ * `packages/maestro-sdk/src/registry/command.ts:595-600`).
149
+ *
150
+ * Only that answer says the node is ABSENT. Every other failure — `Error
151
+ * retrieving node` (`command.ts:586-591`, which carries the thrown error in
152
+ * `Instructions`), a refusal, a timeout, output that is not JSON — says the
153
+ * lookup did not work, which is a different fact with a different remedy. The
154
+ * `Message` is the discriminator because the envelope's `ErrorCode` is the
155
+ * generic `unknown_error` for both (`formatter.ts:1490-1502`).
156
+ */
157
+ export function isNodeNotFound(result) {
158
+ if (result.code === 0 || result.refused === true || result.timedOut === true)
159
+ return false;
160
+ const envelope = parseEnvelope(result.stdout);
161
+ if (envelope === undefined || isSuccess(envelope))
162
+ return false;
163
+ return said(getCI(envelope, "Message"))?.toLowerCase() === "node not found";
164
+ }
package/dist/serialize.js CHANGED
@@ -13,6 +13,7 @@ import { dirname, join } from 'node:path';
13
13
  import { Expr, toExpr, SCHEDULE_PRESETS, DELAY_PRESETS, parseIxpProjectType, flatNameOf, flatNameSegmentsOf } from './flow-sdk.js';
14
14
  import { FlowAction, FlowTrigger } from './core/node-classes.js';
15
15
  import { connectorRawNodeRefusal } from './core/connector-raw-node.js';
16
+ import { dataFabricUnknownInputs } from './core/typed-raw-node.js';
16
17
  import { bindingSelfNameMessage } from './core/binding-messages.js';
17
18
  import { OUTCOME_HANDLE_NODE_TYPES, assertHitlExitInvariant, hitlExitModel, hitlExitViolation, outcomeSlug } from './core/hitl-routing.js';
18
19
  import { declaredExits, hasExits, stopsAtEnd } from './core/step-ports.js';
@@ -3928,7 +3929,14 @@ export function serialize(built, opts = {}) {
3928
3929
  display: { label, ...(def?.display?.icon ? { icon: def.display.icon } : {}) },
3929
3930
  };
3930
3931
  col++;
3931
- if (def?.model)
3932
+ // A Data Fabric node gets no node-level `model`, whichever path emits it
3933
+ // (the typed verbs, or a rawNode through emitContribution). Studio Web
3934
+ // never writes one (flow-core node.ts: "`node.model` is never written";
3935
+ // canvas `nodeToInstance` omits it), and export seeds it from the
3936
+ // definition (services bpmn-to-xml.ts), so a copy here can only go stale.
3937
+ // `outputs` stay: the canvas persists them (`source: '=response'`), and
3938
+ // `variables.nodes[]` is derived from them.
3939
+ if (def?.model && !type.startsWith('core.datafabric.'))
3932
3940
  node.model = subNodeId(clone(def.model), id);
3933
3941
  if (def?.outputDefinition)
3934
3942
  node.outputs = subNodeId(clone(def.outputDefinition), id);
@@ -4170,6 +4178,12 @@ export function serialize(built, opts = {}) {
4170
4178
  const connectorRefusal = connectorRawNodeRefusal(def.nodeType);
4171
4179
  if (connectorRefusal)
4172
4180
  throw new Error(`${where}: ${connectorRefusal}`);
4181
+ // A Data Fabric key the product never reads (`_sortOptions`) is refused for
4182
+ // every contribution, whichever class or SDK copy made it: `validate`
4183
+ // cannot see it, and the node would ignore it at run time.
4184
+ const unknownInput = dataFabricUnknownInputs(def.nodeType, def.version, contribution.inputs);
4185
+ if (unknownInput?.severity === 'error')
4186
+ throw new Error(`${where}: ${unknownInput.message}`);
4173
4187
  const id = stepUid(scope, stepName);
4174
4188
  const node = makeNode(def.nodeType, id, stepName, { parentId, def, requestedVersion });
4175
4189
  node.inputs = renderDeep(contribution.inputs ?? {}, scope.rename);
@@ -4382,7 +4396,7 @@ export function serialize(built, opts = {}) {
4382
4396
  ...(s.guardrails === undefined ? {} : { guardrails: s.guardrails }),
4383
4397
  // The two DESCRIPTOR ARRAYS. `agentInputVariables` entries carry the
4384
4398
  // binding expression; `agentOutputVariables` entries are name + type only,
4385
- // and a runner coerces the model's completion to exactly these keys.
4399
+ // and the runtime's structured final call fills exactly these keys.
4386
4400
  //
4387
4401
  // `id` is the FLAT NAME, not the author's — the platform re-derives this
4388
4402
  // array from the node's prompts and keeps a pre-existing entry only when
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/maestro-builder-sdk",
3
- "version": "6.8.5",
3
+ "version": "6.9.0",
4
4
  "description": "Build UiPath Flow, Case, and BPMN artifacts by writing TypeScript.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://docs.uipath.com/maestro",
@@ -87,5 +87,5 @@
87
87
  "@types/node": "^22.7.0",
88
88
  "esbuild": "^0.28.1"
89
89
  },
90
- "gitref": "07d7820cfddf1603444213859a340b4e9dbe643e"
90
+ "gitref": "b7baf691c4126ec18eb3388d0d0a70b09301f24d"
91
91
  }