@uipath/function-tool 1.203.0-preview.205 → 1.204.0-preview.206

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.
@@ -0,0 +1,961 @@
1
+ import {
2
+ getAuthEnv
3
+ } from "./packager-tool-4r3qztwy.js";
4
+ import {
5
+ OutputFormatter,
6
+ RESULTS,
7
+ UIPATH_HOME_DIR,
8
+ catchError,
9
+ getOutputFormat,
10
+ getOutputFormatExplicit,
11
+ getOutputSink,
12
+ logger,
13
+ processContext
14
+ } from "./packager-tool-5s8z8be9.js";
15
+ import"./packager-tool-0gctz400.js";
16
+ import"./packager-tool-qa9gftnj.js";
17
+ import {
18
+ getFileSystem
19
+ } from "./packager-tool-g0hcxnae.js";
20
+ import"./packager-tool-yc4kw997.js";
21
+ import"./packager-tool-1de529jm.js";
22
+
23
+ // ../uipath-python-bridge/src/cache.ts
24
+ async function readCache(cacheFile) {
25
+ const fs = getFileSystem();
26
+ const [existsError, exists] = await catchError(fs.exists(cacheFile));
27
+ if (existsError || !exists)
28
+ return null;
29
+ const [readError, content] = await catchError(fs.readFile(cacheFile, "utf-8"));
30
+ if (readError) {
31
+ logger.warn(`Failed to load cache: ${readError}`);
32
+ return null;
33
+ }
34
+ const [parseError, cache] = catchError(() => JSON.parse(content));
35
+ if (parseError) {
36
+ logger.warn(`Failed to parse cache: ${parseError}`);
37
+ return null;
38
+ }
39
+ return cache;
40
+ }
41
+ // ../uipath-python-bridge/src/config.ts
42
+ var DEFAULT_CONFIG = {
43
+ allowedPythonVersions: ["3.14", "3.13", "3.12", "3.11"],
44
+ packageName: "uipath",
45
+ cacheFileName: ".uipath-python-cache.json"
46
+ };
47
+ function getAllowedPythonVersions() {
48
+ const envVersions = process.env.PYTHON_TOOL_PYTHON_VERSIONS;
49
+ if (envVersions) {
50
+ return envVersions.split(",").map((v) => v.trim()).filter(Boolean);
51
+ }
52
+ return DEFAULT_CONFIG.allowedPythonVersions;
53
+ }
54
+ function getPackageName() {
55
+ return DEFAULT_CONFIG.packageName;
56
+ }
57
+ function getCacheFileName() {
58
+ return DEFAULT_CONFIG.cacheFileName;
59
+ }
60
+ function matchesPattern(args, pattern) {
61
+ if (typeof pattern === "string") {
62
+ return args.includes(pattern);
63
+ }
64
+ if (Array.isArray(pattern)) {
65
+ for (let i = 0;i <= args.length - pattern.length; i++) {
66
+ let match = true;
67
+ for (let j = 0;j < pattern.length; j++) {
68
+ if (args[i + j] !== pattern[j]) {
69
+ match = false;
70
+ break;
71
+ }
72
+ }
73
+ if (match) {
74
+ return true;
75
+ }
76
+ }
77
+ }
78
+ return false;
79
+ }
80
+ function processCommandArgs(args, rules) {
81
+ const hasForceFlag = args.includes("--force");
82
+ const argsWithoutForce = args.filter((arg) => arg !== "--force");
83
+ for (const rule of rules) {
84
+ if (matchesPattern(argsWithoutForce, rule.pattern)) {
85
+ switch (rule.action) {
86
+ case "filter":
87
+ if (hasForceFlag) {
88
+ return {
89
+ allowed: true,
90
+ args: [...argsWithoutForce]
91
+ };
92
+ }
93
+ return {
94
+ allowed: false,
95
+ args: [],
96
+ errorMessage: rule.filterMessage || "This command is not allowed"
97
+ };
98
+ case "append":
99
+ if (rule.appendArgs && rule.appendArgs.length > 0) {
100
+ return {
101
+ allowed: true,
102
+ args: [...argsWithoutForce, ...rule.appendArgs]
103
+ };
104
+ }
105
+ break;
106
+ }
107
+ }
108
+ }
109
+ return {
110
+ allowed: true,
111
+ args: [...argsWithoutForce]
112
+ };
113
+ }
114
+ // ../uipath-python-bridge/src/execute.ts
115
+ import { spawn } from "node:child_process";
116
+
117
+ // ../uipath-python-bridge/src/scaffold-type.ts
118
+ import { execFile } from "node:child_process";
119
+ var PROBE_TIMEOUT_MS = 15000;
120
+ var TYPE_OPTION = /^--type(=|$)/;
121
+ var TYPE_LISTED = /^[ \t]*--type(?![\w-])/m;
122
+ function hasTypeOption(args) {
123
+ return args.some((arg) => TYPE_OPTION.test(arg));
124
+ }
125
+ async function supportsNewTypeOption(exePath, cwd, env = {}, signal) {
126
+ return new Promise((resolve) => {
127
+ execFile(exePath, ["new", "--help"], {
128
+ cwd,
129
+ env: { ...process.env, ...env },
130
+ timeout: PROBE_TIMEOUT_MS,
131
+ windowsHide: true,
132
+ signal
133
+ }, (error, stdout) => {
134
+ resolve(!error && TYPE_LISTED.test(typeof stdout === "string" ? stdout : ""));
135
+ });
136
+ });
137
+ }
138
+
139
+ // ../uipath-python-bridge/src/execute.ts
140
+ var ENV_FILE_NAME = ".env";
141
+ function isHelpCommand(args) {
142
+ return args.includes("--help") || args.includes("-h");
143
+ }
144
+ function hasOption(args, option) {
145
+ return args.some((arg) => arg === option || arg.startsWith(`${option}=`));
146
+ }
147
+ function applyOutputFormatOption(args, option, supportedFormats) {
148
+ if (!option || !getOutputFormatExplicit() || isHelpCommand(args) || hasOption(args, option)) {
149
+ return args;
150
+ }
151
+ const format = getOutputFormat();
152
+ if (supportedFormats && !supportedFormats.includes(format)) {
153
+ return args;
154
+ }
155
+ return [...args, option, format];
156
+ }
157
+ function matchesRequiredCommand(args, commands) {
158
+ return args[0] !== undefined && commands.includes(args[0]);
159
+ }
160
+ function envValueFromFile(content, name) {
161
+ const envLine = content.split(/\r?\n/).map((line) => line.trim()).find((line) => {
162
+ if (!line || line.startsWith("#")) {
163
+ return false;
164
+ }
165
+ const [key, ...valueParts2] = line.split("=");
166
+ return key.trim() === name && valueParts2.join("=").trim().length > 0;
167
+ });
168
+ if (!envLine) {
169
+ return null;
170
+ }
171
+ const [, ...valueParts] = envLine.split("=");
172
+ return valueParts.join("=").trim().replace(/^["']|["']$/g, "") || null;
173
+ }
174
+ async function getConfiguredEnvValue(fs, name) {
175
+ const processValue = process.env[name]?.trim();
176
+ if (processValue) {
177
+ return processValue;
178
+ }
179
+ const envPath = fs.path.join(fs.env.cwd(), ENV_FILE_NAME);
180
+ if (!await fs.exists(envPath)) {
181
+ return null;
182
+ }
183
+ const [readError, content] = await catchError(fs.readFile(envPath, "utf-8"));
184
+ return !readError && typeof content === "string" ? envValueFromFile(content, name) : null;
185
+ }
186
+ var fail = (reason, message, instructions, extras = {}) => ({
187
+ ok: false,
188
+ reason,
189
+ message,
190
+ instructions,
191
+ exitCode: extras.exitCode ?? 1,
192
+ stderr: extras.stderr
193
+ });
194
+ function childStdio(stdio) {
195
+ switch (stdio) {
196
+ case "pipe":
197
+ return ["ignore", "pipe", "pipe"];
198
+ case "stdout-to-stderr":
199
+ return ["inherit", "pipe", "inherit"];
200
+ default:
201
+ return "inherit";
202
+ }
203
+ }
204
+ async function runUipathPythonCommand(args, options = {}) {
205
+ const fs = getFileSystem();
206
+ const cacheFile = fs.path.join(fs.env.homedir(), UIPATH_HOME_DIR, getCacheFileName());
207
+ const toolLabel = options.toolName ?? options.commandPrefix ?? "codedagent";
208
+ const setupCommand = `uip ${toolLabel} setup`;
209
+ const setupHint = `Run '${setupCommand}' first to configure the environment. ` + `Activate the project's virtual environment and run ${setupCommand} --force.`;
210
+ const cache = await readCache(cacheFile);
211
+ if (!cache?.uipathExePath) {
212
+ return fail("python_not_configured", "Python not configured.", setupHint);
213
+ }
214
+ if (!await fs.exists(cache.uipathExePath)) {
215
+ return fail("uipath_exe_missing", "uipath executable not found. Please install it using 'pip install uipath' or 'uv add uipath'.", setupHint);
216
+ }
217
+ const processed = processCommandArgs(args, options.commandRules ?? []);
218
+ if (!processed.allowed) {
219
+ return fail("command_disabled", processed.errorMessage || "Command disabled.", "Use of --force flag is not recommended. Favor alternate command.", { exitCode: 3 });
220
+ }
221
+ if (options.pinNewType && processed.args[0] === "new" && !isHelpCommand(processed.args) && hasTypeOption(processed.args)) {
222
+ return fail("new_type_not_allowed", "--type is not a valid option.", "Remove --type and re-run.");
223
+ }
224
+ const processedArgs = applyOutputFormatOption(processed.args, options.outputFormatOption, options.outputFormats);
225
+ const commandArgs = options.commandPrefix ? [options.commandPrefix, ...processedArgs] : [...processedArgs];
226
+ const requiredEnv = {};
227
+ for (const requirement of options.requiredEnvironmentVariables ?? []) {
228
+ const applies = matchesRequiredCommand(processed.args, requirement.commands) && (!requirement.skipWhenHelp || !isHelpCommand(processed.args));
229
+ if (!applies) {
230
+ continue;
231
+ }
232
+ const value = await getConfiguredEnvValue(fs, requirement.name);
233
+ if (!value) {
234
+ return fail("required_env_missing", `${requirement.name} environment variable not found.`, requirement.instructions);
235
+ }
236
+ requiredEnv[requirement.name] = value;
237
+ }
238
+ const { authEnv, loginStatus } = await getAuthEnv();
239
+ if (!loginStatus) {
240
+ logger.debug("Auth unavailable, continuing without credentials.");
241
+ }
242
+ if (options.commandPreflight) {
243
+ const preflight = options.commandPreflight;
244
+ const [preflightError, preflightResult] = await catchError(Promise.resolve().then(() => preflight({
245
+ args: commandArgs,
246
+ loginStatus,
247
+ authEnv
248
+ })));
249
+ if (preflightError) {
250
+ return fail("preflight_error", preflightError.message, "Check your credentials and configuration, then retry.");
251
+ }
252
+ if (preflightResult && !preflightResult.allowed) {
253
+ return fail("preflight_rejected", preflightResult.message || "Command preflight check failed.", preflightResult.instructions || "Update the command configuration and try again.", { exitCode: preflightResult.exitCode ?? 1 });
254
+ }
255
+ }
256
+ const stdio = options.stdio ?? "inherit";
257
+ const cwd = options.cwd ?? fs.env.cwd();
258
+ if (options.pinNewType && processed.args[0] === "new" && !isHelpCommand(processed.args) && await supportsNewTypeOption(cache.uipathExePath, cwd, { ...authEnv, ...requiredEnv }, options.signal)) {
259
+ commandArgs.push("--type", options.pinNewType);
260
+ }
261
+ const proc = spawn(cache.uipathExePath, commandArgs, {
262
+ cwd,
263
+ env: {
264
+ ...process.env,
265
+ ...authEnv,
266
+ ...requiredEnv
267
+ },
268
+ stdio: childStdio(stdio),
269
+ signal: options.signal
270
+ });
271
+ let stdout = "";
272
+ let stderr = "";
273
+ if (stdio === "pipe") {
274
+ proc.stdout?.setEncoding("utf8");
275
+ proc.stderr?.setEncoding("utf8");
276
+ proc.stdout?.on("data", (chunk) => {
277
+ stdout += chunk;
278
+ });
279
+ proc.stderr?.on("data", (chunk) => {
280
+ stderr += chunk;
281
+ });
282
+ } else if (stdio === "stdout-to-stderr") {
283
+ proc.stdout?.setEncoding("utf8");
284
+ proc.stdout?.on("data", (chunk) => {
285
+ getOutputSink().writeErr(chunk);
286
+ });
287
+ }
288
+ const [spawnError, exitCode] = await catchError(new Promise((resolve, reject) => {
289
+ proc.on("close", (code) => resolve(code ?? 1));
290
+ proc.on("error", reject);
291
+ }));
292
+ if (spawnError) {
293
+ return fail("spawn_failed", spawnError.message, setupHint, {
294
+ stderr: stdio === "pipe" ? stderr : undefined
295
+ });
296
+ }
297
+ if (exitCode !== 0) {
298
+ return fail("command_failed", `Python 'uipath ${commandArgs.join(" ")}' exited with code ${exitCode}.`, stdio === "pipe" && stderr ? "See the captured stderr below for the Python tool's error output." : "Re-run with verbose flags or stream stdio inherit to diagnose.", {
299
+ exitCode,
300
+ stderr: stdio === "pipe" ? stderr : undefined
301
+ });
302
+ }
303
+ return {
304
+ ok: true,
305
+ pythonExePath: cache.uipathExePath,
306
+ exitCode: 0,
307
+ stdout: stdio === "pipe" ? stdout : undefined,
308
+ stderr: stdio === "pipe" ? stderr : undefined
309
+ };
310
+ }
311
+ var CLI_RESULT_BY_REASON = {
312
+ python_not_configured: RESULTS.ConfigError,
313
+ uipath_exe_missing: RESULTS.ConfigError,
314
+ command_disabled: RESULTS.ValidationError,
315
+ new_type_not_allowed: RESULTS.ValidationError,
316
+ required_env_missing: RESULTS.ConfigError,
317
+ preflight_error: RESULTS.Failure,
318
+ preflight_rejected: RESULTS.ConfigError,
319
+ spawn_failed: RESULTS.Failure
320
+ };
321
+ function reportUipathPythonFailure(result) {
322
+ if (!result.ok && result.reason !== "command_failed") {
323
+ OutputFormatter.error({
324
+ Result: CLI_RESULT_BY_REASON[result.reason],
325
+ Message: result.message,
326
+ Instructions: result.instructions
327
+ });
328
+ }
329
+ return result.exitCode;
330
+ }
331
+ function createExecuteCommand(config) {
332
+ const executeCommand = async (rawArgs, _options) => {
333
+ const runIndex = rawArgs.indexOf("exec");
334
+ const actualArgs = runIndex >= 0 ? rawArgs.slice(runIndex + 1) : [];
335
+ const result = await runUipathPythonCommand(actualArgs, {
336
+ commandPrefix: config.commandPrefix,
337
+ toolName: config.toolName,
338
+ pinNewType: config.pinNewType,
339
+ commandRules: config.commandRules,
340
+ commandPreflight: config.commandPreflight,
341
+ requiredEnvironmentVariables: config.requiredEnvironmentVariables,
342
+ outputFormatOption: config.outputFormatOption,
343
+ outputFormats: config.outputFormats,
344
+ stdio: "inherit"
345
+ });
346
+ processContext.exit(reportUipathPythonFailure(result));
347
+ return result;
348
+ };
349
+ const registerExecCommand = (program) => {
350
+ program.command("exec", { hidden: true }).description("Execute the uipath package with provided arguments").allowUnknownOption(true).allowExcessArguments(true).trackedAction(processContext, async (_options, cmd) => {
351
+ await executeCommand(["_", "_", "exec", ...cmd.args], {});
352
+ });
353
+ };
354
+ return { executeCommand, registerExecCommand };
355
+ }
356
+ // ../uipath-python-bridge/src/python-service.ts
357
+ import { spawn as spawn2 } from "node:child_process";
358
+ import { platform } from "node:os";
359
+ class PythonService {
360
+ cacheFile;
361
+ config;
362
+ uipathExePath;
363
+ packageVersion;
364
+ constructor(config) {
365
+ const fs = getFileSystem();
366
+ this.config = config;
367
+ this.cacheFile = config.cacheFile || fs.path.join(fs.env.homedir(), UIPATH_HOME_DIR, getCacheFileName());
368
+ }
369
+ async setup(packageName, force = false) {
370
+ const [error, result] = await catchError(this._setupInternal(packageName, force));
371
+ if (error) {
372
+ return { success: false, error: error.message };
373
+ }
374
+ return result;
375
+ }
376
+ async _setupInternal(packageName, force) {
377
+ const virtualEnv = process.env.VIRTUAL_ENV || undefined;
378
+ const dormantVenv = await this.checkDormantVenv();
379
+ if (dormantVenv) {
380
+ return dormantVenv;
381
+ }
382
+ if (!force) {
383
+ const reused = await this.reuseCache(packageName, virtualEnv);
384
+ if (reused)
385
+ return reused;
386
+ }
387
+ logger.info("Checking Python...");
388
+ const detection = virtualEnv ? await this.detectVenvPython(virtualEnv) : await this.detectPython();
389
+ if (!detection.success || !detection.pythonPath || !detection.version) {
390
+ return {
391
+ success: false,
392
+ error: detection.error || "Failed to detect Python"
393
+ };
394
+ }
395
+ const pythonPath = detection.pythonPath;
396
+ const version = detection.version;
397
+ const source = detection.source ?? "path";
398
+ this.uipathExePath = detection.uipathExePath;
399
+ if (!this.uipathExePath) {
400
+ if (virtualEnv) {
401
+ return {
402
+ success: false,
403
+ pythonPath,
404
+ pythonVersion: version,
405
+ packageInstalled: false,
406
+ source,
407
+ error: `Package '${packageName}' is not installed in the active virtual environment: ${virtualEnv}`,
408
+ instructions: `Install it inside the active virtual environment (uv pip install ${packageName} or the framework package), then re-run uip ${this.config.toolName ?? "codedagent"} setup --force.`
409
+ };
410
+ }
411
+ return {
412
+ success: false,
413
+ pythonPath,
414
+ pythonVersion: version,
415
+ packageInstalled: false,
416
+ source,
417
+ error: `Package '${packageName}' is not installed.
418
+
419
+ To install the package, run:
420
+ ${pythonPath} -m pip install ${packageName}`
421
+ };
422
+ }
423
+ const versionResult = await this.getUipathVersion(this.uipathExePath);
424
+ if ("error" in versionResult) {
425
+ return {
426
+ success: false,
427
+ pythonPath,
428
+ pythonVersion: version,
429
+ packageInstalled: true,
430
+ uipathExePath: this.uipathExePath,
431
+ source,
432
+ error: versionResult.error
433
+ };
434
+ }
435
+ this.packageVersion = versionResult.version;
436
+ await this.saveCache({
437
+ pythonPath,
438
+ version,
439
+ lastValidated: new Date().toISOString(),
440
+ packageName,
441
+ uipathExePath: this.uipathExePath,
442
+ packageVersion: this.packageVersion,
443
+ source
444
+ });
445
+ return {
446
+ success: true,
447
+ pythonPath,
448
+ pythonVersion: version,
449
+ packageInstalled: true,
450
+ packageVersion: this.packageVersion,
451
+ uipathExePath: this.uipathExePath,
452
+ source
453
+ };
454
+ }
455
+ async reuseCache(packageName, virtualEnv) {
456
+ const fs = getFileSystem();
457
+ logger.debug("Checking cache...");
458
+ const cached = await this.loadCache();
459
+ if (!cached)
460
+ return null;
461
+ if (!cached.uipathExePath || !await fs.exists(cached.uipathExePath)) {
462
+ logger.warn("Cached Python path no longer exists, re-detecting...");
463
+ return null;
464
+ }
465
+ if (virtualEnv && !isPathInside(cached.uipathExePath, virtualEnv)) {
466
+ logger.warn(`Cached uipath executable ${cached.uipathExePath} is outside the active virtual environment ${virtualEnv}, re-detecting...`);
467
+ return null;
468
+ }
469
+ const pythonPath = cached.pythonPath;
470
+ const version = cached.version;
471
+ this.uipathExePath = cached.uipathExePath;
472
+ const source = cached.source ?? (virtualEnv ? "venv" : "path");
473
+ logger.info(`Using cached uipath executable: ${this.uipathExePath}`);
474
+ this.packageVersion = cached.packageVersion;
475
+ if (!this.packageVersion) {
476
+ const versionResult = await this.getUipathVersion(this.uipathExePath);
477
+ if ("error" in versionResult) {
478
+ return {
479
+ success: false,
480
+ pythonPath,
481
+ pythonVersion: version,
482
+ packageInstalled: true,
483
+ uipathExePath: this.uipathExePath,
484
+ source,
485
+ error: versionResult.error
486
+ };
487
+ }
488
+ this.packageVersion = versionResult.version;
489
+ }
490
+ await this.saveCache({
491
+ pythonPath,
492
+ version,
493
+ lastValidated: new Date().toISOString(),
494
+ packageName,
495
+ uipathExePath: this.uipathExePath,
496
+ packageVersion: this.packageVersion,
497
+ source
498
+ });
499
+ return {
500
+ success: true,
501
+ pythonPath,
502
+ pythonVersion: version,
503
+ packageInstalled: true,
504
+ packageVersion: this.packageVersion,
505
+ uipathExePath: this.uipathExePath,
506
+ source
507
+ };
508
+ }
509
+ async detectVenvPython(virtualEnv) {
510
+ const fs = getFileSystem();
511
+ const isWindows = platform() === "win32";
512
+ const command = isWindows ? fs.path.join(virtualEnv, "Scripts", "python.exe") : fs.path.join(virtualEnv, "bin", "python");
513
+ const checkedVersions = [];
514
+ const searchedCommands = [];
515
+ const version = await this.checkPythonVersion(command, [], checkedVersions, searchedCommands);
516
+ if (!version) {
517
+ return {
518
+ success: false,
519
+ error: this.buildNotFoundError(checkedVersions, searchedCommands)
520
+ };
521
+ }
522
+ const metadataPath = await this.findUipathExeFromMetadata(fs, command, []);
523
+ let uipathExePath = metadataPath;
524
+ if (!uipathExePath) {
525
+ const candidate = isWindows ? fs.path.join(virtualEnv, "Scripts", "uipath.exe") : fs.path.join(virtualEnv, "bin", "uipath");
526
+ logger.debug(`Checking for uipath executable at: ${candidate}`);
527
+ if (await fs.exists(candidate)) {
528
+ logger.info(`Found uipath executable at: ${candidate}`);
529
+ uipathExePath = candidate;
530
+ } else {
531
+ logger.error(`uipath executable not found in virtual environment. Checked: ${candidate}`);
532
+ }
533
+ }
534
+ return {
535
+ success: true,
536
+ pythonPath: command,
537
+ version,
538
+ uipathExePath,
539
+ source: "venv"
540
+ };
541
+ }
542
+ async checkDormantVenv() {
543
+ if (process.env.VIRTUAL_ENV) {
544
+ return null;
545
+ }
546
+ const fs = getFileSystem();
547
+ if (!await fs.exists(fs.path.join(fs.env.cwd(), ".venv"))) {
548
+ return null;
549
+ }
550
+ return {
551
+ success: false,
552
+ error: "Found .venv in current directory but no virtual environment is activated.",
553
+ instructions: process.platform === "win32" ? "Run '.venv\\Scripts\\activate' first, then re-run setup." : "Run 'source .venv/bin/activate' first, then re-run setup."
554
+ };
555
+ }
556
+ async detectPython() {
557
+ const pythonCommands = this.getDefaultPythonPaths();
558
+ const checkedVersions = [];
559
+ const searchedCommands = [];
560
+ for (const { command, args } of pythonCommands) {
561
+ const result = await this.checkPythonCommand(command, args, checkedVersions, searchedCommands);
562
+ if (result)
563
+ return result;
564
+ }
565
+ return {
566
+ success: false,
567
+ error: this.buildNotFoundError(checkedVersions, searchedCommands)
568
+ };
569
+ }
570
+ async checkPythonCommand(command, args, checkedVersions, searchedCommands) {
571
+ const fs = getFileSystem();
572
+ const version = await this.checkPythonVersion(command, args, checkedVersions, searchedCommands);
573
+ if (!version)
574
+ return null;
575
+ const commandStr = args.length > 0 ? `${command} ${args.join(" ")}` : command;
576
+ const uipathExePath = await this.findUipathExe(fs, command, args);
577
+ return {
578
+ success: true,
579
+ pythonPath: commandStr,
580
+ version,
581
+ uipathExePath,
582
+ source: "path"
583
+ };
584
+ }
585
+ async checkPythonVersion(command, args, checkedVersions, searchedCommands) {
586
+ const commandStr = args.length > 0 ? `${command} ${args.join(" ")}` : command;
587
+ searchedCommands.push(commandStr);
588
+ logger.debug(`Checking Python command: ${commandStr}`);
589
+ const versionArgs = [...args, "--version"];
590
+ const versionProc = spawn2(command, versionArgs, {
591
+ stdio: ["ignore", "pipe", "pipe"]
592
+ });
593
+ const stdoutChunks = [];
594
+ const stderrChunks = [];
595
+ versionProc.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
596
+ versionProc.stderr.on("data", (chunk) => stderrChunks.push(chunk));
597
+ const [versionError, exitCode] = await catchError(new Promise((resolve, reject) => {
598
+ versionProc.on("exit", (code) => resolve(code ?? 1));
599
+ versionProc.on("error", (error) => reject(error));
600
+ }));
601
+ if (versionError) {
602
+ logger.debug(`Failed to check ${commandStr}: ${versionError}`);
603
+ return null;
604
+ }
605
+ const output = Buffer.concat(stdoutChunks).toString("utf-8");
606
+ const stderr = Buffer.concat(stderrChunks).toString("utf-8");
607
+ logger.debug(`${output.trim()}`);
608
+ if (stderr.trim()) {
609
+ logger.debug(`stderr: ${stderr.trim()}`);
610
+ }
611
+ if (exitCode !== 0)
612
+ return null;
613
+ const version = output.trim().replace("Python ", "");
614
+ const majorMinor = version.match(/^(\d+\.\d+)/)?.[1];
615
+ if (majorMinor) {
616
+ checkedVersions.push(`${version} at ${commandStr}`);
617
+ }
618
+ if (!majorMinor || !this.config.allowedVersions.includes(majorMinor)) {
619
+ logger.info(`Python ${version} at ${commandStr} is not in allowed versions`);
620
+ return null;
621
+ }
622
+ logger.info(`Found valid Python: ${commandStr} - ${version}`);
623
+ return version;
624
+ }
625
+ async findUipathExe(fs, command, args) {
626
+ const metadataPath = await this.findUipathExeFromMetadata(fs, command, args);
627
+ if (metadataPath)
628
+ return metadataPath;
629
+ const isWindows = platform() === "win32";
630
+ const candidatePaths = [];
631
+ const virtualEnv = process.env.VIRTUAL_ENV;
632
+ if (virtualEnv) {
633
+ candidatePaths.push(isWindows ? fs.path.join(virtualEnv, "Scripts", "uipath.exe") : fs.path.join(virtualEnv, "bin", "uipath"));
634
+ }
635
+ const prefix = await this.getPythonPrefix(command, args);
636
+ if (prefix) {
637
+ candidatePaths.push(isWindows ? fs.path.join(prefix, "Scripts", "uipath.exe") : fs.path.join(prefix, "bin", "uipath"));
638
+ }
639
+ for (const candidate of candidatePaths) {
640
+ logger.debug(`Checking for uipath executable at: ${candidate}`);
641
+ if (await fs.exists(candidate)) {
642
+ logger.info(`Found uipath executable at: ${candidate}`);
643
+ return candidate;
644
+ }
645
+ }
646
+ const pathCandidates = this.getPathUipathCandidates(fs, isWindows);
647
+ for (const candidate of pathCandidates) {
648
+ logger.debug(`Checking for uipath executable on PATH at: ${candidate}`);
649
+ if (await fs.exists(candidate)) {
650
+ logger.info(`Found uipath executable at: ${candidate}`);
651
+ return candidate;
652
+ }
653
+ }
654
+ const checkedCandidates = [...candidatePaths, ...pathCandidates];
655
+ logger.error(`uipath executable not found. Checked: ${checkedCandidates.join(", ")}`);
656
+ return;
657
+ }
658
+ async findUipathExeFromMetadata(fs, command, args) {
659
+ const script = `
660
+ from importlib.metadata import distribution, PackageNotFoundError
661
+ import os
662
+ import sys
663
+ try:
664
+ d = distribution("uipath")
665
+ except PackageNotFoundError:
666
+ sys.exit(2)
667
+ for f in d.files or []:
668
+ if f.name.lower() in ("uipath", "uipath.exe"):
669
+ print(os.path.normpath(str(f.locate())))
670
+ sys.exit(0)
671
+ sys.exit(3)
672
+ `;
673
+ const proc = spawn2(command, [...args, "-c", script], {
674
+ stdio: ["ignore", "pipe", "pipe"]
675
+ });
676
+ const stdoutChunks = [];
677
+ const stderrChunks = [];
678
+ proc.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
679
+ proc.stderr.on("data", (chunk) => stderrChunks.push(chunk));
680
+ const [error, exitCode] = await catchError(new Promise((resolve, reject) => {
681
+ proc.on("exit", (code) => resolve(code ?? 1));
682
+ proc.on("error", (err) => reject(err));
683
+ }));
684
+ if (error) {
685
+ logger.debug(`Failed to locate uipath executable: ${error}`);
686
+ return;
687
+ }
688
+ if (exitCode !== 0) {
689
+ const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
690
+ logger.debug(`uipath executable metadata lookup exited with code ${exitCode}${stderr ? `: ${stderr}` : ""}`);
691
+ return;
692
+ }
693
+ const resolved = Buffer.concat(stdoutChunks).toString("utf-8").trim();
694
+ if (!resolved) {
695
+ logger.debug("uipath executable metadata lookup returned empty path");
696
+ return;
697
+ }
698
+ if (!await fs.exists(resolved)) {
699
+ logger.debug(`uipath executable path from metadata does not exist: ${resolved}`);
700
+ return;
701
+ }
702
+ logger.info(`Found uipath executable at: ${resolved}`);
703
+ return resolved;
704
+ }
705
+ async getPythonPrefix(command, args) {
706
+ const proc = spawn2(command, [...args, "-c", "import sys; print(sys.prefix)"], {
707
+ stdio: ["ignore", "pipe", "pipe"]
708
+ });
709
+ const stdoutChunks = [];
710
+ proc.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
711
+ const [error, exitCode] = await catchError(new Promise((resolve, reject) => {
712
+ proc.on("exit", (code) => resolve(code ?? 1));
713
+ proc.on("error", (err) => reject(err));
714
+ }));
715
+ if (error) {
716
+ logger.debug(`Failed to locate uipath executable: ${error}`);
717
+ return;
718
+ }
719
+ if (exitCode !== 0)
720
+ return;
721
+ const prefix = Buffer.concat(stdoutChunks).toString("utf-8").trim();
722
+ return prefix || undefined;
723
+ }
724
+ async getUipathVersion(exePath) {
725
+ const proc = spawn2(exePath, ["--version"], {
726
+ stdio: ["ignore", "pipe", "pipe"]
727
+ });
728
+ const stdoutChunks = [];
729
+ const stderrChunks = [];
730
+ proc.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
731
+ proc.stderr.on("data", (chunk) => stderrChunks.push(chunk));
732
+ const [spawnError, exitCode] = await catchError(new Promise((resolve, reject) => {
733
+ proc.on("exit", (code) => resolve(code ?? 1));
734
+ proc.on("error", (err) => reject(err));
735
+ }));
736
+ if (spawnError) {
737
+ return {
738
+ error: `Failed to run '${exePath} --version': ${spawnError.message}`
739
+ };
740
+ }
741
+ const stdout = Buffer.concat(stdoutChunks).toString("utf-8").trim();
742
+ const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
743
+ if (exitCode !== 0) {
744
+ return {
745
+ error: `'${exePath} --version' exited with code ${exitCode}${stderr ? `: ${stderr}` : ""}`
746
+ };
747
+ }
748
+ const match = stdout.match(/uipath\s+version\s+(\S+)/i);
749
+ if (!match) {
750
+ return {
751
+ error: `Could not parse version from '${exePath} --version' output: ${stdout || "<empty>"}`
752
+ };
753
+ }
754
+ return { version: match[1] };
755
+ }
756
+ getPathUipathCandidates(fs, isWindows) {
757
+ const pathValue = process.env.PATH || (isWindows ? process.env.Path : undefined);
758
+ if (!pathValue)
759
+ return [];
760
+ const delimiter = isWindows ? ";" : ":";
761
+ const pathEntries = pathValue.split(delimiter).map((entry) => entry.trim().replace(/^"|"$/g, "")).filter((entry) => entry.length > 0);
762
+ const executableNames = isWindows ? this.getWindowsExecutableNames() : ["uipath"];
763
+ return pathEntries.flatMap((entry) => executableNames.map((name) => fs.path.join(entry, name)));
764
+ }
765
+ getWindowsExecutableNames() {
766
+ const pathExt = process.env.PATHEXT || ".COM;.EXE;.BAT;.CMD";
767
+ const extensions = pathExt.split(";").map((extension) => extension.trim().toLowerCase()).filter((extension) => extension.length > 0).map((extension) => extension.startsWith(".") ? extension : `.${extension}`);
768
+ return [
769
+ "uipath",
770
+ ...extensions.map((extension) => `uipath${extension}`)
771
+ ];
772
+ }
773
+ buildNotFoundError(checkedVersions, searchedCommands) {
774
+ let msg = `No compatible Python installation found.
775
+
776
+ `;
777
+ msg += `Required versions: ${this.config.allowedVersions.join(", ")}
778
+ `;
779
+ if (checkedVersions.length > 0) {
780
+ msg += `
781
+ Found Python installations (incompatible versions):
782
+ `;
783
+ for (const ver of checkedVersions) {
784
+ msg += ` - ${ver}
785
+ `;
786
+ }
787
+ } else {
788
+ msg += `
789
+ No Python installations found in PATH or default locations.
790
+ `;
791
+ }
792
+ msg += `
793
+ Searched commands (${searchedCommands.length} commands):
794
+ `;
795
+ const uniqueCommands = [...new Set(searchedCommands)].slice(0, 10);
796
+ for (const cmd of uniqueCommands) {
797
+ msg += ` - ${cmd}
798
+ `;
799
+ }
800
+ if (searchedCommands.length > 10) {
801
+ msg += ` ... and ${searchedCommands.length - 10} more
802
+ `;
803
+ }
804
+ msg += `
805
+ Please install one of the required Python versions:
806
+ `;
807
+ msg += ` - Download from: https://www.python.org/downloads/
808
+ `;
809
+ msg += ` - Or use environment variable: PYTHON_TOOL_PYTHON_VERSIONS=<versions>
810
+ `;
811
+ return msg;
812
+ }
813
+ getDefaultPythonPaths() {
814
+ const paths = [];
815
+ const isWindows = platform() === "win32";
816
+ if (isWindows) {
817
+ paths.push({ command: "python", args: [] });
818
+ paths.push({ command: "python3", args: [] });
819
+ for (const version of this.config.allowedVersions) {
820
+ paths.push({ command: `python${version}`, args: [] });
821
+ }
822
+ for (const version of this.config.allowedVersions) {
823
+ paths.push({ command: "py", args: [`-${version}`] });
824
+ }
825
+ paths.push({ command: "py", args: [] });
826
+ } else {
827
+ for (const version of this.config.allowedVersions) {
828
+ paths.push({ command: `python${version}`, args: [] });
829
+ }
830
+ paths.push({ command: "python3", args: [] });
831
+ paths.push({ command: "python", args: [] });
832
+ }
833
+ return paths;
834
+ }
835
+ async loadCache() {
836
+ return readCache(this.cacheFile);
837
+ }
838
+ async saveCache(cache) {
839
+ const fs = getFileSystem();
840
+ const dir = fs.path.dirname(this.cacheFile);
841
+ const [mkdirError] = await catchError(fs.mkdir(dir));
842
+ if (mkdirError) {
843
+ logger.warn(`Failed to create cache directory: ${mkdirError}`);
844
+ return;
845
+ }
846
+ const [writeError] = await catchError(fs.writeFile(this.cacheFile, JSON.stringify(cache, null, 2)));
847
+ if (writeError) {
848
+ logger.warn(`Failed to save cache: ${writeError}`);
849
+ return;
850
+ }
851
+ logger.info(`Cache saved to ${this.cacheFile}`);
852
+ }
853
+ }
854
+ function isPathInside(target, root) {
855
+ const normalize = (value) => {
856
+ let normalized = value.replace(/\\/g, "/").replace(/\/+$/, "");
857
+ if (platform() === "win32")
858
+ normalized = normalized.toLowerCase();
859
+ return normalized;
860
+ };
861
+ const normalizedTarget = normalize(target);
862
+ const normalizedRoot = normalize(root);
863
+ return normalizedTarget === normalizedRoot || normalizedTarget.startsWith(`${normalizedRoot}/`);
864
+ }
865
+ // ../uipath-python-bridge/src/register.ts
866
+ function createSetupCommand(config) {
867
+ return (program) => {
868
+ program.command("setup").description("Detect Python installation and verify package is installed").option("--force", "Force re-detection of Python even if cached", false).trackedAction(processContext, async (options) => {
869
+ const packageName = config.packageName || getPackageName();
870
+ const allowedVersions = getAllowedPythonVersions();
871
+ if (allowedVersions.length === 0) {
872
+ OutputFormatter.error({
873
+ Result: RESULTS.Failure,
874
+ Message: "Invalid configuration. Allowed Python list misconfigured.",
875
+ Instructions: "Check env variable PYTHON_TOOL_PYTHON_VERSIONS"
876
+ });
877
+ processContext.exit(1);
878
+ return;
879
+ }
880
+ logger.info(`
881
+ Searching for Python installations: ${allowedVersions}`);
882
+ const service = new PythonService({
883
+ allowedVersions,
884
+ packageName,
885
+ toolName: config.toolName
886
+ });
887
+ const result = await service.setup(packageName, options.force);
888
+ if (!result.success) {
889
+ OutputFormatter.error({
890
+ Result: RESULTS.Failure,
891
+ Message: result.error || "Setup failed with no error.",
892
+ Instructions: result.instructions || `Install a supported Python version and the '${packageName}' tool, then re-run setup. For uv installs, run 'uv tool install ${packageName}' and ensure the uv tool directory is on PATH.`
893
+ });
894
+ processContext.exit(1);
895
+ return;
896
+ }
897
+ OutputFormatter.success({
898
+ Result: RESULTS.Success,
899
+ Code: config.successCode,
900
+ Data: {
901
+ PythonPath: result.pythonPath,
902
+ Package: packageName,
903
+ PackageInstalled: result.packageInstalled ? "Yes" : "No",
904
+ PackageVersion: result.packageVersion ?? "N/A",
905
+ UipathPath: result.uipathExePath,
906
+ Source: result.source
907
+ }
908
+ });
909
+ processContext.exit(0);
910
+ });
911
+ };
912
+ }
913
+ function createDefaultAction(executeCommand) {
914
+ return (program) => {
915
+ program.trackedAction(processContext, async () => {
916
+ const userArgs = program.args.length > 0 ? normalizeDefaultActionArgs(program.args) : [];
917
+ const extra = userArgs.length > 0 ? userArgs : ["--help"];
918
+ await executeCommand(["_", "_", "exec", ...extra], {});
919
+ });
920
+ };
921
+ }
922
+ function normalizeDefaultActionArgs(args) {
923
+ return args.length === 1 && args[0] === "-h" ? ["--help"] : args;
924
+ }
925
+ function createHelpCommand(executeCommand) {
926
+ return (program) => {
927
+ program.command("help").description("Display help for the python tool").trackedAction(processContext, async () => {
928
+ await executeCommand(["_", "_", "exec", "--help"], {});
929
+ });
930
+ };
931
+ }
932
+ function createCommandForwarder(executeCommand) {
933
+ return (program) => {
934
+ program.on("command:*", async (operands) => {
935
+ const unknownCommand = operands[0];
936
+ if (!program.commands.some((c) => c.name() === unknownCommand)) {
937
+ await executeCommand(["_", "_", "exec", ...operands], {});
938
+ }
939
+ });
940
+ };
941
+ }
942
+ export {
943
+ PythonService,
944
+ createCommandForwarder,
945
+ createDefaultAction,
946
+ createExecuteCommand,
947
+ createHelpCommand,
948
+ createSetupCommand,
949
+ getAllowedPythonVersions,
950
+ getCacheFileName,
951
+ getPackageName,
952
+ hasTypeOption,
953
+ isPathInside,
954
+ processCommandArgs,
955
+ readCache,
956
+ reportUipathPythonFailure,
957
+ runUipathPythonCommand,
958
+ supportsNewTypeOption
959
+ };
960
+
961
+ //# debugId=C0732B4F0D2C235064756E2164756E21