@uipath/auth 1.0.4 → 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.
@@ -7,7 +7,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
7
7
  });
8
8
 
9
9
  // src/loginStatus.ts
10
- import { getFileSystem as getFileSystem2 } from "@uipath/filesystem";
10
+ import { getFileSystem as getFileSystem3 } from "@uipath/filesystem";
11
11
 
12
12
  // src/catch-error.ts
13
13
  function isPromiseLike(value) {
@@ -69,30 +69,7 @@ class InvalidBaseUrlError extends Error {
69
69
  this.name = "InvalidBaseUrlError";
70
70
  }
71
71
  }
72
- var DEFAULT_SCOPES = [
73
- "offline_access",
74
- "ProcessMining",
75
- "OrchestratorApiUserAccess",
76
- "StudioWebBackend",
77
- "IdentityServerApi",
78
- "ConnectionService",
79
- "DataService",
80
- "DataServiceApiUserAccess",
81
- "DocumentUnderstanding",
82
- "EnterpriseContextService",
83
- "Directory",
84
- "JamJamApi",
85
- "LLMGateway",
86
- "LLMOps",
87
- "OMS",
88
- "RCS.FolderAuthorization",
89
- "RCS.TagsManagement",
90
- "TestmanagerApiUserAccess",
91
- "AutomationSolutions",
92
- "StudioWebTypeCacheService",
93
- "Docs.GPT.Search",
94
- "Insights"
95
- ];
72
+ var DEFAULT_SCOPES = ["openid", "profile", "offline_access"];
96
73
  var normalizeAndValidateBaseUrl = (rawUrl) => {
97
74
  let baseUrl = rawUrl;
98
75
  if (baseUrl.endsWith("/identity_/")) {
@@ -142,7 +119,8 @@ var resolveConfigAsync = async ({
142
119
  if (!clientSecret && fileAuth.clientSecret) {
143
120
  clientSecret = fileAuth.clientSecret;
144
121
  }
145
- const scopes = customScopes && customScopes.length > 0 ? customScopes : fileAuth.scopes && fileAuth.scopes.length > 0 ? fileAuth.scopes : DEFAULT_SCOPES;
122
+ const isExternalAppAuth = clientId !== DEFAULT_CLIENT_ID && Boolean(clientSecret);
123
+ const scopes = customScopes && customScopes.length > 0 ? customScopes : fileAuth.scopes && fileAuth.scopes.length > 0 ? fileAuth.scopes : isExternalAppAuth ? [] : DEFAULT_SCOPES;
146
124
  return {
147
125
  clientId,
148
126
  clientSecret,
@@ -234,6 +212,7 @@ var getTokenExpiration = (accessToken) => {
234
212
 
235
213
  // src/envAuth.ts
236
214
  var ENV_AUTH_ENABLE_VAR = "UIPATH_CLI_ENABLE_ENV_AUTH";
215
+ var ENFORCE_ROBOT_AUTH_VAR = "UIPATH_CLI_ENFORCE_ROBOT_AUTH";
237
216
  var ENV_AUTH_VARS = {
238
217
  token: "UIPATH_CLI_AUTH_TOKEN",
239
218
  organizationName: "UIPATH_CLI_ORGANIZATION_NAME",
@@ -249,6 +228,7 @@ class EnvAuthConfigError extends Error {
249
228
  }
250
229
  }
251
230
  var isEnvAuthEnabled = () => process.env[ENV_AUTH_ENABLE_VAR] === "true";
231
+ var isRobotAuthEnforced = () => process.env[ENFORCE_ROBOT_AUTH_VAR] === "true";
252
232
  var requireEnv = (name) => {
253
233
  const value = process.env[name];
254
234
  if (!value) {
@@ -292,6 +272,7 @@ var readAuthFromEnv = () => {
292
272
  };
293
273
 
294
274
  // src/robotClientFallback.ts
275
+ import { getFileSystem } from "@uipath/filesystem";
295
276
  var DEFAULT_TIMEOUT_MS = 1000;
296
277
  var CLOSE_TIMEOUT_MS = 500;
297
278
  var NOTICE_SENTINEL = Symbol.for("@uipath/auth/robotFallbackNoticePrinted");
@@ -303,6 +284,35 @@ var printNoticeOnce = () => {
303
284
  catchError(() => process.stderr.write(`Using UiPath Robot credentials. Run 'uip login' for a dedicated session.
304
285
  `));
305
286
  };
287
+ var ROBOT_USER_SERVICES_PIPE = "UiPathUserServices";
288
+ var ROBOT_USER_SERVICES_ALTERNATE_PIPE = `${ROBOT_USER_SERVICES_PIPE}Alternate`;
289
+ var PIPE_NAME_MAX_LENGTH = 103;
290
+ var getRobotIpcPipeNames = async () => {
291
+ const fs = getFileSystem();
292
+ const username = fs.env.getenv("USER") ?? fs.env.getenv("USERNAME");
293
+ if (!username) {
294
+ throw new Error("Unable to determine current username");
295
+ }
296
+ const tempPath = fs.env.getenv("TMPDIR") ?? "/tmp/";
297
+ return [ROBOT_USER_SERVICES_PIPE, ROBOT_USER_SERVICES_ALTERNATE_PIPE].map((baseName) => fs.path.join(tempPath, `${baseName}_${username}`).substring(0, PIPE_NAME_MAX_LENGTH));
298
+ };
299
+ var defaultIsRobotIpcAvailable = async () => {
300
+ if (process.platform === "win32") {
301
+ return true;
302
+ }
303
+ const [pipeNamesError, pipeNames] = await catchError(getRobotIpcPipeNames());
304
+ if (pipeNamesError || !pipeNames) {
305
+ return false;
306
+ }
307
+ const fs = getFileSystem();
308
+ for (const pipeName of pipeNames) {
309
+ const [existsError, exists] = await catchError(fs.exists(pipeName));
310
+ if (!existsError && exists === true) {
311
+ return true;
312
+ }
313
+ }
314
+ return false;
315
+ };
306
316
  var withTimeout = (promise, timeoutMs) => new Promise((resolve, reject) => {
307
317
  const timer = setTimeout(() => reject(new Error(`Robot IPC call timed out after ${timeoutMs}ms`)), timeoutMs);
308
318
  promise.then((value) => {
@@ -334,14 +344,20 @@ var defaultLoadModule = async () => {
334
344
  var tryRobotClientFallback = async (options = {}) => {
335
345
  if (isBrowser())
336
346
  return;
337
- if (process.env.CI || process.env.GITHUB_ACTIONS) {
338
- return;
339
- }
340
- if (process.env.UIPATH_URL) {
341
- return;
347
+ if (!options.force) {
348
+ if (process.env.CI || process.env.GITHUB_ACTIONS) {
349
+ return;
350
+ }
351
+ if (process.env.UIPATH_URL) {
352
+ return;
353
+ }
342
354
  }
343
355
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
356
+ const isRobotIpcAvailable = options.isRobotIpcAvailable ?? defaultIsRobotIpcAvailable;
344
357
  const loadModule = options.loadModule ?? defaultLoadModule;
358
+ if (!await isRobotIpcAvailable()) {
359
+ return;
360
+ }
345
361
  const mod = await loadModule();
346
362
  if (!mod)
347
363
  return;
@@ -441,7 +457,7 @@ var refreshAccessToken = async ({
441
457
  };
442
458
 
443
459
  // src/utils/envFile.ts
444
- import { getFileSystem } from "@uipath/filesystem";
460
+ import { getFileSystem as getFileSystem2 } from "@uipath/filesystem";
445
461
  var DEFAULT_AUTH_FILENAME = AUTH_FILENAME;
446
462
  var DEFAULT_ENV_FILENAME = `${UIPATH_HOME_DIR}/${AUTH_FILENAME}`;
447
463
  var KNOWN_ERROR_CODES = new Set([
@@ -488,7 +504,7 @@ var probeAsync = async (fs, candidate) => {
488
504
  }
489
505
  };
490
506
  var resolveEnvFileLocationAsync = async (envFilePath = DEFAULT_ENV_FILENAME) => {
491
- const fs = getFileSystem();
507
+ const fs = getFileSystem2();
492
508
  if (fs.path.isAbsolute(envFilePath)) {
493
509
  const probe2 = await probeAsync(fs, envFilePath);
494
510
  return probe2.exists ? { exists: true, absolutePath: envFilePath, source: "absolute" } : {
@@ -539,7 +555,7 @@ var resolveEnvFilePathAsync = async (envFilePath = DEFAULT_ENV_FILENAME) => {
539
555
  };
540
556
  };
541
557
  var loadEnvFileAsync = async ({ envPath }) => {
542
- const fs = getFileSystem();
558
+ const fs = getFileSystem2();
543
559
  const absolutePath = fs.path.isAbsolute(envPath) ? envPath : fs.path.join(fs.env.cwd(), envPath);
544
560
  if (!await fs.exists(absolutePath)) {
545
561
  throw new Error(`Environment file not found: ${envPath}`);
@@ -573,7 +589,7 @@ var saveEnvFileAsync = async ({
573
589
  data,
574
590
  merge = true
575
591
  }) => {
576
- const fs = getFileSystem();
592
+ const fs = getFileSystem2();
577
593
  const absolutePath = fs.path.isAbsolute(envPath) ? envPath : fs.path.join(fs.env.homedir(), envPath);
578
594
  let existingData = {};
579
595
  if (merge && await fs.exists(absolutePath)) {
@@ -613,20 +629,167 @@ function normalizeTokenRefreshFailure() {
613
629
  function normalizeTokenRefreshUnavailableFailure() {
614
630
  return "token refresh failed before authentication completed";
615
631
  }
616
- var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
617
- if (isEnvAuthEnabled()) {
618
- return readAuthFromEnv();
632
+ function errorMessage(error) {
633
+ return error instanceof Error ? error.message : String(error);
634
+ }
635
+ function computeExpirationThreshold(ensureTokenValidityMinutes) {
636
+ return new Date(Date.now() + (ensureTokenValidityMinutes ?? 0) * 60 * 1000);
637
+ }
638
+ async function runRefreshLocked(inputs) {
639
+ const {
640
+ absolutePath,
641
+ refreshToken: callerRefreshToken,
642
+ customAuthority,
643
+ ensureTokenValidityMinutes,
644
+ loadEnvFile,
645
+ saveEnvFile,
646
+ refreshFn,
647
+ resolveConfig
648
+ } = inputs;
649
+ const expirationThreshold = computeExpirationThreshold(ensureTokenValidityMinutes);
650
+ let fresh;
651
+ try {
652
+ fresh = await loadEnvFile({ envPath: absolutePath });
653
+ } catch (error) {
654
+ return {
655
+ kind: "fail",
656
+ status: {
657
+ loginStatus: "Refresh Failed",
658
+ hint: "Could not read the auth file while refreshing. Retry, or run 'uip login' to re-authenticate.",
659
+ tokenRefresh: {
660
+ attempted: false,
661
+ success: false,
662
+ errorMessage: `auth file read failed: ${errorMessage(error)}`
663
+ }
664
+ }
665
+ };
619
666
  }
620
- const { envFilePath = DEFAULT_ENV_FILENAME, ensureTokenValidityMinutes } = options;
667
+ const freshAccess = fresh.UIPATH_ACCESS_TOKEN;
668
+ const freshExp = freshAccess ? getTokenExpiration(freshAccess) : undefined;
669
+ if (freshAccess && freshExp && freshExp > expirationThreshold) {
670
+ return {
671
+ kind: "ok",
672
+ accessToken: freshAccess,
673
+ refreshToken: fresh.UIPATH_REFRESH_TOKEN ?? callerRefreshToken,
674
+ expiration: freshExp,
675
+ tokenRefresh: { attempted: false, success: true }
676
+ };
677
+ }
678
+ const tokenForIdP = fresh.UIPATH_REFRESH_TOKEN ?? callerRefreshToken;
679
+ let refreshedAccess;
680
+ let refreshedRefresh;
681
+ try {
682
+ const config = await resolveConfig({ customAuthority });
683
+ const refreshed = await refreshFn({
684
+ refreshToken: tokenForIdP,
685
+ tokenEndpoint: config.tokenEndpoint,
686
+ clientId: config.clientId,
687
+ expectedAuthority: customAuthority
688
+ });
689
+ refreshedAccess = refreshed.accessToken;
690
+ refreshedRefresh = refreshed.refreshToken;
691
+ } catch (error) {
692
+ const isOAuthFailure = isTokenRefreshOAuthFailure(error);
693
+ 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.";
694
+ const message = isOAuthFailure ? normalizeTokenRefreshFailure() : normalizeTokenRefreshUnavailableFailure();
695
+ return {
696
+ kind: "fail",
697
+ status: {
698
+ loginStatus: "Refresh Failed",
699
+ hint,
700
+ tokenRefresh: {
701
+ attempted: true,
702
+ success: false,
703
+ errorMessage: message
704
+ }
705
+ }
706
+ };
707
+ }
708
+ const refreshedExp = getTokenExpiration(refreshedAccess);
709
+ if (!refreshedExp || refreshedExp <= new Date) {
710
+ return {
711
+ kind: "fail",
712
+ status: {
713
+ loginStatus: "Refresh Failed",
714
+ hint: "The identity server returned an unusable token. Run 'uip login' to re-authenticate.",
715
+ tokenRefresh: {
716
+ attempted: true,
717
+ success: false,
718
+ errorMessage: "refreshed token has no valid expiration claim"
719
+ }
720
+ }
721
+ };
722
+ }
723
+ try {
724
+ await saveEnvFile({
725
+ envPath: absolutePath,
726
+ data: {
727
+ UIPATH_ACCESS_TOKEN: refreshedAccess,
728
+ UIPATH_REFRESH_TOKEN: refreshedRefresh
729
+ },
730
+ merge: true
731
+ });
732
+ return {
733
+ kind: "ok",
734
+ accessToken: refreshedAccess,
735
+ refreshToken: refreshedRefresh,
736
+ expiration: refreshedExp,
737
+ tokenRefresh: { attempted: true, success: true }
738
+ };
739
+ } catch (error) {
740
+ const msg = errorMessage(error);
741
+ return {
742
+ kind: "ok",
743
+ accessToken: refreshedAccess,
744
+ refreshToken: refreshedRefresh,
745
+ expiration: refreshedExp,
746
+ 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.`,
747
+ tokenRefresh: {
748
+ attempted: true,
749
+ success: true,
750
+ errorMessage: `persistence failed: ${msg}`
751
+ }
752
+ };
753
+ }
754
+ }
755
+ var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
621
756
  const {
622
757
  resolveEnvFilePath = resolveEnvFilePathAsync,
623
758
  loadEnvFile = loadEnvFileAsync,
624
759
  saveEnvFile = saveEnvFileAsync,
625
- getFs = getFileSystem2,
760
+ getFs = getFileSystem3,
626
761
  refreshToken: refreshTokenFn = refreshAccessToken,
627
762
  resolveConfig = resolveConfigAsync,
628
763
  robotFallback = tryRobotClientFallback
629
764
  } = deps;
765
+ if (isRobotAuthEnforced()) {
766
+ if (isEnvAuthEnabled()) {
767
+ throw new EnvAuthConfigError(`${ENV_AUTH_ENABLE_VAR}=true and ${ENFORCE_ROBOT_AUTH_VAR}=true ` + `are mutually exclusive. Unset one of them and re-run.`);
768
+ }
769
+ const robotCreds = await robotFallback({ force: true });
770
+ if (!robotCreds) {
771
+ return {
772
+ loginStatus: "Not logged in",
773
+ hint: `${ENFORCE_ROBOT_AUTH_VAR}=true but the UiPath Robot ` + `session is unavailable. Start and sign in to the Assistant, ` + `or unset ${ENFORCE_ROBOT_AUTH_VAR} to fall back to file or ` + `env-var authentication.`
774
+ };
775
+ }
776
+ const expiration2 = getTokenExpiration(robotCreds.accessToken);
777
+ return {
778
+ loginStatus: "Logged in",
779
+ accessToken: robotCreds.accessToken,
780
+ baseUrl: robotCreds.baseUrl,
781
+ organizationName: robotCreds.organizationName,
782
+ organizationId: robotCreds.organizationId,
783
+ tenantName: robotCreds.tenantName,
784
+ tenantId: robotCreds.tenantId,
785
+ expiration: expiration2,
786
+ source: "robot" /* Robot */
787
+ };
788
+ }
789
+ if (isEnvAuthEnabled()) {
790
+ return readAuthFromEnv();
791
+ }
792
+ const { envFilePath = DEFAULT_ENV_FILENAME, ensureTokenValidityMinutes } = options;
630
793
  const { absolutePath } = await resolveEnvFilePath(envFilePath);
631
794
  if (absolutePath === undefined) {
632
795
  const robotCreds = await robotFallback();
@@ -663,73 +826,103 @@ var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
663
826
  let refreshToken = credentials.UIPATH_REFRESH_TOKEN;
664
827
  let expiration = getTokenExpiration(accessToken);
665
828
  let persistenceWarning;
829
+ let lockReleaseFailed = false;
666
830
  let tokenRefresh;
667
- const expirationThreshold = new Date(Date.now() + (ensureTokenValidityMinutes ?? 0) * 60 * 1000);
668
- if (expiration && expiration <= expirationThreshold && refreshToken) {
669
- let refreshedAccess;
670
- let refreshedRefresh;
831
+ const outerThreshold = computeExpirationThreshold(ensureTokenValidityMinutes);
832
+ const tryGlobalCredsHint = async () => {
833
+ const fs = getFs();
834
+ const globalPath = fs.path.join(fs.env.homedir(), envFilePath);
835
+ if (absolutePath === globalPath)
836
+ return;
837
+ if (!await fs.exists(globalPath))
838
+ return;
671
839
  try {
672
- const config = await resolveConfig({
673
- customAuthority: credentials.UIPATH_URL
674
- });
675
- const refreshed = await refreshTokenFn({
676
- refreshToken,
677
- tokenEndpoint: config.tokenEndpoint,
678
- clientId: config.clientId,
679
- expectedAuthority: credentials.UIPATH_URL
680
- });
681
- refreshedAccess = refreshed.accessToken;
682
- refreshedRefresh = refreshed.refreshToken;
683
- } catch (error) {
684
- const isOAuthFailure = isTokenRefreshOAuthFailure(error);
685
- 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.";
686
- const errorMessage = isOAuthFailure ? normalizeTokenRefreshFailure() : normalizeTokenRefreshUnavailableFailure();
687
- return {
688
- loginStatus: "Refresh Failed",
689
- hint,
690
- tokenRefresh: {
691
- attempted: true,
692
- success: false,
693
- errorMessage
694
- }
695
- };
840
+ const globalCreds = await loadEnvFile({ envPath: globalPath });
841
+ if (!globalCreds.UIPATH_ACCESS_TOKEN)
842
+ return;
843
+ const globalExp = getTokenExpiration(globalCreds.UIPATH_ACCESS_TOKEN);
844
+ if (globalExp && globalExp <= new Date)
845
+ return;
846
+ 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.`;
847
+ } catch {
848
+ return;
696
849
  }
697
- const refreshedExp = getTokenExpiration(refreshedAccess);
698
- if (!refreshedExp || refreshedExp <= new Date) {
850
+ };
851
+ if (expiration && expiration <= outerThreshold && refreshToken) {
852
+ let release;
853
+ try {
854
+ release = await getFs().acquireLock(absolutePath);
855
+ } catch (error) {
856
+ const msg = errorMessage(error);
857
+ const globalHint = await tryGlobalCredsHint();
858
+ if (globalHint) {
859
+ return {
860
+ loginStatus: "Expired",
861
+ accessToken,
862
+ refreshToken,
863
+ baseUrl: credentials.UIPATH_URL,
864
+ organizationName: credentials.UIPATH_ORGANIZATION_NAME,
865
+ organizationId: credentials.UIPATH_ORGANIZATION_ID,
866
+ tenantName: credentials.UIPATH_TENANT_NAME,
867
+ tenantId: credentials.UIPATH_TENANT_ID,
868
+ expiration,
869
+ source: "file" /* File */,
870
+ hint: globalHint,
871
+ tokenRefresh: {
872
+ attempted: false,
873
+ success: false,
874
+ errorMessage: `lock acquisition failed: ${msg}`
875
+ }
876
+ };
877
+ }
699
878
  return {
700
879
  loginStatus: "Refresh Failed",
701
- hint: "The identity server returned an unusable token. Run 'uip login' to re-authenticate.",
880
+ 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.",
702
881
  tokenRefresh: {
703
- attempted: true,
882
+ attempted: false,
704
883
  success: false,
705
- errorMessage: "refreshed token has no valid expiration claim"
884
+ errorMessage: `lock acquisition failed: ${msg}`
706
885
  }
707
886
  };
708
887
  }
709
- accessToken = refreshedAccess;
710
- refreshToken = refreshedRefresh;
711
- expiration = refreshedExp;
888
+ let lockedFailure;
712
889
  try {
713
- await saveEnvFile({
714
- envPath: absolutePath,
715
- data: {
716
- UIPATH_ACCESS_TOKEN: accessToken,
717
- UIPATH_REFRESH_TOKEN: refreshToken
718
- },
719
- merge: true
890
+ const outcome = await runRefreshLocked({
891
+ absolutePath,
892
+ refreshToken,
893
+ customAuthority: credentials.UIPATH_URL,
894
+ ensureTokenValidityMinutes,
895
+ loadEnvFile,
896
+ saveEnvFile,
897
+ refreshFn: refreshTokenFn,
898
+ resolveConfig
720
899
  });
721
- tokenRefresh = {
722
- attempted: true,
723
- success: true
724
- };
725
- } catch (error) {
726
- const msg = error instanceof Error ? error.message : String(error);
727
- 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.`;
728
- tokenRefresh = {
729
- attempted: true,
730
- success: true,
731
- errorMessage: `persistence failed: ${msg}`
732
- };
900
+ if (outcome.kind === "fail") {
901
+ lockedFailure = outcome.status;
902
+ } else {
903
+ accessToken = outcome.accessToken;
904
+ refreshToken = outcome.refreshToken;
905
+ expiration = outcome.expiration;
906
+ tokenRefresh = outcome.tokenRefresh;
907
+ if (outcome.persistenceWarning) {
908
+ persistenceWarning = outcome.persistenceWarning;
909
+ }
910
+ }
911
+ } finally {
912
+ try {
913
+ await release();
914
+ } catch {
915
+ lockReleaseFailed = true;
916
+ }
917
+ }
918
+ if (lockedFailure) {
919
+ const globalHint = await tryGlobalCredsHint();
920
+ const base = globalHint ? {
921
+ ...lockedFailure,
922
+ loginStatus: "Expired",
923
+ hint: globalHint
924
+ } : lockedFailure;
925
+ return lockReleaseFailed ? { ...base, lockReleaseFailed: true } : base;
733
926
  }
734
927
  }
735
928
  const result = {
@@ -744,23 +937,13 @@ var getLoginStatusWithDeps = async (options = {}, deps = {}) => {
744
937
  expiration,
745
938
  source: "file" /* File */,
746
939
  ...persistenceWarning ? { hint: persistenceWarning, persistenceFailed: true } : {},
940
+ ...lockReleaseFailed ? { lockReleaseFailed: true } : {},
747
941
  ...tokenRefresh ? { tokenRefresh } : {}
748
942
  };
749
943
  if (result.loginStatus === "Expired") {
750
- const fs = getFs();
751
- const globalPath = fs.path.join(fs.env.homedir(), envFilePath);
752
- if (absolutePath !== globalPath && await fs.exists(globalPath)) {
753
- try {
754
- const globalCreds = await loadEnvFile({
755
- envPath: globalPath
756
- });
757
- if (globalCreds.UIPATH_ACCESS_TOKEN) {
758
- const globalExp = getTokenExpiration(globalCreds.UIPATH_ACCESS_TOKEN);
759
- if (!globalExp || globalExp > new Date) {
760
- 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.`;
761
- }
762
- }
763
- } catch {}
944
+ const globalHint = await tryGlobalCredsHint();
945
+ if (globalHint) {
946
+ result.hint = globalHint;
764
947
  }
765
948
  }
766
949
  return result;
@@ -793,7 +976,7 @@ var getAuthContext = async (options = {}) => {
793
976
  throw new Error("Tenant ID not available. Ensure UIPATH_TENANT_ID is set.");
794
977
  }
795
978
  if (options.requireTenantName && tenantName === undefined) {
796
- throw new Error("Tenant not provided and UIPATH_TENANT_NAME not set. Please provide a tenant argument or set UIPATH_TENANT_NAME.");
979
+ throw new Error("Tenant not provided and UIPATH_TENANT_NAME not set. Run 'uip login' to select a tenant, or use 'uip login tenant set <tenant>' to switch tenants.");
797
980
  }
798
981
  return {
799
982
  baseUrl: status.baseUrl,
@@ -820,9 +1003,11 @@ var clientCredentialsLogin = async ({
820
1003
  const params = {
821
1004
  grant_type: "client_credentials",
822
1005
  client_id: config.clientId,
823
- client_secret: config.clientSecret ?? "",
824
- scope: config.scopes.join(" ")
1006
+ client_secret: config.clientSecret ?? ""
825
1007
  };
1008
+ if (config.scopes.length > 0) {
1009
+ params.scope = config.scopes.join(" ");
1010
+ }
826
1011
  const tokenResponse = await fetch(config.tokenEndpoint, {
827
1012
  method: "POST",
828
1013
  headers: {
@@ -860,16 +1045,30 @@ Troubleshooting:` + `
860
1045
  };
861
1046
  };
862
1047
  // src/logout.ts
863
- import { getFileSystem as getFileSystem3 } from "@uipath/filesystem";
1048
+ import { getFileSystem as getFileSystem4 } from "@uipath/filesystem";
864
1049
  async function logoutWithDeps(options, deps = {}) {
865
1050
  const {
866
1051
  resolveEnvFilePath = resolveEnvFilePathAsync,
867
- getFileSystem: getFs = getFileSystem3
1052
+ getFileSystem: getFs = getFileSystem4
868
1053
  } = deps;
869
1054
  const fs = getFs();
870
1055
  const { absolutePath } = await resolveEnvFilePath(options.file);
871
1056
  if (absolutePath && await fs.exists(absolutePath)) {
872
- await fs.rm(absolutePath);
1057
+ let release;
1058
+ try {
1059
+ if (typeof fs.acquireLock === "function") {
1060
+ release = await fs.acquireLock(absolutePath);
1061
+ }
1062
+ } catch {
1063
+ release = undefined;
1064
+ }
1065
+ try {
1066
+ await fs.rm(absolutePath);
1067
+ } finally {
1068
+ if (release) {
1069
+ await release().catch(() => {});
1070
+ }
1071
+ }
873
1072
  return {
874
1073
  success: true,
875
1074
  message: `Logged out successfully. Removed ${absolutePath}`,
@@ -0,0 +1,27 @@
1
+ export * from "./authContext";
2
+ export * from "./clientCredentials";
3
+ export { type AuthFileConfig, InvalidBaseUrlError, setAuthFileConfig, } from "./config";
4
+ export { ENFORCE_ROBOT_AUTH_VAR, ENV_AUTH_ENABLE_VAR, ENV_AUTH_VARS, EnvAuthConfigError, isEnvAuthEnabled, isRobotAuthEnforced, readAuthFromEnv, } from "./envAuth";
5
+ export * from "./interactive";
6
+ export * from "./loginStatus";
7
+ export * from "./logout";
8
+ export { type SelectFromList, selectTenantWithDeps } from "./selectTenant";
9
+ export { AUTH_TIMEOUT_ERROR_CODE } from "./server";
10
+ export { fetchTenantsAndOrganizations, type Tenant, type TenantsAndOrganizations, } from "./tenantSelection";
11
+ export * from "./tokenRefresh";
12
+ export type { BaseCredentials } from "./types";
13
+ export { DEFAULT_AUTH_FILENAME, DEFAULT_ENV_FILENAME, type EnvFileErrorCode, type EnvFileLocation, type EnvFileSource, type EnvFileUnusable, type EnvFileUnusableReason, loadEnvFileAsync, resolveEnvFileLocationAsync, resolveEnvFilePathAsync, saveEnvFileAsync, } from "./utils/envFile";
14
+ export { parseJWT } from "./utils/jwt";
15
+ export { isBrowser, isNode } from "./utils/platform";
16
+ interface AuthenticateOptions {
17
+ baseUrl?: string;
18
+ clientId?: string;
19
+ clientSecret?: string;
20
+ redirectUri?: URL;
21
+ scope?: string[];
22
+ organization?: string;
23
+ }
24
+ export declare const authenticate: ({ baseUrl, clientId, clientSecret, redirectUri, scope, organization, }: AuthenticateOptions) => Promise<{
25
+ accessToken: string;
26
+ refreshToken: string;
27
+ }>;