@uipath/maestro-builder-sdk 5.2.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/LICENSE +21 -0
- package/README.md +322 -0
- package/dist/bindings-v2.d.ts +37 -0
- package/dist/bindings-v2.js +155 -0
- package/dist/bindings.d.ts +6 -0
- package/dist/bindings.js +6 -0
- package/dist/bpmn/bpmn-expr-check.d.ts +41 -0
- package/dist/bpmn/bpmn-expr-check.js +218 -0
- package/dist/bpmn/bpmn-sdk.d.ts +2257 -0
- package/dist/bpmn/bpmn-sdk.js +1476 -0
- package/dist/bpmn/check-cli.d.ts +3 -0
- package/dist/bpmn/check-cli.js +32 -0
- package/dist/bpmn/check.d.ts +25 -0
- package/dist/bpmn/check.js +430 -0
- package/dist/bpmn/compile-cli.d.ts +3 -0
- package/dist/bpmn/compile-cli.js +60 -0
- package/dist/bpmn/connector.d.ts +56 -0
- package/dist/bpmn/connector.js +199 -0
- package/dist/bpmn/decompile-cli.d.ts +8 -0
- package/dist/bpmn/decompile-cli.js +54 -0
- package/dist/bpmn/decompile.d.ts +6 -0
- package/dist/bpmn/decompile.js +1108 -0
- package/dist/bpmn/format-profile.d.ts +27 -0
- package/dist/bpmn/format-profile.js +11 -0
- package/dist/bpmn/intsvc.d.ts +77 -0
- package/dist/bpmn/intsvc.js +155 -0
- package/dist/bpmn/load.d.ts +4 -0
- package/dist/bpmn/load.js +22 -0
- package/dist/bpmn/merge-cli.d.ts +3 -0
- package/dist/bpmn/merge-cli.js +45 -0
- package/dist/bpmn/merge.d.ts +19 -0
- package/dist/bpmn/merge.js +293 -0
- package/dist/bpmn/registry-types.json +1869 -0
- package/dist/bpmn/serialize.d.ts +28 -0
- package/dist/bpmn/serialize.js +757 -0
- package/dist/bpmn/tidy-cli.d.ts +2 -0
- package/dist/bpmn/tidy-cli.js +39 -0
- package/dist/bpmn/typed-node.d.ts +382 -0
- package/dist/bpmn/typed-node.js +551 -0
- package/dist/bpmn/uipath-moddle.v1.json +823 -0
- package/dist/bpmn/xml-ranges.d.ts +45 -0
- package/dist/bpmn/xml-ranges.js +151 -0
- package/dist/case/case-expr-check.d.ts +35 -0
- package/dist/case/case-expr-check.js +85 -0
- package/dist/case/case-sdk.d.ts +1282 -0
- package/dist/case/case-sdk.js +923 -0
- package/dist/case/check-cli.d.ts +3 -0
- package/dist/case/check-cli.js +31 -0
- package/dist/case/check.d.ts +18 -0
- package/dist/case/check.js +456 -0
- package/dist/case/compile-cli.d.ts +3 -0
- package/dist/case/compile-cli.js +291 -0
- package/dist/case/decompile-cli.d.ts +9 -0
- package/dist/case/decompile-cli.js +117 -0
- package/dist/case/decompile.d.ts +187 -0
- package/dist/case/decompile.js +1170 -0
- package/dist/case/entry-points.d.ts +79 -0
- package/dist/case/entry-points.js +118 -0
- package/dist/case/format-profile.d.ts +60 -0
- package/dist/case/format-profile.js +79 -0
- package/dist/case/ids.d.ts +24 -0
- package/dist/case/ids.js +76 -0
- package/dist/case/load.d.ts +12 -0
- package/dist/case/load.js +32 -0
- package/dist/case/preserve.d.ts +133 -0
- package/dist/case/preserve.js +105 -0
- package/dist/case/serialize.d.ts +10 -0
- package/dist/case/serialize.js +1414 -0
- package/dist/check-cli.d.ts +3 -0
- package/dist/check-cli.js +118 -0
- package/dist/check.d.ts +146 -0
- package/dist/check.js +4898 -0
- package/dist/cli/commands/bpmn.d.ts +2 -0
- package/dist/cli/commands/bpmn.js +73 -0
- package/dist/cli/commands/case.d.ts +2 -0
- package/dist/cli/commands/case.js +60 -0
- package/dist/cli/commands/flow.d.ts +2 -0
- package/dist/cli/commands/flow.js +99 -0
- package/dist/cli/commands/registry.d.ts +10 -0
- package/dist/cli/commands/registry.js +170 -0
- package/dist/cli/common.d.ts +30 -0
- package/dist/cli/common.js +92 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +18 -0
- package/dist/cli-run.d.ts +2 -0
- package/dist/cli-run.js +42 -0
- package/dist/compile-cli.d.ts +3 -0
- package/dist/compile-cli.js +160 -0
- package/dist/config.d.ts +47 -0
- package/dist/config.js +201 -0
- package/dist/core/actions.d.ts +3244 -0
- package/dist/core/actions.js +1062 -0
- package/dist/core/binding-messages.d.ts +1 -0
- package/dist/core/binding-messages.js +16 -0
- package/dist/core/bindings.d.ts +12 -0
- package/dist/core/bindings.js +67 -0
- package/dist/core/ceql.d.ts +73 -0
- package/dist/core/ceql.js +272 -0
- package/dist/core/cli-spelling.d.ts +39 -0
- package/dist/core/cli-spelling.js +62 -0
- package/dist/core/connector-raw-node.d.ts +21 -0
- package/dist/core/connector-raw-node.js +47 -0
- package/dist/core/connectors.d.ts +184 -0
- package/dist/core/connectors.js +128 -0
- package/dist/core/expr-check.d.ts +93 -0
- package/dist/core/expr-check.js +186 -0
- package/dist/core/expr.d.ts +323 -0
- package/dist/core/expr.js +400 -0
- package/dist/core/index.d.ts +10 -0
- package/dist/core/index.js +10 -0
- package/dist/core/library.d.ts +440 -0
- package/dist/core/library.js +1183 -0
- package/dist/core/list-envelope.d.ts +45 -0
- package/dist/core/list-envelope.js +97 -0
- package/dist/core/lookup-store.d.ts +22 -0
- package/dist/core/lookup-store.js +85 -0
- package/dist/core/lookups.d.ts +227 -0
- package/dist/core/lookups.js +231 -0
- package/dist/core/node-classes.d.ts +234 -0
- package/dist/core/node-classes.js +158 -0
- package/dist/core/script-return.d.ts +68 -0
- package/dist/core/script-return.js +197 -0
- package/dist/core/stable-id.d.ts +28 -0
- package/dist/core/stable-id.js +37 -0
- package/dist/core-definitions.json +16249 -0
- package/dist/decompile-cli.d.ts +3 -0
- package/dist/decompile-cli.js +131 -0
- package/dist/decompile.d.ts +138 -0
- package/dist/decompile.js +2482 -0
- package/dist/emit.d.ts +10 -0
- package/dist/emit.js +60 -0
- package/dist/event-filters.d.ts +130 -0
- package/dist/event-filters.js +137 -0
- package/dist/flow-expr-check.d.ts +17 -0
- package/dist/flow-expr-check.js +193 -0
- package/dist/flow-sdk.d.ts +1341 -0
- package/dist/flow-sdk.js +1164 -0
- package/dist/flow-variables.d.ts +13 -0
- package/dist/flow-variables.js +42 -0
- package/dist/format-profile.d.ts +72 -0
- package/dist/format-profile.js +97 -0
- package/dist/generators/_connections.py +287 -0
- package/dist/generators/_events.py +355 -0
- package/dist/generators/_lookups.py +114 -0
- package/dist/generators/_naming.py +185 -0
- package/dist/generators/_objects.py +299 -0
- package/dist/generators/_resolve.py +273 -0
- package/dist/generators/convert_library_to_md.py +1441 -0
- package/dist/generators/extract_library.py +125 -0
- package/dist/generators/generate_connectors_ts.py +936 -0
- package/dist/generators/generate_library_json.py +2527 -0
- package/dist/generators/generate_triggers_ts.py +110 -0
- package/dist/generators/materialize_registry.py +155 -0
- package/dist/generators/prepare_connector.py +1688 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +35 -0
- package/dist/ixp-definition.json +1982 -0
- package/dist/library.d.ts +6 -0
- package/dist/library.js +6 -0
- package/dist/load.d.ts +16 -0
- package/dist/load.js +95 -0
- package/dist/merge-cli.d.ts +3 -0
- package/dist/merge-cli.js +47 -0
- package/dist/merge.d.ts +125 -0
- package/dist/merge.js +0 -0
- package/dist/migrate.d.ts +15 -0
- package/dist/migrate.js +245 -0
- package/dist/node-runtime.d.ts +3 -0
- package/dist/node-runtime.js +22 -0
- package/dist/package-name.d.ts +17 -0
- package/dist/package-name.js +17 -0
- package/dist/ref-resolve.d.ts +77 -0
- package/dist/ref-resolve.js +170 -0
- package/dist/registry/cache.d.ts +55 -0
- package/dist/registry/cache.js +134 -0
- package/dist/registry/curated-overlay.d.ts +16 -0
- package/dist/registry/curated-overlay.js +62 -0
- package/dist/registry/extractor.d.ts +22 -0
- package/dist/registry/extractor.js +18 -0
- package/dist/registry/materialize-bin.d.ts +2 -0
- package/dist/registry/materialize-bin.js +7 -0
- package/dist/registry/materialize-cli.d.ts +1 -0
- package/dist/registry/materialize-cli.js +46 -0
- package/dist/registry/materializer.d.ts +23 -0
- package/dist/registry/materializer.js +34 -0
- package/dist/registry/path-cli.d.ts +1 -0
- package/dist/registry/path-cli.js +56 -0
- package/dist/registry/prepare-cli.d.ts +62 -0
- package/dist/registry/prepare-cli.js +185 -0
- package/dist/registry/pull-cli.d.ts +1 -0
- package/dist/registry/pull-cli.js +274 -0
- package/dist/registry/python.d.ts +26 -0
- package/dist/registry/python.js +46 -0
- package/dist/registry/search-cli.d.ts +52 -0
- package/dist/registry/search-cli.js +159 -0
- package/dist/schema-version.d.ts +165 -0
- package/dist/schema-version.js +148 -0
- package/dist/serialize.d.ts +64 -0
- package/dist/serialize.js +6398 -0
- package/lib/README.md +112 -0
- package/lib/library-json.samples/index.json +158 -0
- package/lib/library-json.samples/invariant-collections.json +83 -0
- package/lib/library-json.samples/list-envelopes.json +123 -0
- package/lib/library-json.samples/uipath-atlassian-jira/create-issue@1.0.0.json +131 -0
- package/lib/library-json.samples/uipath-atlassian-jira/create-issue@1.0.0.v1def.json +306 -0
- package/lib/library-json.samples/uipath-atlassian-jira/generic/create_issue/create@1.0.0.json +86 -0
- package/lib/library-json.samples/uipath-atlassian-jira/generic/create_issue/create@1.0.0.v1def.json +208 -0
- package/lib/library-json.samples/uipath-atlassian-jira/get-issue@1.0.0.json +2040 -0
- package/lib/library-json.samples/uipath-atlassian-jira/get-issue@1.0.0.v1def.json +195 -0
- package/lib/library-json.samples/uipath-google-vertex/execute-google-vertex-agent@1.0.0.json +169 -0
- package/lib/library-json.samples/uipath-google-vertex/execute-google-vertex-agent@1.0.0.v1def.json +209 -0
- package/lib/library-json.samples/uipath-http-webhook/event-http-webhook@1.0.0.json +63 -0
- package/lib/library-json.samples/uipath-http-webhook/event-http-webhook@1.0.0.v1def.json +262 -0
- package/lib/library-json.samples/uipath-http-webhook/trigger-http-webhook@1.0.0.json +63 -0
- package/lib/library-json.samples/uipath-http-webhook/trigger-http-webhook@1.0.0.v1def.json +213 -0
- package/lib/library-json.samples/uipath-microsoft-azureactivedirectory/list-groups@1.0.0.json +227 -0
- package/lib/library-json.samples/uipath-microsoft-azureactivedirectory/list-groups@1.0.0.v1def.json +373 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/event-file-created@1.0.0.json +116 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/event-file-created@1.0.0.v1def.json +369 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/event-list-item-added@1.0.0.json +56 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/event-list-item-added@1.0.0.v1def.json +256 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/trigger-file-created@1.0.0.json +116 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/trigger-file-created@1.0.0.v1def.json +320 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/trigger-list-item-added@1.0.0.json +56 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/trigger-list-item-added@1.0.0.v1def.json +207 -0
- package/lib/library-json.samples/uipath-microsoft-outlook365/event-email-received@1.0.0.json +107 -0
- package/lib/library-json.samples/uipath-microsoft-outlook365/event-email-received@1.0.0.v1def.json +264 -0
- package/lib/library-json.samples/uipath-microsoft-outlook365/send-email@1.0.0.json +142 -0
- package/lib/library-json.samples/uipath-microsoft-outlook365/send-email@1.0.0.v1def.json +306 -0
- package/lib/library-json.samples/uipath-microsoft-outlook365/trigger-email-received@1.0.0.json +107 -0
- package/lib/library-json.samples/uipath-microsoft-outlook365/trigger-email-received@1.0.0.v1def.json +217 -0
- package/lib/library-json.samples/uipath-microsoft-powerautomate/invoke-a-microsoft-power-automate-flow@1.0.0.json +85 -0
- package/lib/library-json.samples/uipath-microsoft-powerautomate/invoke-a-microsoft-power-automate-flow@1.0.0.v1def.json +209 -0
- package/lib/library-json.samples/uipath-salesforce-slack/send-message-to-user@1.0.0.json +755 -0
- package/lib/library-json.samples/uipath-salesforce-slack/send-message-to-user@1.0.0.v1def.json +513 -0
- package/lib/library-json.samples/uipath-uipath-dataservice/query-entity-records@1.0.0.json +122 -0
- package/lib/library-json.samples/uipath-uipath-dataservice/query-entity-records@1.0.0.v1def.json +227 -0
- package/package.json +85 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface FlowGlobalDeclaration {
|
|
2
|
+
id: string;
|
|
3
|
+
name?: string;
|
|
4
|
+
direction: 'in' | 'out' | 'inout';
|
|
5
|
+
type: string;
|
|
6
|
+
defaultValue?: unknown;
|
|
7
|
+
triggerNodeId?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
subType?: string;
|
|
10
|
+
schema?: Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
/** Read both supported Flow variable spellings into one declaration table. */
|
|
13
|
+
export declare function readFlowGlobals(variables: unknown, strict?: boolean): FlowGlobalDeclaration[];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const CONTAINER_KEYS = new Set(['globals', 'nodes', 'variableUpdates']);
|
|
2
|
+
const DIRECTIONS = new Set(['in', 'out', 'inout']);
|
|
3
|
+
/** Read both supported Flow variable spellings into one declaration table. */
|
|
4
|
+
export function readFlowGlobals(variables, strict = false) {
|
|
5
|
+
if (variables === undefined || variables === null)
|
|
6
|
+
return [];
|
|
7
|
+
if (typeof variables !== 'object' || Array.isArray(variables)) {
|
|
8
|
+
if (strict)
|
|
9
|
+
throw new Error('decompile: variables must be an object.');
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
12
|
+
const source = variables;
|
|
13
|
+
const globals = Array.isArray(source.globals)
|
|
14
|
+
? source.globals.filter((value) => value !== null && typeof value === 'object')
|
|
15
|
+
: [];
|
|
16
|
+
const seen = new Set(globals.map((global) => global.id));
|
|
17
|
+
const unknown = [];
|
|
18
|
+
if (source.globals !== undefined && !Array.isArray(source.globals))
|
|
19
|
+
unknown.push('globals');
|
|
20
|
+
for (const [id, value] of Object.entries(source)) {
|
|
21
|
+
if (CONTAINER_KEYS.has(id))
|
|
22
|
+
continue;
|
|
23
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
24
|
+
unknown.push(id);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const declaration = value;
|
|
28
|
+
if (typeof declaration.type !== 'string' || !DIRECTIONS.has(String(declaration.direction))) {
|
|
29
|
+
unknown.push(id);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (!seen.has(id)) {
|
|
33
|
+
globals.push({ ...declaration, id });
|
|
34
|
+
seen.add(id);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (strict && unknown.length > 0) {
|
|
38
|
+
throw new Error(`decompile: unrecognized variables key${unknown.length === 1 ? '' : 's'}: `
|
|
39
|
+
+ `${unknown.map((key) => JSON.stringify(key)).join(', ')}.`);
|
|
40
|
+
}
|
|
41
|
+
return globals;
|
|
42
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Flow file-format profile — serializer-owned (design §5.4).
|
|
3
|
+
*
|
|
4
|
+
* Top-level `version` in a `.flow` file is the FILE-FORMAT version, not an
|
|
5
|
+
* author-controlled artifact version: every supported reader accepts the fleet
|
|
6
|
+
* write floor, and each migration hop validates its input against that
|
|
7
|
+
* version's schema. The profile pins what this SDK build writes BY DEFAULT;
|
|
8
|
+
* `.version()` never reached the file and is deprecated, and compiling the same
|
|
9
|
+
* source with the same SDK build is deterministic.
|
|
10
|
+
*
|
|
11
|
+
* `.schemaVersion(v)` declares a different target for one flow, out of the set
|
|
12
|
+
* this build can actually write ({@link FLOW_SCHEMA_VERSIONS}) — that is what
|
|
13
|
+
* lets a decompile PRESERVE the version it read instead of quietly rewriting a
|
|
14
|
+
* 1.10 file as 1.9. See `schema-version.ts`.
|
|
15
|
+
*
|
|
16
|
+
* The pinned value tracks `flow-workbench`'s `MINIMUM_SUPPORTED_SCHEMA_VERSION`
|
|
17
|
+
* (the fleet write floor), snapshotted in `flow-workbench-ootb.snapshot.json`
|
|
18
|
+
* — `scripts/audit-flow-coverage.mjs` reports drift between the two on every
|
|
19
|
+
* run, so a floor bump upstream becomes visible here without a sibling
|
|
20
|
+
* checkout.
|
|
21
|
+
*/
|
|
22
|
+
import type { SchemaVersionPolicy } from './schema-version.js';
|
|
23
|
+
export declare const FLOW_FORMAT_PROFILE: {
|
|
24
|
+
/**
|
|
25
|
+
* The file-format version an emitted `.flow` declares when the author does
|
|
26
|
+
* not say otherwise. `1.9` is the current fleet write floor; it is
|
|
27
|
+
* structurally identical to `1.8` (the 1.8→1.9 migration only annotates
|
|
28
|
+
* stored Dynamic Extract definitions, which this SDK never emits), so
|
|
29
|
+
* emitting it skips a no-op migration hop on every open.
|
|
30
|
+
*/
|
|
31
|
+
readonly version: "1.9";
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Which Flow file-format versions `.schemaVersion()` may declare.
|
|
35
|
+
*
|
|
36
|
+
* **The band is 1.6 through 1.10, and it was read out of `flow-workbench`
|
|
37
|
+
* rather than assumed.** Every schema in it is the previous one plus a new
|
|
38
|
+
* version literal:
|
|
39
|
+
*
|
|
40
|
+
* ```ts
|
|
41
|
+
* export const workflowSchemaV1_10 = workflowSchemaV1_9.extend({ version: z.literal('1.10') });
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* — and the same line, one version down, defines 1.9, 1.8, 1.7 and 1.6
|
|
45
|
+
* (`packages/flow-schema/src/versions/v1.*.ts`). So the bytes this SDK emits
|
|
46
|
+
* satisfy all five schemas, and declaring one of them is a claim the serializer
|
|
47
|
+
* can actually back. Each hop between them is a DATA migration, and each targets
|
|
48
|
+
* something this SDK either never emits or already emits in its post-hop form:
|
|
49
|
+
* 1.6→1.7 rewrites three HITL sub-type node types, 1.7→1.8 reshapes
|
|
50
|
+
* document-validation HITL inputs, 1.8→1.9 annotates stored Dynamic Extract
|
|
51
|
+
* definitions, 1.9→1.10 repairs connector binding names.
|
|
52
|
+
*
|
|
53
|
+
* **Why the floor is 1.6.** Below it the shape genuinely differs: expressions
|
|
54
|
+
* are legacy `=js:` strings rather than the `{ type, expression, fieldType }`
|
|
55
|
+
* envelope this SDK writes (the envelope arrives with the 1.2→1.3 hop), and the
|
|
56
|
+
* 1.3→1.4 hop REVERTS agent and inline-agent-resource node inputs to the legacy
|
|
57
|
+
* string form. A target below 1.6 would hand a reader a document its own hops
|
|
58
|
+
* would reshape, so it is refused rather than stamped. That is the same line
|
|
59
|
+
* `migrate.ts`'s `VERSION_CONDITIONAL` ladder draws at `from: '1.6'` — the
|
|
60
|
+
* ladder gate and this floor are the same fact stated twice, which is why the
|
|
61
|
+
* gate is inert today and becomes live the day a lower target is supported.
|
|
62
|
+
*
|
|
63
|
+
* **Why 1.9 stays the DEFAULT** rather than the newest version: the floor is a
|
|
64
|
+
* deployed-reader boundary, not a quality ranking. `flow-workbench`'s own
|
|
65
|
+
* `MINIMUM_SUPPORTED_SCHEMA_VERSION` comment says not to advance it "until every
|
|
66
|
+
* deployed reader — including the CLI, Studio Web, and VS Code — can read the
|
|
67
|
+
* target version". So declaring 1.10 buys currency at the cost of reader
|
|
68
|
+
* compatibility and declaring 1.8 buys compatibility at the cost of a migration
|
|
69
|
+
* hop on every open; `check()` names the trade in both directions rather than
|
|
70
|
+
* picking for the author.
|
|
71
|
+
*/
|
|
72
|
+
export declare const FLOW_SCHEMA_VERSIONS: SchemaVersionPolicy;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
export const FLOW_FORMAT_PROFILE = {
|
|
2
|
+
/**
|
|
3
|
+
* The file-format version an emitted `.flow` declares when the author does
|
|
4
|
+
* not say otherwise. `1.9` is the current fleet write floor; it is
|
|
5
|
+
* structurally identical to `1.8` (the 1.8→1.9 migration only annotates
|
|
6
|
+
* stored Dynamic Extract definitions, which this SDK never emits), so
|
|
7
|
+
* emitting it skips a no-op migration hop on every open.
|
|
8
|
+
*/
|
|
9
|
+
version: '1.9',
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Which Flow file-format versions `.schemaVersion()` may declare.
|
|
13
|
+
*
|
|
14
|
+
* **The band is 1.6 through 1.10, and it was read out of `flow-workbench`
|
|
15
|
+
* rather than assumed.** Every schema in it is the previous one plus a new
|
|
16
|
+
* version literal:
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* export const workflowSchemaV1_10 = workflowSchemaV1_9.extend({ version: z.literal('1.10') });
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* — and the same line, one version down, defines 1.9, 1.8, 1.7 and 1.6
|
|
23
|
+
* (`packages/flow-schema/src/versions/v1.*.ts`). So the bytes this SDK emits
|
|
24
|
+
* satisfy all five schemas, and declaring one of them is a claim the serializer
|
|
25
|
+
* can actually back. Each hop between them is a DATA migration, and each targets
|
|
26
|
+
* something this SDK either never emits or already emits in its post-hop form:
|
|
27
|
+
* 1.6→1.7 rewrites three HITL sub-type node types, 1.7→1.8 reshapes
|
|
28
|
+
* document-validation HITL inputs, 1.8→1.9 annotates stored Dynamic Extract
|
|
29
|
+
* definitions, 1.9→1.10 repairs connector binding names.
|
|
30
|
+
*
|
|
31
|
+
* **Why the floor is 1.6.** Below it the shape genuinely differs: expressions
|
|
32
|
+
* are legacy `=js:` strings rather than the `{ type, expression, fieldType }`
|
|
33
|
+
* envelope this SDK writes (the envelope arrives with the 1.2→1.3 hop), and the
|
|
34
|
+
* 1.3→1.4 hop REVERTS agent and inline-agent-resource node inputs to the legacy
|
|
35
|
+
* string form. A target below 1.6 would hand a reader a document its own hops
|
|
36
|
+
* would reshape, so it is refused rather than stamped. That is the same line
|
|
37
|
+
* `migrate.ts`'s `VERSION_CONDITIONAL` ladder draws at `from: '1.6'` — the
|
|
38
|
+
* ladder gate and this floor are the same fact stated twice, which is why the
|
|
39
|
+
* gate is inert today and becomes live the day a lower target is supported.
|
|
40
|
+
*
|
|
41
|
+
* **Why 1.9 stays the DEFAULT** rather than the newest version: the floor is a
|
|
42
|
+
* deployed-reader boundary, not a quality ranking. `flow-workbench`'s own
|
|
43
|
+
* `MINIMUM_SUPPORTED_SCHEMA_VERSION` comment says not to advance it "until every
|
|
44
|
+
* deployed reader — including the CLI, Studio Web, and VS Code — can read the
|
|
45
|
+
* target version". So declaring 1.10 buys currency at the cost of reader
|
|
46
|
+
* compatibility and declaring 1.8 buys compatibility at the cost of a migration
|
|
47
|
+
* hop on every open; `check()` names the trade in both directions rather than
|
|
48
|
+
* picking for the author.
|
|
49
|
+
*/
|
|
50
|
+
export const FLOW_SCHEMA_VERSIONS = {
|
|
51
|
+
family: 'flow',
|
|
52
|
+
mode: 'enumerated',
|
|
53
|
+
method: '.schemaVersion',
|
|
54
|
+
default: FLOW_FORMAT_PROFILE.version,
|
|
55
|
+
latestKnown: '1.10',
|
|
56
|
+
migration: 'Moving a document between versions is `uip maestro flow migrate`\'s job — it owns the hops, '
|
|
57
|
+
+ 'including data repairs this SDK cannot reproduce from the source alone.',
|
|
58
|
+
writable: [
|
|
59
|
+
{
|
|
60
|
+
version: '1.6',
|
|
61
|
+
evidence: 'The oldest version whose shape this SDK emits: the `{ type, expression, fieldType }` envelope form. '
|
|
62
|
+
+ '`workflowSchemaV1_6` is `workflowSchemaV1_5.extend({ version })`, and the 1.5→1.6 hop only renames the '
|
|
63
|
+
+ 'built-in `deeprag` agent tool node type to `summarize` (MST-10301) — a node type this SDK does not emit.',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
version: '1.7',
|
|
67
|
+
sameBytesAs: '1.6',
|
|
68
|
+
evidence: '`workflowSchemaV1_7 = workflowSchemaV1_6.extend({ version })`. The 1.6→1.7 hop is data-only, over the '
|
|
69
|
+
+ 'three HITL sub-type node types (quick-form, coded-action-app, document-validation).',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
version: '1.8',
|
|
73
|
+
sameBytesAs: '1.6',
|
|
74
|
+
evidence: '`workflowSchemaV1_8 = workflowSchemaV1_7.extend({ version })`. The 1.7→1.8 hop is data-only, over '
|
|
75
|
+
+ 'document-validation HITL inputs (drops `inputs.documentId`, adds the Action App section). The deployed '
|
|
76
|
+
+ 'corpus this SDK is measured against is largely 1.8, so preserving it is the common brownfield case.',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
version: '1.9',
|
|
80
|
+
sameBytesAs: '1.6',
|
|
81
|
+
evidence: 'The fleet write floor (`MINIMUM_SUPPORTED_SCHEMA_VERSION`) and what this SDK has emitted and validated '
|
|
82
|
+
+ 'all along. `workflowSchemaV1_9 = workflowSchemaV1_8.extend({ version })`; the 1.8→1.9 hop only '
|
|
83
|
+
+ 'annotates stored Dynamic Extract definitions, which this SDK never emits.',
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
version: '1.10',
|
|
87
|
+
sameBytesAs: '1.6',
|
|
88
|
+
evidence: '`workflowSchemaV1_10 = workflowSchemaV1_9.extend({ version: z.literal("1.10") })` — the 1.10 schema '
|
|
89
|
+
+ 're-declares only the version literal. Its hop (`migrations/src/migrations/v1.9-to-v1.10.ts`) is a DATA '
|
|
90
|
+
+ "REPAIR: it renames a connector's single `ConnectionId` binding to `<connector> connection`, and its "
|
|
91
|
+
+ '`down()` restores only the version marker. That repair targets a defect this SDK cannot produce — '
|
|
92
|
+
+ 'connection binding names come from the definition verbatim (`model.bindings.values[].name`, else the '
|
|
93
|
+
+ '`<bindings.…>` placeholder in `model.context`), never built from the connector key (card G22: the '
|
|
94
|
+
+ 'shortcut is wrong for 892 of 2,648 registry definitions).',
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
};
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Find the connection for a connector, and write its bindings.
|
|
3
|
+
|
|
4
|
+
`prepare` already knows which connector it is preparing. Making the author go
|
|
5
|
+
and find that connector's connection id by hand — and then its folder key, and
|
|
6
|
+
then hand-write `bindings.json` around both — is work the command can do itself
|
|
7
|
+
from information it already has.
|
|
8
|
+
|
|
9
|
+
Measured on the eval corpus, that hand-work was 4 to 7 tool calls per task:
|
|
10
|
+
listing connections, filtering them, then (because nothing said the folder key
|
|
11
|
+
was on the connection record) three or four more spent searching Orchestrator
|
|
12
|
+
folders for one matching the symbolic name in the prompt.
|
|
13
|
+
|
|
14
|
+
THE ONE THING THIS MUST NOT DO IS GUESS. A tenant can carry several connections
|
|
15
|
+
for the same connector — this repo's test tenant has five for Slack — and they
|
|
16
|
+
are not interchangeable: a user who exists in one workspace does not exist in
|
|
17
|
+
another, so picking the wrong one makes a lookup unresolvable for a reason that
|
|
18
|
+
points nowhere near the actual mistake. That failure was observed directly
|
|
19
|
+
(`skill-flow-slack-dm-lookup` named the wrong workspace and burned its whole
|
|
20
|
+
turn budget), which is why ambiguity here is an error listing the candidates and
|
|
21
|
+
never a silent choice.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import json
|
|
27
|
+
import subprocess
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"ConnectionError_",
|
|
31
|
+
"byoa_connections",
|
|
32
|
+
"byoa_listing",
|
|
33
|
+
"discover_connection",
|
|
34
|
+
"lookup_connection",
|
|
35
|
+
"merge_bindings",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ConnectionError_(Exception):
|
|
40
|
+
"""No usable connection, with the reason and the way forward."""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _list(all_folders: bool, byoa: bool = False) -> list[dict]:
|
|
44
|
+
command = ["uip", "is", "connections", "list", "--output", "json"]
|
|
45
|
+
if all_folders:
|
|
46
|
+
command.insert(4, "--all-folders")
|
|
47
|
+
if byoa:
|
|
48
|
+
command.insert(4, "--byoa")
|
|
49
|
+
result = subprocess.run(command, capture_output=True, text=True)
|
|
50
|
+
if result.returncode != 0:
|
|
51
|
+
raise ConnectionError_(
|
|
52
|
+
"listing connections failed: "
|
|
53
|
+
f"{result.stderr.strip() or 'no output'}"
|
|
54
|
+
)
|
|
55
|
+
try:
|
|
56
|
+
payload = json.loads(result.stdout)
|
|
57
|
+
except ValueError as error:
|
|
58
|
+
raise ConnectionError_(f"connection listing was not JSON: {error}") from error
|
|
59
|
+
data = payload.get("Data")
|
|
60
|
+
if isinstance(data, dict):
|
|
61
|
+
data = data.get("items")
|
|
62
|
+
return data if isinstance(data, list) else []
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _matching(rows: list[dict], connector_key: str, name: str | None) -> list[dict]:
|
|
66
|
+
out = [
|
|
67
|
+
row for row in rows
|
|
68
|
+
if row.get("ConnectorKey") == connector_key
|
|
69
|
+
# A Failed connection is listed like any other and cannot serve a
|
|
70
|
+
# request; selecting one produces an error about the operation rather
|
|
71
|
+
# than about the connection, which sends the reader the wrong way.
|
|
72
|
+
and str(row.get("State", "")).lower() == "enabled"
|
|
73
|
+
]
|
|
74
|
+
if name:
|
|
75
|
+
out = [row for row in out if row.get("Name") == name]
|
|
76
|
+
return out
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def discover_connection(connector_key: str, name: str | None = None) -> dict:
|
|
80
|
+
"""The one enabled connection for `connector_key`, or raise.
|
|
81
|
+
|
|
82
|
+
Tries the DEFAULT listing first and only widens to `--all-folders` if that
|
|
83
|
+
finds nothing. That order matters and is not just politeness to the API: the
|
|
84
|
+
default listing is the caller's own folder, which on a real tenant is one
|
|
85
|
+
connection where the tenant-wide view is many. Widening first would turn an
|
|
86
|
+
unambiguous case into an ambiguous one.
|
|
87
|
+
"""
|
|
88
|
+
rows = _matching(_list(all_folders=False), connector_key, name)
|
|
89
|
+
scope = "your folder"
|
|
90
|
+
if not rows:
|
|
91
|
+
rows = _matching(_list(all_folders=True), connector_key, name)
|
|
92
|
+
scope = "the tenant"
|
|
93
|
+
|
|
94
|
+
if not rows:
|
|
95
|
+
qualifier = f" named {name!r}" if name else ""
|
|
96
|
+
raise ConnectionError_(
|
|
97
|
+
f"no enabled {connector_key} connection{qualifier} found in {scope}. "
|
|
98
|
+
f"List what exists with:\n"
|
|
99
|
+
f" uip is connections list --all-folders"
|
|
100
|
+
)
|
|
101
|
+
if len(rows) > 1:
|
|
102
|
+
# Every candidate line carries its id: a NAME is not a key on a tenant.
|
|
103
|
+
# The eval tenant holds three Slack connections all named
|
|
104
|
+
# `is-sandboxes` (two Enabled, in different folders), so the old remedy —
|
|
105
|
+
# "name the one you want with --connection <name>" — handed back the very
|
|
106
|
+
# flag the caller had just used, and the doc-following author dead-ended
|
|
107
|
+
# (skill-flow-e2e-escalation-orchestrator-paths, 2026-09-01: placeholder
|
|
108
|
+
# GUIDs hand-written into bindings.json, 401 at the Slack node).
|
|
109
|
+
listed = "\n".join(
|
|
110
|
+
f" {row.get('Name')} --connection-id {row.get('Id')}"
|
|
111
|
+
f" (folder: {row.get('Folder')})"
|
|
112
|
+
for row in rows
|
|
113
|
+
)
|
|
114
|
+
names = {str(row.get("Name")) for row in rows}
|
|
115
|
+
if name is not None or len(names) < len(rows):
|
|
116
|
+
remedy = (
|
|
117
|
+
"They share a name, so --connection <name> cannot pick one; pass "
|
|
118
|
+
"the id of the one in the folder you mean with --connection-id:"
|
|
119
|
+
)
|
|
120
|
+
else:
|
|
121
|
+
remedy = (
|
|
122
|
+
"Name the one you want with --connection <name>, or pass its id "
|
|
123
|
+
"with --connection-id:"
|
|
124
|
+
)
|
|
125
|
+
raise ConnectionError_(
|
|
126
|
+
f"{len(rows)} enabled {connector_key} connections in {scope}, and they "
|
|
127
|
+
f"are not interchangeable — a record that exists in one workspace need "
|
|
128
|
+
f"not exist in another. {remedy}\n{listed}"
|
|
129
|
+
)
|
|
130
|
+
return rows[0]
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _enabled_listing(rows: list[dict], connector_key: str) -> str:
|
|
134
|
+
"""The enabled connections for `connector_key`, one per line, pasteable."""
|
|
135
|
+
enabled = _matching(rows, connector_key, None)
|
|
136
|
+
if not enabled:
|
|
137
|
+
return (
|
|
138
|
+
f" (none — this tenant has no Enabled {connector_key} connection; "
|
|
139
|
+
"repair or create one in Integration Service first)"
|
|
140
|
+
)
|
|
141
|
+
return "\n".join(
|
|
142
|
+
f" {row.get('Name')} --connection-id {row.get('Id')}"
|
|
143
|
+
f" (folder: {row.get('Folder')})"
|
|
144
|
+
for row in enabled
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def lookup_connection(connector_key: str, connection_id: str) -> dict:
|
|
149
|
+
"""The record for an explicitly named connection, or raise unless it can serve.
|
|
150
|
+
|
|
151
|
+
An explicit `--connection-id` used to skip every check discovery applies, and
|
|
152
|
+
that skip is where a whole class of silent failures came from. A connection
|
|
153
|
+
in `State: Failed` is listed like any other, and `uip is resources describe`
|
|
154
|
+
against it answers `Result: Success` with NO request fields — the tenant has
|
|
155
|
+
no schema to give for a connection it cannot reach. `prepare` then wrote an
|
|
156
|
+
overlay with zero body inputs, printed "0 input field(s)" as a success, and
|
|
157
|
+
the next `compile` rejected every real field as unknown, sending the author
|
|
158
|
+
hunting through the SDK for a flag that does not exist.
|
|
159
|
+
|
|
160
|
+
Measured on the eval tenant (Salesforce `Opportunity` / `Update`): the Failed
|
|
161
|
+
connection returned 0 request fields, the Enabled one 13 (`StageName` among
|
|
162
|
+
them). The state was visible in the same `connections list` the author had
|
|
163
|
+
already run; a projection that dropped the `State` column hid it.
|
|
164
|
+
|
|
165
|
+
So: resolve the id against the tenant-wide listing, and refuse — naming the
|
|
166
|
+
state found and the enabled alternatives — rather than describe through it.
|
|
167
|
+
"""
|
|
168
|
+
rows = _list(all_folders=True)
|
|
169
|
+
match = next(
|
|
170
|
+
(row for row in rows if str(row.get("Id", "")) == str(connection_id)), None
|
|
171
|
+
)
|
|
172
|
+
alternatives = _enabled_listing(rows, connector_key)
|
|
173
|
+
if match is None:
|
|
174
|
+
raise ConnectionError_(
|
|
175
|
+
f"connection {connection_id} is not in this tenant's listing "
|
|
176
|
+
f"(`uip is connections list --all-folders`). Enabled {connector_key} "
|
|
177
|
+
f"connections:\n{alternatives}\n"
|
|
178
|
+
"Or omit --connection-id and prepare selects the connection itself."
|
|
179
|
+
)
|
|
180
|
+
actual_key = match.get("ConnectorKey")
|
|
181
|
+
if actual_key and actual_key != connector_key:
|
|
182
|
+
raise ConnectionError_(
|
|
183
|
+
f"connection {match.get('Name')} ({connection_id}) belongs to connector "
|
|
184
|
+
f"{actual_key}, not {connector_key}. Enabled {connector_key} "
|
|
185
|
+
f"connections:\n{alternatives}\n"
|
|
186
|
+
"Or omit --connection-id and prepare selects the connection itself."
|
|
187
|
+
)
|
|
188
|
+
state = str(match.get("State", "") or "")
|
|
189
|
+
if state.lower() != "enabled":
|
|
190
|
+
shown = state or "in an unknown state"
|
|
191
|
+
raise ConnectionError_(
|
|
192
|
+
f"connection {match.get('Name')} ({connection_id}) is {shown}, not "
|
|
193
|
+
"Enabled. A connection that is not Enabled answers field discovery "
|
|
194
|
+
"with an EMPTY schema, so the overlay would carry no fields and the "
|
|
195
|
+
"next compile would still reject every input as unknown. Enabled "
|
|
196
|
+
f"{connector_key} connections:\n{alternatives}\n"
|
|
197
|
+
"Or omit --connection-id and prepare selects the connection itself."
|
|
198
|
+
)
|
|
199
|
+
return match
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def byoa_connections(connector_key: str) -> list[dict]:
|
|
203
|
+
"""The enabled BYOA connections for `connector_key`.
|
|
204
|
+
|
|
205
|
+
RAISES on a failed listing rather than answering an empty list. The
|
|
206
|
+
difference is the whole point: "you have no BYOA connection" tells the
|
|
207
|
+
caller to go create one, and a listing that failed says nothing of the
|
|
208
|
+
kind. Answering [] for both turns a broken call into confident, wrong
|
|
209
|
+
advice — the failure-is-not-emptiness rule this module already applies to
|
|
210
|
+
ordinary discovery.
|
|
211
|
+
"""
|
|
212
|
+
return _matching(_list(all_folders=True, byoa=True), connector_key, None)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def byoa_listing(rows: list[dict], connector_key: str) -> str:
|
|
216
|
+
"""The BYOA candidates, one pasteable line each, or why there are none."""
|
|
217
|
+
if not rows:
|
|
218
|
+
return (
|
|
219
|
+
f" (none — this tenant has no enabled BYOA {connector_key} "
|
|
220
|
+
"connection; create one in Integration Service with your own "
|
|
221
|
+
"OAuth app, then re-run)"
|
|
222
|
+
)
|
|
223
|
+
return "\n".join(
|
|
224
|
+
f" {row.get('Name')} --connection-id {row.get('Id')}"
|
|
225
|
+
f" (folder: {row.get('Folder')})"
|
|
226
|
+
for row in rows
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def merge_bindings(
|
|
231
|
+
path: str,
|
|
232
|
+
connection: dict,
|
|
233
|
+
connection_name: str,
|
|
234
|
+
folder_name: str,
|
|
235
|
+
) -> tuple[bool, bool]:
|
|
236
|
+
"""Add connection + folder entries to `bindings.json`, in place.
|
|
237
|
+
|
|
238
|
+
ADDITIVE AND IDEMPOTENT. An existing entry under the same symbolic name is
|
|
239
|
+
updated, never duplicated, and every other entry is left exactly as it was —
|
|
240
|
+
`bindings.json` is an authored file that may already carry bindings for
|
|
241
|
+
connectors this run knows nothing about.
|
|
242
|
+
|
|
243
|
+
Returns `(wrote_connection, wrote_folder)` describing what changed, so the
|
|
244
|
+
caller can report the specific thing rather than "updated bindings".
|
|
245
|
+
"""
|
|
246
|
+
import os
|
|
247
|
+
|
|
248
|
+
document: dict = {"schemaVersion": "1", "bindings": []}
|
|
249
|
+
if os.path.exists(path):
|
|
250
|
+
try:
|
|
251
|
+
with open(path, encoding="utf-8") as handle:
|
|
252
|
+
loaded = json.load(handle)
|
|
253
|
+
if isinstance(loaded, dict):
|
|
254
|
+
document = loaded
|
|
255
|
+
document.setdefault("schemaVersion", "1")
|
|
256
|
+
document.setdefault("bindings", [])
|
|
257
|
+
except (OSError, ValueError):
|
|
258
|
+
# A malformed bindings.json is the author's file and not ours to
|
|
259
|
+
# discard: refuse rather than overwrite work we cannot read.
|
|
260
|
+
raise ConnectionError_(
|
|
261
|
+
f"{path} exists but is not readable JSON. Fix or remove it; "
|
|
262
|
+
"this command will not overwrite a file it cannot parse."
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
def upsert(symbolic: str, key: str, attribute: str) -> bool:
|
|
266
|
+
for entry in document["bindings"]:
|
|
267
|
+
if entry.get("name") == symbolic or entry.get("id") == symbolic:
|
|
268
|
+
changed = entry.get("resourceKey") != key
|
|
269
|
+
entry.update({
|
|
270
|
+
"id": entry.get("id", symbolic), "name": symbolic,
|
|
271
|
+
"resource": "Connection", "resourceKey": key,
|
|
272
|
+
"default": key, "propertyAttribute": attribute,
|
|
273
|
+
})
|
|
274
|
+
return changed
|
|
275
|
+
document["bindings"].append({
|
|
276
|
+
"id": symbolic, "name": symbolic, "resource": "Connection",
|
|
277
|
+
"resourceKey": key, "default": key, "propertyAttribute": attribute,
|
|
278
|
+
})
|
|
279
|
+
return True
|
|
280
|
+
|
|
281
|
+
wrote_connection = upsert(connection_name, str(connection["Id"]), "ConnectionId")
|
|
282
|
+
wrote_folder = upsert(folder_name, str(connection["FolderKey"]), "FolderKey")
|
|
283
|
+
|
|
284
|
+
with open(path, "w", encoding="utf-8") as handle:
|
|
285
|
+
json.dump(document, handle, indent=2)
|
|
286
|
+
handle.write("\n")
|
|
287
|
+
return wrote_connection, wrote_folder
|