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.
package/README.md CHANGED
@@ -182,11 +182,10 @@ opensteer auth logout
182
182
  `--no-browser` on remote shells, containers, or CI and paste the printed URL
183
183
  into a browser manually. In `--json` mode, login prompts go to stderr and the
184
184
  final JSON result stays on stdout.
185
- - Saved machine logins remain scoped per resolved cloud host (`baseUrl` +
186
- `siteUrl`). The CLI also remembers the last selected cloud host, so
187
- `opensteer auth status`, `opensteer auth logout`, and other cloud commands
188
- reuse it by default unless `--base-url`, `--site-url`, or env vars select a
189
- different host.
185
+ - Saved machine logins remain scoped per resolved cloud API host (`baseUrl`).
186
+ The CLI also remembers the last selected cloud host, so `opensteer auth
187
+ status`, `opensteer auth logout`, and other cloud commands reuse it by
188
+ default unless `--base-url` or env vars select a different host.
190
189
 
191
190
  - `OPENSTEER_BASE_URL` overrides the default cloud host
192
191
  - `OPENSTEER_ACCESS_TOKEN` provides bearer auth for cloud commands
package/bin/opensteer.mjs CHANGED
@@ -1149,7 +1149,6 @@ Environment:
1149
1149
  OPENSTEER_API_KEY Cloud API key credential
1150
1150
  OPENSTEER_ACCESS_TOKEN Cloud bearer access token credential
1151
1151
  OPENSTEER_BASE_URL Override cloud control-plane base URL
1152
- OPENSTEER_CLOUD_SITE_URL Override cloud site URL for device login endpoints
1153
1152
  OPENSTEER_AUTH_SCHEME Cloud auth scheme: api-key (default) or bearer
1154
1153
  OPENSTEER_REMOTE_ANNOUNCE Cloud session announcement policy: always (default), off, tty
1155
1154
  `)
@@ -11,8 +11,9 @@ import {
11
11
  resolveCloudSelection,
12
12
  resolveConfigWithEnv,
13
13
  resolveNamespace,
14
- resolveNamespaceDir
15
- } from "./chunk-WDRMHPWL.js";
14
+ resolveNamespaceDir,
15
+ selectCloudCredential
16
+ } from "./chunk-KE35RQOJ.js";
16
17
  import {
17
18
  flattenExtractionDataToFieldPlan
18
19
  } from "./chunk-3H5RRIMZ.js";
@@ -10081,30 +10082,20 @@ var Opensteer = class _Opensteer {
10081
10082
  this.pool = new BrowserPool(resolved.browser || {});
10082
10083
  if (cloudSelection.cloud) {
10083
10084
  const cloudConfig = resolved.cloud && typeof resolved.cloud === "object" ? resolved.cloud : void 0;
10084
- const apiKey = cloudConfig?.apiKey?.trim();
10085
- const accessToken = cloudConfig?.accessToken?.trim();
10086
- if (apiKey && accessToken) {
10087
- throw new Error(
10088
- "Cloud mode cannot use both cloud.apiKey and cloud.accessToken. Set only one credential."
10089
- );
10090
- }
10091
- let credential = "";
10092
- let authScheme = cloudConfig?.authScheme ?? "api-key";
10093
- if (accessToken) {
10094
- credential = accessToken;
10095
- authScheme = "bearer";
10096
- } else if (apiKey) {
10097
- credential = apiKey;
10098
- }
10085
+ const credential = selectCloudCredential({
10086
+ apiKey: cloudConfig?.apiKey,
10087
+ accessToken: cloudConfig?.accessToken,
10088
+ authScheme: cloudConfig?.authScheme
10089
+ });
10099
10090
  if (!credential) {
10100
10091
  throw new Error(
10101
10092
  "Cloud mode requires credentials via cloud.apiKey/cloud.accessToken or OPENSTEER_API_KEY/OPENSTEER_ACCESS_TOKEN."
10102
10093
  );
10103
10094
  }
10104
10095
  this.cloud = createCloudRuntimeState(
10105
- credential,
10096
+ credential.token,
10106
10097
  cloudConfig?.baseUrl,
10107
- authScheme
10098
+ credential.authScheme
10108
10099
  );
10109
10100
  } else {
10110
10101
  this.cloud = null;
@@ -2,7 +2,7 @@ import {
2
2
  cloudAuthHeaders,
3
3
  normalizeCloudBaseUrl,
4
4
  parseCloudHttpError
5
- } from "./chunk-WDRMHPWL.js";
5
+ } from "./chunk-KE35RQOJ.js";
6
6
 
7
7
  // src/cloud/browser-profile-client.ts
8
8
  var BrowserProfileClient = class {
@@ -261,6 +261,67 @@ import fs from "fs";
261
261
  import path2 from "path";
262
262
  import { fileURLToPath } from "url";
263
263
  import { parse as parseDotenv } from "dotenv";
264
+
265
+ // src/cloud/credential-selection.ts
266
+ function selectCloudCredential(options) {
267
+ const apiKey = normalizeNonEmptyString(options.apiKey);
268
+ const accessToken = normalizeNonEmptyString(options.accessToken);
269
+ if (apiKey) {
270
+ if (options.authScheme === "bearer") {
271
+ return {
272
+ apiKey,
273
+ authScheme: "bearer",
274
+ kind: "access-token",
275
+ token: apiKey,
276
+ compatibilityBearerApiKey: true
277
+ };
278
+ }
279
+ return {
280
+ apiKey,
281
+ authScheme: "api-key",
282
+ kind: "api-key",
283
+ token: apiKey
284
+ };
285
+ }
286
+ if (accessToken) {
287
+ return {
288
+ accessToken,
289
+ authScheme: "bearer",
290
+ kind: "access-token",
291
+ token: accessToken
292
+ };
293
+ }
294
+ return null;
295
+ }
296
+ function selectCloudCredentialByPrecedence(layers, authScheme) {
297
+ for (const layer of layers) {
298
+ const hasApiKey = layer.hasApiKey ?? Object.prototype.hasOwnProperty.call(layer, "apiKey");
299
+ const hasAccessToken = layer.hasAccessToken ?? Object.prototype.hasOwnProperty.call(layer, "accessToken");
300
+ if (!hasApiKey && !hasAccessToken) {
301
+ continue;
302
+ }
303
+ return {
304
+ source: layer.source,
305
+ apiKey: layer.apiKey,
306
+ accessToken: layer.accessToken,
307
+ hasApiKey,
308
+ hasAccessToken,
309
+ credential: selectCloudCredential({
310
+ apiKey: layer.apiKey,
311
+ accessToken: layer.accessToken,
312
+ authScheme: layer.authScheme ?? authScheme
313
+ })
314
+ };
315
+ }
316
+ return null;
317
+ }
318
+ function normalizeNonEmptyString(value) {
319
+ if (typeof value !== "string") return void 0;
320
+ const normalized = value.trim();
321
+ return normalized.length ? normalized : void 0;
322
+ }
323
+
324
+ // src/config.ts
264
325
  var DEFAULT_CONFIG = {
265
326
  browser: {
266
327
  headless: false,
@@ -552,11 +613,6 @@ function normalizeCloudOptions(value) {
552
613
  }
553
614
  return value;
554
615
  }
555
- function normalizeNonEmptyString(value) {
556
- if (typeof value !== "string") return void 0;
557
- const normalized = value.trim();
558
- return normalized.length ? normalized : void 0;
559
- }
560
616
  function parseCloudEnabled(value, source) {
561
617
  if (value == null) return void 0;
562
618
  if (typeof value === "boolean") return value;
@@ -565,6 +621,18 @@ function parseCloudEnabled(value, source) {
565
621
  `Invalid ${source} value "${String(value)}". Use true, false, or a cloud options object.`
566
622
  );
567
623
  }
624
+ function resolveCloudCredentialFields(selectedLayer) {
625
+ const credential = selectedLayer?.credential;
626
+ if (!credential) return {};
627
+ if (credential.kind === "api-key" || credential.compatibilityBearerApiKey === true && selectedLayer?.source !== "env") {
628
+ return {
629
+ apiKey: credential.token
630
+ };
631
+ }
632
+ return {
633
+ accessToken: credential.token
634
+ };
635
+ }
568
636
  function resolveCloudSelection(config, env = process.env) {
569
637
  const configCloud = parseCloudEnabled(config.cloud, "cloud");
570
638
  if (configCloud !== void 0) {
@@ -601,6 +669,9 @@ function resolveConfigWithEnv(input = {}, options = {}) {
601
669
  });
602
670
  assertNoLegacyAiConfig(".opensteer/config.json", fileConfig);
603
671
  assertNoLegacyRuntimeConfig(".opensteer/config.json", fileConfig);
672
+ const fileCloudOptions = normalizeCloudOptions(fileConfig.cloud);
673
+ const fileHasCloudApiKey = hasOwn(fileCloudOptions, "apiKey");
674
+ const fileHasCloudAccessToken = hasOwn(fileCloudOptions, "accessToken");
604
675
  const fileRootDir = typeof fileConfig.storage?.rootDir === "string" ? fileConfig.storage.rootDir : void 0;
605
676
  const envRootDir = input.storage?.rootDir ?? fileRootDir ?? initialRootDir;
606
677
  const env = resolveEnv(envRootDir, {
@@ -639,13 +710,6 @@ function resolveConfigWithEnv(input = {}, options = {}) {
639
710
  const envAccessTokenRaw = resolveOpensteerAccessToken(env);
640
711
  const envBaseUrl = resolveOpensteerBaseUrl(env);
641
712
  const envAuthScheme = resolveOpensteerAuthScheme(env);
642
- if (envApiKey && envAccessTokenRaw) {
643
- throw new Error(
644
- "OPENSTEER_API_KEY and OPENSTEER_ACCESS_TOKEN are mutually exclusive. Set only one."
645
- );
646
- }
647
- const envAccessToken = envAccessTokenRaw || (envAuthScheme === "bearer" ? envApiKey : void 0);
648
- const envApiCredential = envAuthScheme === "bearer" && !envAccessTokenRaw ? void 0 : envApiKey;
649
713
  const envCloudProfileId = resolveOpensteerCloudProfileId(env);
650
714
  const envCloudProfileReuseIfActive = resolveOpensteerCloudProfileReuseIfActive(env);
651
715
  const envCloudAnnounce = parseCloudAnnounce(
@@ -674,11 +738,6 @@ function resolveConfigWithEnv(input = {}, options = {}) {
674
738
  const inputHasCloudBaseUrl = Boolean(
675
739
  inputCloudOptions && Object.prototype.hasOwnProperty.call(inputCloudOptions, "baseUrl")
676
740
  );
677
- if (normalizeNonEmptyString(inputCloudOptions?.apiKey) && normalizeNonEmptyString(inputCloudOptions?.accessToken)) {
678
- throw new Error(
679
- "cloud.apiKey and cloud.accessToken are mutually exclusive. Set only one."
680
- );
681
- }
682
741
  const cloudSelection = resolveCloudSelection({
683
742
  cloud: resolved.cloud
684
743
  }, env);
@@ -689,11 +748,6 @@ function resolveConfigWithEnv(input = {}, options = {}) {
689
748
  accessToken: resolvedCloudAccessTokenRaw,
690
749
  ...resolvedCloudRest
691
750
  } = resolvedCloud;
692
- if (normalizeNonEmptyString(resolvedCloudApiKeyRaw) && normalizeNonEmptyString(resolvedCloudAccessTokenRaw)) {
693
- throw new Error(
694
- "Cloud config cannot include both apiKey and accessToken at the same time."
695
- );
696
- }
697
751
  const resolvedCloudBrowserProfile = normalizeCloudBrowserProfileOptions(
698
752
  resolvedCloud.browserProfile,
699
753
  "resolved.cloud.browserProfile"
@@ -705,25 +759,40 @@ function resolveConfigWithEnv(input = {}, options = {}) {
705
759
  const browserProfile = inputCloudBrowserProfile ?? envCloudBrowserProfile ?? resolvedCloudBrowserProfile;
706
760
  let authScheme = inputAuthScheme ?? envAuthScheme ?? parseAuthScheme(resolvedCloud.authScheme, "cloud.authScheme") ?? "api-key";
707
761
  const announce = inputCloudAnnounce ?? envCloudAnnounce ?? parseCloudAnnounce(resolvedCloud.announce, "cloud.announce") ?? "always";
708
- const credentialOverriddenByInput = inputHasCloudApiKey || inputHasCloudAccessToken;
709
- let apiKey = normalizeNonEmptyString(resolvedCloudApiKeyRaw);
710
- let accessToken = normalizeNonEmptyString(resolvedCloudAccessTokenRaw);
711
- if (!credentialOverriddenByInput) {
712
- if (envAccessToken) {
713
- accessToken = envAccessToken;
714
- apiKey = void 0;
715
- } else if (envApiCredential) {
716
- apiKey = envApiCredential;
717
- accessToken = void 0;
718
- }
719
- }
762
+ const selectedCredentialLayer = selectCloudCredentialByPrecedence(
763
+ [
764
+ {
765
+ source: "input",
766
+ apiKey: inputCloudOptions?.apiKey,
767
+ accessToken: inputCloudOptions?.accessToken,
768
+ hasApiKey: inputHasCloudApiKey,
769
+ hasAccessToken: inputHasCloudAccessToken
770
+ },
771
+ {
772
+ source: "env",
773
+ apiKey: envApiKey,
774
+ accessToken: envAccessTokenRaw,
775
+ hasApiKey: envApiKey !== void 0,
776
+ hasAccessToken: envAccessTokenRaw !== void 0
777
+ },
778
+ {
779
+ source: "file",
780
+ apiKey: fileCloudOptions?.apiKey,
781
+ accessToken: fileCloudOptions?.accessToken,
782
+ hasApiKey: fileHasCloudApiKey,
783
+ hasAccessToken: fileHasCloudAccessToken
784
+ }
785
+ ],
786
+ authScheme
787
+ );
788
+ const { apiKey, accessToken } = resolveCloudCredentialFields(selectedCredentialLayer);
720
789
  if (accessToken) {
721
790
  authScheme = "bearer";
722
791
  }
723
792
  resolved.cloud = {
724
793
  ...resolvedCloudRest,
725
- ...inputHasCloudApiKey ? { apiKey: resolvedCloudApiKeyRaw } : apiKey ? { apiKey } : {},
726
- ...inputHasCloudAccessToken ? { accessToken: resolvedCloudAccessTokenRaw } : accessToken ? { accessToken } : {},
794
+ ...apiKey ? { apiKey } : selectedCredentialLayer?.hasApiKey && !accessToken ? { apiKey: selectedCredentialLayer.apiKey } : {},
795
+ ...accessToken ? { accessToken } : selectedCredentialLayer?.hasAccessToken && !apiKey ? { accessToken: selectedCredentialLayer.accessToken } : {},
727
796
  authScheme,
728
797
  announce,
729
798
  ...browserProfile ? { browserProfile } : {}
@@ -1300,6 +1369,7 @@ export {
1300
1369
  createKeychainStore,
1301
1370
  extractErrorMessage,
1302
1371
  normalizeError,
1372
+ selectCloudCredential,
1303
1373
  normalizeNamespace,
1304
1374
  resolveNamespaceDir,
1305
1375
  resolveCloudSelection,