opensteer 0.6.1 → 0.6.3

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.
@@ -659,6 +659,65 @@ function toJsonSafeValue(value, seen) {
659
659
  return void 0;
660
660
  }
661
661
 
662
+ // src/cloud/credential-selection.ts
663
+ function selectCloudCredential(options) {
664
+ const apiKey = normalizeNonEmptyString(options.apiKey);
665
+ const accessToken = normalizeNonEmptyString(options.accessToken);
666
+ if (apiKey) {
667
+ if (options.authScheme === "bearer") {
668
+ return {
669
+ apiKey,
670
+ authScheme: "bearer",
671
+ kind: "access-token",
672
+ token: apiKey,
673
+ compatibilityBearerApiKey: true
674
+ };
675
+ }
676
+ return {
677
+ apiKey,
678
+ authScheme: "api-key",
679
+ kind: "api-key",
680
+ token: apiKey
681
+ };
682
+ }
683
+ if (accessToken) {
684
+ return {
685
+ accessToken,
686
+ authScheme: "bearer",
687
+ kind: "access-token",
688
+ token: accessToken
689
+ };
690
+ }
691
+ return null;
692
+ }
693
+ function selectCloudCredentialByPrecedence(layers, authScheme) {
694
+ for (const layer of layers) {
695
+ const hasApiKey = layer.hasApiKey ?? Object.prototype.hasOwnProperty.call(layer, "apiKey");
696
+ const hasAccessToken = layer.hasAccessToken ?? Object.prototype.hasOwnProperty.call(layer, "accessToken");
697
+ if (!hasApiKey && !hasAccessToken) {
698
+ continue;
699
+ }
700
+ return {
701
+ source: layer.source,
702
+ apiKey: layer.apiKey,
703
+ accessToken: layer.accessToken,
704
+ hasApiKey,
705
+ hasAccessToken,
706
+ credential: selectCloudCredential({
707
+ apiKey: layer.apiKey,
708
+ accessToken: layer.accessToken,
709
+ authScheme: layer.authScheme ?? authScheme
710
+ })
711
+ };
712
+ }
713
+ return null;
714
+ }
715
+ function normalizeNonEmptyString(value) {
716
+ if (typeof value !== "string") return void 0;
717
+ const normalized = value.trim();
718
+ return normalized.length ? normalized : void 0;
719
+ }
720
+
662
721
  // src/storage/namespace.ts
663
722
  var import_path = __toESM(require("path"), 1);
664
723
  var DEFAULT_NAMESPACE = "default";
@@ -983,11 +1042,6 @@ function normalizeCloudOptions(value) {
983
1042
  }
984
1043
  return value;
985
1044
  }
986
- function normalizeNonEmptyString(value) {
987
- if (typeof value !== "string") return void 0;
988
- const normalized = value.trim();
989
- return normalized.length ? normalized : void 0;
990
- }
991
1045
  function parseCloudEnabled(value, source) {
992
1046
  if (value == null) return void 0;
993
1047
  if (typeof value === "boolean") return value;
@@ -996,6 +1050,18 @@ function parseCloudEnabled(value, source) {
996
1050
  `Invalid ${source} value "${String(value)}". Use true, false, or a cloud options object.`
997
1051
  );
998
1052
  }
1053
+ function resolveCloudCredentialFields(selectedLayer) {
1054
+ const credential = selectedLayer?.credential;
1055
+ if (!credential) return {};
1056
+ if (credential.kind === "api-key" || credential.compatibilityBearerApiKey === true && selectedLayer?.source !== "env") {
1057
+ return {
1058
+ apiKey: credential.token
1059
+ };
1060
+ }
1061
+ return {
1062
+ accessToken: credential.token
1063
+ };
1064
+ }
999
1065
  function resolveCloudSelection(config, env = process.env) {
1000
1066
  const configCloud = parseCloudEnabled(config.cloud, "cloud");
1001
1067
  if (configCloud !== void 0) {
@@ -1032,6 +1098,9 @@ function resolveConfigWithEnv(input = {}, options = {}) {
1032
1098
  });
1033
1099
  assertNoLegacyAiConfig(".opensteer/config.json", fileConfig);
1034
1100
  assertNoLegacyRuntimeConfig(".opensteer/config.json", fileConfig);
1101
+ const fileCloudOptions = normalizeCloudOptions(fileConfig.cloud);
1102
+ const fileHasCloudApiKey = hasOwn(fileCloudOptions, "apiKey");
1103
+ const fileHasCloudAccessToken = hasOwn(fileCloudOptions, "accessToken");
1035
1104
  const fileRootDir = typeof fileConfig.storage?.rootDir === "string" ? fileConfig.storage.rootDir : void 0;
1036
1105
  const envRootDir = input.storage?.rootDir ?? fileRootDir ?? initialRootDir;
1037
1106
  const env = resolveEnv(envRootDir, {
@@ -1070,13 +1139,6 @@ function resolveConfigWithEnv(input = {}, options = {}) {
1070
1139
  const envAccessTokenRaw = resolveOpensteerAccessToken(env);
1071
1140
  const envBaseUrl = resolveOpensteerBaseUrl(env);
1072
1141
  const envAuthScheme = resolveOpensteerAuthScheme(env);
1073
- if (envApiKey && envAccessTokenRaw) {
1074
- throw new Error(
1075
- "OPENSTEER_API_KEY and OPENSTEER_ACCESS_TOKEN are mutually exclusive. Set only one."
1076
- );
1077
- }
1078
- const envAccessToken = envAccessTokenRaw || (envAuthScheme === "bearer" ? envApiKey : void 0);
1079
- const envApiCredential = envAuthScheme === "bearer" && !envAccessTokenRaw ? void 0 : envApiKey;
1080
1142
  const envCloudProfileId = resolveOpensteerCloudProfileId(env);
1081
1143
  const envCloudProfileReuseIfActive = resolveOpensteerCloudProfileReuseIfActive(env);
1082
1144
  const envCloudAnnounce = parseCloudAnnounce(
@@ -1105,11 +1167,6 @@ function resolveConfigWithEnv(input = {}, options = {}) {
1105
1167
  const inputHasCloudBaseUrl = Boolean(
1106
1168
  inputCloudOptions && Object.prototype.hasOwnProperty.call(inputCloudOptions, "baseUrl")
1107
1169
  );
1108
- if (normalizeNonEmptyString(inputCloudOptions?.apiKey) && normalizeNonEmptyString(inputCloudOptions?.accessToken)) {
1109
- throw new Error(
1110
- "cloud.apiKey and cloud.accessToken are mutually exclusive. Set only one."
1111
- );
1112
- }
1113
1170
  const cloudSelection = resolveCloudSelection({
1114
1171
  cloud: resolved.cloud
1115
1172
  }, env);
@@ -1120,11 +1177,6 @@ function resolveConfigWithEnv(input = {}, options = {}) {
1120
1177
  accessToken: resolvedCloudAccessTokenRaw,
1121
1178
  ...resolvedCloudRest
1122
1179
  } = resolvedCloud;
1123
- if (normalizeNonEmptyString(resolvedCloudApiKeyRaw) && normalizeNonEmptyString(resolvedCloudAccessTokenRaw)) {
1124
- throw new Error(
1125
- "Cloud config cannot include both apiKey and accessToken at the same time."
1126
- );
1127
- }
1128
1180
  const resolvedCloudBrowserProfile = normalizeCloudBrowserProfileOptions(
1129
1181
  resolvedCloud.browserProfile,
1130
1182
  "resolved.cloud.browserProfile"
@@ -1136,25 +1188,40 @@ function resolveConfigWithEnv(input = {}, options = {}) {
1136
1188
  const browserProfile = inputCloudBrowserProfile ?? envCloudBrowserProfile ?? resolvedCloudBrowserProfile;
1137
1189
  let authScheme = inputAuthScheme ?? envAuthScheme ?? parseAuthScheme(resolvedCloud.authScheme, "cloud.authScheme") ?? "api-key";
1138
1190
  const announce = inputCloudAnnounce ?? envCloudAnnounce ?? parseCloudAnnounce(resolvedCloud.announce, "cloud.announce") ?? "always";
1139
- const credentialOverriddenByInput = inputHasCloudApiKey || inputHasCloudAccessToken;
1140
- let apiKey = normalizeNonEmptyString(resolvedCloudApiKeyRaw);
1141
- let accessToken = normalizeNonEmptyString(resolvedCloudAccessTokenRaw);
1142
- if (!credentialOverriddenByInput) {
1143
- if (envAccessToken) {
1144
- accessToken = envAccessToken;
1145
- apiKey = void 0;
1146
- } else if (envApiCredential) {
1147
- apiKey = envApiCredential;
1148
- accessToken = void 0;
1149
- }
1150
- }
1191
+ const selectedCredentialLayer = selectCloudCredentialByPrecedence(
1192
+ [
1193
+ {
1194
+ source: "input",
1195
+ apiKey: inputCloudOptions?.apiKey,
1196
+ accessToken: inputCloudOptions?.accessToken,
1197
+ hasApiKey: inputHasCloudApiKey,
1198
+ hasAccessToken: inputHasCloudAccessToken
1199
+ },
1200
+ {
1201
+ source: "env",
1202
+ apiKey: envApiKey,
1203
+ accessToken: envAccessTokenRaw,
1204
+ hasApiKey: envApiKey !== void 0,
1205
+ hasAccessToken: envAccessTokenRaw !== void 0
1206
+ },
1207
+ {
1208
+ source: "file",
1209
+ apiKey: fileCloudOptions?.apiKey,
1210
+ accessToken: fileCloudOptions?.accessToken,
1211
+ hasApiKey: fileHasCloudApiKey,
1212
+ hasAccessToken: fileHasCloudAccessToken
1213
+ }
1214
+ ],
1215
+ authScheme
1216
+ );
1217
+ const { apiKey, accessToken } = resolveCloudCredentialFields(selectedCredentialLayer);
1151
1218
  if (accessToken) {
1152
1219
  authScheme = "bearer";
1153
1220
  }
1154
1221
  resolved.cloud = {
1155
1222
  ...resolvedCloudRest,
1156
- ...inputHasCloudApiKey ? { apiKey: resolvedCloudApiKeyRaw } : apiKey ? { apiKey } : {},
1157
- ...inputHasCloudAccessToken ? { accessToken: resolvedCloudAccessTokenRaw } : accessToken ? { accessToken } : {},
1223
+ ...apiKey ? { apiKey } : selectedCredentialLayer?.hasApiKey && !accessToken ? { apiKey: selectedCredentialLayer.apiKey } : {},
1224
+ ...accessToken ? { accessToken } : selectedCredentialLayer?.hasAccessToken && !apiKey ? { accessToken: selectedCredentialLayer.accessToken } : {},
1158
1225
  authScheme,
1159
1226
  announce,
1160
1227
  ...browserProfile ? { browserProfile } : {}
@@ -11780,30 +11847,20 @@ var Opensteer = class _Opensteer {
11780
11847
  this.pool = new BrowserPool(resolved.browser || {});
11781
11848
  if (cloudSelection.cloud) {
11782
11849
  const cloudConfig = resolved.cloud && typeof resolved.cloud === "object" ? resolved.cloud : void 0;
11783
- const apiKey = cloudConfig?.apiKey?.trim();
11784
- const accessToken = cloudConfig?.accessToken?.trim();
11785
- if (apiKey && accessToken) {
11786
- throw new Error(
11787
- "Cloud mode cannot use both cloud.apiKey and cloud.accessToken. Set only one credential."
11788
- );
11789
- }
11790
- let credential = "";
11791
- let authScheme = cloudConfig?.authScheme ?? "api-key";
11792
- if (accessToken) {
11793
- credential = accessToken;
11794
- authScheme = "bearer";
11795
- } else if (apiKey) {
11796
- credential = apiKey;
11797
- }
11850
+ const credential = selectCloudCredential({
11851
+ apiKey: cloudConfig?.apiKey,
11852
+ accessToken: cloudConfig?.accessToken,
11853
+ authScheme: cloudConfig?.authScheme
11854
+ });
11798
11855
  if (!credential) {
11799
11856
  throw new Error(
11800
11857
  "Cloud mode requires credentials via cloud.apiKey/cloud.accessToken or OPENSTEER_API_KEY/OPENSTEER_ACCESS_TOKEN."
11801
11858
  );
11802
11859
  }
11803
11860
  this.cloud = createCloudRuntimeState(
11804
- credential,
11861
+ credential.token,
11805
11862
  cloudConfig?.baseUrl,
11806
- authScheme
11863
+ credential.authScheme
11807
11864
  );
11808
11865
  } else {
11809
11866
  this.cloud = null;
@@ -14691,59 +14748,21 @@ var import_open = __toESM(require("open"), 1);
14691
14748
 
14692
14749
  // src/auth/credential-resolver.ts
14693
14750
  function resolveCloudCredential(options) {
14694
- const flagApiKey = normalizeToken(options.apiKeyFlag);
14695
- const flagAccessToken = normalizeToken(options.accessTokenFlag);
14696
- if (flagApiKey && flagAccessToken) {
14697
- throw new Error("--api-key and --access-token are mutually exclusive.");
14698
- }
14699
- if (flagAccessToken) {
14700
- return {
14701
- kind: "access-token",
14702
- source: "flag",
14703
- token: flagAccessToken,
14704
- authScheme: "bearer"
14705
- };
14706
- }
14707
- if (flagApiKey) {
14708
- return {
14709
- kind: "api-key",
14710
- source: "flag",
14711
- token: flagApiKey,
14712
- authScheme: "api-key"
14713
- };
14751
+ const flagCredential = selectCloudCredential({
14752
+ apiKey: options.apiKeyFlag,
14753
+ accessToken: options.accessTokenFlag
14754
+ });
14755
+ if (flagCredential) {
14756
+ return toResolvedCloudCredential("flag", flagCredential);
14714
14757
  }
14715
14758
  const envAuthScheme = parseEnvAuthScheme(options.env.OPENSTEER_AUTH_SCHEME);
14716
- const envApiKey = normalizeToken(options.env.OPENSTEER_API_KEY);
14717
- const envAccessToken = normalizeToken(options.env.OPENSTEER_ACCESS_TOKEN);
14718
- if (envApiKey && envAccessToken) {
14719
- throw new Error(
14720
- "OPENSTEER_API_KEY and OPENSTEER_ACCESS_TOKEN are mutually exclusive. Set only one."
14721
- );
14722
- }
14723
- if (envAccessToken) {
14724
- return {
14725
- kind: "access-token",
14726
- source: "env",
14727
- token: envAccessToken,
14728
- authScheme: "bearer"
14729
- };
14730
- }
14731
- if (envApiKey) {
14732
- if (envAuthScheme === "bearer") {
14733
- return {
14734
- kind: "access-token",
14735
- source: "env",
14736
- token: envApiKey,
14737
- authScheme: "bearer",
14738
- compatibilityBearerApiKey: true
14739
- };
14740
- }
14741
- return {
14742
- kind: "api-key",
14743
- source: "env",
14744
- token: envApiKey,
14745
- authScheme: envAuthScheme ?? "api-key"
14746
- };
14759
+ const envCredential = selectCloudCredential({
14760
+ apiKey: options.env.OPENSTEER_API_KEY,
14761
+ accessToken: options.env.OPENSTEER_ACCESS_TOKEN,
14762
+ authScheme: envAuthScheme
14763
+ });
14764
+ if (envCredential) {
14765
+ return toResolvedCloudCredential("env", envCredential);
14747
14766
  }
14748
14767
  return null;
14749
14768
  }
@@ -14773,19 +14792,33 @@ function normalizeToken(value) {
14773
14792
  const normalized = value.trim();
14774
14793
  return normalized.length ? normalized : void 0;
14775
14794
  }
14795
+ function toResolvedCloudCredential(source, credential) {
14796
+ if (credential.compatibilityBearerApiKey) {
14797
+ return {
14798
+ kind: credential.kind,
14799
+ source,
14800
+ token: credential.token,
14801
+ authScheme: credential.authScheme,
14802
+ compatibilityBearerApiKey: true
14803
+ };
14804
+ }
14805
+ return {
14806
+ kind: credential.kind,
14807
+ source,
14808
+ token: credential.token,
14809
+ authScheme: credential.authScheme
14810
+ };
14811
+ }
14776
14812
 
14777
14813
  // src/auth/machine-credential-store.ts
14778
14814
  var import_node_crypto2 = require("crypto");
14779
14815
  var import_node_fs2 = __toESM(require("fs"), 1);
14780
14816
  var import_node_os2 = __toESM(require("os"), 1);
14781
14817
  var import_node_path2 = __toESM(require("path"), 1);
14782
- var METADATA_VERSION = 1;
14783
- var ACTIVE_TARGET_VERSION = 1;
14818
+ var METADATA_VERSION = 2;
14819
+ var ACTIVE_TARGET_VERSION = 2;
14784
14820
  var KEYCHAIN_SERVICE = "com.opensteer.cli.cloud";
14785
14821
  var KEYCHAIN_ACCOUNT_PREFIX = "machine:";
14786
- var LEGACY_KEYCHAIN_ACCOUNT = "machine";
14787
- var LEGACY_METADATA_FILE_NAME = "cli-login.json";
14788
- var LEGACY_FALLBACK_SECRET_FILE_NAME = "cli-login.secret.json";
14789
14822
  var ACTIVE_TARGET_FILE_NAME = "cli-target.json";
14790
14823
  var MachineCredentialStore = class {
14791
14824
  authDir;
@@ -14800,8 +14833,11 @@ var MachineCredentialStore = class {
14800
14833
  this.warn = options.warn ?? (() => void 0);
14801
14834
  }
14802
14835
  readCloudCredential(target) {
14803
- const slot = resolveCredentialSlot(this.authDir, target);
14804
- return this.readCredentialSlot(slot, target) ?? this.readAndMigrateLegacyCredential(target);
14836
+ const normalizedTarget = normalizeCloudCredentialTarget(target);
14837
+ return this.readCredentialSlot(
14838
+ resolveCredentialSlot(this.authDir, normalizedTarget),
14839
+ normalizedTarget
14840
+ );
14805
14841
  }
14806
14842
  writeCloudCredential(args) {
14807
14843
  const accessToken = args.accessToken.trim();
@@ -14809,12 +14845,8 @@ var MachineCredentialStore = class {
14809
14845
  if (!accessToken || !refreshToken2) {
14810
14846
  throw new Error("Cannot persist empty machine credential secrets.");
14811
14847
  }
14812
- const baseUrl = normalizeCredentialUrl(args.baseUrl, "baseUrl");
14813
- const siteUrl = normalizeCredentialUrl(args.siteUrl, "siteUrl");
14814
- const slot = resolveCredentialSlot(this.authDir, {
14815
- baseUrl,
14816
- siteUrl
14817
- });
14848
+ const baseUrl = normalizeCredentialUrl(args.baseUrl);
14849
+ const slot = resolveCredentialSlot(this.authDir, { baseUrl });
14818
14850
  ensureDirectory(this.authDir);
14819
14851
  const secretPayload = {
14820
14852
  accessToken,
@@ -14841,7 +14873,6 @@ var MachineCredentialStore = class {
14841
14873
  version: METADATA_VERSION,
14842
14874
  secretBackend,
14843
14875
  baseUrl,
14844
- siteUrl,
14845
14876
  scope: args.scope,
14846
14877
  obtainedAt: args.obtainedAt,
14847
14878
  expiresAt: args.expiresAt,
@@ -14853,23 +14884,18 @@ var MachineCredentialStore = class {
14853
14884
  return readActiveCloudTargetMetadata(resolveActiveTargetPath(this.authDir));
14854
14885
  }
14855
14886
  writeActiveCloudTarget(target) {
14856
- const baseUrl = normalizeCredentialUrl(target.baseUrl, "baseUrl");
14857
- const siteUrl = normalizeCredentialUrl(target.siteUrl, "siteUrl");
14887
+ const baseUrl = normalizeCredentialUrl(target.baseUrl);
14858
14888
  ensureDirectory(this.authDir);
14859
14889
  writeJsonFile(resolveActiveTargetPath(this.authDir), {
14860
14890
  version: ACTIVE_TARGET_VERSION,
14861
14891
  baseUrl,
14862
- siteUrl,
14863
14892
  updatedAt: Date.now()
14864
14893
  });
14865
14894
  }
14866
14895
  clearCloudCredential(target) {
14867
- this.clearCredentialSlot(resolveCredentialSlot(this.authDir, target));
14868
- const legacySlot = resolveLegacyCredentialSlot(this.authDir);
14869
- const legacyMetadata = readMetadata(legacySlot.metadataPath);
14870
- if (legacyMetadata && matchesCredentialTarget(legacyMetadata, target)) {
14871
- this.clearCredentialSlot(legacySlot);
14872
- }
14896
+ this.clearCredentialSlot(
14897
+ resolveCredentialSlot(this.authDir, normalizeCloudCredentialTarget(target))
14898
+ );
14873
14899
  }
14874
14900
  readCredentialSlot(slot, target) {
14875
14901
  const metadata = readMetadata(slot.metadataPath);
@@ -14885,7 +14911,6 @@ var MachineCredentialStore = class {
14885
14911
  }
14886
14912
  return {
14887
14913
  baseUrl: metadata.baseUrl,
14888
- siteUrl: metadata.siteUrl,
14889
14914
  scope: metadata.scope,
14890
14915
  accessToken: secret.accessToken,
14891
14916
  refreshToken: secret.refreshToken,
@@ -14893,16 +14918,6 @@ var MachineCredentialStore = class {
14893
14918
  expiresAt: metadata.expiresAt
14894
14919
  };
14895
14920
  }
14896
- readAndMigrateLegacyCredential(target) {
14897
- const legacySlot = resolveLegacyCredentialSlot(this.authDir);
14898
- const legacyCredential = this.readCredentialSlot(legacySlot, target);
14899
- if (!legacyCredential) {
14900
- return null;
14901
- }
14902
- this.writeCloudCredential(legacyCredential);
14903
- this.clearCredentialSlot(legacySlot);
14904
- return legacyCredential;
14905
- }
14906
14921
  readSecret(slot, backend) {
14907
14922
  if (backend === "keychain" && this.keychain) {
14908
14923
  try {
@@ -14943,9 +14958,8 @@ function createMachineCredentialStore(options = {}) {
14943
14958
  return new MachineCredentialStore(options);
14944
14959
  }
14945
14960
  function resolveCredentialSlot(authDir, target) {
14946
- const normalizedBaseUrl = normalizeCredentialUrl(target.baseUrl, "baseUrl");
14947
- const normalizedSiteUrl = normalizeCredentialUrl(target.siteUrl, "siteUrl");
14948
- const storageKey = (0, import_node_crypto2.createHash)("sha256").update(`${normalizedBaseUrl}\0${normalizedSiteUrl}`).digest("hex").slice(0, 24);
14961
+ const normalizedBaseUrl = normalizeCredentialUrl(target.baseUrl);
14962
+ const storageKey = (0, import_node_crypto2.createHash)("sha256").update(normalizedBaseUrl).digest("hex").slice(0, 24);
14949
14963
  return {
14950
14964
  keychainAccount: `${KEYCHAIN_ACCOUNT_PREFIX}${storageKey}`,
14951
14965
  metadataPath: import_node_path2.default.join(authDir, `cli-login.${storageKey}.json`),
@@ -14955,26 +14969,24 @@ function resolveCredentialSlot(authDir, target) {
14955
14969
  )
14956
14970
  };
14957
14971
  }
14958
- function resolveLegacyCredentialSlot(authDir) {
14959
- return {
14960
- keychainAccount: LEGACY_KEYCHAIN_ACCOUNT,
14961
- metadataPath: import_node_path2.default.join(authDir, LEGACY_METADATA_FILE_NAME),
14962
- fallbackSecretPath: import_node_path2.default.join(authDir, LEGACY_FALLBACK_SECRET_FILE_NAME)
14963
- };
14964
- }
14965
14972
  function resolveActiveTargetPath(authDir) {
14966
14973
  return import_node_path2.default.join(authDir, ACTIVE_TARGET_FILE_NAME);
14967
14974
  }
14968
14975
  function matchesCredentialTarget(value, target) {
14969
- return normalizeCredentialUrl(value.baseUrl, "baseUrl") === normalizeCredentialUrl(target.baseUrl, "baseUrl") && normalizeCredentialUrl(value.siteUrl, "siteUrl") === normalizeCredentialUrl(target.siteUrl, "siteUrl");
14976
+ return normalizeCredentialUrl(value.baseUrl) === normalizeCredentialUrl(target.baseUrl);
14970
14977
  }
14971
- function normalizeCredentialUrl(value, field) {
14978
+ function normalizeCredentialUrl(value) {
14972
14979
  const normalized = stripTrailingSlashes(value.trim());
14973
14980
  if (!normalized) {
14974
- throw new Error(`Cannot persist machine credential without ${field}.`);
14981
+ throw new Error("Cannot persist machine credential without baseUrl.");
14975
14982
  }
14976
14983
  return normalized;
14977
14984
  }
14985
+ function normalizeCloudCredentialTarget(target) {
14986
+ return {
14987
+ baseUrl: normalizeCredentialUrl(target.baseUrl)
14988
+ };
14989
+ }
14978
14990
  function resolveConfigDir(appName, env) {
14979
14991
  if (process.platform === "win32") {
14980
14992
  const appData = env.APPDATA?.trim() || import_node_path2.default.join(import_node_os2.default.homedir(), "AppData", "Roaming");
@@ -15013,7 +15025,6 @@ function readMetadata(filePath) {
15013
15025
  return null;
15014
15026
  }
15015
15027
  if (typeof parsed.baseUrl !== "string" || !parsed.baseUrl.trim()) return null;
15016
- if (typeof parsed.siteUrl !== "string" || !parsed.siteUrl.trim()) return null;
15017
15028
  if (!Array.isArray(parsed.scope)) return null;
15018
15029
  if (typeof parsed.obtainedAt !== "number") return null;
15019
15030
  if (typeof parsed.expiresAt !== "number") return null;
@@ -15022,7 +15033,6 @@ function readMetadata(filePath) {
15022
15033
  version: parsed.version,
15023
15034
  secretBackend: parsed.secretBackend,
15024
15035
  baseUrl: parsed.baseUrl,
15025
- siteUrl: parsed.siteUrl,
15026
15036
  scope: parsed.scope.filter(
15027
15037
  (value) => typeof value === "string"
15028
15038
  ),
@@ -15047,12 +15057,8 @@ function readActiveCloudTargetMetadata(filePath) {
15047
15057
  if (typeof parsed.baseUrl !== "string" || !parsed.baseUrl.trim()) {
15048
15058
  return null;
15049
15059
  }
15050
- if (typeof parsed.siteUrl !== "string" || !parsed.siteUrl.trim()) {
15051
- return null;
15052
- }
15053
15060
  return {
15054
- baseUrl: parsed.baseUrl,
15055
- siteUrl: parsed.siteUrl
15061
+ baseUrl: parsed.baseUrl
15056
15062
  };
15057
15063
  } catch {
15058
15064
  return null;
@@ -15077,14 +15083,13 @@ function readSecretFile(filePath) {
15077
15083
  return null;
15078
15084
  }
15079
15085
  try {
15080
- const raw = import_node_fs2.default.readFileSync(filePath, "utf8");
15081
- return parseSecretPayload(raw);
15086
+ return parseSecretPayload(import_node_fs2.default.readFileSync(filePath, "utf8"));
15082
15087
  } catch {
15083
15088
  return null;
15084
15089
  }
15085
15090
  }
15086
- function writeJsonFile(filePath, payload, options = {}) {
15087
- import_node_fs2.default.writeFileSync(filePath, JSON.stringify(payload, null, 2), {
15091
+ function writeJsonFile(filePath, value, options = {}) {
15092
+ import_node_fs2.default.writeFileSync(filePath, JSON.stringify(value, null, 2), {
15088
15093
  encoding: "utf8",
15089
15094
  mode: options.mode ?? 384
15090
15095
  });
@@ -15104,6 +15109,8 @@ var CliAuthHttpError = class extends Error {
15104
15109
  this.body = body;
15105
15110
  }
15106
15111
  };
15112
+ var DEFAULT_AUTH_SITE_URL = "https://opensteer.com";
15113
+ var INTERNAL_AUTH_SITE_URL_ENV = "OPENSTEER_INTERNAL_AUTH_SITE_URL";
15107
15114
  function resolveBaseUrl(provided, env) {
15108
15115
  const baseUrl = normalizeCloudBaseUrl(
15109
15116
  (provided || env.OPENSTEER_BASE_URL || DEFAULT_CLOUD_BASE_URL).trim()
@@ -15111,16 +15118,19 @@ function resolveBaseUrl(provided, env) {
15111
15118
  assertSecureUrl(baseUrl, "--base-url");
15112
15119
  return baseUrl;
15113
15120
  }
15114
- function resolveSiteUrl(provided, baseUrl, env) {
15115
- const siteUrl = normalizeCloudBaseUrl(
15116
- (provided || env.OPENSTEER_CLOUD_SITE_URL || deriveSiteUrlFromBaseUrl(baseUrl)).trim()
15121
+ function resolveAuthSiteUrl(env) {
15122
+ const authSiteUrl = normalizeCloudBaseUrl(
15123
+ (env[INTERNAL_AUTH_SITE_URL_ENV] || DEFAULT_AUTH_SITE_URL).trim()
15124
+ );
15125
+ assertSecureUrl(
15126
+ authSiteUrl,
15127
+ `environment variable ${INTERNAL_AUTH_SITE_URL_ENV}`
15117
15128
  );
15118
- assertSecureUrl(siteUrl, "--site-url");
15119
- return siteUrl;
15129
+ return authSiteUrl;
15120
15130
  }
15121
- function hasExplicitCloudTargetSelection(providedBaseUrl, providedSiteUrl, env) {
15131
+ function hasExplicitCloudTargetSelection(providedBaseUrl, env) {
15122
15132
  return Boolean(
15123
- providedBaseUrl?.trim() || providedSiteUrl?.trim() || env.OPENSTEER_BASE_URL?.trim() || env.OPENSTEER_CLOUD_SITE_URL?.trim()
15133
+ providedBaseUrl?.trim() || env.OPENSTEER_BASE_URL?.trim()
15124
15134
  );
15125
15135
  }
15126
15136
  function readRememberedCloudTarget(store) {
@@ -15130,57 +15140,21 @@ function readRememberedCloudTarget(store) {
15130
15140
  }
15131
15141
  try {
15132
15142
  const baseUrl = normalizeCloudBaseUrl(activeTarget.baseUrl);
15133
- const siteUrl = normalizeCloudBaseUrl(activeTarget.siteUrl);
15134
15143
  assertSecureUrl(baseUrl, "--base-url");
15135
- assertSecureUrl(siteUrl, "--site-url");
15136
- return {
15137
- baseUrl,
15138
- siteUrl
15139
- };
15144
+ return { baseUrl };
15140
15145
  } catch {
15141
15146
  return null;
15142
15147
  }
15143
15148
  }
15144
- function resolveCloudTarget(args, env, store) {
15145
- if (!hasExplicitCloudTargetSelection(args.baseUrl, args.siteUrl, env)) {
15149
+ function resolveCloudTarget(args, env, store, options = {}) {
15150
+ if (options.allowRememberedTarget !== false && !hasExplicitCloudTargetSelection(args.baseUrl, env)) {
15146
15151
  const rememberedTarget = readRememberedCloudTarget(store);
15147
15152
  if (rememberedTarget) {
15148
15153
  return rememberedTarget;
15149
15154
  }
15150
15155
  }
15151
15156
  const baseUrl = resolveBaseUrl(args.baseUrl, env);
15152
- const siteUrl = resolveSiteUrl(args.siteUrl, baseUrl, env);
15153
- return {
15154
- baseUrl,
15155
- siteUrl
15156
- };
15157
- }
15158
- function deriveSiteUrlFromBaseUrl(baseUrl) {
15159
- let parsed;
15160
- try {
15161
- parsed = new URL(baseUrl);
15162
- } catch {
15163
- return "https://opensteer.com";
15164
- }
15165
- const hostname = parsed.hostname.toLowerCase();
15166
- if (hostname.startsWith("api.")) {
15167
- parsed.hostname = hostname.slice("api.".length);
15168
- parsed.pathname = "";
15169
- parsed.search = "";
15170
- parsed.hash = "";
15171
- return normalizeCloudBaseUrl(parsed.toString());
15172
- }
15173
- if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1") {
15174
- parsed.port = "3001";
15175
- parsed.pathname = "";
15176
- parsed.search = "";
15177
- parsed.hash = "";
15178
- return normalizeCloudBaseUrl(parsed.toString());
15179
- }
15180
- parsed.pathname = "";
15181
- parsed.search = "";
15182
- parsed.hash = "";
15183
- return normalizeCloudBaseUrl(parsed.toString());
15157
+ return { baseUrl };
15184
15158
  }
15185
15159
  function assertSecureUrl(value, flag) {
15186
15160
  let parsed;
@@ -15255,10 +15229,10 @@ function parseCliOauthError(error) {
15255
15229
  interval: typeof root.interval === "number" ? root.interval : void 0
15256
15230
  };
15257
15231
  }
15258
- async function startDeviceAuthorization(siteUrl, fetchFn) {
15232
+ async function startDeviceAuthorization(authSiteUrl, fetchFn) {
15259
15233
  const response = await postJson(
15260
15234
  fetchFn,
15261
- `${siteUrl}/api/cli-auth/device/start`,
15235
+ `${authSiteUrl}/api/cli-auth/device/start`,
15262
15236
  {
15263
15237
  scope: ["cloud:browser"]
15264
15238
  }
@@ -15268,18 +15242,18 @@ async function startDeviceAuthorization(siteUrl, fetchFn) {
15268
15242
  }
15269
15243
  return response;
15270
15244
  }
15271
- async function pollDeviceToken(siteUrl, deviceCode, fetchFn) {
15245
+ async function pollDeviceToken(authSiteUrl, deviceCode, fetchFn) {
15272
15246
  return await postJson(
15273
15247
  fetchFn,
15274
- `${siteUrl}/api/cli-auth/device/token`,
15248
+ `${authSiteUrl}/api/cli-auth/device/token`,
15275
15249
  {
15276
15250
  grant_type: "urn:ietf:params:oauth:grant-type:device_code",
15277
15251
  device_code: deviceCode
15278
15252
  }
15279
15253
  );
15280
15254
  }
15281
- async function refreshToken(siteUrl, refreshTokenValue, fetchFn) {
15282
- return await postJson(fetchFn, `${siteUrl}/api/cli-auth/token`, {
15255
+ async function refreshToken(authSiteUrl, refreshTokenValue, fetchFn) {
15256
+ return await postJson(fetchFn, `${authSiteUrl}/api/cli-auth/token`, {
15283
15257
  grant_type: "refresh_token",
15284
15258
  refresh_token: refreshTokenValue
15285
15259
  });
@@ -15297,7 +15271,7 @@ async function openDefaultBrowser(url) {
15297
15271
  }
15298
15272
  }
15299
15273
  async function runDeviceLoginFlow(args) {
15300
- const start = await startDeviceAuthorization(args.siteUrl, args.fetchFn);
15274
+ const start = await startDeviceAuthorization(args.authSiteUrl, args.fetchFn);
15301
15275
  if (args.openBrowser) {
15302
15276
  args.writeProgress(
15303
15277
  "Opening your default browser for Opensteer CLI authentication.\n"
@@ -15342,7 +15316,7 @@ ${start.verification_uri_complete}
15342
15316
  await args.sleep(pollIntervalMs);
15343
15317
  try {
15344
15318
  const tokenPayload = await pollDeviceToken(
15345
- args.siteUrl,
15319
+ args.authSiteUrl,
15346
15320
  start.device_code,
15347
15321
  args.fetchFn
15348
15322
  );
@@ -15390,7 +15364,7 @@ ${start.verification_uri_complete}
15390
15364
  }
15391
15365
  async function refreshSavedCredential(saved, deps) {
15392
15366
  const tokenPayload = await refreshToken(
15393
- saved.siteUrl,
15367
+ resolveAuthSiteUrl(deps.env),
15394
15368
  saved.refreshToken,
15395
15369
  deps.fetchFn
15396
15370
  );
@@ -15403,7 +15377,6 @@ async function refreshSavedCredential(saved, deps) {
15403
15377
  };
15404
15378
  deps.store.writeCloudCredential({
15405
15379
  baseUrl: saved.baseUrl,
15406
- siteUrl: saved.siteUrl,
15407
15380
  scope: updated.scope,
15408
15381
  accessToken: updated.accessToken,
15409
15382
  refreshToken: updated.refreshToken,
@@ -15432,8 +15405,7 @@ async function ensureSavedCredentialIsFresh(saved, deps) {
15432
15405
  const oauth = parseCliOauthError(error.body);
15433
15406
  if (oauth?.error === "invalid_grant" || oauth?.error === "expired_token") {
15434
15407
  deps.store.clearCloudCredential({
15435
- baseUrl: saved.baseUrl,
15436
- siteUrl: saved.siteUrl
15408
+ baseUrl: saved.baseUrl
15437
15409
  });
15438
15410
  return null;
15439
15411
  }
@@ -15468,7 +15440,7 @@ async function ensureCloudCredentialsForCommand(options) {
15468
15440
  `);
15469
15441
  }
15470
15442
  });
15471
- const { baseUrl, siteUrl } = resolveCloudTarget(options, env, store);
15443
+ const { baseUrl } = resolveCloudTarget(options, env, store);
15472
15444
  const initialCredential = resolveCloudCredential({
15473
15445
  env,
15474
15446
  apiKeyFlag: options.apiKeyFlag,
@@ -15476,11 +15448,9 @@ async function ensureCloudCredentialsForCommand(options) {
15476
15448
  });
15477
15449
  let credential = initialCredential;
15478
15450
  if (!credential) {
15479
- const saved = store.readCloudCredential({
15480
- baseUrl,
15481
- siteUrl
15482
- });
15451
+ const saved = store.readCloudCredential({ baseUrl });
15483
15452
  const freshSaved = saved ? await ensureSavedCredentialIsFresh(saved, {
15453
+ env,
15484
15454
  fetchFn,
15485
15455
  store,
15486
15456
  now,
@@ -15498,7 +15468,7 @@ async function ensureCloudCredentialsForCommand(options) {
15498
15468
  if (!credential) {
15499
15469
  if (options.autoLoginIfNeeded && (options.interactive ?? false)) {
15500
15470
  const loggedIn = await runDeviceLoginFlow({
15501
- siteUrl,
15471
+ authSiteUrl: resolveAuthSiteUrl(env),
15502
15472
  fetchFn,
15503
15473
  writeProgress,
15504
15474
  openExternalUrl,
@@ -15508,7 +15478,6 @@ async function ensureCloudCredentialsForCommand(options) {
15508
15478
  });
15509
15479
  store.writeCloudCredential({
15510
15480
  baseUrl,
15511
- siteUrl,
15512
15481
  scope: loggedIn.scope,
15513
15482
  accessToken: loggedIn.accessToken,
15514
15483
  refreshToken: loggedIn.refreshToken,
@@ -15526,20 +15495,15 @@ async function ensureCloudCredentialsForCommand(options) {
15526
15495
  throw new Error(toAuthMissingMessage(options.commandName));
15527
15496
  }
15528
15497
  }
15529
- store.writeActiveCloudTarget({
15530
- baseUrl,
15531
- siteUrl
15532
- });
15498
+ store.writeActiveCloudTarget({ baseUrl });
15533
15499
  applyCloudCredentialToEnv(env, credential);
15534
15500
  env.OPENSTEER_BASE_URL = baseUrl;
15535
- env.OPENSTEER_CLOUD_SITE_URL = siteUrl;
15536
15501
  return {
15537
15502
  token: credential.token,
15538
15503
  authScheme: credential.authScheme,
15539
15504
  source: credential.source,
15540
15505
  kind: credential.kind,
15541
- baseUrl,
15542
- siteUrl
15506
+ baseUrl
15543
15507
  };
15544
15508
  }
15545
15509
 
@@ -15557,7 +15521,6 @@ Cloud auth options (all commands):
15557
15521
  --api-key <key> Cloud API key (defaults to OPENSTEER_API_KEY)
15558
15522
  --access-token <token> Cloud bearer access token (defaults to OPENSTEER_ACCESS_TOKEN)
15559
15523
  --base-url <url> Cloud API base URL (defaults to env or the last selected host)
15560
- --site-url <url> Cloud site URL for login/refresh/revoke flows
15561
15524
  --auth-scheme <scheme> api-key (default) or bearer
15562
15525
  --json JSON output (progress logs go to stderr)
15563
15526
 
@@ -15644,13 +15607,6 @@ function parseListArgs(rawArgs) {
15644
15607
  i = value.nextIndex;
15645
15608
  continue;
15646
15609
  }
15647
- if (arg === "--site-url") {
15648
- const value = readFlagValue(rawArgs, i, "--site-url");
15649
- if (!value.ok) return { mode: "error", error: value.error };
15650
- args.siteUrl = value.value;
15651
- i = value.nextIndex;
15652
- continue;
15653
- }
15654
15610
  if (arg === "--auth-scheme") {
15655
15611
  const value = readFlagValue(rawArgs, i, "--auth-scheme");
15656
15612
  if (!value.ok) return { mode: "error", error: value.error };
@@ -15743,13 +15699,6 @@ function parseCreateArgs(rawArgs) {
15743
15699
  i = value.nextIndex;
15744
15700
  continue;
15745
15701
  }
15746
- if (arg === "--site-url") {
15747
- const value = readFlagValue(rawArgs, i, "--site-url");
15748
- if (!value.ok) return { mode: "error", error: value.error };
15749
- args.siteUrl = value.value;
15750
- i = value.nextIndex;
15751
- continue;
15752
- }
15753
15702
  if (arg === "--auth-scheme") {
15754
15703
  const value = readFlagValue(rawArgs, i, "--auth-scheme");
15755
15704
  if (!value.ok) return { mode: "error", error: value.error };
@@ -15868,13 +15817,6 @@ function parseSyncArgs(rawArgs) {
15868
15817
  i = value.nextIndex;
15869
15818
  continue;
15870
15819
  }
15871
- if (arg === "--site-url") {
15872
- const value = readFlagValue(rawArgs, i, "--site-url");
15873
- if (!value.ok) return { mode: "error", error: value.error };
15874
- args.siteUrl = value.value;
15875
- i = value.nextIndex;
15876
- continue;
15877
- }
15878
15820
  if (arg === "--auth-scheme") {
15879
15821
  const value = readFlagValue(rawArgs, i, "--auth-scheme");
15880
15822
  if (!value.ok) return { mode: "error", error: value.error };
@@ -15965,7 +15907,6 @@ async function buildCloudAuthContext(args, deps) {
15965
15907
  apiKeyFlag: args.apiKey,
15966
15908
  accessTokenFlag: args.accessToken,
15967
15909
  baseUrl: args.baseUrl,
15968
- siteUrl: args.siteUrl,
15969
15910
  interactive: deps.isInteractive(),
15970
15911
  autoLoginIfNeeded: true,
15971
15912
  writeProgress: args.json ? deps.writeStderr : deps.writeStdout,
@@ -15987,7 +15928,6 @@ async function buildCloudAuthContext(args, deps) {
15987
15928
  return {
15988
15929
  token: ensured.token,
15989
15930
  baseUrl: ensured.baseUrl,
15990
- siteUrl: ensured.siteUrl,
15991
15931
  authScheme: ensured.authScheme,
15992
15932
  kind: ensured.kind,
15993
15933
  source: ensured.source