@uipath/maestro-builder-sdk 6.16.14 → 6.17.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.
Files changed (60) hide show
  1. package/dist/api-index.md +108 -107
  2. package/dist/api-members.md +295 -291
  3. package/dist/bindings-v2.d.ts +39 -1
  4. package/dist/bindings-v2.js +289 -9
  5. package/dist/bpmn/compile-cli.js +11 -2
  6. package/dist/bpmn/decompile.js +2 -1
  7. package/dist/bpmn/registry-fallback.js +9 -7
  8. package/dist/case/compile-cli.js +16 -6
  9. package/dist/case/decompile-cli.js +2 -1
  10. package/dist/case/decompile.js +3 -2
  11. package/dist/case/merge-cli.js +3 -2
  12. package/dist/check.js +219 -37
  13. package/dist/cli-result.d.ts +9 -0
  14. package/dist/cli-result.js +12 -0
  15. package/dist/compile-cli.js +78 -53
  16. package/dist/core/actions.d.ts +70 -16
  17. package/dist/core/agentic-process-forms.d.ts +51 -0
  18. package/dist/core/agentic-process-forms.js +49 -0
  19. package/dist/core/binding-messages.d.ts +5 -0
  20. package/dist/core/comment-text.d.ts +29 -0
  21. package/dist/core/comment-text.js +40 -0
  22. package/dist/core/contribution-bindings.d.ts +73 -0
  23. package/dist/core/contribution-bindings.js +105 -0
  24. package/dist/core/expr-check.js +7 -2
  25. package/dist/core/json-file.d.ts +1 -0
  26. package/dist/core/json-file.js +12 -0
  27. package/dist/core/script-return.js +4 -1
  28. package/dist/core/typed-raw-node.js +1 -0
  29. package/dist/core/typescript-module.d.ts +2 -0
  30. package/dist/core/typescript-module.js +18 -0
  31. package/dist/core-definitions.json +210 -0
  32. package/dist/decompile-cli.js +2 -1
  33. package/dist/decompile.js +64 -24
  34. package/dist/emit.js +4 -3
  35. package/dist/flow-sdk.d.ts +14 -9
  36. package/dist/flow-sdk.js +14 -4
  37. package/dist/flow-step-tree.d.ts +2 -3
  38. package/dist/flow-step-tree.js +2 -3
  39. package/dist/load.js +1 -1
  40. package/dist/merge-cli.js +3 -2
  41. package/dist/merge.d.ts +2 -1
  42. package/dist/merge.js +0 -0
  43. package/dist/path-within.d.ts +33 -0
  44. package/dist/path-within.js +41 -0
  45. package/dist/registry/archive/extract.js +2 -5
  46. package/dist/registry/curated-overlay.js +3 -2
  47. package/dist/registry/generators/descriptors.js +20 -18
  48. package/dist/registry/generators/triggers.js +4 -4
  49. package/dist/registry/prepare/messages.js +2 -1
  50. package/dist/registry/prepare/source-labels.js +60 -1
  51. package/dist/registry/prepare-cli.js +14 -4
  52. package/dist/registry/pull-cli.js +3 -2
  53. package/dist/registry/uip/client.d.ts +28 -3
  54. package/dist/registry/uip/client.js +95 -34
  55. package/dist/registry/uip/host.d.ts +41 -0
  56. package/dist/registry/uip/host.js +312 -0
  57. package/dist/serialize.d.ts +7 -0
  58. package/dist/serialize.js +390 -270
  59. package/dist/workdir.js +1 -1
  60. package/package.json +3 -3
@@ -8852,6 +8852,216 @@
8852
8852
  ]
8853
8853
  }
8854
8854
  },
8855
+ "uipath.agent.autonomous@1.2": {
8856
+ "nodeType": "uipath.agent.autonomous",
8857
+ "version": "1.2",
8858
+ "category": "agent",
8859
+ "description": "AI agent that completes tasks autonomously",
8860
+ "tags": [
8861
+ "agentic",
8862
+ "ai",
8863
+ "autonomous",
8864
+ "agent"
8865
+ ],
8866
+ "sortOrder": 5,
8867
+ "supportsErrorHandling": true,
8868
+ "display": {
8869
+ "label": "Autonomous Agent",
8870
+ "icon": "autonomous-agent",
8871
+ "shape": "rectangle"
8872
+ },
8873
+ "handleConfiguration": [
8874
+ {
8875
+ "position": "top",
8876
+ "handles": [
8877
+ {
8878
+ "id": "escalation",
8879
+ "type": "source",
8880
+ "handleType": "artifact",
8881
+ "label": "Escalations",
8882
+ "showButton": true,
8883
+ "constraints": {
8884
+ "allowedTargets": [
8885
+ {
8886
+ "nodeType": "uipath.agent.resource.escalation"
8887
+ },
8888
+ {
8889
+ "nodeType": "uipath.agent.resource.escalation.*"
8890
+ }
8891
+ ],
8892
+ "validationMessage": "Only Agent Escalation nodes can connect here"
8893
+ }
8894
+ }
8895
+ ]
8896
+ },
8897
+ {
8898
+ "position": "bottom",
8899
+ "handles": [
8900
+ {
8901
+ "id": "context",
8902
+ "type": "source",
8903
+ "handleType": "artifact",
8904
+ "label": "Context",
8905
+ "showButton": true,
8906
+ "constraints": {
8907
+ "allowedTargets": [
8908
+ {
8909
+ "nodeType": "uipath.agent.resource.context.*"
8910
+ }
8911
+ ],
8912
+ "validationMessage": "Only Context resource nodes can connect here"
8913
+ }
8914
+ },
8915
+ {
8916
+ "id": "tool",
8917
+ "type": "source",
8918
+ "handleType": "artifact",
8919
+ "label": "Tools",
8920
+ "showButton": true,
8921
+ "constraints": {
8922
+ "allowedTargets": [
8923
+ {
8924
+ "nodeType": "uipath.agent.resource.tool.*"
8925
+ }
8926
+ ],
8927
+ "validationMessage": "Only Tool resource nodes can connect here"
8928
+ }
8929
+ }
8930
+ ]
8931
+ },
8932
+ {
8933
+ "position": "left",
8934
+ "handles": [
8935
+ {
8936
+ "id": "input",
8937
+ "type": "target",
8938
+ "handleType": "input"
8939
+ }
8940
+ ]
8941
+ },
8942
+ {
8943
+ "position": "right",
8944
+ "handles": [
8945
+ {
8946
+ "id": "success",
8947
+ "type": "source",
8948
+ "handleType": "output",
8949
+ "isDefaultForType": true
8950
+ },
8951
+ {
8952
+ "id": "error",
8953
+ "type": "source",
8954
+ "handleType": "output",
8955
+ "label": "Error",
8956
+ "visible": "{inputs.errorHandlingEnabled}",
8957
+ "constraints": {
8958
+ "maxConnections": 1
8959
+ }
8960
+ }
8961
+ ]
8962
+ }
8963
+ ],
8964
+ "inputDefinition": {
8965
+ "type": "object",
8966
+ "properties": {
8967
+ "systemPrompt": {
8968
+ "type": "string",
8969
+ "minLength": 1,
8970
+ "errorMessage": {
8971
+ "minLength": "System prompt is required"
8972
+ }
8973
+ },
8974
+ "userPrompt": {
8975
+ "type": "string",
8976
+ "minLength": 1,
8977
+ "errorMessage": {
8978
+ "minLength": "User prompt is required"
8979
+ }
8980
+ },
8981
+ "model": {
8982
+ "type": "string",
8983
+ "minLength": 1,
8984
+ "errorMessage": {
8985
+ "minLength": "Model is required"
8986
+ }
8987
+ },
8988
+ "temperature": {
8989
+ "type": "number",
8990
+ "minimum": 0,
8991
+ "maximum": 1
8992
+ },
8993
+ "maxTokenPerResponse": {
8994
+ "type": "number",
8995
+ "minimum": 0,
8996
+ "maximum": 16384
8997
+ },
8998
+ "modelMaxTokens": {
8999
+ "type": "number",
9000
+ "minimum": 0
9001
+ },
9002
+ "maxIterations": {
9003
+ "type": "number",
9004
+ "minimum": 1,
9005
+ "maximum": 100
9006
+ },
9007
+ "guardrails": {
9008
+ "type": "array"
9009
+ },
9010
+ "agentInputVariables": {
9011
+ "type": "array"
9012
+ },
9013
+ "agentOutputVariables": {
9014
+ "type": "array"
9015
+ }
9016
+ },
9017
+ "required": [
9018
+ "systemPrompt",
9019
+ "userPrompt",
9020
+ "model"
9021
+ ]
9022
+ },
9023
+ "inputDefaults": {},
9024
+ "outputDefinition": {
9025
+ "output": {
9026
+ "type": "object",
9027
+ "description": "Agent response",
9028
+ "source": "=result.response",
9029
+ "var": "output"
9030
+ },
9031
+ "error": {
9032
+ "type": "object",
9033
+ "description": "Error information if the node fails",
9034
+ "source": "=Error",
9035
+ "var": "error"
9036
+ }
9037
+ },
9038
+ "model": {
9039
+ "source": true,
9040
+ "type": "bpmn:ServiceTask",
9041
+ "serviceType": "Orchestrator.StartInlineAgentJob",
9042
+ "version": "v2",
9043
+ "context": [
9044
+ {
9045
+ "name": "_label",
9046
+ "type": "string",
9047
+ "value": ""
9048
+ },
9049
+ {
9050
+ "name": "name",
9051
+ "type": "string",
9052
+ "value": ""
9053
+ },
9054
+ {
9055
+ "name": "entryPoint",
9056
+ "type": "string",
9057
+ "value": ""
9058
+ }
9059
+ ]
9060
+ },
9061
+ "debug": {
9062
+ "runtime": "bpmnEngine"
9063
+ }
9064
+ },
8855
9065
  "uipath.agent.autonomous@1.3": {
8856
9066
  "nodeType": "uipath.agent.autonomous",
8857
9067
  "version": "1.3",
@@ -25,6 +25,7 @@ import { decompile } from './decompile.js';
25
25
  import { resolveFlowRefs } from './ref-resolve.js';
26
26
  import { flagValue, runWhenInvokedDirectly } from './cli-run.js';
27
27
  import { PACKAGE_NAME } from './package-name.js';
28
+ import { commentText } from './core/comment-text.js';
28
29
  /** Derive a PascalCase base name from the flow name/id for the default output file. */
29
30
  function baseName(flow) {
30
31
  const src = flow.name ?? flow.id ?? 'Flow';
@@ -101,7 +102,7 @@ function pipelineScript(originalPath, tsPath, pipelinePath) {
101
102
  const base = pipelinePath.replace(/\.pipeline\.mjs$/, '');
102
103
  return `#!/usr/bin/env node
103
104
  // Generated by flow-decompile — the brownfield read/modify/write pipeline for
104
- // ${basename(originalPath)}. Run it, edit ${basename(tsPath)}, run it again.
105
+ // ${commentText(basename(originalPath))}. Run it, edit ${commentText(basename(tsPath))}, run it again.
105
106
  import { execFileSync, spawnSync } from 'node:child_process';
106
107
  import { existsSync } from 'node:fs';
107
108
 
package/dist/decompile.js CHANGED
@@ -35,6 +35,7 @@ import { dirname, join } from 'node:path';
35
35
  import { fileURLToPath } from 'node:url';
36
36
  import { inferScriptShape } from './core/script-return.js';
37
37
  import { DEFAULT_TRIGGER_ID } from './core/expr.js';
38
+ import { commentText } from './core/comment-text.js';
38
39
  import { parseIxpProjectType } from './core/actions.js';
39
40
  import { readFlowGlobals } from './flow-variables.js';
40
41
  import { missingBindingLabels } from './core/binding-messages.js';
@@ -95,7 +96,19 @@ function warnOnce(message) {
95
96
  * emitted source; saying so here is the difference between a loop that fails
96
97
  * at step 2 with a hint and one that fails at step 2 with a puzzle.
97
98
  */
98
- const CORE_DEFINITION_KEYS = new Set(Object.keys(JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), 'core-definitions.json'), 'utf8'))));
99
+ const CORE_DEFINITIONS = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), 'core-definitions.json'), 'utf8'));
100
+ const CORE_DEFINITION_KEYS = new Set(Object.keys(CORE_DEFINITIONS));
101
+ /**
102
+ * The label the canvas gives a freshly dropped `node`: its definition's
103
+ * `display.label`, from the file's own `definitions[]`, else the bundled core
104
+ * definition. `serialize` uses the same label when the author sets none.
105
+ */
106
+ function definitionLabel(node) {
107
+ const key = `${node.type}@${node.typeVersion}`;
108
+ const def = runDefs.get(key) ?? CORE_DEFINITIONS[key] ?? runDefs.get(node.type);
109
+ const label = def?.display?.label;
110
+ return typeof label === 'string' ? label : undefined;
111
+ }
99
112
  function verClause(node) {
100
113
  const tv = node.typeVersion;
101
114
  if (!tv) {
@@ -124,6 +137,25 @@ function verArg(node) {
124
137
  const clause = verClause(node);
125
138
  return clause ? `, { ${clause} }` : '';
126
139
  }
140
+ /**
141
+ * A trigger node's option clauses: its exact `version`, and its canvas `label`
142
+ * when that differs from the definition's label (`definitionLabel`).
143
+ */
144
+ function triggerOptParts(node) {
145
+ const parts = [];
146
+ const ver = verClause(node);
147
+ if (ver)
148
+ parts.push(ver);
149
+ const label = node.display?.label;
150
+ if (typeof label === 'string' && label !== definitionLabel(node))
151
+ parts.push(`label: ${str(label)}`);
152
+ return parts;
153
+ }
154
+ /** `triggerOptParts` as the `.trigger(spec, { ... })` options argument. */
155
+ function triggerOptsArg(node) {
156
+ const parts = triggerOptParts(node);
157
+ return parts.length ? `, { ${parts.join(', ')} }` : '';
158
+ }
127
159
  /**
128
160
  * The full node-options argument for steps that can carry both an exact
129
161
  * `version` and mid-flow `updates` (the format's `variableUpdates[nodeId]`,
@@ -1083,11 +1115,12 @@ function sidecarAgentInputs(node) {
1083
1115
  ? numberUnless(settings.maxIterations, STUDIO_WEB_DEFAULT_MAX_ITERATIONS)
1084
1116
  : typeof settings.maxIterations === 'number' ? settings.maxIterations : undefined;
1085
1117
  // `mode` is a harness choice on the inline agent only; the conversational and
1086
- // voice sidecars always carry their family's fixed value. Naming it is also
1087
- // what selects the 1.3 definition, so on a 1.3 node even `'standard'` was the
1088
- // author's.
1118
+ // voice sidecars always carry their family's fixed value. `'standard'` is the
1119
+ // sidecar's default, so only another value is the author's. (Naming it no
1120
+ // longer selects a definition version — the node's `{ version }` carries that,
1121
+ // flow-builder-sdk#956.)
1089
1122
  const mode = node.type === T.inlineAgent && typeof settings.mode === 'string'
1090
- && (settings.mode !== 'standard' || node.typeVersion === '1.3')
1123
+ && settings.mode !== 'standard'
1091
1124
  ? settings.mode : undefined;
1092
1125
  return {
1093
1126
  ...(system === undefined ? {} : { systemPrompt: system }),
@@ -1616,7 +1649,7 @@ function unboundMarker(node, labels, key, action) {
1616
1649
  + `The source leaves ${missing.length > 1 ? 'them' : 'it'} out, and compile refuses the node (BINDING_MISSING) `
1617
1650
  + `until it names both labels: run \`${prepareCommand(key, action ?? '<action>')}\`, then pass `
1618
1651
  + `\`connection\` and \`folder\`.`);
1619
- return ` /* TODO: bind ${names}; compile refuses this node until it has both labels (BINDING_MISSING) */`;
1652
+ return ` /* TODO: bind ${commentText(names)}; compile refuses this node until it has both labels (BINDING_MISSING) */`;
1620
1653
  }
1621
1654
  function emitEventSubscription(node, imp, inputNames) {
1622
1655
  const prefix = node.type.startsWith('uipath.connector.event.') ? 'uipath.connector.event.'
@@ -1705,6 +1738,13 @@ function emitPublishedResource(node, imp, inputNames) {
1705
1738
  // definition to read there is nothing to recover it from.
1706
1739
  const name = binding('name');
1707
1740
  const folderPath = binding('folderPath');
1741
+ // A sibling project of the same solution (#951): the model names its project
1742
+ // instead of a folder. `isInSolution` is flow-core's marker; an in-solution
1743
+ // agent emitted before #951 carries only `projectId`. Read back as
1744
+ // `location: 'in-solution'` so the recompile takes the in-solution path
1745
+ // instead of a published one with an empty folder.
1746
+ const projectId = typeof def?.model?.projectId === 'string' ? def.model.projectId : undefined;
1747
+ const inSolution = def?.model?.isInSolution === true || projectId !== undefined;
1708
1748
  const inputs = { ...(node.inputs ?? {}) };
1709
1749
  // The design-time fire-and-forget switch is an OPTION on the factory, not a
1710
1750
  // process argument — serialize adds it to `inputs` after rendering the
@@ -1720,7 +1760,9 @@ function emitPublishedResource(node, imp, inputNames) {
1720
1760
  return `${imp.use(family.factory)}(${opts([
1721
1761
  ['key', str(key)],
1722
1762
  ['name', name !== undefined ? str(name) : undefined],
1723
- ['folderPath', folderPath !== undefined ? str(folderPath) : undefined],
1763
+ ['location', inSolution ? str('in-solution') : undefined],
1764
+ ['folderPath', !inSolution && folderPath !== undefined ? str(folderPath) : undefined],
1765
+ ['projectId', inSolution && projectId !== undefined ? str(projectId) : undefined],
1724
1766
  ['inputs', inputRows.length ? objLit(inputRows) : undefined],
1725
1767
  ['returns', returns],
1726
1768
  ['form', family.form !== undefined ? str(family.form) : undefined],
@@ -1792,7 +1834,7 @@ function unsupportedSpec(node, imp, o) {
1792
1834
  }
1793
1835
  // No definition to carry: a mock publishes nothing and takes no inputs, so it
1794
1836
  // is a safe structural stand-in, and the marker records what it replaced.
1795
- return `${imp.use('mock')}() /* TODO: unsupported node type ${node.type} */`;
1837
+ return `${imp.use('mock')}() /* TODO: unsupported node type ${commentText(node.type)} */`;
1796
1838
  }
1797
1839
  /** `some-node.id` → `someNodeId`, for a hoisted const name. */
1798
1840
  function camel(id) {
@@ -1902,39 +1944,39 @@ function emitTrigger(e, depth, trigger, imp, o, inputNames = new Set()) {
1902
1944
  // The default trigger, spelled out: decompiled source is deliberately more
1903
1945
  // explicit than hand-authored source so the node's exact definition version
1904
1946
  // survives recompilation (design §5.3).
1905
- e.line(depth, `.trigger(${imp.use('manual')}()${verArg(trigger)})`);
1947
+ e.line(depth, `.trigger(${imp.use('manual')}()${triggerOptsArg(trigger)})`);
1906
1948
  return;
1907
1949
  }
1908
1950
  if (trigger.type === T.triggerScheduled) {
1909
- e.line(depth, `.trigger(${imp.use('scheduled')}(${opts([['every', str(scheduleEvery(trigger))]])})${verArg(trigger)})`);
1951
+ e.line(depth, `.trigger(${imp.use('scheduled')}(${opts([['every', str(scheduleEvery(trigger))]])})${triggerOptsArg(trigger)})`);
1910
1952
  return;
1911
1953
  }
1912
1954
  if (trigger.type === T.triggerForm) {
1913
1955
  // The schema is DERIVED from the flow's inputs at compile time, so nothing
1914
1956
  // of it needs recovering — the factory takes no arguments.
1915
- e.line(depth, `.trigger(${imp.use('formTrigger')}()${verArg(trigger)})`);
1957
+ e.line(depth, `.trigger(${imp.use('formTrigger')}()${triggerOptsArg(trigger)})`);
1916
1958
  return;
1917
1959
  }
1918
1960
  if (trigger.type === T.triggerConversation) {
1919
- e.line(depth, `.trigger(${imp.use('conversationTrigger')}()${verArg(trigger)})`);
1961
+ e.line(depth, `.trigger(${imp.use('conversationTrigger')}()${triggerOptsArg(trigger)})`);
1920
1962
  return;
1921
1963
  }
1922
1964
  if (trigger.type === T.triggerVoice) {
1923
- e.line(depth, `.trigger(${imp.use('voiceTrigger')}()${verArg(trigger)})`);
1965
+ e.line(depth, `.trigger(${imp.use('voiceTrigger')}()${triggerOptsArg(trigger)})`);
1924
1966
  return;
1925
1967
  }
1926
1968
  // A connector EVENT trigger — the same subscription body as a mid-flow wait.
1927
1969
  {
1928
1970
  const sub = emitEventSubscription(trigger, imp, inputNames);
1929
1971
  if (sub !== undefined) {
1930
- e.line(depth, `.trigger(${imp.use('onEvent')}(${sub})${verArg(trigger)})`);
1972
+ e.line(depth, `.trigger(${imp.use('onEvent')}(${sub})${triggerOptsArg(trigger)})`);
1931
1973
  return;
1932
1974
  }
1933
1975
  }
1934
1976
  if (o.bestEffort === false) {
1935
1977
  throw new Error(`decompile: unsupported trigger type "${trigger.type}".`);
1936
1978
  }
1937
- e.line(depth, `/* TODO: unsupported trigger type ${trigger.type} */`);
1979
+ e.line(depth, `/* TODO: unsupported trigger type ${commentText(trigger.type)} */`);
1938
1980
  }
1939
1981
  // ─── Control-flow reconstruction (Phase 3) ────────────────────────────────────
1940
1982
  //
@@ -2350,7 +2392,7 @@ function region(cursor, stop, scope, ipdom, ctx, refInto) {
2350
2392
  // Unstructured fan-out we can't fold — best-effort marker.
2351
2393
  if (ctx.o.bestEffort === false)
2352
2394
  throw new Error(`decompile: unstructured fan-out at "${node.id}".`);
2353
- segs.push(`/* TODO: unstructured fan-out at ${node.id} (${succ.length} successors) */`);
2395
+ segs.push(`/* TODO: unstructured fan-out at ${commentText(node.id)} (${commentText(succ.length)} successors) */`);
2354
2396
  return segs;
2355
2397
  }
2356
2398
  return segs;
@@ -2637,7 +2679,7 @@ function emitSubflow(node, ctx) {
2637
2679
  if (!child) {
2638
2680
  if (ctx.o.bestEffort === false)
2639
2681
  throw new Error(`decompile: subflow "${node.id}" has no body in subflows[].`);
2640
- return `${ctx.imp.use('mock')}() /* TODO: subflow ${node.id} body missing */`;
2682
+ return `${ctx.imp.use('mock')}() /* TODO: subflow ${commentText(node.id)} body missing */`;
2641
2683
  }
2642
2684
  const childName = `${node.id}Child`;
2643
2685
  const childSrc = emitChildFlow(childName, child, ctx);
@@ -2736,7 +2778,7 @@ export function decompile(flow, options = {}) {
2736
2778
  if (runStrict)
2737
2779
  throw new Error(`decompile: ${schema.problem}`);
2738
2780
  warnOnce(schema.problem);
2739
- e.line(d, `// TODO: ${schema.problem}`);
2781
+ e.line(d, `// TODO: ${commentText(schema.problem)}`);
2740
2782
  }
2741
2783
  if (flow.name !== undefined && flow.name !== flow.id)
2742
2784
  e.line(d, `.name(${str(flow.name)})`);
@@ -2772,7 +2814,7 @@ export function decompile(flow, options = {}) {
2772
2814
  throw new Error(`decompile: ${message}`);
2773
2815
  warnOnce(message);
2774
2816
  for (const n of detached) {
2775
- e.line(d, `// TODO: node ${str(n.id)} (${n.type}) is not reachable from any trigger and is NOT in this source; recompiling drops it. Connect it in the .flow or edit its JSON directly.`);
2817
+ e.line(d, `// TODO: node ${commentText(str(n.id))} (${commentText(n.type)}) is not reachable from any trigger and is NOT in this source; recompiling drops it. Connect it in the .flow or edit its JSON directly.`);
2776
2818
  }
2777
2819
  }
2778
2820
  // `{ shared }` input copies on the roots (#924). Any other trigger-level
@@ -2786,7 +2828,7 @@ export function decompile(flow, options = {}) {
2786
2828
  if (runStrict)
2787
2829
  throw new Error(`decompile: ${message}`);
2788
2830
  warnOnce(message);
2789
- e.line(d, `// TODO: ${message}`);
2831
+ e.line(d, `// TODO: ${commentText(message)}`);
2790
2832
  }
2791
2833
  emitVariables(e, d, flow, imp, new Set(triggerNodes.filter((t) => t !== trigger).map((t) => t.id)), shared);
2792
2834
  // Reconstruct the body: structured control flow recovered from the graph.
@@ -2876,7 +2918,7 @@ function entryPointSeg(t, scope, ipdom, ctx) {
2876
2918
  : (() => {
2877
2919
  if (ctx.o.bestEffort === false)
2878
2920
  throw new Error(`decompile: unsupported entry-point trigger type "${t.type}".`);
2879
- return `${ctx.imp.use('manual')}() /* TODO: unsupported trigger type ${t.type} */`;
2921
+ return `${ctx.imp.use('manual')}() /* TODO: unsupported trigger type ${commentText(t.type)} */`;
2880
2922
  })();
2881
2923
  const scoped = readFlowGlobals(ctx.flow.variables, runStrict)
2882
2924
  .filter((g) => g.direction === 'in' && g.triggerNodeId === t.id);
@@ -2885,9 +2927,7 @@ function entryPointSeg(t, scope, ipdom, ctx) {
2885
2927
  const sharedOf = (g) => ctx.shared?.byInput.get(`${t.id}\0${g.id}`);
2886
2928
  optParts.push(`inputs: { ${scoped.map((g) => `${key(g.name ?? g.id)}: ${declExpr(g, true, ctx.imp, sharedOf(g))}`).join(', ')} }`);
2887
2929
  }
2888
- const ver = verClause(t);
2889
- if (ver)
2890
- optParts.push(ver);
2930
+ optParts.push(...triggerOptParts(t));
2891
2931
  const first = scopeSucc(ctx.graph, t.id, scope)[0];
2892
2932
  const prefixSegs = first !== undefined ? region(first, EXIT, scope, ipdom, ctx) : [];
2893
2933
  const optArgText = optParts.length ? `, { ${optParts.join(', ')} }` : (prefixSegs.length ? ', undefined' : '');
package/dist/emit.js CHANGED
@@ -25,13 +25,14 @@
25
25
  * are the ones `.build()` and `serialize()` raise themselves. Callers who want
26
26
  * the static pass should call `check()` — or use `compile-cli`.
27
27
  */
28
- import { writeFileSync, existsSync, mkdirSync } from 'node:fs';
28
+ import { existsSync, mkdirSync } from 'node:fs';
29
29
  import { dirname, join } from 'node:path';
30
30
  import { serialize } from './serialize.js';
31
31
  import { Library } from './library.js';
32
32
  import { Bindings } from './bindings.js';
33
33
  import { resolveLibraryDir } from './core/library-cache.js';
34
34
  import { defaultBindingsFile } from './workdir.js';
35
+ import { writeJsonFile } from './core/json-file.js';
35
36
  /**
36
37
  * Resolve the connector library the same way `compile-cli` does: only load it
37
38
  * when it is actually there, so a flow with no connectors emits fine without a
@@ -51,11 +52,11 @@ export function emitFlow(built, path, opts = {}) {
51
52
  library: opts.library ?? defaultLibrary(opts.libraryDir),
52
53
  bindings: opts.bindings ?? new Bindings(bindFile),
53
54
  // Same channel `compile` uses; a host that wants them elsewhere passes `warn`.
54
- warn: opts.warn ?? ((w) => console.error(` ⚠ [${w.code}] ${w.message}`)),
55
+ warn: opts.warn ?? ((w) => process.stderr.write(` ⚠ [${w.code}] ${w.message}\n`)),
55
56
  });
56
57
  const dir = dirname(path);
57
58
  if (dir && dir !== '.' && !existsSync(dir))
58
59
  mkdirSync(dir, { recursive: true });
59
- writeFileSync(path, JSON.stringify(flowJson, null, 2) + '\n');
60
+ writeJsonFile(path, flowJson);
60
61
  return flowJson;
61
62
  }
@@ -373,7 +373,8 @@ export interface NodeOptions {
373
373
  updates?: Record<string, Expr | unknown>;
374
374
  /**
375
375
  * What the canvas shows on this node (`display.label`). Defaults to the step
376
- * name.
376
+ * name; on a trigger (`.trigger()`, `.entryPoint()`) to the trigger
377
+ * definition's label, e.g. `Manual trigger`.
377
378
  *
378
379
  * @remarks
379
380
  * Purely cosmetic, and deliberately separate from the step name: the NAME is
@@ -386,8 +387,10 @@ export interface NodeOptions {
386
387
  * .step('fetchInv', http({ url, managed: true }), { label: 'Fetch invoice' })
387
388
  * ```
388
389
  *
389
- * `.branch()`, `.switch()`, `.loop()`, `.doWhile()`, `.parallel()` and
390
- * `.terminate()` take their label as a positional argument instead.
390
+ * `.branch()`, `.switch()`, `.loop()`, `.doWhile()`, `.parallel()`,
391
+ * `.trigger()` and `.entryPoint()` take it here too, in their options
392
+ * argument. Only `.terminate()` also has a positional label
393
+ * (`.terminate('stop', 'Stop here')`); when both are given, this option wins.
391
394
  */
392
395
  label?: string;
393
396
  }
@@ -663,7 +666,7 @@ export interface BuiltEntryPoint {
663
666
  id: string;
664
667
  /** What fires this root: any built-in trigger factory, or a custom trigger. */
665
668
  trigger: TriggerSpec;
666
- /** Node options for the trigger node — exact `version` selection. */
669
+ /** Node options for the trigger node — exact `version` selection and canvas `label`. */
667
670
  options?: NodeOptions;
668
671
  /** Inputs scoped to this root (globals bound to it via `triggerNodeId`). */
669
672
  inputs: VarDecl[];
@@ -1235,7 +1238,8 @@ declare class FlowBuilder extends StepList {
1235
1238
  *
1236
1239
  * @param spec - What starts the flow, from `scheduled(...)` or `onEvent(...)`.
1237
1240
  * @param options - Node options; `version` selects the exact definition
1238
- * version (see {@link NodeOptions}).
1241
+ * version and `label` sets what the canvas shows on the trigger node
1242
+ * (default: the trigger definition's label, e.g. `Scheduled trigger`). See {@link NodeOptions}.
1239
1243
  * @returns This builder, so calls chain.
1240
1244
  * @defaultValue the manual trigger
1241
1245
  */
@@ -1281,13 +1285,14 @@ declare class FlowBuilder extends StepList {
1281
1285
  * @param trigger - What fires this root: `manual()`, `scheduled(...)`,
1282
1286
  * `onEvent(...)`, `formTrigger()` (one field per scoped input),
1283
1287
  * `conversationTrigger()`, or `voiceTrigger()`.
1284
- * @param options - `version` (exact trigger definition version) and `inputs`
1285
- * — this root's scoped inputs, declared like `.input()`.
1288
+ * @param options - `version` (exact trigger definition version), `label`
1289
+ * (what the canvas shows on the trigger node; default: the trigger
1290
+ * definition's label, e.g. `Manual trigger`), and `inputs` — this root's scoped inputs, declared like
1291
+ * `.input()`.
1286
1292
  * @param prefixFn - Optional sub-builder for the root's prefix steps.
1287
1293
  * @returns This builder, so calls chain.
1288
1294
  */
1289
- entryPoint(id: string, trigger: TriggerSpec | FlowTrigger, options?: {
1290
- version?: string;
1295
+ entryPoint(id: string, trigger: TriggerSpec | FlowTrigger, options?: Pick<NodeOptions, 'version' | 'label'> & {
1291
1296
  inputs?: Record<string, TypeDesc | VarSpec>;
1292
1297
  }, prefixFn?: (b: StepList) => void): this;
1293
1298
  /**
package/dist/flow-sdk.js CHANGED
@@ -942,7 +942,8 @@ class FlowBuilder extends StepList {
942
942
  *
943
943
  * @param spec - What starts the flow, from `scheduled(...)` or `onEvent(...)`.
944
944
  * @param options - Node options; `version` selects the exact definition
945
- * version (see {@link NodeOptions}).
945
+ * version and `label` sets what the canvas shows on the trigger node
946
+ * (default: the trigger definition's label, e.g. `Scheduled trigger`). See {@link NodeOptions}.
946
947
  * @returns This builder, so calls chain.
947
948
  * @defaultValue the manual trigger
948
949
  */
@@ -996,8 +997,10 @@ class FlowBuilder extends StepList {
996
997
  * @param trigger - What fires this root: `manual()`, `scheduled(...)`,
997
998
  * `onEvent(...)`, `formTrigger()` (one field per scoped input),
998
999
  * `conversationTrigger()`, or `voiceTrigger()`.
999
- * @param options - `version` (exact trigger definition version) and `inputs`
1000
- * — this root's scoped inputs, declared like `.input()`.
1000
+ * @param options - `version` (exact trigger definition version), `label`
1001
+ * (what the canvas shows on the trigger node; default: the trigger
1002
+ * definition's label, e.g. `Manual trigger`), and `inputs` — this root's scoped inputs, declared like
1003
+ * `.input()`.
1001
1004
  * @param prefixFn - Optional sub-builder for the root's prefix steps.
1002
1005
  * @returns This builder, so calls chain.
1003
1006
  */
@@ -1011,7 +1014,14 @@ class FlowBuilder extends StepList {
1011
1014
  this._entryPoints.push({
1012
1015
  id,
1013
1016
  trigger: trigger,
1014
- ...(options?.version !== undefined ? { options: { version: options.version } } : {}),
1017
+ ...(options?.version !== undefined || options?.label !== undefined
1018
+ ? {
1019
+ options: {
1020
+ ...(options.version !== undefined ? { version: options.version } : {}),
1021
+ ...(options.label !== undefined ? { label: options.label } : {}),
1022
+ },
1023
+ }
1024
+ : {}),
1015
1025
  inputs,
1016
1026
  steps: b.steps,
1017
1027
  });
@@ -15,9 +15,8 @@ export declare function stopsAtEnd(steps: readonly Step[]): boolean;
15
15
  * The step lists nested DIRECTLY inside `step` — the bodies a walk over this
16
16
  * flow's own tree must descend into. Exhaustive over `Step['kind']`, so a new
17
17
  * container kind fails to compile here instead of being silently skipped by
18
- * every walker. That is how `check`'s return scan came to miss `.stepSwitch`
19
- * arms and `.stepToList` / `.onError` bodies, and warned NO_RETURN on flows
20
- * whose every path ends in `.return()` (UV-16855).
18
+ * every walker. That is how an earlier `check` return scan came to miss
19
+ * `.stepSwitch` arms and `.stepToList` / `.onError` bodies (UV-16855).
21
20
  *
22
21
  * A subflow's child flow is deliberately NOT a child here: it is another
23
22
  * flow's tree, and its returns say nothing about whether THIS flow ends.
@@ -15,9 +15,8 @@ export function stopsAtEnd(steps) {
15
15
  * The step lists nested DIRECTLY inside `step` — the bodies a walk over this
16
16
  * flow's own tree must descend into. Exhaustive over `Step['kind']`, so a new
17
17
  * container kind fails to compile here instead of being silently skipped by
18
- * every walker. That is how `check`'s return scan came to miss `.stepSwitch`
19
- * arms and `.stepToList` / `.onError` bodies, and warned NO_RETURN on flows
20
- * whose every path ends in `.return()` (UV-16855).
18
+ * every walker. That is how an earlier `check` return scan came to miss
19
+ * `.stepSwitch` arms and `.stepToList` / `.onError` bodies (UV-16855).
21
20
  *
22
21
  * A subflow's child flow is deliberately NOT a child here: it is another
23
22
  * flow's tree, and its returns say nothing about whether THIS flow ends.
package/dist/load.js CHANGED
@@ -13,7 +13,7 @@ export function resolveFlowFile(arg) {
13
13
  if (!statSync(resolvedCandidate, { throwIfNoEntry: false })?.isFile())
14
14
  continue;
15
15
  if (candidate !== arg && argStat?.isDirectory()) {
16
- console.error(`flow source: "${resolvedArg}" is a directory; using "${resolvedCandidate}"`);
16
+ process.stderr.write(`flow source: "${resolvedArg}" is a directory; using "${resolvedCandidate}"\n`);
17
17
  }
18
18
  return resolvedCandidate;
19
19
  }
package/dist/merge-cli.js CHANGED
@@ -12,7 +12,8 @@
12
12
  * See merge.ts.
13
13
  */
14
14
  import { reportResult } from './cli-result.js';
15
- import { readFileSync, writeFileSync } from 'node:fs';
15
+ import { writeJsonFile } from './core/json-file.js';
16
+ import { readFileSync } from 'node:fs';
16
17
  import { basename, resolve } from 'node:path';
17
18
  import { mergeFlow } from './merge.js';
18
19
  import { flagValue, runWhenInvokedDirectly } from './cli-run.js';
@@ -37,7 +38,7 @@ export function run(argv) {
37
38
  warn: (m) => console.error(`flow-merge: warning: ${m}`),
38
39
  });
39
40
  const out = flagValue(argv, '-o') ?? 'merged.flow';
40
- writeFileSync(out, JSON.stringify(merged, null, 2) + '\n');
41
+ writeJsonFile(out, merged);
41
42
  console.log(`flow-merge: wrote ${basename(out)} (${(merged.nodes ?? []).length} nodes, ${(merged.edges ?? []).length} edges)`);
42
43
  reportResult({ path: resolve(out), nodes: (merged.nodes ?? []).length, edges: (merged.edges ?? []).length });
43
44
  return 0;
package/dist/merge.d.ts CHANGED
@@ -25,7 +25,8 @@
25
25
  * as decompile was faithful for every node (the common case).
26
26
  *
27
27
  * Layout, `metadata`, `solutionId`, `projectId`, `runtime` and `layout` are
28
- * preserved from the original; `definitions` and `bindings` are unioned
28
+ * preserved from the original; `subflows` and edges (with their ids) come from
29
+ * `edited`; `definitions` and `bindings` are unioned
29
30
  * (original wins on conflict, since the original's are the tenant-resolved
30
31
  * authority). Node ids are stable by construction (decompile names every step by
31
32
  * its node id), which is what lets every re-attach key on id.
package/dist/merge.js CHANGED
Binary file