@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.
- package/dist/api-index.md +108 -107
- package/dist/api-members.md +295 -291
- package/dist/bindings-v2.d.ts +39 -1
- package/dist/bindings-v2.js +289 -9
- package/dist/bpmn/compile-cli.js +11 -2
- package/dist/bpmn/decompile.js +2 -1
- package/dist/bpmn/registry-fallback.js +9 -7
- package/dist/case/compile-cli.js +16 -6
- package/dist/case/decompile-cli.js +2 -1
- package/dist/case/decompile.js +3 -2
- package/dist/case/merge-cli.js +3 -2
- package/dist/check.js +219 -37
- package/dist/cli-result.d.ts +9 -0
- package/dist/cli-result.js +12 -0
- package/dist/compile-cli.js +78 -53
- package/dist/core/actions.d.ts +70 -16
- package/dist/core/agentic-process-forms.d.ts +51 -0
- package/dist/core/agentic-process-forms.js +49 -0
- package/dist/core/binding-messages.d.ts +5 -0
- package/dist/core/comment-text.d.ts +29 -0
- package/dist/core/comment-text.js +40 -0
- package/dist/core/contribution-bindings.d.ts +73 -0
- package/dist/core/contribution-bindings.js +105 -0
- package/dist/core/expr-check.js +7 -2
- package/dist/core/json-file.d.ts +1 -0
- package/dist/core/json-file.js +12 -0
- package/dist/core/script-return.js +4 -1
- package/dist/core/typed-raw-node.js +1 -0
- package/dist/core/typescript-module.d.ts +2 -0
- package/dist/core/typescript-module.js +18 -0
- package/dist/core-definitions.json +210 -0
- package/dist/decompile-cli.js +2 -1
- package/dist/decompile.js +64 -24
- package/dist/emit.js +4 -3
- package/dist/flow-sdk.d.ts +14 -9
- package/dist/flow-sdk.js +14 -4
- package/dist/flow-step-tree.d.ts +2 -3
- package/dist/flow-step-tree.js +2 -3
- package/dist/load.js +1 -1
- package/dist/merge-cli.js +3 -2
- package/dist/merge.d.ts +2 -1
- package/dist/merge.js +0 -0
- package/dist/path-within.d.ts +33 -0
- package/dist/path-within.js +41 -0
- package/dist/registry/archive/extract.js +2 -5
- package/dist/registry/curated-overlay.js +3 -2
- package/dist/registry/generators/descriptors.js +20 -18
- package/dist/registry/generators/triggers.js +4 -4
- package/dist/registry/prepare/messages.js +2 -1
- package/dist/registry/prepare/source-labels.js +60 -1
- package/dist/registry/prepare-cli.js +14 -4
- package/dist/registry/pull-cli.js +3 -2
- package/dist/registry/uip/client.d.ts +28 -3
- package/dist/registry/uip/client.js +95 -34
- package/dist/registry/uip/host.d.ts +41 -0
- package/dist/registry/uip/host.js +312 -0
- package/dist/serialize.d.ts +7 -0
- package/dist/serialize.js +390 -270
- package/dist/workdir.js +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* path-within — the one path-containment test for anything that writes files.
|
|
3
|
+
*
|
|
4
|
+
* "Is `child` inside `parent`?" looks like a string-prefix question and is not
|
|
5
|
+
* one. `child.startsWith(parent + '/')` is correct only where the separator is
|
|
6
|
+
* `/`: on Windows `path.resolve` returns `C:\proj\P\x`, the check refuses every
|
|
7
|
+
* path, and compile refused every inline agent's `<source>/agent.json`
|
|
8
|
+
* (flow-builder-sdk#946). A prefix test without a trailing separator has the
|
|
9
|
+
* opposite bug: it accepts the sibling `C:\proj\P2\x` as inside `C:\proj\P`.
|
|
10
|
+
*
|
|
11
|
+
* So the test is `relative(parent, child)` instead, which gets both right on
|
|
12
|
+
* every platform, and Windows' case-insensitive drive letters and
|
|
13
|
+
* cross-drive paths (`relative` returns the absolute `D:\x` there) with them.
|
|
14
|
+
*
|
|
15
|
+
* The path implementation is a parameter so the Windows rules can be tested on
|
|
16
|
+
* a Linux runner (`isWithin(a, b, path.win32)`). Callers leave it out.
|
|
17
|
+
*/
|
|
18
|
+
import * as nodePath from 'node:path';
|
|
19
|
+
/** The slice of `node:path` the test needs; `path.win32` and `path.posix` both fit. */
|
|
20
|
+
export type PathApi = Pick<typeof nodePath, 'relative' | 'isAbsolute' | 'sep'>;
|
|
21
|
+
/**
|
|
22
|
+
* `child` is `parent` itself or somewhere beneath it.
|
|
23
|
+
*
|
|
24
|
+
* Both paths should be absolute (from `path.resolve`); relative ones are
|
|
25
|
+
* resolved against the working directory by `relative`.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* isWithin('/proj/P', '/proj/P/abc/agent.json') // true
|
|
29
|
+
* isWithin('/proj/P', '/proj/P2/agent.json') // false — sibling, not child
|
|
30
|
+
* isWithin('C:\\proj\\P', 'C:\\proj\\P\\abc\\agent.json', path.win32) // true
|
|
31
|
+
* isWithin('C:\\proj\\P', 'D:\\agent.json', path.win32) // false — other drive
|
|
32
|
+
*/
|
|
33
|
+
export declare function isWithin(parent: string, child: string, path?: PathApi): boolean;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* path-within — the one path-containment test for anything that writes files.
|
|
3
|
+
*
|
|
4
|
+
* "Is `child` inside `parent`?" looks like a string-prefix question and is not
|
|
5
|
+
* one. `child.startsWith(parent + '/')` is correct only where the separator is
|
|
6
|
+
* `/`: on Windows `path.resolve` returns `C:\proj\P\x`, the check refuses every
|
|
7
|
+
* path, and compile refused every inline agent's `<source>/agent.json`
|
|
8
|
+
* (flow-builder-sdk#946). A prefix test without a trailing separator has the
|
|
9
|
+
* opposite bug: it accepts the sibling `C:\proj\P2\x` as inside `C:\proj\P`.
|
|
10
|
+
*
|
|
11
|
+
* So the test is `relative(parent, child)` instead, which gets both right on
|
|
12
|
+
* every platform, and Windows' case-insensitive drive letters and
|
|
13
|
+
* cross-drive paths (`relative` returns the absolute `D:\x` there) with them.
|
|
14
|
+
*
|
|
15
|
+
* The path implementation is a parameter so the Windows rules can be tested on
|
|
16
|
+
* a Linux runner (`isWithin(a, b, path.win32)`). Callers leave it out.
|
|
17
|
+
*/
|
|
18
|
+
import * as nodePath from 'node:path';
|
|
19
|
+
/**
|
|
20
|
+
* `child` is `parent` itself or somewhere beneath it.
|
|
21
|
+
*
|
|
22
|
+
* Both paths should be absolute (from `path.resolve`); relative ones are
|
|
23
|
+
* resolved against the working directory by `relative`.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* isWithin('/proj/P', '/proj/P/abc/agent.json') // true
|
|
27
|
+
* isWithin('/proj/P', '/proj/P2/agent.json') // false — sibling, not child
|
|
28
|
+
* isWithin('C:\\proj\\P', 'C:\\proj\\P\\abc\\agent.json', path.win32) // true
|
|
29
|
+
* isWithin('C:\\proj\\P', 'D:\\agent.json', path.win32) // false — other drive
|
|
30
|
+
*/
|
|
31
|
+
export function isWithin(parent, child, path = nodePath) {
|
|
32
|
+
const rel = path.relative(parent, child);
|
|
33
|
+
if (rel === '')
|
|
34
|
+
return true;
|
|
35
|
+
// `..` alone or as the first segment leaves `parent`; a segment that merely
|
|
36
|
+
// starts with dots (`..cache`) does not.
|
|
37
|
+
if (rel === '..' || rel.startsWith(`..${path.sep}`))
|
|
38
|
+
return false;
|
|
39
|
+
// Windows: a child on another drive has no relative spelling.
|
|
40
|
+
return !path.isAbsolute(rel);
|
|
41
|
+
}
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
* Python printed to stderr; the caller prints it and exits 1.
|
|
18
18
|
*/
|
|
19
19
|
import { readFileSync } from "node:fs";
|
|
20
|
-
import { join, resolve
|
|
20
|
+
import { join, resolve } from "node:path";
|
|
21
|
+
import { isWithin } from "../../path-within.js";
|
|
21
22
|
import { isFile, writeBytes, ensureDir } from "../files.js";
|
|
22
23
|
import { UnsafeArchive, isSymlinkEntry, safeRelativePath } from "./entry-policy.js";
|
|
23
24
|
import { NotAZip, UnsupportedArchive, entryData, readZipEntries } from "./zip-reader.js";
|
|
@@ -40,10 +41,6 @@ export class ExtractLibraryError extends Error {
|
|
|
40
41
|
this.name = "ExtractLibraryError";
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
|
-
/** `child` is `parent` or lives beneath it — both already `path.resolve`d. */
|
|
44
|
-
function isWithin(parent, child) {
|
|
45
|
-
return child === parent || child.startsWith(parent.endsWith(sep) ? parent : parent + sep);
|
|
46
|
-
}
|
|
47
44
|
export function extractArchive(options) {
|
|
48
45
|
const { archivePath, destination } = options;
|
|
49
46
|
const root = options.root ?? DEFAULT_ROOT;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { cp, mkdir, readdir, readFile
|
|
2
|
+
import { cp, mkdir, readdir, readFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, join, relative, sep } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { sortedStrings } from "./compat/compare.js";
|
|
6
|
+
import { writeJsonFile } from "../core/json-file.js";
|
|
6
7
|
import { consoleReporter } from "./reporter.js";
|
|
7
8
|
/** The corrections and definitions that the raw registry dump does not carry. */
|
|
8
9
|
export function bundledOverlayPath() {
|
|
@@ -129,7 +130,7 @@ reporter = consoleReporter) {
|
|
|
129
130
|
}
|
|
130
131
|
if (additions.length > 0) {
|
|
131
132
|
destination.entries = [...entries, ...additions];
|
|
132
|
-
|
|
133
|
+
writeJsonFile(indexPath, destination);
|
|
133
134
|
}
|
|
134
135
|
return { files: copied, entries: additions.length, skipped: sortedStrings([...skipped]) };
|
|
135
136
|
}
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* node type and so would emit the same const twice.
|
|
20
20
|
*/
|
|
21
21
|
import { prepareCommand } from "../../core/cli-spelling.js";
|
|
22
|
+
import { commentText, docComment } from "../../core/comment-text.js";
|
|
22
23
|
import { PACKAGE_NAME } from "../../package-name.js";
|
|
23
24
|
import { pyJsonString, pyJsonStringify } from "../codecs/pyjson.js";
|
|
24
25
|
import { sortedByCodePoint, sortedStrings } from "../compat/compare.js";
|
|
@@ -58,15 +59,16 @@ export function lookupFieldNote(declared) {
|
|
|
58
59
|
export function emitNestedProperty(name, node, indent, namedTypes, allOptional, isInput = false) {
|
|
59
60
|
const lines = [];
|
|
60
61
|
const declared = node.declared ?? {};
|
|
61
|
-
let desc = pyStrip(strOrEmpty(declared.description))
|
|
62
|
+
let desc = pyStrip(strOrEmpty(declared.description));
|
|
62
63
|
if (isInput && pyTruthy(declared.__lookup)) {
|
|
63
64
|
// The identity goes ON the field, not only in the LOOKUP block above the
|
|
64
65
|
// descriptor: "The reporter of the issue" beside `ConnectorLookupValue<string>`
|
|
65
66
|
// read as "any string", and an e-mail went into `fields.reporter.id`.
|
|
66
67
|
desc = pyStrip(`${desc} ${lookupFieldNote(declared)}`);
|
|
67
68
|
}
|
|
69
|
+
// A multi-line description keeps its breaks: `docComment` opens a block for it.
|
|
68
70
|
if (desc)
|
|
69
|
-
lines.push(
|
|
71
|
+
lines.push(...docComment(desc, indent));
|
|
70
72
|
const optional = allOptional || !subtreeRequired(node) ? "?" : "";
|
|
71
73
|
let rendered = namedTypes.typeFor(name, node);
|
|
72
74
|
if (isInput) {
|
|
@@ -100,11 +102,11 @@ export function emitLookupComment(specs, symbol = "<Descriptor>") {
|
|
|
100
102
|
const lines = [];
|
|
101
103
|
for (const [field, spec] of specs) {
|
|
102
104
|
const target = splitOnce(pyStr(pyTruthy(spec.objectName) ? spec.objectName : spec.path), "?")[0];
|
|
103
|
-
lines.push(` * LOOKUP: \`${field}\` -> ${target}.${spec.value}`);
|
|
105
|
+
lines.push(` * LOOKUP: \`${commentText(field)}\` -> ${commentText(target)}.${commentText(spec.value)}`);
|
|
104
106
|
const aliases = spec.aliases;
|
|
105
107
|
const offered = aliases.size > 0 ? [...aliases.values()].map((a) => `${a}()`) : [];
|
|
106
108
|
const byList = spec.by.map((n) => `\`${n}\``).join(", ");
|
|
107
|
-
lines.push(` * by: ${byList}`);
|
|
109
|
+
lines.push(` * by: ${commentText(byList)}`);
|
|
108
110
|
// THE WHOLE CALL, not just the method names: naming `.byEmail` without the
|
|
109
111
|
// `lookup(Descriptor, 'field')` wrapper says helpers exist but not how to
|
|
110
112
|
// reach one.
|
|
@@ -114,16 +116,16 @@ export function emitLookupComment(specs, symbol = "<Descriptor>") {
|
|
|
114
116
|
: spec.by.length > 0
|
|
115
117
|
? `lookup(${symbol}, '${field}').by('${spec.by[0]}', value)`
|
|
116
118
|
: `lookup(${symbol}, '${field}')`;
|
|
117
|
-
lines.push(` * use: ${call}`);
|
|
119
|
+
lines.push(` * use: ${commentText(call)}`);
|
|
118
120
|
if (offered.length > 0) {
|
|
119
|
-
lines.push(` * also: ${[...aliases.values()].map((a) => `.${a}`).join(", ")} · .by(name, value)`);
|
|
121
|
+
lines.push(` * also: ${commentText([...aliases.values()].map((a) => `.${a}`).join(", "))} · .by(name, value)`);
|
|
120
122
|
}
|
|
121
123
|
else {
|
|
122
124
|
lines.push(" * also: .by(name, value) only — aliases collided");
|
|
123
125
|
}
|
|
124
|
-
lines.push(` * strategy: ${STRATEGY_PROSE[spec.strategy]}`);
|
|
126
|
+
lines.push(` * strategy: ${commentText(STRATEGY_PROSE[spec.strategy])}`);
|
|
125
127
|
if (spec.dependsOn && spec.dependsOn.length > 0) {
|
|
126
|
-
lines.push(` * resolve first: ${spec.dependsOn.map((d) => `\`${d}\``).join(", ")}`);
|
|
128
|
+
lines.push(` * resolve first: ${commentText(spec.dependsOn.map((d) => `\`${d}\``).join(", "))}`);
|
|
127
129
|
}
|
|
128
130
|
}
|
|
129
131
|
return lines;
|
|
@@ -186,16 +188,16 @@ export function emitEvent(entry, inFields, inputTree, outputTree, name, inputTyp
|
|
|
186
188
|
// connector input's are, so the descriptor carries the same specs.
|
|
187
189
|
const specs = specsFromFields(inFields, invariant);
|
|
188
190
|
lines.push("/**");
|
|
189
|
-
lines.push(` * ${pyStr(label)} — ${nodeType}`);
|
|
191
|
+
lines.push(` * ${commentText(pyStr(label))} — ${commentText(nodeType)}`);
|
|
190
192
|
lines.push(" *");
|
|
191
193
|
if (generic) {
|
|
192
194
|
lines.push(" * A GENERIC event: it fires for ONE object of the connection (an entity, a");
|
|
193
|
-
lines.push(` * table, a custom object), so name it — \`onEvent(${name}, { object: '<Object>' })\`.`);
|
|
194
|
-
lines.push(` * \`uip is triggers objects ${pyStr(connectorKey)} <EVENT> --connection-id <id>\` lists them.`);
|
|
195
|
+
lines.push(` * table, a custom object), so name it — \`onEvent(${commentText(name)}, { object: '<Object>' })\`.`);
|
|
196
|
+
lines.push(` * \`uip is triggers objects ${commentText(pyStr(connectorKey))} <EVENT> --connection-id <id>\` lists them.`);
|
|
195
197
|
}
|
|
196
198
|
else {
|
|
197
|
-
lines.push(` * Start a flow on it with \`onEvent(${name}, { where: { … } })\`, or pause for it`);
|
|
198
|
-
lines.push(` * with \`waitForEvent(${name}, { … })\`; \`where\` carries its event parameters.`);
|
|
199
|
+
lines.push(` * Start a flow on it with \`onEvent(${commentText(name)}, { where: { … } })\`, or pause for it`);
|
|
200
|
+
lines.push(` * with \`waitForEvent(${commentText(name)}, { … })\`; \`where\` carries its event parameters.`);
|
|
199
201
|
}
|
|
200
202
|
// The facts the library build stamped from the event's `triggers objects`
|
|
201
203
|
// row. An author holding only the descriptor must learn the requirement here:
|
|
@@ -205,7 +207,7 @@ export function emitEvent(entry, inFields, inputTree, outputTree, name, inputTyp
|
|
|
205
207
|
lines.push(" * Requires a BYOA (bring-your-own-app) connection — on a standard one the");
|
|
206
208
|
lines.push(" * trigger deploys and never fires. Bind it with");
|
|
207
209
|
// Spelled for whoever ran the generator, like every `check` remedy.
|
|
208
|
-
lines.push(` * \`${prepareCommand(pyStr(connectorKey), eventId, "--connection-id", "<id>")}\`,`);
|
|
210
|
+
lines.push(` * \`${commentText(prepareCommand(pyStr(connectorKey), eventId, "--connection-id", "<id>"))}\`,`);
|
|
209
211
|
lines.push(" * which refuses a standard connection and records the check `compile` requires;");
|
|
210
212
|
lines.push(" * `uip is connections list --byoa --all-folders` lists the BYOA connections.");
|
|
211
213
|
}
|
|
@@ -253,7 +255,7 @@ export function emitOp(entry, v1def, inputTree, outputTree, name, inputTypes, ou
|
|
|
253
255
|
const schemaAction = operation.schemaAction ?? {};
|
|
254
256
|
const specs = lookupSpecs(entry, v1def, invariant);
|
|
255
257
|
const lookupComment = specs.size > 0 ? emitLookupComment(specs, name) : [];
|
|
256
|
-
const header = ` * ${pyStr(label)} — ${nodeType}`;
|
|
258
|
+
const header = ` * ${commentText(pyStr(label))} — ${commentText(nodeType)}`;
|
|
257
259
|
if (pyTruthy(operation.objectFromDiscovery)) {
|
|
258
260
|
lines.push("/**");
|
|
259
261
|
lines.push(header);
|
|
@@ -272,8 +274,8 @@ export function emitOp(entry, v1def, inputTree, outputTree, name, inputTypes, ou
|
|
|
272
274
|
lines.push("/**");
|
|
273
275
|
lines.push(header);
|
|
274
276
|
lines.push(" *");
|
|
275
|
-
lines.push(` * \`Inputs\` carries only the fields that SELECT the schema (${parents}).`);
|
|
276
|
-
lines.push(` * The real set comes from the \`${pyStr(schemaAction.name)}\` design-time`);
|
|
277
|
+
lines.push(` * \`Inputs\` carries only the fields that SELECT the schema (${commentText(parents)}).`);
|
|
278
|
+
lines.push(` * The real set comes from the \`${commentText(pyStr(schemaAction.name))}\` design-time`);
|
|
277
279
|
lines.push(" * action — describe with those values supplied to see it.");
|
|
278
280
|
if (lookupComment.length > 0) {
|
|
279
281
|
lines.push(" *");
|
|
@@ -289,7 +291,7 @@ export function emitOp(entry, v1def, inputTree, outputTree, name, inputTypes, ou
|
|
|
289
291
|
lines.push(" */");
|
|
290
292
|
}
|
|
291
293
|
else {
|
|
292
|
-
lines.push(`/** ${pyStr(label)} — ${nodeType} */`);
|
|
294
|
+
lines.push(`/** ${commentText(pyStr(label))} — ${commentText(nodeType)} */`);
|
|
293
295
|
}
|
|
294
296
|
lines.push(`export const ${name} = descriptor<${name}Inputs, ${name}Outputs>()({`);
|
|
295
297
|
lines.push(` nodeType: ${pyJsonString(nodeType)},`);
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* with it, and `tests/registry-triggers-gen.test.ts` now pins the text.
|
|
12
12
|
*/
|
|
13
13
|
import { PACKAGE_NAME } from "../../package-name.js";
|
|
14
|
+
import { docComment } from "../../core/comment-text.js";
|
|
14
15
|
import { pyJsonString, pyJsonStringify } from "../codecs/pyjson.js";
|
|
15
16
|
import { sortedByCodePoint, sortedStrings } from "../compat/compare.js";
|
|
16
17
|
import { afterLast, pyRstrip, pyStrip, upperFirst } from "../compat/pystr.js";
|
|
@@ -32,11 +33,10 @@ function emitTrigger(row, seen) {
|
|
|
32
33
|
seen.add(name);
|
|
33
34
|
const version = "Version" in row ? row.Version : "1.0.0";
|
|
34
35
|
const label = row.DisplayName || eventId;
|
|
35
|
-
const description = pyStrip(String(row.Description || ""))
|
|
36
|
-
|
|
37
|
-
doc = doc.replaceAll("*/", "* /"); // never let a description close the doc comment early
|
|
36
|
+
const description = pyStrip(String(row.Description || ""));
|
|
37
|
+
const doc = description ? `${label} — ${description}` : label;
|
|
38
38
|
return [
|
|
39
|
-
|
|
39
|
+
...docComment(doc),
|
|
40
40
|
`export const ${name} = triggerDescriptor()({`,
|
|
41
41
|
` connector: ${pyJsonString(row.connectorKey)},`,
|
|
42
42
|
` event: ${pyJsonString(eventId)},`,
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* `core/cli-spelling.ts`) without this module reading the environment.
|
|
22
22
|
*/
|
|
23
23
|
import { Refusal, messageOf } from "../errors.js";
|
|
24
|
+
import { commentText } from "../../core/comment-text.js";
|
|
24
25
|
import { plausibleMatches } from "./actions.js";
|
|
25
26
|
import { pyGet, pyOr, pyRepr, pyStrAny, pyTruthy } from "./support.js";
|
|
26
27
|
export const PREFIX = "prepare-connector: ";
|
|
@@ -637,7 +638,7 @@ export function descriptorUsageUnparsedLines(sourcePosix, descriptorName) {
|
|
|
637
638
|
export function descriptorUsageLines(descriptorName, importPath, requiredFields, boundConnection, boundFolder) {
|
|
638
639
|
let inputs = "{}";
|
|
639
640
|
if (requiredFields.length > 0) {
|
|
640
|
-
const placeholders = requiredFields.map(([name, type]) => `${name}: /* ${type}, required */`).join(", ");
|
|
641
|
+
const placeholders = requiredFields.map(([name, type]) => `${name}: /* ${commentText(type)}, required */`).join(", ");
|
|
641
642
|
inputs = `{ ${placeholders} }`;
|
|
642
643
|
}
|
|
643
644
|
return [
|
|
@@ -22,6 +22,10 @@
|
|
|
22
22
|
* import), so a source that a first `prepare` already repointed is still read;
|
|
23
23
|
* - `onEvent(...)` / `waitForEvent(...)` — a subscription object whose
|
|
24
24
|
* `connector` is `'<key>'`, or a descriptor imported as above with its options.
|
|
25
|
+
* - `inlineAgent({ tools })` — each `{ kind: 'connector', connector: '<key>',
|
|
26
|
+
* connection, folder }` tool, a single tool or an array (flow-builder-sdk#903).
|
|
27
|
+
* A tool that is not a same-file object may be a connector tool, so it is
|
|
28
|
+
* unplaceable like any other call; a tool of another `kind` binds nothing.
|
|
25
29
|
*
|
|
26
30
|
* `opts` and the labels in it may be literals or same-file `const`s, through
|
|
27
31
|
* `as const`, `satisfies`, parentheses, spreads of such consts, and property
|
|
@@ -47,7 +51,7 @@ import { relativePosix } from "../compat/paths.js";
|
|
|
47
51
|
import { canonicalPath, flowSources, isRelativeSpecifier } from "./source-import.js";
|
|
48
52
|
import { inWorkDir } from "../../workdir.js";
|
|
49
53
|
const CALLEES = new Set([
|
|
50
|
-
"connector", "onEvent", "waitForEvent", "eventTrigger", "dataService",
|
|
54
|
+
"connector", "onEvent", "waitForEvent", "eventTrigger", "dataService", "inlineAgent",
|
|
51
55
|
]);
|
|
52
56
|
/**
|
|
53
57
|
* Data Fabric's connector key, spelled here rather than imported from
|
|
@@ -313,6 +317,58 @@ function readFile(file, shown, text, connectorKey, moduleDirs, result, family) {
|
|
|
313
317
|
result.unreadable.connection.push(line);
|
|
314
318
|
result.unreadable.folder.push(line);
|
|
315
319
|
};
|
|
320
|
+
/**
|
|
321
|
+
* An inline agent's `tools`: one tool or an array of them. A connector tool
|
|
322
|
+
* carries its labels on the tool object itself, beside its `connector` key.
|
|
323
|
+
*/
|
|
324
|
+
const readAgentTools = (spec, at) => {
|
|
325
|
+
const obj = constObject(spec);
|
|
326
|
+
if (!obj) {
|
|
327
|
+
unplaceable(at, `takes \`${snippet(unwrap(spec))}\`, which is not a same-file object, so its tools`);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
const tools = prop(obj, "tools");
|
|
331
|
+
if (tools.kind === "absent")
|
|
332
|
+
return;
|
|
333
|
+
if (tools.kind === "unknown") {
|
|
334
|
+
unplaceable(at, `takes its tools from ${tools.why}`);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const list = constArray(tools.expr);
|
|
338
|
+
if (!list && !constObject(tools.expr)) {
|
|
339
|
+
unplaceable(at, `takes its tools as \`${snippet(unwrap(tools.expr))}\``);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
const elements = list ? expandArgs(list.elements) : [tools.expr];
|
|
343
|
+
if (!Array.isArray(elements)) {
|
|
344
|
+
unplaceable(at, `${elements.why} in its tools`);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
for (const el of elements) {
|
|
348
|
+
const toolAt = `${where(el, "inlineAgent")} tool`;
|
|
349
|
+
const tool = constObject(el);
|
|
350
|
+
if (!tool) {
|
|
351
|
+
unplaceable(toolAt, `is \`${snippet(unwrap(el))}\`, which is not a same-file object`);
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
const kindProp = prop(tool, "kind");
|
|
355
|
+
if (kindProp.kind === "absent")
|
|
356
|
+
continue;
|
|
357
|
+
const kind = kindProp.kind === "found" ? constString(kindProp.expr) : undefined;
|
|
358
|
+
if (kind === undefined) {
|
|
359
|
+
unplaceable(toolAt, "computes its `kind`");
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
if (kind !== "connector")
|
|
363
|
+
continue;
|
|
364
|
+
const keyProp = prop(tool, "connector");
|
|
365
|
+
const key = keyProp.kind === "found" ? constString(keyProp.expr) : undefined;
|
|
366
|
+
if (key)
|
|
367
|
+
record(key, el, toolAt);
|
|
368
|
+
else
|
|
369
|
+
unplaceable(toolAt, keyProp.kind === "absent" ? "has no `connector`" : "computes its `connector`");
|
|
370
|
+
}
|
|
371
|
+
};
|
|
316
372
|
const visitCalls = (node) => {
|
|
317
373
|
if (ts.isCallExpression(node)) {
|
|
318
374
|
const callee = ts.isIdentifier(node.expression)
|
|
@@ -343,6 +399,9 @@ function readFile(file, shown, text, connectorKey, moduleDirs, result, family) {
|
|
|
343
399
|
record(key, args[callee === "connector" ? 4 : 3], at);
|
|
344
400
|
}
|
|
345
401
|
}
|
|
402
|
+
else if (callee === "inlineAgent") {
|
|
403
|
+
readAgentTools(args[0], at);
|
|
404
|
+
}
|
|
346
405
|
else if (callee === "connector") {
|
|
347
406
|
const [first] = args;
|
|
348
407
|
const literalKey = constString(first);
|
|
@@ -58,10 +58,20 @@ export function pipelineOptions(options) {
|
|
|
58
58
|
* `uip` client and reporter injected so tests replay recordings.
|
|
59
59
|
*/
|
|
60
60
|
export async function prepareConnector(options) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
// One `uip mcp serve` for the ten-or-so calls a prepare makes, instead of a
|
|
62
|
+
// process start per call (flow-builder-sdk#965). Closed when the pipeline
|
|
63
|
+
// is done, whichever way it ends; a host that could not be used says so
|
|
64
|
+
// once, as a warning, and the calls spawn as they did before.
|
|
65
|
+
const client = defaultUipClient({ host: { onFallback: (reason) => consoleReporter.warn(` WARN: ${reason}`) } });
|
|
66
|
+
try {
|
|
67
|
+
return await runPipeline(pipelineOptions(options), {
|
|
68
|
+
client,
|
|
69
|
+
reporter: consoleReporter,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
client.close?.();
|
|
74
|
+
}
|
|
65
75
|
}
|
|
66
76
|
function value(args, name) {
|
|
67
77
|
const index = args.indexOf(name);
|
|
@@ -2,6 +2,7 @@ import { reportResult } from '../cli-result.js';
|
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { mkdir, readFile, rename, rm, stat, symlink, writeFile, } from "node:fs/promises";
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
|
+
import { writeJsonFile } from "../core/json-file.js";
|
|
5
6
|
import { CURRENT_LINK, LIBRARY_JSON_DIR, LIBRARY_MARKDOWN_DIR, cacheEntry, libraryCacheRoot, referencedConnectorKeys, } from "../core/library-cache.js";
|
|
6
7
|
import { extractLibrary } from "./extractor.js";
|
|
7
8
|
import { materializeRegistry } from "./materializer.js";
|
|
@@ -210,14 +211,14 @@ async function isCurrentLibrary(libraryPath, sha256) {
|
|
|
210
211
|
}
|
|
211
212
|
}
|
|
212
213
|
async function writeSource(libraryPath, archive, sha256, archiveSha256, overlaySha256) {
|
|
213
|
-
|
|
214
|
+
writeJsonFile(join(libraryPath, SOURCE_FILE), {
|
|
214
215
|
sha256,
|
|
215
216
|
archiveSha256,
|
|
216
217
|
overlaySha256,
|
|
217
218
|
url: archive.url ?? null,
|
|
218
219
|
archive: archive.checksum ? undefined : archive.path,
|
|
219
220
|
pulledAt: new Date().toISOString(),
|
|
220
|
-
}
|
|
221
|
+
});
|
|
221
222
|
}
|
|
222
223
|
/**
|
|
223
224
|
* Point `current` at an entry, replacing whatever it named.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type HostUipClientOptions } from "./host.js";
|
|
1
2
|
export interface UipRunOptions {
|
|
2
3
|
/** Working directory for the CLI; defaults to the process's. */
|
|
3
4
|
cwd?: string;
|
|
@@ -23,6 +24,12 @@ export interface UipResult {
|
|
|
23
24
|
export interface UipClient {
|
|
24
25
|
/** Run `argv[0] argv.slice(1)` — the first element is the executable, normally `uip`. */
|
|
25
26
|
run(argv: readonly string[], options?: UipRunOptions): Promise<UipResult>;
|
|
27
|
+
/**
|
|
28
|
+
* Release whatever the client holds open — the one long-lived `uip` of
|
|
29
|
+
* {@link HostUipClient}. Optional because most clients hold nothing; a
|
|
30
|
+
* command that may have been given one calls `client.close?.()` when done.
|
|
31
|
+
*/
|
|
32
|
+
close?(): void;
|
|
26
33
|
}
|
|
27
34
|
/** The real thing: spawn the CLI, capture both streams, never throw on a non-zero exit. */
|
|
28
35
|
export declare class SpawnUipClient implements UipClient {
|
|
@@ -50,6 +57,7 @@ export declare class RecordingUipClient implements UipClient {
|
|
|
50
57
|
private readonly directory;
|
|
51
58
|
constructor(inner: UipClient, directory: string);
|
|
52
59
|
run(argv: readonly string[], options?: UipRunOptions): Promise<UipResult>;
|
|
60
|
+
close(): void;
|
|
53
61
|
}
|
|
54
62
|
/**
|
|
55
63
|
* A client that runs nothing and reports every call as refused.
|
|
@@ -93,7 +101,24 @@ export declare class OfflineUipClient implements UipClient {
|
|
|
93
101
|
*
|
|
94
102
|
* Both exist to capture a fixture from a real tenant and to replay one — that
|
|
95
103
|
* is how `fixtures/library-build/` was taken — and neither is meant to be set
|
|
96
|
-
* in a pipeline. They are read
|
|
97
|
-
*
|
|
104
|
+
* in a pipeline. They are read by `uipSwitches()` here, for this and
|
|
105
|
+
* {@link runUipSync}, and nowhere else, so this is the only place an operator can
|
|
106
|
+
* be told what they do.
|
|
107
|
+
*
|
|
108
|
+
* A third, `FLOW_SDK_UIP_HOST=0`, turns off the one-process host a caller may
|
|
109
|
+
* ask for with `host` (see `host.ts`): every call then spawns its own `uip`,
|
|
110
|
+
* which is how the registry ran before flow-builder-sdk#965 and the shape to
|
|
111
|
+
* compare against when a prepare misbehaves.
|
|
112
|
+
*/
|
|
113
|
+
export declare function defaultUipClient(options?: UipRunOptions & {
|
|
114
|
+
host?: HostUipClientOptions | boolean;
|
|
115
|
+
}): UipClient;
|
|
116
|
+
/**
|
|
117
|
+
* Run `uip` synchronously, for the one caller that cannot await:
|
|
118
|
+
* `bpmn/registry-fallback.ts` resolves an extension type while a builder method runs.
|
|
119
|
+
* Same switches as {@link defaultUipClient} (`UIP_REPLAY_DIR` answers from recordings and
|
|
120
|
+
* throws on an unrecorded call; `UIP_RECORD_DIR` writes each result down) and the same
|
|
121
|
+
* contract as {@link SpawnUipClient}: no stdin, a non-zero exit or a timeout is a result,
|
|
122
|
+
* a missing executable throws the install instruction.
|
|
98
123
|
*/
|
|
99
|
-
export declare function
|
|
124
|
+
export declare function runUipSync(argv: readonly string[], options?: UipRunOptions): UipResult;
|