@uipath/audit-tool 1.1.0 → 1.2.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 (3) hide show
  1. package/dist/index.js +648 -152
  2. package/dist/tool.js +648 -152
  3. package/package.json +28 -37
package/dist/tool.js CHANGED
@@ -706,6 +706,7 @@ var init_open = __esm(() => {
706
706
  });
707
707
 
708
708
  // ../../filesystem/src/node.ts
709
+ import { randomUUID } from "node:crypto";
709
710
  import { existsSync } from "node:fs";
710
711
  import * as fs6 from "node:fs/promises";
711
712
  import * as os2 from "node:os";
@@ -787,6 +788,90 @@ class NodeFileSystem {
787
788
  async mkdir(dirPath) {
788
789
  await fs6.mkdir(dirPath, { recursive: true });
789
790
  }
791
+ async acquireLock(lockPath) {
792
+ const canonicalPath = await this.canonicalizeLockTarget(lockPath);
793
+ const lockFile = `${canonicalPath}.lock`;
794
+ const ownerId = randomUUID();
795
+ const start = Date.now();
796
+ while (true) {
797
+ try {
798
+ await fs6.writeFile(lockFile, ownerId, { flag: "wx" });
799
+ return this.createLockRelease(lockFile, ownerId);
800
+ } catch (error) {
801
+ if (!this.hasErrnoCode(error, "EEXIST")) {
802
+ throw error;
803
+ }
804
+ const stats = await fs6.stat(lockFile).catch(() => null);
805
+ if (stats && Date.now() - stats.mtimeMs > LOCK_STALE_MS) {
806
+ const reclaimed = await fs6.rm(lockFile, { force: true }).then(() => true).catch(() => false);
807
+ if (reclaimed)
808
+ continue;
809
+ }
810
+ if (Date.now() - start > LOCK_MAX_WAIT_MS) {
811
+ throw new Error(`ELOCKED: timed out waiting for lock on ${canonicalPath}`);
812
+ }
813
+ await new Promise((resolve2) => setTimeout(resolve2, LOCK_RETRY_MIN_MS + Math.random() * LOCK_RETRY_JITTER_MS));
814
+ }
815
+ }
816
+ }
817
+ async canonicalizeLockTarget(lockPath) {
818
+ const absolute = path2.resolve(lockPath);
819
+ const fullReal = await fs6.realpath(absolute).catch(() => null);
820
+ if (fullReal)
821
+ return fullReal;
822
+ const parent = path2.dirname(absolute);
823
+ const base = path2.basename(absolute);
824
+ const canonicalParent = await fs6.realpath(parent).catch(() => parent);
825
+ return path2.join(canonicalParent, base);
826
+ }
827
+ createLockRelease(lockFile, ownerId) {
828
+ const heartbeatStart = Date.now();
829
+ let heartbeatTimer;
830
+ let stopped = false;
831
+ const stopHeartbeat = () => {
832
+ stopped = true;
833
+ if (heartbeatTimer)
834
+ clearTimeout(heartbeatTimer);
835
+ };
836
+ const scheduleNextHeartbeat = () => {
837
+ if (stopped)
838
+ return;
839
+ if (Date.now() - heartbeatStart >= LOCK_MAX_HOLD_MS) {
840
+ stopped = true;
841
+ return;
842
+ }
843
+ heartbeatTimer = setTimeout(() => {
844
+ runHeartbeat();
845
+ }, LOCK_HEARTBEAT_MS);
846
+ heartbeatTimer.unref?.();
847
+ };
848
+ const runHeartbeat = async () => {
849
+ if (stopped)
850
+ return;
851
+ const current = await fs6.readFile(lockFile, "utf-8").catch(() => null);
852
+ if (stopped)
853
+ return;
854
+ if (current !== ownerId) {
855
+ stopped = true;
856
+ return;
857
+ }
858
+ const now = Date.now() / 1000;
859
+ await fs6.utimes(lockFile, now, now).catch(() => {});
860
+ scheduleNextHeartbeat();
861
+ };
862
+ scheduleNextHeartbeat();
863
+ let released = false;
864
+ return async () => {
865
+ if (released)
866
+ return;
867
+ released = true;
868
+ stopHeartbeat();
869
+ const current = await fs6.readFile(lockFile, "utf-8").catch(() => null);
870
+ if (current === ownerId) {
871
+ await fs6.rm(lockFile, { force: true });
872
+ }
873
+ };
874
+ }
790
875
  async rm(filePath) {
791
876
  await fs6.rm(filePath, { recursive: true, force: true });
792
877
  }
@@ -832,9 +917,13 @@ class NodeFileSystem {
832
917
  }
833
918
  }
834
919
  isEnoent(error) {
835
- return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
920
+ return this.hasErrnoCode(error, "ENOENT");
921
+ }
922
+ hasErrnoCode(error, code) {
923
+ return typeof error === "object" && error !== null && "code" in error && error.code === code;
836
924
  }
837
925
  }
926
+ var LOCK_HEARTBEAT_MS = 5000, LOCK_STALE_MS = 15000, LOCK_MAX_WAIT_MS = 20000, LOCK_MAX_HOLD_MS = 60000, LOCK_RETRY_MIN_MS = 100, LOCK_RETRY_JITTER_MS = 200;
838
927
  var init_node = __esm(() => {
839
928
  init_open();
840
929
  });
@@ -848,7 +937,7 @@ var init_src = __esm(() => {
848
937
 
849
938
  // ../../../node_modules/@uipath/coreipc/index.js
850
939
  var require_coreipc = __commonJS((exports, module) => {
851
- var __dirname = "/Users/alexandru.oltean/github/cli/node_modules/@uipath/coreipc";
940
+ var __dirname = "/home/runner/work/cli/cli/node_modules/@uipath/coreipc";
852
941
  /*! For license information please see index.js.LICENSE.txt */
853
942
  (function(e, t) {
854
943
  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();
@@ -19040,6 +19129,10 @@ var require_dist = __commonJS((exports) => {
19040
19129
  exports.RobotProxyConstructor = RobotProxyConstructor;
19041
19130
  __exportStar(require_agent(), exports);
19042
19131
  });
19132
+ // ../../auth/src/server.ts
19133
+ var init_server = __esm(() => {
19134
+ init_constants();
19135
+ });
19043
19136
 
19044
19137
  // ../../../node_modules/adm-zip/util/constants.js
19045
19138
  var require_constants = __commonJS((exports, module) => {
@@ -21341,7 +21434,8 @@ var require_adm_zip = __commonJS((exports, module) => {
21341
21434
  // package.json
21342
21435
  var package_default = {
21343
21436
  name: "@uipath/audit-tool",
21344
- version: "1.1.0",
21437
+ license: "MIT",
21438
+ version: "1.2.0",
21345
21439
  description: "CLI plugin for the UiPath Audit Service — query event sources, paginate events, and export ZIPs from the long-term store.",
21346
21440
  private: false,
21347
21441
  repository: {
@@ -21382,6 +21476,136 @@ var package_default = {
21382
21476
  }
21383
21477
  };
21384
21478
 
21479
+ // ../../common/src/singleton.ts
21480
+ var PREFIX = "@uipath/common/";
21481
+ var _g = globalThis;
21482
+ function singleton(ctorOrName) {
21483
+ const name = typeof ctorOrName === "string" ? ctorOrName : ctorOrName.name;
21484
+ const key = Symbol.for(PREFIX + name);
21485
+ return {
21486
+ get(fallback) {
21487
+ return _g[key] ?? fallback;
21488
+ },
21489
+ set(value) {
21490
+ _g[key] = value;
21491
+ },
21492
+ clear() {
21493
+ delete _g[key];
21494
+ },
21495
+ getOrInit(factory, guard) {
21496
+ const existing = _g[key];
21497
+ if (existing != null && typeof existing === "object") {
21498
+ if (!guard || guard(existing)) {
21499
+ return existing;
21500
+ }
21501
+ }
21502
+ const instance = factory();
21503
+ _g[key] = instance;
21504
+ return instance;
21505
+ }
21506
+ };
21507
+ }
21508
+
21509
+ // ../../common/src/sdk-user-agent.ts
21510
+ var USER_AGENT_HEADER = "User-Agent";
21511
+ var sdkUserAgentHostToken = singleton("SdkUserAgentHostToken");
21512
+ function userAgentPatchKey(userAgent) {
21513
+ return Symbol.for(`@uipath/common/sdk-user-agent/${userAgent}`);
21514
+ }
21515
+ function splitUserAgentTokens(value) {
21516
+ return value?.trim().split(/\s+/).filter(Boolean) ?? [];
21517
+ }
21518
+ function appendUserAgentToken(value, userAgent) {
21519
+ const tokens = splitUserAgentTokens(value);
21520
+ const seen = new Set(tokens);
21521
+ for (const token of splitUserAgentTokens(userAgent)) {
21522
+ if (!seen.has(token)) {
21523
+ tokens.push(token);
21524
+ seen.add(token);
21525
+ }
21526
+ }
21527
+ return tokens.join(" ");
21528
+ }
21529
+ function getEffectiveUserAgent(userAgent) {
21530
+ return appendUserAgentToken(sdkUserAgentHostToken.get(), userAgent);
21531
+ }
21532
+ function isHeadersLike(headers) {
21533
+ return typeof headers === "object" && headers !== null && "get" in headers && typeof headers.get === "function" && "set" in headers && typeof headers.set === "function";
21534
+ }
21535
+ function getSdkUserAgentToken(pkg) {
21536
+ const packageName = pkg.name.replace(/^@uipath\//, "");
21537
+ return getEffectiveUserAgent(`${packageName}/${pkg.version}`);
21538
+ }
21539
+ function addSdkUserAgentHeader(headers, userAgent) {
21540
+ const result = { ...headers ?? {} };
21541
+ const effectiveUserAgent = getEffectiveUserAgent(userAgent);
21542
+ const headerName = Object.keys(result).find((key) => key.toLowerCase() === USER_AGENT_HEADER.toLowerCase());
21543
+ if (headerName) {
21544
+ result[headerName] = appendUserAgentToken(result[headerName], effectiveUserAgent);
21545
+ } else {
21546
+ result[USER_AGENT_HEADER] = effectiveUserAgent;
21547
+ }
21548
+ return result;
21549
+ }
21550
+ function withSdkUserAgentHeader(headers, userAgent) {
21551
+ const effectiveUserAgent = getEffectiveUserAgent(userAgent);
21552
+ if (isHeadersLike(headers)) {
21553
+ headers.set(USER_AGENT_HEADER, appendUserAgentToken(headers.get(USER_AGENT_HEADER), effectiveUserAgent));
21554
+ return headers;
21555
+ }
21556
+ if (Array.isArray(headers)) {
21557
+ const result = headers.map((entry) => {
21558
+ const [key, value] = entry;
21559
+ return [key, value];
21560
+ });
21561
+ const headerIndex = result.findIndex(([key]) => key.toLowerCase() === USER_AGENT_HEADER.toLowerCase());
21562
+ if (headerIndex >= 0) {
21563
+ const [key, value] = result[headerIndex];
21564
+ result[headerIndex] = [
21565
+ key,
21566
+ appendUserAgentToken(value, effectiveUserAgent)
21567
+ ];
21568
+ } else {
21569
+ result.push([USER_AGENT_HEADER, effectiveUserAgent]);
21570
+ }
21571
+ return result;
21572
+ }
21573
+ return addSdkUserAgentHeader(typeof headers === "object" && headers !== null ? { ...headers } : {}, effectiveUserAgent);
21574
+ }
21575
+ function withUserAgentInitOverride(initOverrides, userAgent) {
21576
+ return async (requestContext) => {
21577
+ const initWithUserAgent = {
21578
+ ...requestContext.init,
21579
+ headers: withSdkUserAgentHeader(requestContext.init.headers, userAgent)
21580
+ };
21581
+ const override = typeof initOverrides === "function" ? await initOverrides({
21582
+ ...requestContext,
21583
+ init: initWithUserAgent
21584
+ }) : initOverrides;
21585
+ return {
21586
+ ...override ?? {},
21587
+ headers: withSdkUserAgentHeader(override?.headers ?? initWithUserAgent.headers, userAgent)
21588
+ };
21589
+ };
21590
+ }
21591
+ function installSdkUserAgentHeader(BaseApiClass, userAgent) {
21592
+ const prototype = BaseApiClass.prototype;
21593
+ const patchKey = userAgentPatchKey(userAgent);
21594
+ if (prototype[patchKey]) {
21595
+ return;
21596
+ }
21597
+ if (typeof prototype.request !== "function") {
21598
+ throw new Error("Generated BaseAPI request function not found.");
21599
+ }
21600
+ const originalRequest = prototype.request;
21601
+ prototype.request = function requestWithUserAgent(context, initOverrides) {
21602
+ return originalRequest.call(this, context, withUserAgentInitOverride(initOverrides, userAgent));
21603
+ };
21604
+ Object.defineProperty(prototype, patchKey, {
21605
+ value: true
21606
+ });
21607
+ }
21608
+
21385
21609
  // ../audit-sdk/generated/src/runtime.ts
21386
21610
  var BASE_PATH = "https://localhost:7008".replace(/\/+$/, "");
21387
21611
 
@@ -21630,6 +21854,49 @@ class VoidApiResponse {
21630
21854
  return;
21631
21855
  }
21632
21856
  }
21857
+ // ../audit-sdk/package.json
21858
+ var package_default2 = {
21859
+ name: "@uipath/audit-sdk",
21860
+ license: "MIT",
21861
+ version: "1.2.0",
21862
+ description: "SDK for the UiPath Audit Service — query audit event sources, list events, and download long-term-store exports.",
21863
+ repository: {
21864
+ type: "git",
21865
+ url: "https://github.com/UiPath/cli.git",
21866
+ directory: "packages/admin/audit-sdk"
21867
+ },
21868
+ publishConfig: {
21869
+ registry: "https://npm.pkg.github.com/@uipath"
21870
+ },
21871
+ keywords: ["uipath", "audit", "sdk"],
21872
+ type: "module",
21873
+ main: "./dist/index.js",
21874
+ types: "./dist/src/index.d.ts",
21875
+ exports: {
21876
+ ".": {
21877
+ types: "./dist/src/index.d.ts",
21878
+ default: "./dist/index.js"
21879
+ }
21880
+ },
21881
+ files: ["dist"],
21882
+ private: true,
21883
+ scripts: {
21884
+ build: "bun build ./src/index.ts --outdir dist --format esm --target node && tsc -p tsconfig.build.json --noCheck",
21885
+ generate: "bun run src/scripts/generate-sdk.ts",
21886
+ lint: "biome check ."
21887
+ },
21888
+ devDependencies: {
21889
+ "@openapitools/openapi-generator-cli": "^2.31.1",
21890
+ "@types/node": "^25.5.0",
21891
+ "@uipath/auth": "workspace:*",
21892
+ "@uipath/common": "workspace:*",
21893
+ typescript: "^6.0.2"
21894
+ }
21895
+ };
21896
+
21897
+ // ../audit-sdk/src/user-agent.ts
21898
+ var SDK_USER_AGENT = getSdkUserAgentToken(package_default2);
21899
+ installSdkUserAgentHeader(BaseAPI, SDK_USER_AGENT);
21633
21900
 
21634
21901
  // ../audit-sdk/generated/src/models/AuditEventStatus.ts
21635
21902
  var AuditEventStatus = {
@@ -21890,32 +22157,7 @@ class InvalidBaseUrlError extends Error {
21890
22157
  this.name = "InvalidBaseUrlError";
21891
22158
  }
21892
22159
  }
21893
- var DEFAULT_SCOPES = [
21894
- "offline_access",
21895
- "ProcessMining",
21896
- "OrchestratorApiUserAccess",
21897
- "StudioWebBackend",
21898
- "IdentityServerApi",
21899
- "ConnectionService",
21900
- "DataService",
21901
- "DataServiceApiUserAccess",
21902
- "DocumentUnderstanding",
21903
- "EnterpriseContextService",
21904
- "Directory",
21905
- "JamJamApi",
21906
- "LLMGateway",
21907
- "LLMOps",
21908
- "OMS",
21909
- "RCS.FolderAuthorization",
21910
- "RCS.TagsManagement",
21911
- "TestmanagerApiUserAccess",
21912
- "AutomationSolutions",
21913
- "StudioWebTypeCacheService",
21914
- "Docs.GPT.Search",
21915
- "Insights",
21916
- "ReferenceToken",
21917
- "Audit.Read"
21918
- ];
22160
+ var DEFAULT_SCOPES = ["openid", "profile", "offline_access"];
21919
22161
  var normalizeAndValidateBaseUrl = (rawUrl) => {
21920
22162
  let baseUrl = rawUrl;
21921
22163
  if (baseUrl.endsWith("/identity_/")) {
@@ -21965,7 +22207,8 @@ var resolveConfigAsync = async ({
21965
22207
  if (!clientSecret && fileAuth.clientSecret) {
21966
22208
  clientSecret = fileAuth.clientSecret;
21967
22209
  }
21968
- const scopes = customScopes && customScopes.length > 0 ? customScopes : fileAuth.scopes && fileAuth.scopes.length > 0 ? fileAuth.scopes : DEFAULT_SCOPES;
22210
+ const isExternalAppAuth = clientId !== DEFAULT_CLIENT_ID && Boolean(clientSecret);
22211
+ const scopes = customScopes && customScopes.length > 0 ? customScopes : fileAuth.scopes && fileAuth.scopes.length > 0 ? fileAuth.scopes : isExternalAppAuth ? [] : DEFAULT_SCOPES;
21969
22212
  return {
21970
22213
  clientId,
21971
22214
  clientSecret,
@@ -22465,6 +22708,129 @@ function normalizeTokenRefreshFailure() {
22465
22708
  function normalizeTokenRefreshUnavailableFailure() {
22466
22709
  return "token refresh failed before authentication completed";
22467
22710
  }
22711
+ function errorMessage(error) {
22712
+ return error instanceof Error ? error.message : String(error);
22713
+ }
22714
+ function computeExpirationThreshold(ensureTokenValidityMinutes) {
22715
+ return new Date(Date.now() + (ensureTokenValidityMinutes ?? 0) * 60 * 1000);
22716
+ }
22717
+ async function runRefreshLocked(inputs) {
22718
+ const {
22719
+ absolutePath,
22720
+ refreshToken: callerRefreshToken,
22721
+ customAuthority,
22722
+ ensureTokenValidityMinutes,
22723
+ loadEnvFile,
22724
+ saveEnvFile,
22725
+ refreshFn,
22726
+ resolveConfig
22727
+ } = inputs;
22728
+ const expirationThreshold = computeExpirationThreshold(ensureTokenValidityMinutes);
22729
+ let fresh;
22730
+ try {
22731
+ fresh = await loadEnvFile({ envPath: absolutePath });
22732
+ } catch (error) {
22733
+ return {
22734
+ kind: "fail",
22735
+ status: {
22736
+ loginStatus: "Refresh Failed",
22737
+ hint: "Could not read the auth file while refreshing. Retry, or run 'uip login' to re-authenticate.",
22738
+ tokenRefresh: {
22739
+ attempted: false,
22740
+ success: false,
22741
+ errorMessage: `auth file read failed: ${errorMessage(error)}`
22742
+ }
22743
+ }
22744
+ };
22745
+ }
22746
+ const freshAccess = fresh.UIPATH_ACCESS_TOKEN;
22747
+ const freshExp = freshAccess ? getTokenExpiration(freshAccess) : undefined;
22748
+ if (freshAccess && freshExp && freshExp > expirationThreshold) {
22749
+ return {
22750
+ kind: "ok",
22751
+ accessToken: freshAccess,
22752
+ refreshToken: fresh.UIPATH_REFRESH_TOKEN ?? callerRefreshToken,
22753
+ expiration: freshExp,
22754
+ tokenRefresh: { attempted: false, success: true }
22755
+ };
22756
+ }
22757
+ const tokenForIdP = fresh.UIPATH_REFRESH_TOKEN ?? callerRefreshToken;
22758
+ let refreshedAccess;
22759
+ let refreshedRefresh;
22760
+ try {
22761
+ const config = await resolveConfig({ customAuthority });
22762
+ const refreshed = await refreshFn({
22763
+ refreshToken: tokenForIdP,
22764
+ tokenEndpoint: config.tokenEndpoint,
22765
+ clientId: config.clientId,
22766
+ expectedAuthority: customAuthority
22767
+ });
22768
+ refreshedAccess = refreshed.accessToken;
22769
+ refreshedRefresh = refreshed.refreshToken;
22770
+ } catch (error) {
22771
+ const isOAuthFailure = isTokenRefreshOAuthFailure(error);
22772
+ 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.";
22773
+ const message = isOAuthFailure ? normalizeTokenRefreshFailure() : normalizeTokenRefreshUnavailableFailure();
22774
+ return {
22775
+ kind: "fail",
22776
+ status: {
22777
+ loginStatus: "Refresh Failed",
22778
+ hint,
22779
+ tokenRefresh: {
22780
+ attempted: true,
22781
+ success: false,
22782
+ errorMessage: message
22783
+ }
22784
+ }
22785
+ };
22786
+ }
22787
+ const refreshedExp = getTokenExpiration(refreshedAccess);
22788
+ if (!refreshedExp || refreshedExp <= new Date) {
22789
+ return {
22790
+ kind: "fail",
22791
+ status: {
22792
+ loginStatus: "Refresh Failed",
22793
+ hint: "The identity server returned an unusable token. Run 'uip login' to re-authenticate.",
22794
+ tokenRefresh: {
22795
+ attempted: true,
22796
+ success: false,
22797
+ errorMessage: "refreshed token has no valid expiration claim"
22798
+ }
22799
+ }
22800
+ };
22801
+ }
22802
+ try {
22803
+ await saveEnvFile({
22804
+ envPath: absolutePath,
22805
+ data: {
22806
+ UIPATH_ACCESS_TOKEN: refreshedAccess,
22807
+ UIPATH_REFRESH_TOKEN: refreshedRefresh
22808
+ },
22809
+ merge: true
22810
+ });
22811
+ return {
22812
+ kind: "ok",
22813
+ accessToken: refreshedAccess,
22814
+ refreshToken: refreshedRefresh,
22815
+ expiration: refreshedExp,
22816
+ tokenRefresh: { attempted: true, success: true }
22817
+ };
22818
+ } catch (error) {
22819
+ const msg = errorMessage(error);
22820
+ return {
22821
+ kind: "ok",
22822
+ accessToken: refreshedAccess,
22823
+ refreshToken: refreshedRefresh,
22824
+ expiration: refreshedExp,
22825
+ 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.`,
22826
+ tokenRefresh: {
22827
+ attempted: true,
22828
+ success: true,
22829
+ errorMessage: `persistence failed: ${msg}`
22830
+ }
22831
+ };
22832
+ }
22833
+ }
22468
22834
  var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
22469
22835
  const {
22470
22836
  resolveEnvFilePath = resolveEnvFilePathAsync,
@@ -22539,73 +22905,103 @@ var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
22539
22905
  let refreshToken = credentials.UIPATH_REFRESH_TOKEN;
22540
22906
  let expiration = getTokenExpiration(accessToken);
22541
22907
  let persistenceWarning;
22908
+ let lockReleaseFailed = false;
22542
22909
  let tokenRefresh;
22543
- const expirationThreshold = new Date(Date.now() + (ensureTokenValidityMinutes ?? 0) * 60 * 1000);
22544
- if (expiration && expiration <= expirationThreshold && refreshToken) {
22545
- let refreshedAccess;
22546
- let refreshedRefresh;
22910
+ const outerThreshold = computeExpirationThreshold(ensureTokenValidityMinutes);
22911
+ const tryGlobalCredsHint = async () => {
22912
+ const fs7 = getFs();
22913
+ const globalPath = fs7.path.join(fs7.env.homedir(), envFilePath);
22914
+ if (absolutePath === globalPath)
22915
+ return;
22916
+ if (!await fs7.exists(globalPath))
22917
+ return;
22547
22918
  try {
22548
- const config = await resolveConfig({
22549
- customAuthority: credentials.UIPATH_URL
22550
- });
22551
- const refreshed = await refreshTokenFn({
22552
- refreshToken,
22553
- tokenEndpoint: config.tokenEndpoint,
22554
- clientId: config.clientId,
22555
- expectedAuthority: credentials.UIPATH_URL
22556
- });
22557
- refreshedAccess = refreshed.accessToken;
22558
- refreshedRefresh = refreshed.refreshToken;
22559
- } catch (error) {
22560
- const isOAuthFailure = isTokenRefreshOAuthFailure(error);
22561
- 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.";
22562
- const errorMessage = isOAuthFailure ? normalizeTokenRefreshFailure() : normalizeTokenRefreshUnavailableFailure();
22563
- return {
22564
- loginStatus: "Refresh Failed",
22565
- hint,
22566
- tokenRefresh: {
22567
- attempted: true,
22568
- success: false,
22569
- errorMessage
22570
- }
22571
- };
22919
+ const globalCreds = await loadEnvFile({ envPath: globalPath });
22920
+ if (!globalCreds.UIPATH_ACCESS_TOKEN)
22921
+ return;
22922
+ const globalExp = getTokenExpiration(globalCreds.UIPATH_ACCESS_TOKEN);
22923
+ if (globalExp && globalExp <= new Date)
22924
+ return;
22925
+ 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.`;
22926
+ } catch {
22927
+ return;
22572
22928
  }
22573
- const refreshedExp = getTokenExpiration(refreshedAccess);
22574
- if (!refreshedExp || refreshedExp <= new Date) {
22929
+ };
22930
+ if (expiration && expiration <= outerThreshold && refreshToken) {
22931
+ let release;
22932
+ try {
22933
+ release = await getFs().acquireLock(absolutePath);
22934
+ } catch (error) {
22935
+ const msg = errorMessage(error);
22936
+ const globalHint = await tryGlobalCredsHint();
22937
+ if (globalHint) {
22938
+ return {
22939
+ loginStatus: "Expired",
22940
+ accessToken,
22941
+ refreshToken,
22942
+ baseUrl: credentials.UIPATH_URL,
22943
+ organizationName: credentials.UIPATH_ORGANIZATION_NAME,
22944
+ organizationId: credentials.UIPATH_ORGANIZATION_ID,
22945
+ tenantName: credentials.UIPATH_TENANT_NAME,
22946
+ tenantId: credentials.UIPATH_TENANT_ID,
22947
+ expiration,
22948
+ source: "file" /* File */,
22949
+ hint: globalHint,
22950
+ tokenRefresh: {
22951
+ attempted: false,
22952
+ success: false,
22953
+ errorMessage: `lock acquisition failed: ${msg}`
22954
+ }
22955
+ };
22956
+ }
22575
22957
  return {
22576
22958
  loginStatus: "Refresh Failed",
22577
- hint: "The identity server returned an unusable token. Run 'uip login' to re-authenticate.",
22959
+ 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.",
22578
22960
  tokenRefresh: {
22579
- attempted: true,
22961
+ attempted: false,
22580
22962
  success: false,
22581
- errorMessage: "refreshed token has no valid expiration claim"
22963
+ errorMessage: `lock acquisition failed: ${msg}`
22582
22964
  }
22583
22965
  };
22584
22966
  }
22585
- accessToken = refreshedAccess;
22586
- refreshToken = refreshedRefresh;
22587
- expiration = refreshedExp;
22967
+ let lockedFailure;
22588
22968
  try {
22589
- await saveEnvFile({
22590
- envPath: absolutePath,
22591
- data: {
22592
- UIPATH_ACCESS_TOKEN: accessToken,
22593
- UIPATH_REFRESH_TOKEN: refreshToken
22594
- },
22595
- merge: true
22969
+ const outcome = await runRefreshLocked({
22970
+ absolutePath,
22971
+ refreshToken,
22972
+ customAuthority: credentials.UIPATH_URL,
22973
+ ensureTokenValidityMinutes,
22974
+ loadEnvFile,
22975
+ saveEnvFile,
22976
+ refreshFn: refreshTokenFn,
22977
+ resolveConfig
22596
22978
  });
22597
- tokenRefresh = {
22598
- attempted: true,
22599
- success: true
22600
- };
22601
- } catch (error) {
22602
- const msg = error instanceof Error ? error.message : String(error);
22603
- 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.`;
22604
- tokenRefresh = {
22605
- attempted: true,
22606
- success: true,
22607
- errorMessage: `persistence failed: ${msg}`
22608
- };
22979
+ if (outcome.kind === "fail") {
22980
+ lockedFailure = outcome.status;
22981
+ } else {
22982
+ accessToken = outcome.accessToken;
22983
+ refreshToken = outcome.refreshToken;
22984
+ expiration = outcome.expiration;
22985
+ tokenRefresh = outcome.tokenRefresh;
22986
+ if (outcome.persistenceWarning) {
22987
+ persistenceWarning = outcome.persistenceWarning;
22988
+ }
22989
+ }
22990
+ } finally {
22991
+ try {
22992
+ await release();
22993
+ } catch {
22994
+ lockReleaseFailed = true;
22995
+ }
22996
+ }
22997
+ if (lockedFailure) {
22998
+ const globalHint = await tryGlobalCredsHint();
22999
+ const base = globalHint ? {
23000
+ ...lockedFailure,
23001
+ loginStatus: "Expired",
23002
+ hint: globalHint
23003
+ } : lockedFailure;
23004
+ return lockReleaseFailed ? { ...base, lockReleaseFailed: true } : base;
22609
23005
  }
22610
23006
  }
22611
23007
  const result = {
@@ -22620,23 +23016,13 @@ var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
22620
23016
  expiration,
22621
23017
  source: "file" /* File */,
22622
23018
  ...persistenceWarning ? { hint: persistenceWarning, persistenceFailed: true } : {},
23019
+ ...lockReleaseFailed ? { lockReleaseFailed: true } : {},
22623
23020
  ...tokenRefresh ? { tokenRefresh } : {}
22624
23021
  };
22625
23022
  if (result.loginStatus === "Expired") {
22626
- const fs7 = getFs();
22627
- const globalPath = fs7.path.join(fs7.env.homedir(), envFilePath);
22628
- if (absolutePath !== globalPath && await fs7.exists(globalPath)) {
22629
- try {
22630
- const globalCreds = await loadEnvFile({
22631
- envPath: globalPath
22632
- });
22633
- if (globalCreds.UIPATH_ACCESS_TOKEN) {
22634
- const globalExp = getTokenExpiration(globalCreds.UIPATH_ACCESS_TOKEN);
22635
- if (!globalExp || globalExp > new Date) {
22636
- 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.`;
22637
- }
22638
- }
22639
- } catch {}
23023
+ const globalHint = await tryGlobalCredsHint();
23024
+ if (globalHint) {
23025
+ result.hint = globalHint;
22640
23026
  }
22641
23027
  }
22642
23028
  return result;
@@ -22653,6 +23039,10 @@ var getLoginStatusAsync = async (options = {}) => {
22653
23039
  init_src();
22654
23040
  // ../../auth/src/logout.ts
22655
23041
  init_src();
23042
+
23043
+ // ../../auth/src/index.ts
23044
+ init_server();
23045
+
22656
23046
  // ../audit-sdk/src/client-factory.ts
22657
23047
  async function createAuditConfig(options) {
22658
23048
  const status = await getLoginStatusAsync({
@@ -22679,7 +23069,8 @@ async function createAuditConfig(options) {
22679
23069
  const bearerToken = options.s2sToken ?? status.accessToken;
22680
23070
  return new Configuration({
22681
23071
  basePath,
22682
- apiKey: () => `Bearer ${bearerToken}`
23072
+ apiKey: () => `Bearer ${bearerToken}`,
23073
+ headers: addSdkUserAgentHeader(undefined, SDK_USER_AGENT)
22683
23074
  });
22684
23075
  }
22685
23076
  async function createApiClient(ApiClass, options) {
@@ -22823,10 +23214,15 @@ async function extractErrorDetails(error, options) {
22823
23214
  }
22824
23215
  if (parsedBody?.errorCode && typeof parsedBody.errorCode === "string") {
22825
23216
  context.errorCode = parsedBody.errorCode;
23217
+ } else if (parsedBody?.code && typeof parsedBody.code === "string") {
23218
+ context.errorCode = parsedBody.code;
22826
23219
  }
22827
23220
  if (parsedBody?.requestId && typeof parsedBody.requestId === "string") {
22828
23221
  context.requestId = parsedBody.requestId;
22829
23222
  }
23223
+ if (parsedBody?.traceId && typeof parsedBody.traceId === "string") {
23224
+ context.traceId = parsedBody.traceId;
23225
+ }
22830
23226
  if (status === 429) {
22831
23227
  const resp = response;
22832
23228
  const headersObj = resp?.headers;
@@ -22846,7 +23242,35 @@ async function extractErrorDetails(error, options) {
22846
23242
  }
22847
23243
  }
22848
23244
  const hasContext = Object.keys(context).length > 0;
22849
- return { result, message, details, ...hasContext ? { context } : {} };
23245
+ let parsedErrors;
23246
+ if (parsedBody?.errors && typeof parsedBody.errors === "object") {
23247
+ const errors = {};
23248
+ for (const [field, raw] of Object.entries(parsedBody.errors)) {
23249
+ if (Array.isArray(raw)) {
23250
+ const messages = raw.map((entry) => {
23251
+ if (typeof entry === "string")
23252
+ return entry;
23253
+ if (entry && typeof entry === "object" && typeof entry.message === "string") {
23254
+ return entry.message;
23255
+ }
23256
+ return String(entry);
23257
+ }).filter(Boolean);
23258
+ if (messages.length > 0)
23259
+ errors[field] = messages;
23260
+ } else if (typeof raw === "string") {
23261
+ errors[field] = [raw];
23262
+ }
23263
+ }
23264
+ if (Object.keys(errors).length > 0)
23265
+ parsedErrors = errors;
23266
+ }
23267
+ return {
23268
+ result,
23269
+ message,
23270
+ details,
23271
+ ...hasContext ? { context } : {},
23272
+ ...parsedErrors ? { parsedErrors } : {}
23273
+ };
22850
23274
  }
22851
23275
  async function extractErrorMessage(error, options) {
22852
23276
  const { message } = await extractErrorDetails(error, options);
@@ -22859,36 +23283,6 @@ Command.prototype.examples = function(examples) {
22859
23283
  examplesByCommand.set(this, examples);
22860
23284
  return this;
22861
23285
  };
22862
- // ../../common/src/singleton.ts
22863
- var PREFIX = "@uipath/common/";
22864
- var _g = globalThis;
22865
- function singleton(ctorOrName) {
22866
- const name = typeof ctorOrName === "string" ? ctorOrName : ctorOrName.name;
22867
- const key = Symbol.for(PREFIX + name);
22868
- return {
22869
- get(fallback) {
22870
- return _g[key] ?? fallback;
22871
- },
22872
- set(value) {
22873
- _g[key] = value;
22874
- },
22875
- clear() {
22876
- delete _g[key];
22877
- },
22878
- getOrInit(factory, guard) {
22879
- const existing = _g[key];
22880
- if (existing != null && typeof existing === "object") {
22881
- if (!guard || guard(existing)) {
22882
- return existing;
22883
- }
22884
- }
22885
- const instance = factory();
22886
- _g[key] = instance;
22887
- return instance;
22888
- }
22889
- };
22890
- }
22891
-
22892
23286
  // ../../common/src/output-context.ts
22893
23287
  function createStorage() {
22894
23288
  const [error, mod] = catchError2(() => __require("node:async_hooks"));
@@ -27978,6 +28372,60 @@ function escapeNonAscii(jsonText) {
27978
28372
  function needsAsciiSafeJson(sink) {
27979
28373
  return process.platform === "win32" && !sink.capabilities.isInteractive;
27980
28374
  }
28375
+ function isPlainRecord(value) {
28376
+ if (value === null || typeof value !== "object")
28377
+ return false;
28378
+ const prototype = Object.getPrototypeOf(value);
28379
+ return prototype === Object.prototype || prototype === null;
28380
+ }
28381
+ function toLowerCamelCaseKey(key) {
28382
+ if (!key)
28383
+ return key;
28384
+ if (/[_\-\s]/.test(key)) {
28385
+ const [firstPart, ...restParts] = key.split(/[_\-\s]+/).filter(Boolean);
28386
+ if (!firstPart)
28387
+ return key;
28388
+ return [
28389
+ toLowerCamelCaseSimpleKey(firstPart),
28390
+ ...restParts.map((part) => {
28391
+ const normalized = toLowerCamelCaseSimpleKey(part);
28392
+ return normalized.charAt(0).toUpperCase() + normalized.slice(1);
28393
+ })
28394
+ ].join("");
28395
+ }
28396
+ return toLowerCamelCaseSimpleKey(key);
28397
+ }
28398
+ function toLowerCamelCaseSimpleKey(key) {
28399
+ if (/^[A-Z0-9]+$/.test(key))
28400
+ return key.toLowerCase();
28401
+ return key.replace(/^[A-Z]+(?=[A-Z][a-z]|\d|$)|^[A-Z]/, (match) => match.toLowerCase());
28402
+ }
28403
+ function toPascalCaseKey(key) {
28404
+ const lowerCamelKey = toLowerCamelCaseKey(key);
28405
+ return lowerCamelKey ? lowerCamelKey.charAt(0).toUpperCase() + lowerCamelKey.slice(1) : lowerCamelKey;
28406
+ }
28407
+ function toPascalCaseData(value) {
28408
+ if (Array.isArray(value))
28409
+ return value.map(toPascalCaseData);
28410
+ if (!isPlainRecord(value))
28411
+ return value;
28412
+ const result = {};
28413
+ for (const [key, nestedValue] of Object.entries(value)) {
28414
+ result[toPascalCaseKey(key)] = toPascalCaseData(nestedValue);
28415
+ }
28416
+ return result;
28417
+ }
28418
+ function normalizeDataKeys(data) {
28419
+ return toPascalCaseData(data);
28420
+ }
28421
+ function normalizeOutputKeys(data) {
28422
+ const result = {};
28423
+ for (const [key, value] of Object.entries(data)) {
28424
+ const pascalKey = toPascalCaseKey(key);
28425
+ result[pascalKey] = pascalKey === "Data" ? value : toPascalCaseData(value);
28426
+ }
28427
+ return result;
28428
+ }
27981
28429
  function printOutput(data, format = "json", logFn, asciiSafe = false) {
27982
28430
  if (!data) {
27983
28431
  logFn("Empty response object. No data to display.");
@@ -28040,7 +28488,7 @@ function wrapText(text, width) {
28040
28488
  function printTable(data, logFn, externalLogValue) {
28041
28489
  if (data.length === 0)
28042
28490
  return;
28043
- const keys = Object.keys(data[0]).filter((key) => key !== "Code" && key !== "Log");
28491
+ const keys = Object.keys(data[0]).filter((key) => !["code", "log"].includes(key.toLowerCase()));
28044
28492
  const maxWidths = keys.map((key) => Math.max(key.length, ...data.map((item) => cellToString(item[key]).length)));
28045
28493
  const header = keys.map((key, i2) => key.padEnd(maxWidths[i2])).join(" | ");
28046
28494
  logFn(header);
@@ -28055,7 +28503,7 @@ function printTable(data, logFn, externalLogValue) {
28055
28503
  }
28056
28504
  }
28057
28505
  function printVerticalTable(data, logFn = console.log, externalLogValue) {
28058
- const keys = Object.keys(data).filter((key) => key !== "Code" && key !== "Log");
28506
+ const keys = Object.keys(data).filter((key) => !["code", "log"].includes(key.toLowerCase()));
28059
28507
  if (keys.length === 0)
28060
28508
  return;
28061
28509
  const maxKeyWidth = Math.max(...keys.map((key) => key.length));
@@ -28071,7 +28519,7 @@ function printVerticalTable(data, logFn = console.log, externalLogValue) {
28071
28519
  function printResizableTable(data, logFn = console.log, externalLogValue) {
28072
28520
  if (data.length === 0)
28073
28521
  return;
28074
- const keys = Object.keys(data[0]).filter((key) => key !== "Code" && key !== "Log");
28522
+ const keys = Object.keys(data[0]).filter((key) => !["code", "log"].includes(key.toLowerCase()));
28075
28523
  if (keys.length === 0)
28076
28524
  return;
28077
28525
  if (!process.stdout.isTTY) {
@@ -28147,8 +28595,26 @@ function printResizableTable(data, logFn = console.log, externalLogValue) {
28147
28595
  function toYaml(data) {
28148
28596
  return dump(data);
28149
28597
  }
28598
+ class FilterEvaluationError extends Error {
28599
+ __brand = "FilterEvaluationError";
28600
+ filter;
28601
+ instructions;
28602
+ result = RESULTS.ValidationError;
28603
+ constructor(filter, cause) {
28604
+ const underlying = cause instanceof Error ? cause.message : String(cause);
28605
+ super(`Filter '${filter}' failed to evaluate: ${underlying}`);
28606
+ this.name = "FilterEvaluationError";
28607
+ this.filter = filter;
28608
+ 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(@)'.";
28609
+ }
28610
+ }
28150
28611
  function applyFilter(data, filter) {
28151
- const result = search(data, filter);
28612
+ let result;
28613
+ try {
28614
+ result = search(data, filter);
28615
+ } catch (err) {
28616
+ throw new FilterEvaluationError(filter, err);
28617
+ }
28152
28618
  if (result == null)
28153
28619
  return [];
28154
28620
  if (Array.isArray(result)) {
@@ -28165,13 +28631,18 @@ function applyFilter(data, filter) {
28165
28631
  }
28166
28632
  var OutputFormatter;
28167
28633
  ((OutputFormatter) => {
28168
- function success(data) {
28634
+ function success(data, options) {
28169
28635
  data.Log ??= getLogFilePath() || undefined;
28636
+ const normalize = !options?.preserveDataKeys;
28637
+ if (normalize) {
28638
+ data.Data = normalizeDataKeys(data.Data);
28639
+ }
28170
28640
  const filter = getOutputFilter();
28171
28641
  if (filter) {
28172
- data.Data = applyFilter(data.Data, filter);
28642
+ const filtered = applyFilter(data.Data, filter);
28643
+ data.Data = normalize ? normalizeDataKeys(filtered) : filtered;
28173
28644
  }
28174
- logOutput(data, getOutputFormat());
28645
+ logOutput(normalizeOutputKeys(data), getOutputFormat());
28175
28646
  }
28176
28647
  OutputFormatter.success = success;
28177
28648
  function error(data) {
@@ -28181,7 +28652,7 @@ var OutputFormatter;
28181
28652
  result: data.Result,
28182
28653
  message: data.Message
28183
28654
  });
28184
- logOutput(data, getOutputFormat());
28655
+ logOutput(normalizeOutputKeys(data), getOutputFormat());
28185
28656
  }
28186
28657
  OutputFormatter.error = error;
28187
28658
  function emitList(code, items, opts) {
@@ -28202,13 +28673,14 @@ var OutputFormatter;
28202
28673
  function log(data) {
28203
28674
  const format = getOutputFormat();
28204
28675
  const sink = getOutputSink();
28676
+ const normalized = toPascalCaseData(data);
28205
28677
  if (format === "json") {
28206
- const json2 = JSON.stringify(data);
28678
+ const json2 = JSON.stringify(normalized);
28207
28679
  const safe = needsAsciiSafeJson(sink) ? escapeNonAscii(json2) : json2;
28208
28680
  sink.writeErr(`${safe}
28209
28681
  `);
28210
28682
  } else {
28211
- for (const [key, value] of Object.entries(data)) {
28683
+ for (const [key, value] of Object.entries(normalized)) {
28212
28684
  sink.writeErr(`${key}: ${value}
28213
28685
  `);
28214
28686
  }
@@ -28217,12 +28689,16 @@ var OutputFormatter;
28217
28689
  OutputFormatter.log = log;
28218
28690
  function formatToString(data) {
28219
28691
  const filter = getOutputFilter();
28220
- if (filter && "Data" in data && data.Data != null) {
28221
- data.Data = applyFilter(data.Data, filter);
28692
+ if ("Data" in data && data.Data != null) {
28693
+ data.Data = normalizeDataKeys(data.Data);
28694
+ if (filter) {
28695
+ data.Data = normalizeDataKeys(applyFilter(data.Data, filter));
28696
+ }
28222
28697
  }
28698
+ const output = normalizeOutputKeys(data);
28223
28699
  const lines = [];
28224
28700
  const sink = getOutputSink();
28225
- printOutput(data, getOutputFormat(), (msg) => {
28701
+ printOutput(output, getOutputFormat(), (msg) => {
28226
28702
  lines.push(msg);
28227
28703
  }, needsAsciiSafeJson(sink));
28228
28704
  return lines.join(`
@@ -29654,6 +30130,22 @@ function parseBoundedInt(raw, optionName, bounds) {
29654
30130
  }
29655
30131
  return n;
29656
30132
  }
30133
+ // ../../common/src/polling/types.ts
30134
+ var PollOutcome = {
30135
+ Completed: "completed",
30136
+ Timeout: "timeout",
30137
+ Interrupted: "interrupted",
30138
+ Aborted: "aborted",
30139
+ Failed: "failed"
30140
+ };
30141
+
30142
+ // ../../common/src/polling/poll-failure-mapping.ts
30143
+ var REASON_BY_OUTCOME = {
30144
+ [PollOutcome.Timeout]: "poll_timeout",
30145
+ [PollOutcome.Failed]: "poll_failed",
30146
+ [PollOutcome.Interrupted]: "poll_failed",
30147
+ [PollOutcome.Aborted]: "poll_aborted"
30148
+ };
29657
30149
  // ../../common/src/polling/terminal-statuses.ts
29658
30150
  var TERMINAL_STATUSES = new Set([
29659
30151
  "completed",
@@ -29862,17 +30354,21 @@ Command2.prototype.trackedAction = function(context, fn, properties) {
29862
30354
  const telemetryName = deriveCommandPath(command);
29863
30355
  const props = typeof properties === "function" ? properties(...args) : properties;
29864
30356
  const startTime = performance.now();
29865
- let errorMessage;
30357
+ let errorMessage2;
29866
30358
  const [error] = await catchError2(fn(...args));
29867
30359
  if (error) {
29868
- errorMessage = error instanceof Error ? error.message : String(error);
29869
- logger.error(`[trackedAction] ${telemetryName} failed: ${errorMessage}`);
30360
+ errorMessage2 = error instanceof Error ? error.message : String(error);
30361
+ logger.debug(`[trackedAction] ${telemetryName} failed: ${errorMessage2}`);
30362
+ const typed = error;
30363
+ const customInstructions = typeof typed.instructions === "string" ? typed.instructions : undefined;
30364
+ const customResult = typeof typed.result === "string" && typed.result !== RESULTS.Success && Object.values(RESULTS).includes(typed.result) ? typed.result : undefined;
30365
+ const finalResult = customResult ?? RESULTS.Failure;
29870
30366
  OutputFormatter.error({
29871
- Result: RESULTS.Failure,
29872
- Message: errorMessage,
29873
- Instructions: "An unexpected error occurred. Run with --log-level debug for details."
30367
+ Result: finalResult,
30368
+ Message: errorMessage2,
30369
+ Instructions: customInstructions ?? "An unexpected error occurred. Run with --log-level debug for details."
29874
30370
  });
29875
- context.exit(1);
30371
+ context.exit(EXIT_CODES[finalResult]);
29876
30372
  }
29877
30373
  const durationMs = performance.now() - startTime;
29878
30374
  const success = !error && (process.exitCode === undefined || process.exitCode === 0);
@@ -29881,7 +30377,7 @@ Command2.prototype.trackedAction = function(context, fn, properties) {
29881
30377
  ...props,
29882
30378
  duration: String(durationMs),
29883
30379
  success: String(success),
29884
- ...errorMessage ? { errorMessage } : {}
30380
+ ...errorMessage2 ? { errorMessage: errorMessage2 } : {}
29885
30381
  }));
29886
30382
  });
29887
30383
  };