@uipath/llmgw-tool 1.1.0 → 1.195.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.
Files changed (2) hide show
  1. package/dist/tool.js +574 -120
  2. package/package.json +25 -31
package/dist/tool.js CHANGED
@@ -3,7 +3,8 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
3
  // package.json
4
4
  var package_default = {
5
5
  name: "@uipath/llmgw-tool",
6
- version: "1.1.0",
6
+ license: "MIT",
7
+ version: "1.195.0",
7
8
  description: "CLI plugin for UiPath AI Trust Layer Bring-Your-Own LLM connections.",
8
9
  private: false,
9
10
  repository: {
@@ -14,13 +15,17 @@ var package_default = {
14
15
  publishConfig: {
15
16
  registry: "https://npm.pkg.github.com/@uipath"
16
17
  },
17
- keywords: ["cli-tool"],
18
+ keywords: [
19
+ "cli-tool"
20
+ ],
18
21
  type: "module",
19
22
  main: "./dist/tool.js",
20
23
  exports: {
21
24
  ".": "./dist/tool.js"
22
25
  },
23
- files: ["dist"],
26
+ files: [
27
+ "dist"
28
+ ],
24
29
  scripts: {
25
30
  build: "bun build ./src/tool.ts --outdir dist --format esm --target node --external commander",
26
31
  package: "bun run build && bun pm pack",
@@ -39,6 +44,7 @@ var package_default = {
39
44
  };
40
45
 
41
46
  // ../../filesystem/src/node.ts
47
+ import { randomUUID } from "node:crypto";
42
48
  import { existsSync } from "node:fs";
43
49
  import * as fs6 from "node:fs/promises";
44
50
  import * as os2 from "node:os";
@@ -655,6 +661,13 @@ defineLazyProperty(apps, "safari", () => detectPlatformBinary({
655
661
  var open_default = open;
656
662
 
657
663
  // ../../filesystem/src/node.ts
664
+ var LOCK_HEARTBEAT_MS = 5000;
665
+ var LOCK_STALE_MS = 15000;
666
+ var LOCK_MAX_WAIT_MS = 20000;
667
+ var LOCK_MAX_HOLD_MS = 60000;
668
+ var LOCK_RETRY_MIN_MS = 100;
669
+ var LOCK_RETRY_JITTER_MS = 200;
670
+
658
671
  class NodeFileSystem {
659
672
  path = {
660
673
  join: path2.join,
@@ -731,6 +744,90 @@ class NodeFileSystem {
731
744
  async mkdir(dirPath) {
732
745
  await fs6.mkdir(dirPath, { recursive: true });
733
746
  }
747
+ async acquireLock(lockPath) {
748
+ const canonicalPath = await this.canonicalizeLockTarget(lockPath);
749
+ const lockFile = `${canonicalPath}.lock`;
750
+ const ownerId = randomUUID();
751
+ const start = Date.now();
752
+ while (true) {
753
+ try {
754
+ await fs6.writeFile(lockFile, ownerId, { flag: "wx" });
755
+ return this.createLockRelease(lockFile, ownerId);
756
+ } catch (error) {
757
+ if (!this.hasErrnoCode(error, "EEXIST")) {
758
+ throw error;
759
+ }
760
+ const stats = await fs6.stat(lockFile).catch(() => null);
761
+ if (stats && Date.now() - stats.mtimeMs > LOCK_STALE_MS) {
762
+ const reclaimed = await fs6.rm(lockFile, { force: true }).then(() => true).catch(() => false);
763
+ if (reclaimed)
764
+ continue;
765
+ }
766
+ if (Date.now() - start > LOCK_MAX_WAIT_MS) {
767
+ throw new Error(`ELOCKED: timed out waiting for lock on ${canonicalPath}`);
768
+ }
769
+ await new Promise((resolve2) => setTimeout(resolve2, LOCK_RETRY_MIN_MS + Math.random() * LOCK_RETRY_JITTER_MS));
770
+ }
771
+ }
772
+ }
773
+ async canonicalizeLockTarget(lockPath) {
774
+ const absolute = path2.resolve(lockPath);
775
+ const fullReal = await fs6.realpath(absolute).catch(() => null);
776
+ if (fullReal)
777
+ return fullReal;
778
+ const parent = path2.dirname(absolute);
779
+ const base = path2.basename(absolute);
780
+ const canonicalParent = await fs6.realpath(parent).catch(() => parent);
781
+ return path2.join(canonicalParent, base);
782
+ }
783
+ createLockRelease(lockFile, ownerId) {
784
+ const heartbeatStart = Date.now();
785
+ let heartbeatTimer;
786
+ let stopped = false;
787
+ const stopHeartbeat = () => {
788
+ stopped = true;
789
+ if (heartbeatTimer)
790
+ clearTimeout(heartbeatTimer);
791
+ };
792
+ const scheduleNextHeartbeat = () => {
793
+ if (stopped)
794
+ return;
795
+ if (Date.now() - heartbeatStart >= LOCK_MAX_HOLD_MS) {
796
+ stopped = true;
797
+ return;
798
+ }
799
+ heartbeatTimer = setTimeout(() => {
800
+ runHeartbeat();
801
+ }, LOCK_HEARTBEAT_MS);
802
+ heartbeatTimer.unref?.();
803
+ };
804
+ const runHeartbeat = async () => {
805
+ if (stopped)
806
+ return;
807
+ const current = await fs6.readFile(lockFile, "utf-8").catch(() => null);
808
+ if (stopped)
809
+ return;
810
+ if (current !== ownerId) {
811
+ stopped = true;
812
+ return;
813
+ }
814
+ const now = Date.now() / 1000;
815
+ await fs6.utimes(lockFile, now, now).catch(() => {});
816
+ scheduleNextHeartbeat();
817
+ };
818
+ scheduleNextHeartbeat();
819
+ let released = false;
820
+ return async () => {
821
+ if (released)
822
+ return;
823
+ released = true;
824
+ stopHeartbeat();
825
+ const current = await fs6.readFile(lockFile, "utf-8").catch(() => null);
826
+ if (current === ownerId) {
827
+ await fs6.rm(lockFile, { force: true });
828
+ }
829
+ };
830
+ }
734
831
  async rm(filePath) {
735
832
  await fs6.rm(filePath, { recursive: true, force: true });
736
833
  }
@@ -776,7 +873,10 @@ class NodeFileSystem {
776
873
  }
777
874
  }
778
875
  isEnoent(error) {
779
- return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
876
+ return this.hasErrnoCode(error, "ENOENT");
877
+ }
878
+ hasErrnoCode(error, code) {
879
+ return typeof error === "object" && error !== null && "code" in error && error.code === code;
780
880
  }
781
881
  }
782
882
 
@@ -918,10 +1018,15 @@ async function extractErrorDetails(error, options) {
918
1018
  }
919
1019
  if (parsedBody?.errorCode && typeof parsedBody.errorCode === "string") {
920
1020
  context.errorCode = parsedBody.errorCode;
1021
+ } else if (parsedBody?.code && typeof parsedBody.code === "string") {
1022
+ context.errorCode = parsedBody.code;
921
1023
  }
922
1024
  if (parsedBody?.requestId && typeof parsedBody.requestId === "string") {
923
1025
  context.requestId = parsedBody.requestId;
924
1026
  }
1027
+ if (parsedBody?.traceId && typeof parsedBody.traceId === "string") {
1028
+ context.traceId = parsedBody.traceId;
1029
+ }
925
1030
  if (status === 429) {
926
1031
  const resp = response;
927
1032
  const headersObj = resp?.headers;
@@ -941,7 +1046,35 @@ async function extractErrorDetails(error, options) {
941
1046
  }
942
1047
  }
943
1048
  const hasContext = Object.keys(context).length > 0;
944
- return { result, message, details, ...hasContext ? { context } : {} };
1049
+ let parsedErrors;
1050
+ if (parsedBody?.errors && typeof parsedBody.errors === "object") {
1051
+ const errors = {};
1052
+ for (const [field, raw] of Object.entries(parsedBody.errors)) {
1053
+ if (Array.isArray(raw)) {
1054
+ const messages = raw.map((entry) => {
1055
+ if (typeof entry === "string")
1056
+ return entry;
1057
+ if (entry && typeof entry === "object" && typeof entry.message === "string") {
1058
+ return entry.message;
1059
+ }
1060
+ return String(entry);
1061
+ }).filter(Boolean);
1062
+ if (messages.length > 0)
1063
+ errors[field] = messages;
1064
+ } else if (typeof raw === "string") {
1065
+ errors[field] = [raw];
1066
+ }
1067
+ }
1068
+ if (Object.keys(errors).length > 0)
1069
+ parsedErrors = errors;
1070
+ }
1071
+ return {
1072
+ result,
1073
+ message,
1074
+ details,
1075
+ ...hasContext ? { context } : {},
1076
+ ...parsedErrors ? { parsedErrors } : {}
1077
+ };
945
1078
  }
946
1079
  async function extractErrorMessage(error, options) {
947
1080
  const { message } = await extractErrorDetails(error, options);
@@ -6072,6 +6205,60 @@ function escapeNonAscii(jsonText) {
6072
6205
  function needsAsciiSafeJson(sink) {
6073
6206
  return process.platform === "win32" && !sink.capabilities.isInteractive;
6074
6207
  }
6208
+ function isPlainRecord(value) {
6209
+ if (value === null || typeof value !== "object")
6210
+ return false;
6211
+ const prototype = Object.getPrototypeOf(value);
6212
+ return prototype === Object.prototype || prototype === null;
6213
+ }
6214
+ function toLowerCamelCaseKey(key) {
6215
+ if (!key)
6216
+ return key;
6217
+ if (/[_\-\s]/.test(key)) {
6218
+ const [firstPart, ...restParts] = key.split(/[_\-\s]+/).filter(Boolean);
6219
+ if (!firstPart)
6220
+ return key;
6221
+ return [
6222
+ toLowerCamelCaseSimpleKey(firstPart),
6223
+ ...restParts.map((part) => {
6224
+ const normalized = toLowerCamelCaseSimpleKey(part);
6225
+ return normalized.charAt(0).toUpperCase() + normalized.slice(1);
6226
+ })
6227
+ ].join("");
6228
+ }
6229
+ return toLowerCamelCaseSimpleKey(key);
6230
+ }
6231
+ function toLowerCamelCaseSimpleKey(key) {
6232
+ if (/^[A-Z0-9]+$/.test(key))
6233
+ return key.toLowerCase();
6234
+ return key.replace(/^[A-Z]+(?=[A-Z][a-z]|\d|$)|^[A-Z]/, (match) => match.toLowerCase());
6235
+ }
6236
+ function toPascalCaseKey(key) {
6237
+ const lowerCamelKey = toLowerCamelCaseKey(key);
6238
+ return lowerCamelKey ? lowerCamelKey.charAt(0).toUpperCase() + lowerCamelKey.slice(1) : lowerCamelKey;
6239
+ }
6240
+ function toPascalCaseData(value) {
6241
+ if (Array.isArray(value))
6242
+ return value.map(toPascalCaseData);
6243
+ if (!isPlainRecord(value))
6244
+ return value;
6245
+ const result = {};
6246
+ for (const [key, nestedValue] of Object.entries(value)) {
6247
+ result[toPascalCaseKey(key)] = toPascalCaseData(nestedValue);
6248
+ }
6249
+ return result;
6250
+ }
6251
+ function normalizeDataKeys(data) {
6252
+ return toPascalCaseData(data);
6253
+ }
6254
+ function normalizeOutputKeys(data) {
6255
+ const result = {};
6256
+ for (const [key, value] of Object.entries(data)) {
6257
+ const pascalKey = toPascalCaseKey(key);
6258
+ result[pascalKey] = pascalKey === "Data" ? value : toPascalCaseData(value);
6259
+ }
6260
+ return result;
6261
+ }
6075
6262
  function printOutput(data, format = "json", logFn, asciiSafe = false) {
6076
6263
  if (!data) {
6077
6264
  logFn("Empty response object. No data to display.");
@@ -6134,7 +6321,7 @@ function wrapText(text, width) {
6134
6321
  function printTable(data, logFn, externalLogValue) {
6135
6322
  if (data.length === 0)
6136
6323
  return;
6137
- const keys = Object.keys(data[0]).filter((key) => key !== "Code" && key !== "Log");
6324
+ const keys = Object.keys(data[0]).filter((key) => !["code", "log"].includes(key.toLowerCase()));
6138
6325
  const maxWidths = keys.map((key) => Math.max(key.length, ...data.map((item) => cellToString(item[key]).length)));
6139
6326
  const header = keys.map((key, i2) => key.padEnd(maxWidths[i2])).join(" | ");
6140
6327
  logFn(header);
@@ -6149,7 +6336,7 @@ function printTable(data, logFn, externalLogValue) {
6149
6336
  }
6150
6337
  }
6151
6338
  function printVerticalTable(data, logFn = console.log, externalLogValue) {
6152
- const keys = Object.keys(data).filter((key) => key !== "Code" && key !== "Log");
6339
+ const keys = Object.keys(data).filter((key) => !["code", "log"].includes(key.toLowerCase()));
6153
6340
  if (keys.length === 0)
6154
6341
  return;
6155
6342
  const maxKeyWidth = Math.max(...keys.map((key) => key.length));
@@ -6165,7 +6352,7 @@ function printVerticalTable(data, logFn = console.log, externalLogValue) {
6165
6352
  function printResizableTable(data, logFn = console.log, externalLogValue) {
6166
6353
  if (data.length === 0)
6167
6354
  return;
6168
- const keys = Object.keys(data[0]).filter((key) => key !== "Code" && key !== "Log");
6355
+ const keys = Object.keys(data[0]).filter((key) => !["code", "log"].includes(key.toLowerCase()));
6169
6356
  if (keys.length === 0)
6170
6357
  return;
6171
6358
  if (!process.stdout.isTTY) {
@@ -6241,8 +6428,26 @@ function printResizableTable(data, logFn = console.log, externalLogValue) {
6241
6428
  function toYaml(data) {
6242
6429
  return dump(data);
6243
6430
  }
6431
+ class FilterEvaluationError extends Error {
6432
+ __brand = "FilterEvaluationError";
6433
+ filter;
6434
+ instructions;
6435
+ result = RESULTS.ValidationError;
6436
+ constructor(filter, cause) {
6437
+ const underlying = cause instanceof Error ? cause.message : String(cause);
6438
+ super(`Filter '${filter}' failed to evaluate: ${underlying}`);
6439
+ this.name = "FilterEvaluationError";
6440
+ this.filter = filter;
6441
+ this.instructions = `The --output-filter expression '${filter}' failed at evaluation time. ` + "Note that --output-filter operates on the 'Data' field of the envelope, not the full object. " + "For example, on a list result use 'length(@)' instead of 'Data | length(@)'.";
6442
+ }
6443
+ }
6244
6444
  function applyFilter(data, filter) {
6245
- const result = search(data, filter);
6445
+ let result;
6446
+ try {
6447
+ result = search(data, filter);
6448
+ } catch (err) {
6449
+ throw new FilterEvaluationError(filter, err);
6450
+ }
6246
6451
  if (result == null)
6247
6452
  return [];
6248
6453
  if (Array.isArray(result)) {
@@ -6259,13 +6464,18 @@ function applyFilter(data, filter) {
6259
6464
  }
6260
6465
  var OutputFormatter;
6261
6466
  ((OutputFormatter) => {
6262
- function success(data) {
6467
+ function success(data, options) {
6263
6468
  data.Log ??= getLogFilePath() || undefined;
6469
+ const normalize = !options?.preserveDataKeys;
6470
+ if (normalize) {
6471
+ data.Data = normalizeDataKeys(data.Data);
6472
+ }
6264
6473
  const filter = getOutputFilter();
6265
6474
  if (filter) {
6266
- data.Data = applyFilter(data.Data, filter);
6475
+ const filtered = applyFilter(data.Data, filter);
6476
+ data.Data = normalize ? normalizeDataKeys(filtered) : filtered;
6267
6477
  }
6268
- logOutput(data, getOutputFormat());
6478
+ logOutput(normalizeOutputKeys(data), getOutputFormat());
6269
6479
  }
6270
6480
  OutputFormatter.success = success;
6271
6481
  function error(data) {
@@ -6275,7 +6485,7 @@ var OutputFormatter;
6275
6485
  result: data.Result,
6276
6486
  message: data.Message
6277
6487
  });
6278
- logOutput(data, getOutputFormat());
6488
+ logOutput(normalizeOutputKeys(data), getOutputFormat());
6279
6489
  }
6280
6490
  OutputFormatter.error = error;
6281
6491
  function emitList(code, items, opts) {
@@ -6296,13 +6506,14 @@ var OutputFormatter;
6296
6506
  function log(data) {
6297
6507
  const format = getOutputFormat();
6298
6508
  const sink = getOutputSink();
6509
+ const normalized = toPascalCaseData(data);
6299
6510
  if (format === "json") {
6300
- const json2 = JSON.stringify(data);
6511
+ const json2 = JSON.stringify(normalized);
6301
6512
  const safe = needsAsciiSafeJson(sink) ? escapeNonAscii(json2) : json2;
6302
6513
  sink.writeErr(`${safe}
6303
6514
  `);
6304
6515
  } else {
6305
- for (const [key, value] of Object.entries(data)) {
6516
+ for (const [key, value] of Object.entries(normalized)) {
6306
6517
  sink.writeErr(`${key}: ${value}
6307
6518
  `);
6308
6519
  }
@@ -6311,12 +6522,16 @@ var OutputFormatter;
6311
6522
  OutputFormatter.log = log;
6312
6523
  function formatToString(data) {
6313
6524
  const filter = getOutputFilter();
6314
- if (filter && "Data" in data && data.Data != null) {
6315
- data.Data = applyFilter(data.Data, filter);
6525
+ if ("Data" in data && data.Data != null) {
6526
+ data.Data = normalizeDataKeys(data.Data);
6527
+ if (filter) {
6528
+ data.Data = normalizeDataKeys(applyFilter(data.Data, filter));
6529
+ }
6316
6530
  }
6531
+ const output = normalizeOutputKeys(data);
6317
6532
  const lines = [];
6318
6533
  const sink = getOutputSink();
6319
- printOutput(data, getOutputFormat(), (msg) => {
6534
+ printOutput(output, getOutputFormat(), (msg) => {
6320
6535
  lines.push(msg);
6321
6536
  }, needsAsciiSafeJson(sink));
6322
6537
  return lines.join(`
@@ -7731,6 +7946,22 @@ JSONPath.prototype.vm = vm;
7731
7946
  import { Option } from "commander";
7732
7947
  // ../../common/src/option-validators.ts
7733
7948
  import { InvalidArgumentError } from "commander";
7949
+ // ../../common/src/polling/types.ts
7950
+ var PollOutcome = {
7951
+ Completed: "completed",
7952
+ Timeout: "timeout",
7953
+ Interrupted: "interrupted",
7954
+ Aborted: "aborted",
7955
+ Failed: "failed"
7956
+ };
7957
+
7958
+ // ../../common/src/polling/poll-failure-mapping.ts
7959
+ var REASON_BY_OUTCOME = {
7960
+ [PollOutcome.Timeout]: "poll_timeout",
7961
+ [PollOutcome.Failed]: "poll_failed",
7962
+ [PollOutcome.Interrupted]: "poll_failed",
7963
+ [PollOutcome.Aborted]: "poll_aborted"
7964
+ };
7734
7965
  // ../../common/src/polling/terminal-statuses.ts
7735
7966
  var TERMINAL_STATUSES = new Set([
7736
7967
  "completed",
@@ -7758,6 +7989,8 @@ var ScreenLogger;
7758
7989
  }
7759
7990
  ScreenLogger.progress = progress;
7760
7991
  })(ScreenLogger ||= {});
7992
+ // ../../common/src/sdk-user-agent.ts
7993
+ var sdkUserAgentHostToken = singleton("SdkUserAgentHostToken");
7761
7994
  // ../../common/src/tool-provider.ts
7762
7995
  var factorySlot = singleton("PackagerFactoryProvider");
7763
7996
  // ../../common/src/trackedAction.ts
@@ -7943,13 +8176,17 @@ Command2.prototype.trackedAction = function(context, fn, properties) {
7943
8176
  const [error] = await catchError(fn(...args));
7944
8177
  if (error) {
7945
8178
  errorMessage = error instanceof Error ? error.message : String(error);
7946
- logger.error(`[trackedAction] ${telemetryName} failed: ${errorMessage}`);
8179
+ logger.debug(`[trackedAction] ${telemetryName} failed: ${errorMessage}`);
8180
+ const typed = error;
8181
+ const customInstructions = typeof typed.instructions === "string" ? typed.instructions : undefined;
8182
+ const customResult = typeof typed.result === "string" && typed.result !== RESULTS.Success && Object.values(RESULTS).includes(typed.result) ? typed.result : undefined;
8183
+ const finalResult = customResult ?? RESULTS.Failure;
7947
8184
  OutputFormatter.error({
7948
- Result: RESULTS.Failure,
8185
+ Result: finalResult,
7949
8186
  Message: errorMessage,
7950
- Instructions: "An unexpected error occurred. Run with --log-level debug for details."
8187
+ Instructions: customInstructions ?? "An unexpected error occurred. Run with --log-level debug for details."
7951
8188
  });
7952
- context.exit(1);
8189
+ context.exit(EXIT_CODES[finalResult]);
7953
8190
  }
7954
8191
  const durationMs = performance.now() - startTime;
7955
8192
  const success = !error && (process.exitCode === undefined || process.exitCode === 0);
@@ -7993,6 +8230,7 @@ import path3 from "node:path";
7993
8230
  import { fileURLToPath as fileURLToPath2 } from "node:url";
7994
8231
  import childProcess32 from "node:child_process";
7995
8232
  import fs52, { constants as fsConstants22 } from "node:fs/promises";
8233
+ import { randomUUID as randomUUID2 } from "node:crypto";
7996
8234
  import { existsSync as existsSync2 } from "node:fs";
7997
8235
  import * as fs62 from "node:fs/promises";
7998
8236
  import * as os22 from "node:os";
@@ -8744,6 +8982,90 @@ class NodeFileSystem2 {
8744
8982
  async mkdir(dirPath) {
8745
8983
  await fs62.mkdir(dirPath, { recursive: true });
8746
8984
  }
8985
+ async acquireLock(lockPath) {
8986
+ const canonicalPath = await this.canonicalizeLockTarget(lockPath);
8987
+ const lockFile = `${canonicalPath}.lock`;
8988
+ const ownerId = randomUUID2();
8989
+ const start = Date.now();
8990
+ while (true) {
8991
+ try {
8992
+ await fs62.writeFile(lockFile, ownerId, { flag: "wx" });
8993
+ return this.createLockRelease(lockFile, ownerId);
8994
+ } catch (error) {
8995
+ if (!this.hasErrnoCode(error, "EEXIST")) {
8996
+ throw error;
8997
+ }
8998
+ const stats = await fs62.stat(lockFile).catch(() => null);
8999
+ if (stats && Date.now() - stats.mtimeMs > LOCK_STALE_MS2) {
9000
+ const reclaimed = await fs62.rm(lockFile, { force: true }).then(() => true).catch(() => false);
9001
+ if (reclaimed)
9002
+ continue;
9003
+ }
9004
+ if (Date.now() - start > LOCK_MAX_WAIT_MS2) {
9005
+ throw new Error(`ELOCKED: timed out waiting for lock on ${canonicalPath}`);
9006
+ }
9007
+ await new Promise((resolve22) => setTimeout(resolve22, LOCK_RETRY_MIN_MS2 + Math.random() * LOCK_RETRY_JITTER_MS2));
9008
+ }
9009
+ }
9010
+ }
9011
+ async canonicalizeLockTarget(lockPath) {
9012
+ const absolute = path22.resolve(lockPath);
9013
+ const fullReal = await fs62.realpath(absolute).catch(() => null);
9014
+ if (fullReal)
9015
+ return fullReal;
9016
+ const parent = path22.dirname(absolute);
9017
+ const base = path22.basename(absolute);
9018
+ const canonicalParent = await fs62.realpath(parent).catch(() => parent);
9019
+ return path22.join(canonicalParent, base);
9020
+ }
9021
+ createLockRelease(lockFile, ownerId) {
9022
+ const heartbeatStart = Date.now();
9023
+ let heartbeatTimer;
9024
+ let stopped = false;
9025
+ const stopHeartbeat = () => {
9026
+ stopped = true;
9027
+ if (heartbeatTimer)
9028
+ clearTimeout(heartbeatTimer);
9029
+ };
9030
+ const scheduleNextHeartbeat = () => {
9031
+ if (stopped)
9032
+ return;
9033
+ if (Date.now() - heartbeatStart >= LOCK_MAX_HOLD_MS2) {
9034
+ stopped = true;
9035
+ return;
9036
+ }
9037
+ heartbeatTimer = setTimeout(() => {
9038
+ runHeartbeat();
9039
+ }, LOCK_HEARTBEAT_MS2);
9040
+ heartbeatTimer.unref?.();
9041
+ };
9042
+ const runHeartbeat = async () => {
9043
+ if (stopped)
9044
+ return;
9045
+ const current = await fs62.readFile(lockFile, "utf-8").catch(() => null);
9046
+ if (stopped)
9047
+ return;
9048
+ if (current !== ownerId) {
9049
+ stopped = true;
9050
+ return;
9051
+ }
9052
+ const now = Date.now() / 1000;
9053
+ await fs62.utimes(lockFile, now, now).catch(() => {});
9054
+ scheduleNextHeartbeat();
9055
+ };
9056
+ scheduleNextHeartbeat();
9057
+ let released = false;
9058
+ return async () => {
9059
+ if (released)
9060
+ return;
9061
+ released = true;
9062
+ stopHeartbeat();
9063
+ const current = await fs62.readFile(lockFile, "utf-8").catch(() => null);
9064
+ if (current === ownerId) {
9065
+ await fs62.rm(lockFile, { force: true });
9066
+ }
9067
+ };
9068
+ }
8747
9069
  async rm(filePath) {
8748
9070
  await fs62.rm(filePath, { recursive: true, force: true });
8749
9071
  }
@@ -8789,9 +9111,18 @@ class NodeFileSystem2 {
8789
9111
  }
8790
9112
  }
8791
9113
  isEnoent(error) {
8792
- return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
9114
+ return this.hasErrnoCode(error, "ENOENT");
9115
+ }
9116
+ hasErrnoCode(error, code) {
9117
+ return typeof error === "object" && error !== null && "code" in error && error.code === code;
8793
9118
  }
8794
9119
  }
9120
+ var LOCK_HEARTBEAT_MS2 = 5000;
9121
+ var LOCK_STALE_MS2 = 15000;
9122
+ var LOCK_MAX_WAIT_MS2 = 20000;
9123
+ var LOCK_MAX_HOLD_MS2 = 60000;
9124
+ var LOCK_RETRY_MIN_MS2 = 100;
9125
+ var LOCK_RETRY_JITTER_MS2 = 200;
8795
9126
  var init_node = __esm(() => {
8796
9127
  init_open();
8797
9128
  });
@@ -8803,7 +9134,7 @@ var init_src = __esm(() => {
8803
9134
  fsInstance2 = new NodeFileSystem2;
8804
9135
  });
8805
9136
  var require_coreipc = __commonJS((exports, module) => {
8806
- var __dirname3 = "/Users/alexandru.oltean/github/cli/node_modules/@uipath/coreipc";
9137
+ var __dirname3 = "/home/runner/work/cli/cli/node_modules/@uipath/coreipc";
8807
9138
  /*! For license information please see index.js.LICENSE.txt */
8808
9139
  (function(e, t) {
8809
9140
  typeof exports == "object" && typeof module == "object" ? module.exports = t() : typeof define == "function" && define.amd ? define([], t) : typeof exports == "object" ? exports.ipc = t() : e.ipc = t();
@@ -26603,6 +26934,9 @@ var require_dist = __commonJS((exports) => {
26603
26934
  exports.RobotProxyConstructor = RobotProxyConstructor;
26604
26935
  __exportStar(require_agent(), exports);
26605
26936
  });
26937
+ var init_server = __esm(() => {
26938
+ init_constants();
26939
+ });
26606
26940
  init_constants();
26607
26941
  var DEFAULT_CLIENT_ID = "36dea5b8-e8bb-423d-8e7b-c808df8f1c00";
26608
26942
  var AUTH_FILE_CONFIG_KEY = Symbol.for("@uipath/auth/AuthFileConfig");
@@ -26626,32 +26960,7 @@ class InvalidBaseUrlError extends Error {
26626
26960
  this.name = "InvalidBaseUrlError";
26627
26961
  }
26628
26962
  }
26629
- var DEFAULT_SCOPES = [
26630
- "offline_access",
26631
- "ProcessMining",
26632
- "OrchestratorApiUserAccess",
26633
- "StudioWebBackend",
26634
- "IdentityServerApi",
26635
- "ConnectionService",
26636
- "DataService",
26637
- "DataServiceApiUserAccess",
26638
- "DocumentUnderstanding",
26639
- "EnterpriseContextService",
26640
- "Directory",
26641
- "JamJamApi",
26642
- "LLMGateway",
26643
- "LLMOps",
26644
- "OMS",
26645
- "RCS.FolderAuthorization",
26646
- "RCS.TagsManagement",
26647
- "TestmanagerApiUserAccess",
26648
- "AutomationSolutions",
26649
- "StudioWebTypeCacheService",
26650
- "Docs.GPT.Search",
26651
- "Insights",
26652
- "ReferenceToken",
26653
- "Audit.Read"
26654
- ];
26963
+ var DEFAULT_SCOPES = ["openid", "profile", "offline_access"];
26655
26964
  var normalizeAndValidateBaseUrl = (rawUrl) => {
26656
26965
  let baseUrl = rawUrl;
26657
26966
  if (baseUrl.endsWith("/identity_/")) {
@@ -26701,7 +27010,8 @@ var resolveConfigAsync = async ({
26701
27010
  if (!clientSecret && fileAuth.clientSecret) {
26702
27011
  clientSecret = fileAuth.clientSecret;
26703
27012
  }
26704
- const scopes = customScopes && customScopes.length > 0 ? customScopes : fileAuth.scopes && fileAuth.scopes.length > 0 ? fileAuth.scopes : DEFAULT_SCOPES;
27013
+ const isExternalAppAuth = clientId !== DEFAULT_CLIENT_ID && Boolean(clientSecret);
27014
+ const scopes = customScopes && customScopes.length > 0 ? customScopes : fileAuth.scopes && fileAuth.scopes.length > 0 ? fileAuth.scopes : isExternalAppAuth ? [] : DEFAULT_SCOPES;
26705
27015
  return {
26706
27016
  clientId,
26707
27017
  clientSecret,
@@ -27188,6 +27498,129 @@ function normalizeTokenRefreshFailure() {
27188
27498
  function normalizeTokenRefreshUnavailableFailure() {
27189
27499
  return "token refresh failed before authentication completed";
27190
27500
  }
27501
+ function errorMessage(error) {
27502
+ return error instanceof Error ? error.message : String(error);
27503
+ }
27504
+ function computeExpirationThreshold(ensureTokenValidityMinutes) {
27505
+ return new Date(Date.now() + (ensureTokenValidityMinutes ?? 0) * 60 * 1000);
27506
+ }
27507
+ async function runRefreshLocked(inputs) {
27508
+ const {
27509
+ absolutePath,
27510
+ refreshToken: callerRefreshToken,
27511
+ customAuthority,
27512
+ ensureTokenValidityMinutes,
27513
+ loadEnvFile,
27514
+ saveEnvFile,
27515
+ refreshFn,
27516
+ resolveConfig
27517
+ } = inputs;
27518
+ const expirationThreshold = computeExpirationThreshold(ensureTokenValidityMinutes);
27519
+ let fresh;
27520
+ try {
27521
+ fresh = await loadEnvFile({ envPath: absolutePath });
27522
+ } catch (error) {
27523
+ return {
27524
+ kind: "fail",
27525
+ status: {
27526
+ loginStatus: "Refresh Failed",
27527
+ hint: "Could not read the auth file while refreshing. Retry, or run 'uip login' to re-authenticate.",
27528
+ tokenRefresh: {
27529
+ attempted: false,
27530
+ success: false,
27531
+ errorMessage: `auth file read failed: ${errorMessage(error)}`
27532
+ }
27533
+ }
27534
+ };
27535
+ }
27536
+ const freshAccess = fresh.UIPATH_ACCESS_TOKEN;
27537
+ const freshExp = freshAccess ? getTokenExpiration(freshAccess) : undefined;
27538
+ if (freshAccess && freshExp && freshExp > expirationThreshold) {
27539
+ return {
27540
+ kind: "ok",
27541
+ accessToken: freshAccess,
27542
+ refreshToken: fresh.UIPATH_REFRESH_TOKEN ?? callerRefreshToken,
27543
+ expiration: freshExp,
27544
+ tokenRefresh: { attempted: false, success: true }
27545
+ };
27546
+ }
27547
+ const tokenForIdP = fresh.UIPATH_REFRESH_TOKEN ?? callerRefreshToken;
27548
+ let refreshedAccess;
27549
+ let refreshedRefresh;
27550
+ try {
27551
+ const config = await resolveConfig({ customAuthority });
27552
+ const refreshed = await refreshFn({
27553
+ refreshToken: tokenForIdP,
27554
+ tokenEndpoint: config.tokenEndpoint,
27555
+ clientId: config.clientId,
27556
+ expectedAuthority: customAuthority
27557
+ });
27558
+ refreshedAccess = refreshed.accessToken;
27559
+ refreshedRefresh = refreshed.refreshToken;
27560
+ } catch (error) {
27561
+ const isOAuthFailure = isTokenRefreshOAuthFailure(error);
27562
+ const hint = isOAuthFailure ? "Run 'uip login' to re-authenticate — the stored refresh token is invalid or expired." : "Token refresh failed. Check your network connection, then retry or run 'uip login' to re-authenticate.";
27563
+ const message = isOAuthFailure ? normalizeTokenRefreshFailure() : normalizeTokenRefreshUnavailableFailure();
27564
+ return {
27565
+ kind: "fail",
27566
+ status: {
27567
+ loginStatus: "Refresh Failed",
27568
+ hint,
27569
+ tokenRefresh: {
27570
+ attempted: true,
27571
+ success: false,
27572
+ errorMessage: message
27573
+ }
27574
+ }
27575
+ };
27576
+ }
27577
+ const refreshedExp = getTokenExpiration(refreshedAccess);
27578
+ if (!refreshedExp || refreshedExp <= new Date) {
27579
+ return {
27580
+ kind: "fail",
27581
+ status: {
27582
+ loginStatus: "Refresh Failed",
27583
+ hint: "The identity server returned an unusable token. Run 'uip login' to re-authenticate.",
27584
+ tokenRefresh: {
27585
+ attempted: true,
27586
+ success: false,
27587
+ errorMessage: "refreshed token has no valid expiration claim"
27588
+ }
27589
+ }
27590
+ };
27591
+ }
27592
+ try {
27593
+ await saveEnvFile({
27594
+ envPath: absolutePath,
27595
+ data: {
27596
+ UIPATH_ACCESS_TOKEN: refreshedAccess,
27597
+ UIPATH_REFRESH_TOKEN: refreshedRefresh
27598
+ },
27599
+ merge: true
27600
+ });
27601
+ return {
27602
+ kind: "ok",
27603
+ accessToken: refreshedAccess,
27604
+ refreshToken: refreshedRefresh,
27605
+ expiration: refreshedExp,
27606
+ tokenRefresh: { attempted: true, success: true }
27607
+ };
27608
+ } catch (error) {
27609
+ const msg = errorMessage(error);
27610
+ return {
27611
+ kind: "ok",
27612
+ accessToken: refreshedAccess,
27613
+ refreshToken: refreshedRefresh,
27614
+ expiration: refreshedExp,
27615
+ persistenceWarning: `Access token refreshed in memory but could not be written to ${absolutePath}: ${msg}. The next CLI invocation will fail until the file can be updated — run 'uip login' to re-authenticate.`,
27616
+ tokenRefresh: {
27617
+ attempted: true,
27618
+ success: true,
27619
+ errorMessage: `persistence failed: ${msg}`
27620
+ }
27621
+ };
27622
+ }
27623
+ }
27191
27624
  var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
27192
27625
  const {
27193
27626
  resolveEnvFilePath = resolveEnvFilePathAsync,
@@ -27262,73 +27695,103 @@ var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
27262
27695
  let refreshToken = credentials.UIPATH_REFRESH_TOKEN;
27263
27696
  let expiration = getTokenExpiration(accessToken);
27264
27697
  let persistenceWarning;
27698
+ let lockReleaseFailed = false;
27265
27699
  let tokenRefresh;
27266
- const expirationThreshold = new Date(Date.now() + (ensureTokenValidityMinutes ?? 0) * 60 * 1000);
27267
- if (expiration && expiration <= expirationThreshold && refreshToken) {
27268
- let refreshedAccess;
27269
- let refreshedRefresh;
27700
+ const outerThreshold = computeExpirationThreshold(ensureTokenValidityMinutes);
27701
+ const tryGlobalCredsHint = async () => {
27702
+ const fs72 = getFs();
27703
+ const globalPath = fs72.path.join(fs72.env.homedir(), envFilePath);
27704
+ if (absolutePath === globalPath)
27705
+ return;
27706
+ if (!await fs72.exists(globalPath))
27707
+ return;
27270
27708
  try {
27271
- const config = await resolveConfig({
27272
- customAuthority: credentials.UIPATH_URL
27273
- });
27274
- const refreshed = await refreshTokenFn({
27275
- refreshToken,
27276
- tokenEndpoint: config.tokenEndpoint,
27277
- clientId: config.clientId,
27278
- expectedAuthority: credentials.UIPATH_URL
27279
- });
27280
- refreshedAccess = refreshed.accessToken;
27281
- refreshedRefresh = refreshed.refreshToken;
27709
+ const globalCreds = await loadEnvFile({ envPath: globalPath });
27710
+ if (!globalCreds.UIPATH_ACCESS_TOKEN)
27711
+ return;
27712
+ const globalExp = getTokenExpiration(globalCreds.UIPATH_ACCESS_TOKEN);
27713
+ if (globalExp && globalExp <= new Date)
27714
+ return;
27715
+ return `Local credentials file at ${absolutePath} has expired credentials. Valid credentials exist in ${globalPath}. Remove the local file or run 'uip login' to re-authenticate.`;
27716
+ } catch {
27717
+ return;
27718
+ }
27719
+ };
27720
+ if (expiration && expiration <= outerThreshold && refreshToken) {
27721
+ let release;
27722
+ try {
27723
+ release = await getFs().acquireLock(absolutePath);
27282
27724
  } catch (error) {
27283
- const isOAuthFailure = isTokenRefreshOAuthFailure(error);
27284
- const hint = isOAuthFailure ? "Run 'uip login' to re-authenticate — the stored refresh token is invalid or expired." : "Token refresh failed. Check your network connection, then retry or run 'uip login' to re-authenticate.";
27285
- const errorMessage = isOAuthFailure ? normalizeTokenRefreshFailure() : normalizeTokenRefreshUnavailableFailure();
27725
+ const msg = errorMessage(error);
27726
+ const globalHint = await tryGlobalCredsHint();
27727
+ if (globalHint) {
27728
+ return {
27729
+ loginStatus: "Expired",
27730
+ accessToken,
27731
+ refreshToken,
27732
+ baseUrl: credentials.UIPATH_URL,
27733
+ organizationName: credentials.UIPATH_ORGANIZATION_NAME,
27734
+ organizationId: credentials.UIPATH_ORGANIZATION_ID,
27735
+ tenantName: credentials.UIPATH_TENANT_NAME,
27736
+ tenantId: credentials.UIPATH_TENANT_ID,
27737
+ expiration,
27738
+ source: "file",
27739
+ hint: globalHint,
27740
+ tokenRefresh: {
27741
+ attempted: false,
27742
+ success: false,
27743
+ errorMessage: `lock acquisition failed: ${msg}`
27744
+ }
27745
+ };
27746
+ }
27286
27747
  return {
27287
27748
  loginStatus: "Refresh Failed",
27288
- hint,
27749
+ hint: "Could not acquire the auth-file lock — too many concurrent `uip` processes, or a permission issue on the auth directory. Retry, or run 'uip login' to re-authenticate.",
27289
27750
  tokenRefresh: {
27290
- attempted: true,
27751
+ attempted: false,
27291
27752
  success: false,
27292
- errorMessage
27753
+ errorMessage: `lock acquisition failed: ${msg}`
27293
27754
  }
27294
27755
  };
27295
27756
  }
27296
- const refreshedExp = getTokenExpiration(refreshedAccess);
27297
- if (!refreshedExp || refreshedExp <= new Date) {
27298
- return {
27299
- loginStatus: "Refresh Failed",
27300
- hint: "The identity server returned an unusable token. Run 'uip login' to re-authenticate.",
27301
- tokenRefresh: {
27302
- attempted: true,
27303
- success: false,
27304
- errorMessage: "refreshed token has no valid expiration claim"
27305
- }
27306
- };
27307
- }
27308
- accessToken = refreshedAccess;
27309
- refreshToken = refreshedRefresh;
27310
- expiration = refreshedExp;
27757
+ let lockedFailure;
27311
27758
  try {
27312
- await saveEnvFile({
27313
- envPath: absolutePath,
27314
- data: {
27315
- UIPATH_ACCESS_TOKEN: accessToken,
27316
- UIPATH_REFRESH_TOKEN: refreshToken
27317
- },
27318
- merge: true
27759
+ const outcome = await runRefreshLocked({
27760
+ absolutePath,
27761
+ refreshToken,
27762
+ customAuthority: credentials.UIPATH_URL,
27763
+ ensureTokenValidityMinutes,
27764
+ loadEnvFile,
27765
+ saveEnvFile,
27766
+ refreshFn: refreshTokenFn,
27767
+ resolveConfig
27319
27768
  });
27320
- tokenRefresh = {
27321
- attempted: true,
27322
- success: true
27323
- };
27324
- } catch (error) {
27325
- const msg = error instanceof Error ? error.message : String(error);
27326
- persistenceWarning = `Access token refreshed in memory but could not be written to ${absolutePath}: ${msg}. The next CLI invocation will fail until the file can be updated — run 'uip login' to re-authenticate.`;
27327
- tokenRefresh = {
27328
- attempted: true,
27329
- success: true,
27330
- errorMessage: `persistence failed: ${msg}`
27331
- };
27769
+ if (outcome.kind === "fail") {
27770
+ lockedFailure = outcome.status;
27771
+ } else {
27772
+ accessToken = outcome.accessToken;
27773
+ refreshToken = outcome.refreshToken;
27774
+ expiration = outcome.expiration;
27775
+ tokenRefresh = outcome.tokenRefresh;
27776
+ if (outcome.persistenceWarning) {
27777
+ persistenceWarning = outcome.persistenceWarning;
27778
+ }
27779
+ }
27780
+ } finally {
27781
+ try {
27782
+ await release();
27783
+ } catch {
27784
+ lockReleaseFailed = true;
27785
+ }
27786
+ }
27787
+ if (lockedFailure) {
27788
+ const globalHint = await tryGlobalCredsHint();
27789
+ const base = globalHint ? {
27790
+ ...lockedFailure,
27791
+ loginStatus: "Expired",
27792
+ hint: globalHint
27793
+ } : lockedFailure;
27794
+ return lockReleaseFailed ? { ...base, lockReleaseFailed: true } : base;
27332
27795
  }
27333
27796
  }
27334
27797
  const result = {
@@ -27343,23 +27806,13 @@ var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
27343
27806
  expiration,
27344
27807
  source: "file",
27345
27808
  ...persistenceWarning ? { hint: persistenceWarning, persistenceFailed: true } : {},
27809
+ ...lockReleaseFailed ? { lockReleaseFailed: true } : {},
27346
27810
  ...tokenRefresh ? { tokenRefresh } : {}
27347
27811
  };
27348
27812
  if (result.loginStatus === "Expired") {
27349
- const fs72 = getFs();
27350
- const globalPath = fs72.path.join(fs72.env.homedir(), envFilePath);
27351
- if (absolutePath !== globalPath && await fs72.exists(globalPath)) {
27352
- try {
27353
- const globalCreds = await loadEnvFile({
27354
- envPath: globalPath
27355
- });
27356
- if (globalCreds.UIPATH_ACCESS_TOKEN) {
27357
- const globalExp = getTokenExpiration(globalCreds.UIPATH_ACCESS_TOKEN);
27358
- if (!globalExp || globalExp > new Date) {
27359
- result.hint = `Local credentials file at ${absolutePath} has expired credentials. Valid credentials exist in ${globalPath}. Remove the local file or run 'uip login' to re-authenticate.`;
27360
- }
27361
- }
27362
- } catch {}
27813
+ const globalHint = await tryGlobalCredsHint();
27814
+ if (globalHint) {
27815
+ result.hint = globalHint;
27363
27816
  }
27364
27817
  }
27365
27818
  return result;
@@ -27374,6 +27827,7 @@ var getLoginStatusAsync = async (options = {}) => {
27374
27827
  };
27375
27828
  init_src();
27376
27829
  init_src();
27830
+ init_server();
27377
27831
  var SERVICE_PATH = {
27378
27832
  "llm-gateway": "llmgateway_"
27379
27833
  };
package/package.json CHANGED
@@ -1,33 +1,27 @@
1
1
  {
2
- "name": "@uipath/llmgw-tool",
3
- "version": "1.1.0",
4
- "description": "CLI plugin for UiPath AI Trust Layer Bring-Your-Own LLM connections.",
5
- "private": false,
6
- "repository": {
7
- "type": "git",
8
- "url": "https://github.com/UiPath/cli.git",
9
- "directory": "packages/admin/llmgw-tool"
10
- },
11
- "publishConfig": {
12
- "registry": "https://registry.npmjs.org/"
13
- },
14
- "keywords": [
15
- "cli-tool"
16
- ],
17
- "type": "module",
18
- "main": "./dist/tool.js",
19
- "exports": {
20
- ".": "./dist/tool.js"
21
- },
22
- "files": [
23
- "dist"
24
- ],
25
- "devDependencies": {
26
- "@types/bun": "^1.3.11",
27
- "@uipath/llmgw-sdk": "1.1.0",
28
- "@uipath/common": "1.1.0",
29
- "commander": "^14.0.3",
30
- "typescript": "^6.0.2"
31
- },
32
- "gitHead": "06e8c8f566df4b87da4a008635483c62f64f33f0"
2
+ "name": "@uipath/llmgw-tool",
3
+ "license": "MIT",
4
+ "version": "1.195.0",
5
+ "description": "CLI plugin for UiPath AI Trust Layer Bring-Your-Own LLM connections.",
6
+ "private": false,
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/UiPath/cli.git",
10
+ "directory": "packages/admin/llmgw-tool"
11
+ },
12
+ "publishConfig": {
13
+ "registry": "https://registry.npmjs.org/"
14
+ },
15
+ "keywords": [
16
+ "cli-tool"
17
+ ],
18
+ "type": "module",
19
+ "main": "./dist/tool.js",
20
+ "exports": {
21
+ ".": "./dist/tool.js"
22
+ },
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "gitHead": "eecf5713cd579b15783c770d1923e44e730271ea"
33
27
  }