@uipath/codedagent-tool 0.1.12

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/README.md ADDED
@@ -0,0 +1,103 @@
1
+ # Python Tool
2
+
3
+ A command-line tool over [uipath-python](https://github.com/UiPath/uipath-python)
4
+
5
+ ## Features
6
+
7
+ - Automatic Python version detection
8
+ - Python path caching for improved performance
9
+ - Package installation verification
10
+ - Interactive command execution support
11
+
12
+ ## Commands
13
+
14
+ ### setup
15
+
16
+ Detects Python installation and verifies required package is installed.
17
+
18
+ ```bash
19
+ uipath ca setup [options]
20
+ ```
21
+
22
+ Options:
23
+ - `--force` - Force re-detection of Python even if cached
24
+
25
+ ### exec
26
+
27
+ Executes the configured Python package with provided arguments.
28
+
29
+ ```bash
30
+ uipath ca exec [args...]
31
+ ```
32
+
33
+ Options:
34
+ - `--interactive` - enable interactive mode
35
+
36
+ All arguments are passed directly to the Python package.
37
+
38
+ ## Configuration
39
+
40
+ ### Static Configuration
41
+
42
+ The following settings are configured in the code and can be overridden via environment variables:
43
+
44
+ #### Python Versions
45
+
46
+ Default: `3.11`, `3.12`, `3.13`
47
+
48
+ Override with `PYTHON_TOOL_PYTHON_VERSIONS` environment variable:
49
+
50
+ ```bash
51
+ # Windows
52
+ set PYTHON_TOOL_PYTHON_VERSIONS=3.13,3.12,3.11
53
+
54
+ # Linux/macOS
55
+ export PYTHON_TOOL_PYTHON_VERSIONS=3.13,3.12,3.11
56
+ ```
57
+
58
+ ### Cache File
59
+
60
+ The tool uses a cache file (`.codedagent-tool-cache.json`) to store:
61
+ - Detected Python path
62
+ - Python version
63
+ - Package name
64
+ - Last validation timestamp
65
+
66
+ If the cached Python path no longer exists, will ask user to configure.
67
+
68
+ ```
69
+ uip codedagent init
70
+ ❌ Invalid config. Python (C:\Users\georgescu.vlad\AppData\Local\Programs\Python\Python311\python.exe) no longer exists.
71
+ Run 'uip codedagent setup' to reconfigure.
72
+ ```
73
+
74
+ ## Examples
75
+
76
+ ```bash
77
+ # Setup with default package (uipath-python)
78
+ uip codedagent setup
79
+
80
+ # Force re-detection
81
+ uipath codedagent setup --force
82
+
83
+ # Setup with custom package via environment variable
84
+ export CLOUD_TOOL_PACKAGE_NAME=my-package
85
+ uipath codedagent setup
86
+
87
+ # Run package with arguments
88
+ uipath ca run --help
89
+ uipath ca run command --option value
90
+
91
+ # Interactive command
92
+ uipath ca run interactive-shell
93
+
94
+ # Override Python versions via environment variable
95
+ export PYTHON_TOOL_PYTHON_VERSIONS=3.10,3.11
96
+ uipath ca setup
97
+
98
+ # Full configuration example
99
+ export PYTHON_TOOL_PYTHON_VERSIONS=3.10,3.11,3.12
100
+ export CLOUD_TOOL_PACKAGE_NAME=uipath-python
101
+ uipath ca setup
102
+ uipath ca run --version
103
+ ```
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env bun
2
+ // @bun
3
+
4
+ // src/index.ts
5
+ import { Command } from "commander";
6
+ import { metadata, registerCommands } from "./tool.js";
7
+ var program = new Command;
8
+ program.name(metadata.name).version(metadata.version).description(metadata.description);
9
+ await registerCommands(program);
10
+ program.parse(process.argv);
package/dist/tool.js ADDED
@@ -0,0 +1,778 @@
1
+ // package.json
2
+ var package_default = {
3
+ name: "@uipath/codedagent-tool",
4
+ version: "0.1.12",
5
+ description: "Build, run, deploy, and manage AI Agents.",
6
+ keywords: [
7
+ "cli-tool",
8
+ "codedagents",
9
+ "wrapper"
10
+ ],
11
+ type: "module",
12
+ main: "./dist/tool.js",
13
+ exports: {
14
+ ".": "./dist/tool.js"
15
+ },
16
+ private: false,
17
+ maintainers: [
18
+ "aoltean16",
19
+ "mihaigirleanu",
20
+ "vlad-uipath"
21
+ ],
22
+ bin: {
23
+ "codedagent-tool": "./dist/index.js"
24
+ },
25
+ files: [
26
+ "dist"
27
+ ],
28
+ repository: {
29
+ type: "git",
30
+ url: "https://github.com/UiPath/cli.git",
31
+ directory: "packages/codedagent-tool"
32
+ },
33
+ publishConfig: {
34
+ registry: "https://npm.pkg.github.com/@uipath"
35
+ },
36
+ scripts: {
37
+ build: "bun ../../tools/build-tool.ts",
38
+ package: "bun run build && bun pm pack",
39
+ test: "vitest run",
40
+ "test:coverage": "vitest run --coverage",
41
+ lint: "biome check .",
42
+ "lint:fix": "biome check --write ."
43
+ },
44
+ peerDependencies: {
45
+ commander: "^14.0.3",
46
+ "@uipath/common": "^0.1.13",
47
+ "@uipath/auth": "^0.1.9",
48
+ "@uipath/filesystem": "^0.1.6"
49
+ },
50
+ devDependencies: {
51
+ "@types/bun": "^1.3.9",
52
+ typescript: "^5"
53
+ }
54
+ };
55
+
56
+ // src/commands/command.ts
57
+ import { spawn } from "node:child_process";
58
+ import { getLoginStatusAsync } from "@uipath/auth";
59
+ import {
60
+ catchError,
61
+ logger,
62
+ OutputFormatter,
63
+ processContext,
64
+ UIPATH_HOME_DIR
65
+ } from "@uipath/common";
66
+ import { getFileSystem } from "@uipath/filesystem";
67
+
68
+ // src/config.ts
69
+ var COMMAND_RULES = [
70
+ {
71
+ pattern: ["auth"],
72
+ action: "filter",
73
+ filterMessage: `The 'auth' command is disabled in the python tool and is not recommended for use.
74
+ To proceed anyway, use the --force flag.`
75
+ },
76
+ {
77
+ pattern: ["pack"],
78
+ action: "filter",
79
+ filterMessage: `The 'pack' command is disabled in the python tool and is not recommended for use.
80
+ To proceed anyway, use the --force flag.`
81
+ },
82
+ {
83
+ pattern: ["publish"],
84
+ action: "filter",
85
+ filterMessage: `The 'publish' command is disabled in the python tool and is not recommended for use.
86
+ To proceed anyway, use the --force flag.`
87
+ },
88
+ {
89
+ pattern: ["dev"],
90
+ action: "append",
91
+ appendArgs: ["--interactive"]
92
+ }
93
+ ];
94
+ var DEFAULT_CONFIG = {
95
+ allowedPythonVersions: ["3.13", "3.12", "3.11"],
96
+ packageName: "uipath",
97
+ cacheFileName: ".codedagent-tool-cache.json"
98
+ };
99
+ function getAllowedPythonVersions() {
100
+ const envVersions = process.env.PYTHON_TOOL_PYTHON_VERSIONS;
101
+ if (envVersions) {
102
+ return envVersions.split(",").map((v) => v.trim());
103
+ }
104
+ return [...DEFAULT_CONFIG.allowedPythonVersions];
105
+ }
106
+ function getPackageName() {
107
+ return DEFAULT_CONFIG.packageName;
108
+ }
109
+ function getCacheFileName() {
110
+ return DEFAULT_CONFIG.cacheFileName;
111
+ }
112
+ function matchesPattern(args, pattern) {
113
+ if (typeof pattern === "string") {
114
+ return args.includes(pattern);
115
+ }
116
+ if (pattern.length === 0) {
117
+ return false;
118
+ }
119
+ for (let i = 0;i <= args.length - pattern.length; i++) {
120
+ let match = true;
121
+ for (let j = 0;j < pattern.length; j++) {
122
+ if (args[i + j] !== pattern[j]) {
123
+ match = false;
124
+ break;
125
+ }
126
+ }
127
+ if (match) {
128
+ return true;
129
+ }
130
+ }
131
+ return false;
132
+ }
133
+ function processCommandArgs(args) {
134
+ const hasForceFlag = args.includes("--force");
135
+ const argsWithoutForce = args.filter((arg) => arg !== "--force");
136
+ for (const rule of COMMAND_RULES) {
137
+ if (matchesPattern(argsWithoutForce, rule.pattern)) {
138
+ switch (rule.action) {
139
+ case "filter":
140
+ if (hasForceFlag) {
141
+ return {
142
+ allowed: true,
143
+ args: [...argsWithoutForce]
144
+ };
145
+ }
146
+ return {
147
+ allowed: false,
148
+ args: [],
149
+ errorMessage: rule.filterMessage || "This command is not allowed"
150
+ };
151
+ case "append":
152
+ if (rule.appendArgs && rule.appendArgs.length > 0) {
153
+ return {
154
+ allowed: true,
155
+ args: [...argsWithoutForce, ...rule.appendArgs]
156
+ };
157
+ }
158
+ break;
159
+ }
160
+ }
161
+ }
162
+ return {
163
+ allowed: true,
164
+ args: [...argsWithoutForce]
165
+ };
166
+ }
167
+
168
+ // src/commands/command.ts
169
+ var executeCommand = async (rawArgs, _options) => {
170
+ const runIndex = rawArgs.indexOf("exec");
171
+ const actualArgs = runIndex >= 0 ? rawArgs.slice(runIndex + 1) : [];
172
+ const fs = getFileSystem();
173
+ const cacheFile = fs.path.join(fs.env.homedir(), UIPATH_HOME_DIR, getCacheFileName());
174
+ if (!await fs.exists(cacheFile)) {
175
+ OutputFormatter.error({
176
+ Result: "ConfigError",
177
+ Message: "Python not configured.",
178
+ Instructions: "Run 'uip codedagent setup' first to configure environment."
179
+ });
180
+ processContext.exit(1);
181
+ return;
182
+ }
183
+ const [readError, content] = await catchError(fs.readFile(cacheFile, "utf-8"));
184
+ if (readError || !content) {
185
+ OutputFormatter.error({
186
+ Result: "ConfigError",
187
+ Message: "Invalid configuration file.",
188
+ Instructions: "Run 'uip codedagent setup' first to configure environment."
189
+ });
190
+ processContext.exit(1);
191
+ return;
192
+ }
193
+ const [parseError, cache] = catchError(() => JSON.parse(content));
194
+ if (parseError) {
195
+ OutputFormatter.error({
196
+ Result: "ConfigError",
197
+ Message: "Invalid configuration file.",
198
+ Instructions: "Run 'uip codedagent setup' first to configure environment."
199
+ });
200
+ processContext.exit(1);
201
+ return;
202
+ }
203
+ if (!cache.uipathExePath || !await fs.exists(cache.uipathExePath)) {
204
+ OutputFormatter.error({
205
+ Result: "ConfigError",
206
+ Message: "uipath executable not found.",
207
+ Instructions: "Run 'uip codedagent setup' first to configure environment."
208
+ });
209
+ processContext.exit(1);
210
+ return;
211
+ }
212
+ const processed = processCommandArgs(actualArgs);
213
+ if (!processed.allowed) {
214
+ OutputFormatter.error({
215
+ Result: "ValidationError",
216
+ Message: processed.errorMessage || "Command disabled.",
217
+ Instructions: "Use of --force flag is not recommended. Favor alternate command."
218
+ });
219
+ processContext.exit(1);
220
+ return;
221
+ }
222
+ const commandArgs = processed.args.filter((arg) => arg !== "--interactive");
223
+ const authEnv = {};
224
+ const [authError, status] = await catchError(getLoginStatusAsync());
225
+ if (authError) {
226
+ logger.debug(`Auth unavailable — continuing without credentials: ${authError.message}`);
227
+ } else if (status.loginStatus === "Logged in" && status.accessToken) {
228
+ authEnv.UIPATH_ACCESS_TOKEN = status.accessToken;
229
+ if (status.baseUrl) {
230
+ const org = status.organizationName || status.organizationId;
231
+ const tenant = status.tenantName || status.tenantId;
232
+ if (org && tenant) {
233
+ authEnv.UIPATH_URL = `${status.baseUrl.replace(/\/+$/, "")}/${org}/${tenant}`;
234
+ }
235
+ }
236
+ if (status.organizationId)
237
+ authEnv.UIPATH_ORGANIZATION_ID = status.organizationId;
238
+ if (status.organizationName)
239
+ authEnv.UIPATH_ORGANIZATION_NAME = status.organizationName;
240
+ if (status.tenantId)
241
+ authEnv.UIPATH_TENANT_ID = status.tenantId;
242
+ if (status.tenantName)
243
+ authEnv.UIPATH_TENANT_NAME = status.tenantName;
244
+ }
245
+ const proc = spawn(cache.uipathExePath, commandArgs, {
246
+ cwd: process.cwd(),
247
+ env: {
248
+ ...process.env,
249
+ ...authEnv
250
+ },
251
+ stdio: "inherit"
252
+ });
253
+ const [spawnError] = await catchError(new Promise((_resolve, reject) => {
254
+ proc.on("exit", (code) => {
255
+ process.exit(code || 0);
256
+ });
257
+ proc.on("error", reject);
258
+ }));
259
+ if (spawnError) {
260
+ OutputFormatter.error({
261
+ Result: "Failure",
262
+ Message: spawnError.message,
263
+ Instructions: spawnError.message
264
+ });
265
+ processContext.exit(1);
266
+ }
267
+ };
268
+ var registerExecCommand = (program) => {
269
+ program.command("exec", { hidden: true }).description("Execute the ca package with provided arguments").allowUnknownOption(true).allowExcessArguments(true).trackedAction(processContext, async (command) => {
270
+ const rawArgs = command.parent?.rawArgs || process.argv;
271
+ await executeCommand(rawArgs, {});
272
+ });
273
+ };
274
+
275
+ // src/commands/help.ts
276
+ import { processContext as processContext2 } from "@uipath/common";
277
+ var registerDefaultAction = (program) => {
278
+ program.trackedAction(processContext2, async () => {
279
+ const argv = process.argv;
280
+ const cloudIndex = argv.indexOf("codedagent");
281
+ if (cloudIndex >= 0) {
282
+ const afterCloud = argv.slice(cloudIndex + 1);
283
+ if (afterCloud.length > 0) {
284
+ const rawArgs = [
285
+ ...argv.slice(0, cloudIndex + 1),
286
+ "exec",
287
+ ...afterCloud
288
+ ];
289
+ await executeCommand(rawArgs, {});
290
+ } else {
291
+ const rawArgs = [
292
+ ...argv.slice(0, cloudIndex + 1),
293
+ "exec",
294
+ "--help"
295
+ ];
296
+ await executeCommand(rawArgs, {});
297
+ }
298
+ }
299
+ });
300
+ };
301
+ var registerHelpCommand = (program) => {
302
+ program.command("help").description("Display help for the python tool").trackedAction(processContext2, async () => {
303
+ const argv = process.argv;
304
+ const cloudIndex = argv.indexOf("codedagent");
305
+ if (cloudIndex >= 0) {
306
+ const rawArgs = [
307
+ ...argv.slice(0, cloudIndex + 1),
308
+ "exec",
309
+ "--help"
310
+ ];
311
+ await executeCommand(rawArgs, {});
312
+ }
313
+ });
314
+ };
315
+
316
+ // src/commands/setup.ts
317
+ import {
318
+ catchError as catchError3,
319
+ logger as logger3,
320
+ OutputFormatter as OutputFormatter2,
321
+ processContext as processContext3
322
+ } from "@uipath/common";
323
+ import { getFileSystem as getFileSystem3 } from "@uipath/filesystem";
324
+
325
+ // src/services/PythonService.ts
326
+ import { spawn as spawn2 } from "node:child_process";
327
+ import { existsSync } from "node:fs";
328
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
329
+ import { platform } from "node:os";
330
+ import { dirname, join } from "node:path";
331
+ import {
332
+ catchError as catchError2,
333
+ getOutputSink,
334
+ logger as logger2,
335
+ UIPATH_HOME_DIR as UIPATH_HOME_DIR2
336
+ } from "@uipath/common";
337
+ import { getFileSystem as getFileSystem2 } from "@uipath/filesystem";
338
+ class PythonService {
339
+ cacheFile;
340
+ config;
341
+ uipathExePath;
342
+ constructor(config) {
343
+ const fs = getFileSystem2();
344
+ this.config = config;
345
+ this.cacheFile = config.cacheFile || join(fs.env.homedir(), UIPATH_HOME_DIR2, getCacheFileName());
346
+ }
347
+ async setup(packageName, force = false) {
348
+ const [error, result] = await catchError2(this._setupInner(packageName, force));
349
+ if (error) {
350
+ return {
351
+ success: false,
352
+ error: error.message
353
+ };
354
+ }
355
+ return result;
356
+ }
357
+ async _setupInner(packageName, force) {
358
+ let pythonPath;
359
+ let version;
360
+ if (!force) {
361
+ logger2.debug("Checking cache...");
362
+ const cached = await this.loadCache();
363
+ if (cached && existsSync(cached.pythonPath)) {
364
+ pythonPath = cached.pythonPath;
365
+ version = cached.version;
366
+ this.uipathExePath = cached.uipathExePath;
367
+ logger2.info(`Using cached Python: ${pythonPath} (${version})`);
368
+ if (!this.uipathExePath || !existsSync(this.uipathExePath)) {
369
+ return {
370
+ success: false,
371
+ pythonPath,
372
+ pythonVersion: version,
373
+ packageInstalled: false,
374
+ error: `Package '${packageName}' is not installed or executable not found.
375
+
376
+ To install the package, run:
377
+ ${pythonPath} -m pip install ${packageName}`
378
+ };
379
+ }
380
+ logger2.info(`Using cached uipath executable: ${this.uipathExePath}`);
381
+ await this.saveCache({
382
+ pythonPath,
383
+ version,
384
+ lastValidated: new Date().toISOString(),
385
+ packageName,
386
+ uipathExePath: this.uipathExePath
387
+ });
388
+ return {
389
+ success: true,
390
+ pythonPath,
391
+ pythonVersion: version,
392
+ packageInstalled: true
393
+ };
394
+ } else if (cached) {
395
+ logger2.warn("Cached Python path no longer exists, re-detecting...");
396
+ }
397
+ }
398
+ logger2.info("Checking Python...");
399
+ const detection = await this.detectPython();
400
+ if (!detection.success || !detection.pythonPath || !detection.version) {
401
+ return {
402
+ success: false,
403
+ error: detection.error || "Failed to detect Python"
404
+ };
405
+ }
406
+ pythonPath = detection.pythonPath;
407
+ version = detection.version;
408
+ this.uipathExePath = detection.uipathExePath;
409
+ if (!this.uipathExePath) {
410
+ return {
411
+ success: false,
412
+ pythonPath,
413
+ pythonVersion: version,
414
+ packageInstalled: false,
415
+ error: `Package '${packageName}' is not installed.
416
+
417
+ To install the package, run:
418
+ ${pythonPath} -m pip install ${packageName}`
419
+ };
420
+ }
421
+ await this.saveCache({
422
+ pythonPath,
423
+ version,
424
+ lastValidated: new Date().toISOString(),
425
+ packageName,
426
+ uipathExePath: this.uipathExePath
427
+ });
428
+ return {
429
+ success: true,
430
+ pythonPath,
431
+ pythonVersion: version,
432
+ packageInstalled: true
433
+ };
434
+ }
435
+ async run(options) {
436
+ const { args, interactive = false } = options;
437
+ const uipathExePath = options.uipathExePath || this.uipathExePath;
438
+ if (!uipathExePath || !existsSync(uipathExePath)) {
439
+ return {
440
+ success: false,
441
+ exitCode: 1,
442
+ error: "uipath executable not found. Please run setup first."
443
+ };
444
+ }
445
+ logger2.info(`Executing: ${uipathExePath} ${args.join(" ")}`);
446
+ const proc = spawn2(uipathExePath, args, {
447
+ cwd: process.cwd(),
448
+ env: process.env,
449
+ stdio: interactive ? "inherit" : "pipe"
450
+ });
451
+ if (!interactive) {
452
+ const stdoutChunks = [];
453
+ const stderrChunks = [];
454
+ const sink = getOutputSink();
455
+ proc.stdout?.on("data", (chunk) => {
456
+ stdoutChunks.push(chunk);
457
+ sink.writeOut(chunk.toString());
458
+ });
459
+ proc.stderr?.on("data", (chunk) => {
460
+ stderrChunks.push(chunk);
461
+ sink.writeErr(chunk.toString());
462
+ });
463
+ }
464
+ const [error, exitCode] = await catchError2(new Promise((resolve, reject) => {
465
+ proc.on("exit", (code) => resolve(code || 0));
466
+ proc.on("error", (error2) => reject(error2));
467
+ }));
468
+ if (error) {
469
+ return {
470
+ success: false,
471
+ exitCode: 1,
472
+ error: error.message
473
+ };
474
+ }
475
+ return {
476
+ success: exitCode === 0,
477
+ exitCode
478
+ };
479
+ }
480
+ async detectPython() {
481
+ const pythonCommands = this.getDefaultPythonPaths();
482
+ const checkedVersions = [];
483
+ const searchedCommands = [];
484
+ for (const { command, args } of pythonCommands) {
485
+ const commandStr = args.length > 0 ? `${command} ${args.join(" ")}` : command;
486
+ const [outerError, detectionResult] = await catchError2((async () => {
487
+ searchedCommands.push(commandStr);
488
+ logger2.debug(`Checking Python command: ${commandStr}`);
489
+ const versionArgs = [...args, "--version"];
490
+ const versionProc = spawn2(command, versionArgs, {
491
+ stdio: ["ignore", "pipe", "pipe"]
492
+ });
493
+ const stdoutChunks = [];
494
+ const stderrChunks = [];
495
+ versionProc.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
496
+ versionProc.stderr.on("data", (chunk) => stderrChunks.push(chunk));
497
+ const exitCode = await new Promise((resolve, reject) => {
498
+ versionProc.on("exit", (code) => resolve(code || 0));
499
+ versionProc.on("error", (error) => reject(error));
500
+ });
501
+ const output = Buffer.concat([
502
+ ...stdoutChunks,
503
+ ...stderrChunks
504
+ ]).toString("utf-8");
505
+ logger2.debug(`${output.trim()}`);
506
+ if (exitCode === 0) {
507
+ const version = output.trim().replace("Python ", "");
508
+ const majorMinor = version.match(/^(\d+\.\d+)/)?.[1];
509
+ if (majorMinor) {
510
+ checkedVersions.push(`${version} at ${commandStr}`);
511
+ }
512
+ if (majorMinor && this.config.allowedVersions.includes(majorMinor)) {
513
+ logger2.info(`Found valid Python: ${commandStr} - ${version}`);
514
+ const pythonPath = commandStr;
515
+ let uipathExePath;
516
+ const [locateError] = await catchError2((async () => {
517
+ const isWindows = platform() === "win32";
518
+ const candidatePaths = [];
519
+ const virtualEnv = process.env.VIRTUAL_ENV;
520
+ if (virtualEnv) {
521
+ const venvUipathPath = isWindows ? join(virtualEnv, "Scripts", "uipath.exe") : join(virtualEnv, "bin", "uipath");
522
+ candidatePaths.push(venvUipathPath);
523
+ }
524
+ const prefixArgs = [
525
+ ...args,
526
+ "-c",
527
+ "import sys; print(sys.prefix)"
528
+ ];
529
+ const prefixProc = spawn2(command, prefixArgs, {
530
+ stdio: ["ignore", "pipe", "pipe"]
531
+ });
532
+ const prefixChunks = [];
533
+ prefixProc.stdout.on("data", (chunk) => prefixChunks.push(chunk));
534
+ const prefixExitCode = await new Promise((resolve, reject) => {
535
+ prefixProc.on("exit", (code) => resolve(code || 0));
536
+ prefixProc.on("error", (error) => reject(error));
537
+ });
538
+ if (prefixExitCode === 0) {
539
+ const prefix = Buffer.concat(prefixChunks).toString("utf-8").trim();
540
+ const prefixUipathPath = isWindows ? join(prefix, "Scripts", "uipath.exe") : join(prefix, "bin", "uipath");
541
+ candidatePaths.push(prefixUipathPath);
542
+ }
543
+ for (const candidate of candidatePaths) {
544
+ logger2.debug(`Checking for uipath executable at: ${candidate}`);
545
+ if (existsSync(candidate)) {
546
+ uipathExePath = candidate;
547
+ logger2.info(`Found uipath executable at: ${uipathExePath}`);
548
+ break;
549
+ }
550
+ }
551
+ if (!uipathExePath) {
552
+ logger2.error(`uipath executable not found. Checked: ${candidatePaths.join(", ")}`);
553
+ }
554
+ })());
555
+ if (locateError) {
556
+ logger2.debug(`Failed to locate uipath executable: ${locateError}`);
557
+ }
558
+ return {
559
+ success: true,
560
+ pythonPath,
561
+ version,
562
+ uipathExePath
563
+ };
564
+ } else {
565
+ logger2.info(`Python ${version} at ${commandStr} is not in allowed versions`);
566
+ }
567
+ }
568
+ return;
569
+ })());
570
+ if (outerError) {
571
+ logger2.debug(`Failed to check ${commandStr}: ${outerError}`);
572
+ continue;
573
+ }
574
+ if (detectionResult) {
575
+ return detectionResult;
576
+ }
577
+ }
578
+ let errorMessage = `No compatible Python installation found.
579
+
580
+ `;
581
+ errorMessage += `Required versions: ${this.config.allowedVersions.join(", ")}
582
+ `;
583
+ if (checkedVersions.length > 0) {
584
+ errorMessage += `
585
+ Found Python installations (incompatible versions):
586
+ `;
587
+ for (const ver of checkedVersions) {
588
+ errorMessage += ` - ${ver}
589
+ `;
590
+ }
591
+ } else {
592
+ errorMessage += `
593
+ No Python installations found in PATH or default locations.
594
+ `;
595
+ }
596
+ errorMessage += `
597
+ Searched commands (${searchedCommands.length} commands):
598
+ `;
599
+ const uniqueCommands = [...new Set(searchedCommands)].slice(0, 10);
600
+ for (const cmd of uniqueCommands) {
601
+ errorMessage += ` - ${cmd}
602
+ `;
603
+ }
604
+ if (searchedCommands.length > 10) {
605
+ errorMessage += ` ... and ${searchedCommands.length - 10} more
606
+ `;
607
+ }
608
+ errorMessage += `
609
+ Please install one of the required Python versions:
610
+ `;
611
+ errorMessage += ` - Download from: https://www.python.org/downloads/
612
+ `;
613
+ errorMessage += ` - Or use environment variable: PYTHON_TOOL_PYTHON_VERSIONS=<versions>
614
+ `;
615
+ return {
616
+ success: false,
617
+ error: errorMessage
618
+ };
619
+ }
620
+ getDefaultPythonPaths() {
621
+ const paths = [];
622
+ const isWindows = platform() === "win32";
623
+ if (isWindows) {
624
+ paths.push({ command: "python", args: [] });
625
+ paths.push({ command: "python3", args: [] });
626
+ for (const version of this.config.allowedVersions) {
627
+ paths.push({ command: `python${version}`, args: [] });
628
+ }
629
+ for (const version of this.config.allowedVersions) {
630
+ paths.push({ command: "py", args: [`-${version}`] });
631
+ }
632
+ paths.push({ command: "py", args: [] });
633
+ } else {
634
+ for (const version of this.config.allowedVersions) {
635
+ paths.push({ command: `python${version}`, args: [] });
636
+ }
637
+ paths.push({ command: "python3", args: [] });
638
+ paths.push({ command: "python", args: [] });
639
+ }
640
+ return paths;
641
+ }
642
+ async loadCache() {
643
+ if (!existsSync(this.cacheFile)) {
644
+ return null;
645
+ }
646
+ const [error, content] = await catchError2(readFile(this.cacheFile, "utf-8"));
647
+ if (error) {
648
+ logger2.warn(`Failed to load cache: ${error}`);
649
+ return null;
650
+ }
651
+ const [parseError, parsed] = catchError2(() => JSON.parse(content));
652
+ if (parseError) {
653
+ logger2.warn(`Failed to load cache: ${parseError}`);
654
+ return null;
655
+ }
656
+ return parsed;
657
+ }
658
+ async saveCache(cache) {
659
+ const [error] = await catchError2((async () => {
660
+ const dir = dirname(this.cacheFile);
661
+ await mkdir(dir, { recursive: true });
662
+ await writeFile(this.cacheFile, JSON.stringify(cache, null, 2), "utf-8");
663
+ logger2.info(`Cache saved to ${this.cacheFile}`);
664
+ })());
665
+ if (error) {
666
+ logger2.warn(`Failed to save cache: ${error}`);
667
+ }
668
+ }
669
+ async getCachedPython() {
670
+ const cache = await this.loadCache();
671
+ if (cache && existsSync(cache.pythonPath)) {
672
+ return cache.pythonPath;
673
+ }
674
+ return null;
675
+ }
676
+ }
677
+
678
+ // src/commands/setup.ts
679
+ var registerSetupCommand = (program) => {
680
+ program.command("setup").description("Detect Python installation and verify package is installed").option("--force", "Force re-detection of Python even if cached", false).trackedAction(processContext3, async (options) => {
681
+ const packageName = getPackageName();
682
+ const allowedVersions = getAllowedPythonVersions();
683
+ if (allowedVersions.length === 0) {
684
+ OutputFormatter2.error({
685
+ Result: "Failure",
686
+ Message: "Invalid configuration. Allowed python list missconfigured.",
687
+ Instructions: "Check env variable PYTHON_TOOL_PYTHON_VERSIONS"
688
+ });
689
+ processContext3.exit(1);
690
+ return;
691
+ }
692
+ const fs = getFileSystem3();
693
+ const venvPath = fs.path.join(fs.env.cwd(), ".venv");
694
+ if (await fs.exists(venvPath) && !process.env.VIRTUAL_ENV) {
695
+ OutputFormatter2.error({
696
+ Result: "Failure",
697
+ Message: "Found .venv in current directory but no virtual environment is activated.",
698
+ Instructions: process.platform === "win32" ? "Run '.venv\\Scripts\\activate' first, then re-run setup." : "Run 'source .venv/bin/activate' first, then re-run setup."
699
+ });
700
+ processContext3.exit(1);
701
+ return;
702
+ }
703
+ logger3.info(`
704
+ Searching for python instalations: ${allowedVersions}`);
705
+ const service = new PythonService({
706
+ allowedVersions,
707
+ packageName
708
+ });
709
+ const [error, result] = await catchError3(service.setup(packageName, options.force));
710
+ if (error) {
711
+ OutputFormatter2.error({
712
+ Result: "Failure",
713
+ Message: error.message,
714
+ Instructions: error.message
715
+ });
716
+ processContext3.exit(1);
717
+ return;
718
+ }
719
+ if (!result.success) {
720
+ OutputFormatter2.error({
721
+ Result: "Failure",
722
+ Message: result.error || "Setup failed with no error.",
723
+ Instructions: result.instructions || ""
724
+ });
725
+ processContext3.exit(1);
726
+ return;
727
+ }
728
+ OutputFormatter2.success({
729
+ Result: "Success",
730
+ Code: "CodedAgentsSetup",
731
+ Data: {
732
+ PythonPath: result.pythonPath,
733
+ Package: packageName,
734
+ PackageInstalled: result.packageInstalled ? "Yes" : "No",
735
+ PackageVersion: result.packageVersion ?? "N/A"
736
+ }
737
+ });
738
+ });
739
+ };
740
+
741
+ // src/tool.ts
742
+ var metadata = {
743
+ name: "codedagent-tool",
744
+ version: package_default.version,
745
+ description: "Build, run, deploy, and manage AI Agents.",
746
+ commandPrefix: "codedagent"
747
+ };
748
+ var registerCommands = async (program) => {
749
+ program.allowUnknownOption(true);
750
+ program.allowExcessArguments(true);
751
+ program.helpOption(false);
752
+ registerDefaultAction(program);
753
+ registerSetupCommand(program);
754
+ registerExecCommand(program);
755
+ registerHelpCommand(program);
756
+ program.on("command:*", async (operands) => {
757
+ const unknownCommand = operands[0];
758
+ const knownCommands = ["setup", "exec", "help"];
759
+ if (!knownCommands.includes(unknownCommand)) {
760
+ const argv = process.argv;
761
+ const cloudIndex = argv.indexOf("codedagent");
762
+ if (cloudIndex >= 0) {
763
+ const afterCloud = argv.slice(cloudIndex + 1);
764
+ const rawArgs = [
765
+ ...argv.slice(0, cloudIndex + 1),
766
+ "exec",
767
+ ...afterCloud
768
+ ];
769
+ await executeCommand(rawArgs, { format: "table" });
770
+ }
771
+ }
772
+ });
773
+ program.showHelpAfterError(false);
774
+ };
775
+ export {
776
+ registerCommands,
777
+ metadata
778
+ };
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@uipath/codedagent-tool",
3
+ "version": "0.1.12",
4
+ "description": "Build, run, deploy, and manage AI Agents.",
5
+ "keywords": [
6
+ "cli-tool",
7
+ "codedagents",
8
+ "wrapper"
9
+ ],
10
+ "type": "module",
11
+ "main": "./dist/tool.js",
12
+ "exports": {
13
+ ".": "./dist/tool.js"
14
+ },
15
+ "private": false,
16
+ "maintainers": [
17
+ "aoltean16",
18
+ "mihaigirleanu",
19
+ "vlad-uipath"
20
+ ],
21
+ "bin": {
22
+ "codedagent-tool": "./dist/index.js"
23
+ },
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/UiPath/cli.git",
30
+ "directory": "packages/codedagent-tool"
31
+ },
32
+ "publishConfig": {
33
+ "registry": "https://registry.npmjs.org/"
34
+ },
35
+ "scripts": {
36
+ "build": "bun ../../tools/build-tool.ts",
37
+ "package": "bun run build && bun pm pack",
38
+ "test": "vitest run",
39
+ "test:coverage": "vitest run --coverage",
40
+ "lint": "biome check .",
41
+ "lint:fix": "biome check --write ."
42
+ },
43
+ "peerDependencies": {
44
+ "commander": "^14.0.3",
45
+ "@uipath/common": "^0.1.13",
46
+ "@uipath/auth": "^0.1.9",
47
+ "@uipath/filesystem": "^0.1.6"
48
+ },
49
+ "devDependencies": {
50
+ "@types/bun": "^1.3.9",
51
+ "typescript": "^5"
52
+ }
53
+ }