@uipath/maestro-builder-sdk 6.18.8 → 6.18.10
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/bpmn/uipath-moddle.v1.json +5 -0
- package/dist/case/decompile-cli.js +2 -8
- package/dist/decompile-cli.js +2 -9
- package/dist/registry/pull-cli.d.ts +21 -0
- package/dist/registry/pull-cli.js +73 -8
- package/dist/registry/uip/launch.d.ts +11 -1
- package/dist/registry/uip/launch.js +50 -43
- package/dist/registry/uip/pipeline-front-door.d.ts +2 -0
- package/dist/registry/uip/pipeline-front-door.js +88 -0
- package/package.json +2 -2
|
@@ -25,6 +25,7 @@ import { Library } from '../core/library.js';
|
|
|
25
25
|
import { decompileWithDiagnostics } from './decompile.js';
|
|
26
26
|
import { CASE_IMPORT } from '../package-name.js';
|
|
27
27
|
import { commentText } from '../core/comment-text.js';
|
|
28
|
+
import { pipelineFrontDoor } from '../registry/uip/pipeline-front-door.js';
|
|
28
29
|
/** Derive a PascalCase base name from the case name for the default output file. */
|
|
29
30
|
function baseName(plan) {
|
|
30
31
|
const src = plan.name ?? plan.id ?? 'Case';
|
|
@@ -90,8 +91,7 @@ function pipelineScript(originalPath, tsPath, pipelinePath, importSpecifier, bes
|
|
|
90
91
|
const base = pipelinePath.replace(/\.pipeline\.mjs$/, '');
|
|
91
92
|
return `#!/usr/bin/env node
|
|
92
93
|
// Generated by case decompile — edit ${commentText(basename(tsPath))}, then run this file.
|
|
93
|
-
|
|
94
|
-
import { existsSync } from 'node:fs';
|
|
94
|
+
${pipelineFrontDoor('case')}
|
|
95
95
|
|
|
96
96
|
const ORIGINAL = ${JSON.stringify(originalPath)};
|
|
97
97
|
const TS = ${JSON.stringify(tsPath)};
|
|
@@ -100,12 +100,6 @@ const IMPORT = ${JSON.stringify(importSpecifier)};
|
|
|
100
100
|
const LIBRARY = ${JSON.stringify(libraryDir)};
|
|
101
101
|
const BEST_EFFORT = ${JSON.stringify(bestEffort)};
|
|
102
102
|
|
|
103
|
-
const uipProbe = spawnSync('uip', ['maestro', 'case', 'compile', '--help'], { stdio: 'ignore' });
|
|
104
|
-
const frontDoor = !uipProbe.error && uipProbe.status === 0
|
|
105
|
-
? { file: 'uip', prefix: ['maestro', 'case'] }
|
|
106
|
-
: { file: 'npx', prefix: ['--no-install', 'flow-sdk', 'case'] };
|
|
107
|
-
const run = (verb, args) =>
|
|
108
|
-
execFileSync(frontDoor.file, [...frontDoor.prefix, verb, ...args], { stdio: 'inherit' });
|
|
109
103
|
const withLibrary = (args) => LIBRARY ? [...args, '--library', LIBRARY] : args;
|
|
110
104
|
|
|
111
105
|
if (!existsSync(TS)) {
|
package/dist/decompile-cli.js
CHANGED
|
@@ -26,6 +26,7 @@ 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
28
|
import { commentText } from './core/comment-text.js';
|
|
29
|
+
import { pipelineFrontDoor } from './registry/uip/pipeline-front-door.js';
|
|
29
30
|
/** Derive a PascalCase base name from the flow name/id for the default output file. */
|
|
30
31
|
function baseName(flow) {
|
|
31
32
|
const src = flow.name ?? flow.id ?? 'Flow';
|
|
@@ -103,8 +104,7 @@ function pipelineScript(originalPath, tsPath, pipelinePath) {
|
|
|
103
104
|
return `#!/usr/bin/env node
|
|
104
105
|
// Generated by flow-decompile — the brownfield read/modify/write pipeline for
|
|
105
106
|
// ${commentText(basename(originalPath))}. Run it, edit ${commentText(basename(tsPath))}, run it again.
|
|
106
|
-
|
|
107
|
-
import { existsSync } from 'node:fs';
|
|
107
|
+
${pipelineFrontDoor('flow')}
|
|
108
108
|
|
|
109
109
|
const ORIGINAL = ${JSON.stringify(originalPath)};
|
|
110
110
|
const TS = ${JSON.stringify(tsPath)};
|
|
@@ -112,13 +112,6 @@ const BASELINE = ${JSON.stringify(base + '.baseline.flow')};
|
|
|
112
112
|
const EDITED = ${JSON.stringify(base + '.edited.flow')};
|
|
113
113
|
const MERGED = ${JSON.stringify(base + '.merged.flow')};
|
|
114
114
|
|
|
115
|
-
const uipProbe = spawnSync('uip', ['maestro', 'flow', 'compile', '--help'], { stdio: 'ignore' });
|
|
116
|
-
const frontDoor = !uipProbe.error && uipProbe.status === 0
|
|
117
|
-
? { file: 'uip', prefix: ['maestro', 'flow'] }
|
|
118
|
-
: { file: 'npx', prefix: ['--no-install', 'flow-sdk', 'flow'] };
|
|
119
|
-
const run = (verb, args) =>
|
|
120
|
-
execFileSync(frontDoor.file, [...frontDoor.prefix, verb, ...args], { stdio: 'inherit' });
|
|
121
|
-
|
|
122
115
|
// Ensure the decompiled source exists (a fresh clone may have only the .flow).
|
|
123
116
|
if (!existsSync(TS)) run('decompile', [ORIGINAL, '-o', TS, '--no-pipeline']);
|
|
124
117
|
|
|
@@ -20,4 +20,25 @@ export declare const CONNECTORS_LIBRARY_VERSION_ENV = "CONNECTORS_LIBRARY_VERSIO
|
|
|
20
20
|
* rather than guessed at, so a typo cannot fall back to "latest" silently.
|
|
21
21
|
*/
|
|
22
22
|
export declare function connectorsLibraryUrl(argv: string[], env?: Record<string, string | undefined>): string;
|
|
23
|
+
/**
|
|
24
|
+
* Point `current` at an entry, replacing whatever it named.
|
|
25
|
+
*
|
|
26
|
+
* Nothing to do when `current` already resolves to the entry — the unchanged
|
|
27
|
+
* pull, which is most of them.
|
|
28
|
+
*
|
|
29
|
+
* Otherwise the new link is made under a temp name and renamed over `current`.
|
|
30
|
+
* On POSIX that rename is atomic over an existing symlink, so a concurrent
|
|
31
|
+
* compile never finds the link missing. On Windows `current` is a junction, and
|
|
32
|
+
* a rename onto an existing junction fails with `EPERM` (flow-builder-sdk#945),
|
|
33
|
+
* so there the old link is parked under `current.old-<pid>` first, the new one
|
|
34
|
+
* renamed in, and the parked one removed; a failed rename puts the parked link
|
|
35
|
+
* back. That leaves a moment where `current` does not exist, which Windows
|
|
36
|
+
* gives no way to avoid with a junction.
|
|
37
|
+
*
|
|
38
|
+
* Links are removed with `unlink`, never a recursive `rm`: the link's target is
|
|
39
|
+
* the extracted library, and removing the link must not reach into it.
|
|
40
|
+
*
|
|
41
|
+
* @param platform - Which branch to take; a parameter so both run in tests.
|
|
42
|
+
*/
|
|
43
|
+
export declare function pointCurrentAt(root: string, sha256: string, platform?: NodeJS.Platform): Promise<void>;
|
|
23
44
|
export declare function run(argv: string[]): Promise<number>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { reportResult } from '../cli-result.js';
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
-
import { mkdir, readFile, rename, rm, stat, symlink, writeFile, } from "node:fs/promises";
|
|
3
|
+
import { mkdir, readFile, realpath, rename, rm, stat, symlink, unlink, writeFile, } from "node:fs/promises";
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
5
|
import { writeJsonFile } from "../core/json-file.js";
|
|
6
6
|
import { CURRENT_LINK, LIBRARY_JSON_DIR, LIBRARY_MARKDOWN_DIR, cacheEntry, libraryCacheRoot, referencedConnectorKeys, } from "../core/library-cache.js";
|
|
@@ -215,16 +215,81 @@ async function writeSource(libraryPath, archive, sha256, archiveSha256, overlayS
|
|
|
215
215
|
/**
|
|
216
216
|
* Point `current` at an entry, replacing whatever it named.
|
|
217
217
|
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
218
|
+
* Nothing to do when `current` already resolves to the entry — the unchanged
|
|
219
|
+
* pull, which is most of them.
|
|
220
|
+
*
|
|
221
|
+
* Otherwise the new link is made under a temp name and renamed over `current`.
|
|
222
|
+
* On POSIX that rename is atomic over an existing symlink, so a concurrent
|
|
223
|
+
* compile never finds the link missing. On Windows `current` is a junction, and
|
|
224
|
+
* a rename onto an existing junction fails with `EPERM` (flow-builder-sdk#945),
|
|
225
|
+
* so there the old link is parked under `current.old-<pid>` first, the new one
|
|
226
|
+
* renamed in, and the parked one removed; a failed rename puts the parked link
|
|
227
|
+
* back. That leaves a moment where `current` does not exist, which Windows
|
|
228
|
+
* gives no way to avoid with a junction.
|
|
229
|
+
*
|
|
230
|
+
* Links are removed with `unlink`, never a recursive `rm`: the link's target is
|
|
231
|
+
* the extracted library, and removing the link must not reach into it.
|
|
232
|
+
*
|
|
233
|
+
* @param platform - Which branch to take; a parameter so both run in tests.
|
|
221
234
|
*/
|
|
222
|
-
async function pointCurrentAt(root, sha256) {
|
|
235
|
+
export async function pointCurrentAt(root, sha256, platform = process.platform) {
|
|
223
236
|
const link = join(root, CURRENT_LINK);
|
|
237
|
+
if (await resolvesTo(link, join(root, sha256)))
|
|
238
|
+
return;
|
|
224
239
|
const temporary = `${link}.tmp-${process.pid}`;
|
|
225
|
-
await
|
|
226
|
-
|
|
227
|
-
|
|
240
|
+
await removeLink(temporary);
|
|
241
|
+
try {
|
|
242
|
+
await symlink(sha256, temporary, "junction");
|
|
243
|
+
if (platform !== "win32") {
|
|
244
|
+
await rename(temporary, link);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const parked = `${link}.old-${process.pid}`;
|
|
248
|
+
await removeLink(parked);
|
|
249
|
+
let wasParked = false;
|
|
250
|
+
try {
|
|
251
|
+
await rename(link, parked);
|
|
252
|
+
wasParked = true;
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
if (error.code !== "ENOENT")
|
|
256
|
+
throw error;
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
await rename(temporary, link);
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
if (wasParked)
|
|
263
|
+
await rename(parked, link).catch(() => undefined);
|
|
264
|
+
throw error;
|
|
265
|
+
}
|
|
266
|
+
if (wasParked)
|
|
267
|
+
await removeLink(parked);
|
|
268
|
+
}
|
|
269
|
+
finally {
|
|
270
|
+
// Gone already after a successful rename; this is for every way out that
|
|
271
|
+
// is not one, so a failed pull leaves no `current.tmp-<pid>` behind.
|
|
272
|
+
await removeLink(temporary);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/** Whether `link` and `entry` name the same directory; false when either is missing. */
|
|
276
|
+
async function resolvesTo(link, entry) {
|
|
277
|
+
try {
|
|
278
|
+
return (await realpath(link)) === (await realpath(entry));
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/** Remove a symlink or junction without following it; a missing one is fine. */
|
|
285
|
+
async function removeLink(path) {
|
|
286
|
+
try {
|
|
287
|
+
await unlink(path);
|
|
288
|
+
}
|
|
289
|
+
catch (error) {
|
|
290
|
+
if (error.code !== "ENOENT")
|
|
291
|
+
throw error;
|
|
292
|
+
}
|
|
228
293
|
}
|
|
229
294
|
export async function run(argv) {
|
|
230
295
|
if (has(argv, "--help") || has(argv, "-h")) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { win32 } from "node:path";
|
|
1
2
|
export interface Launch {
|
|
2
3
|
file: string;
|
|
3
4
|
args: string[];
|
|
@@ -13,12 +14,21 @@ export interface LaunchHost {
|
|
|
13
14
|
execPath: string;
|
|
14
15
|
isFile(path: string): boolean;
|
|
15
16
|
readFile(path: string): string;
|
|
17
|
+
/** Windows path operations (`node:path`'s `win32`), passed in so {@link resolveLaunch} imports nothing. */
|
|
18
|
+
win32: Pick<typeof win32, "join" | "dirname" | "extname">;
|
|
16
19
|
}
|
|
20
|
+
/** The host this process runs on. */
|
|
21
|
+
export declare function ambientLaunchHost(): LaunchHost;
|
|
17
22
|
/**
|
|
18
|
-
* The file and arguments to spawn for `executable args`, without a shell.
|
|
23
|
+
* The file and arguments to spawn for `executable args` on `host`, without a shell.
|
|
24
|
+
*
|
|
25
|
+
* Self-contained on purpose (see the module comment): generated scripts embed
|
|
26
|
+
* `resolveLaunch.toString()`.
|
|
19
27
|
*
|
|
20
28
|
* @throws when, on Windows, the name resolves to a batch shim this cannot read
|
|
21
29
|
* a script out of — naming the shim, so the remedy is not "put uip on PATH"
|
|
22
30
|
* for a `uip` that is on PATH.
|
|
23
31
|
*/
|
|
32
|
+
export declare function resolveLaunch(executable: string, args: readonly string[], host: LaunchHost): Launch;
|
|
33
|
+
/** {@link resolveLaunch} on this process's host. */
|
|
24
34
|
export declare function launchFor(executable: string, args: readonly string[], host?: LaunchHost): Launch;
|
|
@@ -22,6 +22,13 @@
|
|
|
22
22
|
* A name that is not bare (a path, or one with an extension) is spawned as
|
|
23
23
|
* given, and so is anything on another platform.
|
|
24
24
|
*
|
|
25
|
+
* The rules live in ONE self-contained function, {@link resolveLaunch}, because
|
|
26
|
+
* a second copy runs outside this package: the brownfield pipeline scripts that
|
|
27
|
+
* `flow decompile` and `case decompile` write embed its source text
|
|
28
|
+
* (`pipeline-front-door.ts`, flow-builder-sdk#1120). So it may reference only
|
|
29
|
+
* its parameters and globals — no module constant, no import — and a test runs
|
|
30
|
+
* its source text in an empty context to hold that.
|
|
31
|
+
*
|
|
25
32
|
* Example, on Windows with npm's global bin on PATH:
|
|
26
33
|
* launchFor("uip", ["--version"])
|
|
27
34
|
* // -> { file: "C:\\Program Files\\nodejs\\node.exe",
|
|
@@ -29,14 +36,8 @@
|
|
|
29
36
|
*/
|
|
30
37
|
import { readFileSync, statSync } from "node:fs";
|
|
31
38
|
import { win32 } from "node:path";
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
* The script an npm (`cmd-shim`) or pnpm shim hands its arguments to: the last
|
|
35
|
-
* quoted `%dp0%\…` / `%~dp0\…` path followed by `%*`. Only a JavaScript target
|
|
36
|
-
* counts; a shim around something else is not one this module can run directly.
|
|
37
|
-
*/
|
|
38
|
-
const SHIM_TARGET = /"%~?dp0%?\\([^"]+\.[cm]?js)"\s+%\*/g;
|
|
39
|
-
function ambientHost() {
|
|
39
|
+
/** The host this process runs on. */
|
|
40
|
+
export function ambientLaunchHost() {
|
|
40
41
|
return {
|
|
41
42
|
platform: process.platform,
|
|
42
43
|
path: process.env.PATH,
|
|
@@ -51,55 +52,61 @@ function ambientHost() {
|
|
|
51
52
|
}
|
|
52
53
|
},
|
|
53
54
|
readFile: (path) => readFileSync(path, "utf8"),
|
|
55
|
+
win32,
|
|
54
56
|
};
|
|
55
57
|
}
|
|
56
|
-
/** True for a name `PATH` lookup applies to: no directory part and no extension. */
|
|
57
|
-
function isBareName(executable) {
|
|
58
|
-
return executable !== "" && !/[\\/]/.test(executable) && win32.extname(executable) === "";
|
|
59
|
-
}
|
|
60
|
-
/** The first `<dir>\<executable><ext>` on PATH, in cmd.exe's order, or undefined. */
|
|
61
|
-
function findOnPath(executable, host) {
|
|
62
|
-
const extensions = (host.pathExt || DEFAULT_PATHEXT).split(";").filter(Boolean);
|
|
63
|
-
for (const dir of (host.path ?? "").split(";")) {
|
|
64
|
-
const trimmed = dir.trim().replace(/^"(.*)"$/, "$1");
|
|
65
|
-
if (trimmed === "")
|
|
66
|
-
continue;
|
|
67
|
-
for (const ext of extensions) {
|
|
68
|
-
const candidate = win32.join(trimmed, executable + ext.toLowerCase());
|
|
69
|
-
if (host.isFile(candidate))
|
|
70
|
-
return candidate;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return undefined;
|
|
74
|
-
}
|
|
75
|
-
/** The script a `.cmd`/`.bat` shim runs, as an absolute path, or undefined. */
|
|
76
|
-
function shimTarget(shim, host) {
|
|
77
|
-
const matches = [...host.readFile(shim).matchAll(SHIM_TARGET)];
|
|
78
|
-
const last = matches.at(-1);
|
|
79
|
-
return last ? win32.join(win32.dirname(shim), last[1]) : undefined;
|
|
80
|
-
}
|
|
81
58
|
/**
|
|
82
|
-
* The file and arguments to spawn for `executable args`, without a shell.
|
|
59
|
+
* The file and arguments to spawn for `executable args` on `host`, without a shell.
|
|
60
|
+
*
|
|
61
|
+
* Self-contained on purpose (see the module comment): generated scripts embed
|
|
62
|
+
* `resolveLaunch.toString()`.
|
|
83
63
|
*
|
|
84
64
|
* @throws when, on Windows, the name resolves to a batch shim this cannot read
|
|
85
65
|
* a script out of — naming the shim, so the remedy is not "put uip on PATH"
|
|
86
66
|
* for a `uip` that is on PATH.
|
|
87
67
|
*/
|
|
88
|
-
export function
|
|
89
|
-
|
|
68
|
+
export function resolveLaunch(executable, args, host) {
|
|
69
|
+
const path = host.win32;
|
|
70
|
+
// Bare: no directory part and no extension. Only a bare name is looked up.
|
|
71
|
+
const bare = executable !== "" && !/[\\/]/.test(executable) && path.extname(executable) === "";
|
|
72
|
+
if (host.platform !== "win32" || !bare)
|
|
90
73
|
return { file: executable, args: [...args] };
|
|
91
|
-
|
|
74
|
+
// The first `<dir>\<executable><ext>` on PATH, in cmd.exe's PATHEXT order.
|
|
75
|
+
const extensions = (host.pathExt || ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean);
|
|
76
|
+
let found;
|
|
77
|
+
search: for (const dir of (host.path ?? "").split(";")) {
|
|
78
|
+
const trimmed = dir.trim().replace(/^"(.*)"$/, "$1");
|
|
79
|
+
if (trimmed === "")
|
|
80
|
+
continue;
|
|
81
|
+
for (const ext of extensions) {
|
|
82
|
+
const candidate = path.join(trimmed, executable + ext.toLowerCase());
|
|
83
|
+
if (host.isFile(candidate)) {
|
|
84
|
+
found = candidate;
|
|
85
|
+
break search;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
92
89
|
// Not on PATH: spawn the bare name and let the ENOENT say so.
|
|
93
|
-
if (
|
|
90
|
+
if (found === undefined)
|
|
94
91
|
return { file: executable, args: [...args] };
|
|
95
|
-
const ext =
|
|
92
|
+
const ext = path.extname(found).toLowerCase();
|
|
96
93
|
if (ext !== ".cmd" && ext !== ".bat")
|
|
97
94
|
return { file: found, args: [...args] };
|
|
98
|
-
|
|
99
|
-
|
|
95
|
+
// The script an npm (cmd-shim) or pnpm shim hands its arguments to: the last
|
|
96
|
+
// quoted `%dp0%\…` / `%~dp0\…` JavaScript path followed by `%*`.
|
|
97
|
+
const matches = [...host.readFile(found).matchAll(/"%~?dp0%?\\([^"]+\.[cm]?js)"\s+%\*/g)];
|
|
98
|
+
const last = matches[matches.length - 1];
|
|
99
|
+
const script = last ? path.join(path.dirname(found), last[1]) : undefined;
|
|
100
|
+
if (script === undefined || !host.isFile(script)) {
|
|
100
101
|
throw new Error(`Could not start ${executable}: ${found} is a batch file, which Node cannot run without a shell, ` +
|
|
101
|
-
(script
|
|
102
|
-
|
|
102
|
+
(script !== undefined
|
|
103
|
+
? `and the script it names (${script}) does not exist.`
|
|
104
|
+
: "and it names no JavaScript entry point to run instead.") +
|
|
105
|
+
" Reinstall it with npm (for the UiPath CLI, `npm install -g @uipath/cli`).");
|
|
103
106
|
}
|
|
104
107
|
return { file: host.execPath, args: [script, ...args] };
|
|
105
108
|
}
|
|
109
|
+
/** {@link resolveLaunch} on this process's host. */
|
|
110
|
+
export function launchFor(executable, args, host = ambientLaunchHost()) {
|
|
111
|
+
return resolveLaunch(executable, args, host);
|
|
112
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The "front door" block of a generated brownfield pipeline script: how the
|
|
3
|
+
* script finds a CLI to run `compile`, `decompile` and `merge` with.
|
|
4
|
+
*
|
|
5
|
+
* `flow decompile` and `case decompile` each write a `<Name>.pipeline.mjs`
|
|
6
|
+
* beside the source. Both used to carry their own copy of this block, and both
|
|
7
|
+
* spawned the bare names `uip` and `npx` without a shell. On Windows both are
|
|
8
|
+
* `.cmd` shims, which Node cannot start that way, so the probe for `uip` failed,
|
|
9
|
+
* the `npx` fallback failed too, and the pipeline could not run at all
|
|
10
|
+
* (flow-builder-sdk#1120, the same defect as #940 in the registry).
|
|
11
|
+
*
|
|
12
|
+
* Now there is one block, and it starts programs through the same rules the
|
|
13
|
+
* registry uses: it embeds the source text of `resolveLaunch`
|
|
14
|
+
* (`registry/uip/launch.ts`), so a fix there reaches every pipeline written
|
|
15
|
+
* after it. The order is unchanged:
|
|
16
|
+
* 1. `uip maestro <kind>`, when `uip maestro <kind> compile --help` exits 0
|
|
17
|
+
* (a CLI that carries the authoring commands);
|
|
18
|
+
* 2. otherwise the SDK's own `flow-sdk` bin, found the way
|
|
19
|
+
* `npx --no-install flow-sdk` found it: `node_modules/.bin` in the script's
|
|
20
|
+
* folder and every folder above it, then `PATH`.
|
|
21
|
+
*
|
|
22
|
+
* The block brings its own imports, `existsSync` among them, and defines
|
|
23
|
+
* `run(verb, args)`; the pipeline body uses both. Nothing in it names a path on
|
|
24
|
+
* the machine that generated it, so a pipeline stays relocatable.
|
|
25
|
+
*
|
|
26
|
+
* Example, for `kind = 'flow'` on a machine whose `uip` has the commands:
|
|
27
|
+
* run('compile', ['A.flow.ts', '-o', 'A.edited.flow'])
|
|
28
|
+
* // POSIX: uip maestro flow compile A.flow.ts -o A.edited.flow
|
|
29
|
+
* // Windows: node.exe <npm>\node_modules\@uipath\cli\dist\index.js maestro flow compile …
|
|
30
|
+
*/
|
|
31
|
+
import { resolveLaunch } from './launch.js';
|
|
32
|
+
/** The script text: imports, the embedded resolver, `frontDoor`, and `run(verb, args)`. */
|
|
33
|
+
export function pipelineFrontDoor(kind) {
|
|
34
|
+
return `import { execFileSync, spawnSync } from 'node:child_process';
|
|
35
|
+
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
36
|
+
import { dirname, join, win32 } from 'node:path';
|
|
37
|
+
import { fileURLToPath } from 'node:url';
|
|
38
|
+
|
|
39
|
+
const KIND = ${JSON.stringify(kind)};
|
|
40
|
+
|
|
41
|
+
// How to start a program by name without a shell, Windows .cmd shims included.
|
|
42
|
+
// Copied from the SDK's registry/uip/launch.ts when this file was generated.
|
|
43
|
+
${resolveLaunch.toString()}
|
|
44
|
+
|
|
45
|
+
const launchHost = (path) => ({
|
|
46
|
+
platform: process.platform,
|
|
47
|
+
path,
|
|
48
|
+
pathExt: process.env.PATHEXT,
|
|
49
|
+
execPath: process.execPath,
|
|
50
|
+
isFile: (p) => { try { return statSync(p).isFile(); } catch { return false; } },
|
|
51
|
+
readFile: (p) => readFileSync(p, 'utf8'),
|
|
52
|
+
win32,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// 1. \`uip maestro <kind>\`, when this CLI carries the authoring commands.
|
|
56
|
+
function uipFrontDoor() {
|
|
57
|
+
let uip;
|
|
58
|
+
try {
|
|
59
|
+
uip = resolveLaunch('uip', [], launchHost(process.env.PATH));
|
|
60
|
+
} catch {
|
|
61
|
+
return undefined; // a uip shim that cannot be read: use the SDK instead
|
|
62
|
+
}
|
|
63
|
+
const probe = spawnSync(uip.file, [...uip.args, 'maestro', KIND, 'compile', '--help'], { stdio: 'ignore' });
|
|
64
|
+
return !probe.error && probe.status === 0 ? { file: uip.file, prefix: [...uip.args, 'maestro', KIND] } : undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 2. The SDK's flow-sdk bin, where \`npx --no-install flow-sdk\` looked for it.
|
|
68
|
+
function sdkFrontDoor() {
|
|
69
|
+
const bins = [];
|
|
70
|
+
for (let dir = dirname(fileURLToPath(import.meta.url)); ; dir = dirname(dir)) {
|
|
71
|
+
bins.push(join(dir, 'node_modules', '.bin'));
|
|
72
|
+
if (dirname(dir) === dir) break;
|
|
73
|
+
}
|
|
74
|
+
let sdk;
|
|
75
|
+
if (process.platform === 'win32') {
|
|
76
|
+
sdk = resolveLaunch('flow-sdk', [], launchHost([...bins, process.env.PATH ?? ''].join(';')));
|
|
77
|
+
} else {
|
|
78
|
+
const local = bins.map((bin) => join(bin, 'flow-sdk')).find((p) => existsSync(p));
|
|
79
|
+
sdk = { file: local ?? 'flow-sdk', args: [] };
|
|
80
|
+
}
|
|
81
|
+
return { file: sdk.file, prefix: [...sdk.args, KIND] };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const frontDoor = uipFrontDoor() ?? sdkFrontDoor();
|
|
85
|
+
const run = (verb, args) =>
|
|
86
|
+
execFileSync(frontDoor.file, [...frontDoor.prefix, verb, ...args], { stdio: 'inherit' });
|
|
87
|
+
`;
|
|
88
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uipath/maestro-builder-sdk",
|
|
3
|
-
"version": "6.18.
|
|
3
|
+
"version": "6.18.10",
|
|
4
4
|
"description": "Build UiPath Flow, Case, and BPMN artifacts by writing TypeScript.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://docs.uipath.com/maestro",
|
|
@@ -92,5 +92,5 @@
|
|
|
92
92
|
"@types/node": "^22.7.0",
|
|
93
93
|
"esbuild": "^0.28.1"
|
|
94
94
|
},
|
|
95
|
-
"gitref": "
|
|
95
|
+
"gitref": "0bbd0a5b9951750e0b3009b17b0188e485266cbe"
|
|
96
96
|
}
|