opensteer 0.4.7 → 0.4.8

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/CHANGELOG.md CHANGED
@@ -21,6 +21,8 @@
21
21
  - Breaking: `OPENSTEER_MODE` now uses `local` or `cloud`; `remote` is no longer a supported value.
22
22
  - Opensteer now enables built-in LLM resolve/extract by default with model `gpt-5.1`.
23
23
  - Cloud mode now falls back to `OPENSTEER_API_KEY` when `cloud.apiKey` is omitted.
24
+ - Added automatic `.env` loading from `storage.rootDir` (default `process.cwd()`) so constructor config can consume env vars without requiring `import 'dotenv/config'`.
25
+ - `.env` autoload follows common precedence (`.env.<NODE_ENV>.local`, `.env.local`, `.env.<NODE_ENV>`, `.env`) with `.env.local` skipped in `test`, does not overwrite existing env values, and can be disabled via `OPENSTEER_DISABLE_DOTENV_AUTOLOAD`.
24
26
  - Mutating actions now include smart best-effort post-action wait with per-action
25
27
  profiles and optional per-call overrides via `wait`.
26
28
  - Added structured interaction diagnostics via `OpensteerActionError` for
package/README.md CHANGED
@@ -164,6 +164,11 @@ Opensteer defaults to local mode.
164
164
  - `OPENSTEER_MODE=local` runs local Playwright.
165
165
  - `OPENSTEER_MODE=cloud` enables cloud mode (requires `OPENSTEER_API_KEY`).
166
166
  - `cloud: true` in constructor config always enables cloud mode.
167
+ - Opensteer auto-loads `.env` files from your `storage.rootDir` (default:
168
+ `process.cwd()`) using this order: `.env.<NODE_ENV>.local`, `.env.local`
169
+ (skipped when `NODE_ENV=test`), `.env.<NODE_ENV>`, `.env`.
170
+ - Existing `process.env` values are never overwritten by `.env` values.
171
+ - Set `OPENSTEER_DISABLE_DOTENV_AUTOLOAD=true` to disable auto-loading.
167
172
 
168
173
  Cloud mode is fail-fast: it does not automatically fall back to local mode.
169
174
 
@@ -5981,6 +5981,7 @@ var BrowserPool = class {
5981
5981
  import fs3 from "fs";
5982
5982
  import path4 from "path";
5983
5983
  import { fileURLToPath } from "url";
5984
+ import { parse as parseDotenv } from "dotenv";
5984
5985
  var DEFAULT_CONFIG = {
5985
5986
  browser: {
5986
5987
  headless: false,
@@ -5996,6 +5997,53 @@ var DEFAULT_CONFIG = {
5996
5997
  model: "gpt-5.1",
5997
5998
  debug: false
5998
5999
  };
6000
+ function dotenvFileOrder(nodeEnv) {
6001
+ const normalized = nodeEnv?.trim() || "";
6002
+ const files = [];
6003
+ if (normalized) {
6004
+ files.push(`.env.${normalized}.local`);
6005
+ }
6006
+ if (normalized !== "test") {
6007
+ files.push(".env.local");
6008
+ }
6009
+ if (normalized) {
6010
+ files.push(`.env.${normalized}`);
6011
+ }
6012
+ files.push(".env");
6013
+ return files;
6014
+ }
6015
+ function loadDotenvValues(rootDir, baseEnv) {
6016
+ const values = {};
6017
+ if (parseBool(baseEnv.OPENSTEER_DISABLE_DOTENV_AUTOLOAD) === true) {
6018
+ return values;
6019
+ }
6020
+ const baseDir = path4.resolve(rootDir);
6021
+ const nodeEnv = baseEnv.NODE_ENV?.trim() || "";
6022
+ for (const filename of dotenvFileOrder(nodeEnv)) {
6023
+ const filePath = path4.join(baseDir, filename);
6024
+ if (!fs3.existsSync(filePath)) continue;
6025
+ try {
6026
+ const raw = fs3.readFileSync(filePath, "utf8");
6027
+ const parsed = parseDotenv(raw);
6028
+ for (const [key, value] of Object.entries(parsed)) {
6029
+ if (values[key] === void 0) {
6030
+ values[key] = value;
6031
+ }
6032
+ }
6033
+ } catch {
6034
+ continue;
6035
+ }
6036
+ }
6037
+ return values;
6038
+ }
6039
+ function resolveEnv(rootDir) {
6040
+ const baseEnv = process.env;
6041
+ const dotenvValues = loadDotenvValues(rootDir, baseEnv);
6042
+ return {
6043
+ ...dotenvValues,
6044
+ ...baseEnv
6045
+ };
6046
+ }
5999
6047
  function hasOwn(config, key) {
6000
6048
  if (!config || typeof config !== "object") return false;
6001
6049
  return Object.prototype.hasOwnProperty.call(config, key);
@@ -6124,16 +6172,13 @@ function parseCloudAnnounce(value, source) {
6124
6172
  `Invalid ${source} value "${value}". Use "always", "off", or "tty".`
6125
6173
  );
6126
6174
  }
6127
- function resolveOpensteerApiKey() {
6128
- const value = process.env.OPENSTEER_API_KEY?.trim();
6175
+ function resolveOpensteerApiKey(env) {
6176
+ const value = env.OPENSTEER_API_KEY?.trim();
6129
6177
  if (!value) return void 0;
6130
6178
  return value;
6131
6179
  }
6132
- function resolveOpensteerAuthScheme() {
6133
- return parseAuthScheme(
6134
- process.env.OPENSTEER_AUTH_SCHEME,
6135
- "OPENSTEER_AUTH_SCHEME"
6136
- );
6180
+ function resolveOpensteerAuthScheme(env) {
6181
+ return parseAuthScheme(env.OPENSTEER_AUTH_SCHEME, "OPENSTEER_AUTH_SCHEME");
6137
6182
  }
6138
6183
  function normalizeCloudOptions(value) {
6139
6184
  if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -6149,7 +6194,7 @@ function parseCloudEnabled(value, source) {
6149
6194
  `Invalid ${source} value "${String(value)}". Use true, false, or a cloud options object.`
6150
6195
  );
6151
6196
  }
6152
- function resolveCloudSelection(config) {
6197
+ function resolveCloudSelection(config, env = process.env) {
6153
6198
  const configCloud = parseCloudEnabled(config.cloud, "cloud");
6154
6199
  if (configCloud !== void 0) {
6155
6200
  return {
@@ -6157,10 +6202,7 @@ function resolveCloudSelection(config) {
6157
6202
  source: "config.cloud"
6158
6203
  };
6159
6204
  }
6160
- const envMode = parseRuntimeMode(
6161
- process.env.OPENSTEER_MODE,
6162
- "OPENSTEER_MODE"
6163
- );
6205
+ const envMode = parseRuntimeMode(env.OPENSTEER_MODE, "OPENSTEER_MODE");
6164
6206
  if (envMode) {
6165
6207
  return {
6166
6208
  cloud: envMode === "cloud",
@@ -6173,41 +6215,49 @@ function resolveCloudSelection(config) {
6173
6215
  };
6174
6216
  }
6175
6217
  function resolveConfig(input = {}) {
6176
- if (process.env.OPENSTEER_AI_MODEL) {
6218
+ const initialRootDir = input.storage?.rootDir ?? process.cwd();
6219
+ const runtimeDefaults = mergeDeep(DEFAULT_CONFIG, {
6220
+ storage: {
6221
+ rootDir: initialRootDir
6222
+ }
6223
+ });
6224
+ assertNoLegacyAiConfig("Opensteer constructor config", input);
6225
+ assertNoLegacyRuntimeConfig("Opensteer constructor config", input);
6226
+ const fileConfig = loadConfigFile(initialRootDir);
6227
+ assertNoLegacyAiConfig(".opensteer/config.json", fileConfig);
6228
+ assertNoLegacyRuntimeConfig(".opensteer/config.json", fileConfig);
6229
+ const fileRootDir = typeof fileConfig.storage?.rootDir === "string" ? fileConfig.storage.rootDir : void 0;
6230
+ const envRootDir = input.storage?.rootDir ?? fileRootDir ?? initialRootDir;
6231
+ const env = resolveEnv(envRootDir);
6232
+ if (env.OPENSTEER_AI_MODEL) {
6177
6233
  throw new Error(
6178
6234
  "OPENSTEER_AI_MODEL is no longer supported. Use OPENSTEER_MODEL instead."
6179
6235
  );
6180
6236
  }
6181
- if (process.env.OPENSTEER_RUNTIME != null) {
6237
+ if (env.OPENSTEER_RUNTIME != null) {
6182
6238
  throw new Error(
6183
6239
  "OPENSTEER_RUNTIME is no longer supported. Use OPENSTEER_MODE instead."
6184
6240
  );
6185
6241
  }
6186
- assertNoLegacyAiConfig("Opensteer constructor config", input);
6187
- assertNoLegacyRuntimeConfig("Opensteer constructor config", input);
6188
- const rootDir = input.storage?.rootDir ?? DEFAULT_CONFIG.storage.rootDir ?? process.cwd();
6189
- const fileConfig = loadConfigFile(rootDir);
6190
- assertNoLegacyAiConfig(".opensteer/config.json", fileConfig);
6191
- assertNoLegacyRuntimeConfig(".opensteer/config.json", fileConfig);
6192
6242
  const envConfig = {
6193
6243
  browser: {
6194
- headless: parseBool(process.env.OPENSTEER_HEADLESS),
6195
- executablePath: process.env.OPENSTEER_BROWSER_PATH || void 0,
6196
- slowMo: parseNumber(process.env.OPENSTEER_SLOW_MO),
6197
- connectUrl: process.env.OPENSTEER_CONNECT_URL || void 0,
6198
- channel: process.env.OPENSTEER_CHANNEL || void 0,
6199
- profileDir: process.env.OPENSTEER_PROFILE_DIR || void 0
6244
+ headless: parseBool(env.OPENSTEER_HEADLESS),
6245
+ executablePath: env.OPENSTEER_BROWSER_PATH || void 0,
6246
+ slowMo: parseNumber(env.OPENSTEER_SLOW_MO),
6247
+ connectUrl: env.OPENSTEER_CONNECT_URL || void 0,
6248
+ channel: env.OPENSTEER_CHANNEL || void 0,
6249
+ profileDir: env.OPENSTEER_PROFILE_DIR || void 0
6200
6250
  },
6201
- model: process.env.OPENSTEER_MODEL || void 0,
6202
- debug: parseBool(process.env.OPENSTEER_DEBUG)
6251
+ model: env.OPENSTEER_MODEL || void 0,
6252
+ debug: parseBool(env.OPENSTEER_DEBUG)
6203
6253
  };
6204
- const mergedWithFile = mergeDeep(DEFAULT_CONFIG, fileConfig);
6254
+ const mergedWithFile = mergeDeep(runtimeDefaults, fileConfig);
6205
6255
  const mergedWithEnv = mergeDeep(mergedWithFile, envConfig);
6206
6256
  const resolved = mergeDeep(mergedWithEnv, input);
6207
- const envApiKey = resolveOpensteerApiKey();
6208
- const envAuthScheme = resolveOpensteerAuthScheme();
6257
+ const envApiKey = resolveOpensteerApiKey(env);
6258
+ const envAuthScheme = resolveOpensteerAuthScheme(env);
6209
6259
  const envCloudAnnounce = parseCloudAnnounce(
6210
- process.env.OPENSTEER_REMOTE_ANNOUNCE,
6260
+ env.OPENSTEER_REMOTE_ANNOUNCE,
6211
6261
  "OPENSTEER_REMOTE_ANNOUNCE"
6212
6262
  );
6213
6263
  const inputCloudOptions = normalizeCloudOptions(input.cloud);
@@ -6224,7 +6274,7 @@ function resolveConfig(input = {}) {
6224
6274
  );
6225
6275
  const cloudSelection = resolveCloudSelection({
6226
6276
  cloud: resolved.cloud
6227
- });
6277
+ }, env);
6228
6278
  if (cloudSelection.cloud) {
6229
6279
  const resolvedCloud = normalizeCloudOptions(resolved.cloud) ?? {};
6230
6280
  const authScheme = inputAuthScheme ?? envAuthScheme ?? parseAuthScheme(resolvedCloud.authScheme, "cloud.authScheme") ?? "api-key";
@@ -806,6 +806,7 @@ var BrowserPool = class {
806
806
  var import_fs = __toESM(require("fs"), 1);
807
807
  var import_path3 = __toESM(require("path"), 1);
808
808
  var import_url = require("url");
809
+ var import_dotenv = require("dotenv");
809
810
 
810
811
  // src/storage/namespace.ts
811
812
  var import_path2 = __toESM(require("path"), 1);
@@ -855,6 +856,53 @@ var DEFAULT_CONFIG = {
855
856
  model: "gpt-5.1",
856
857
  debug: false
857
858
  };
859
+ function dotenvFileOrder(nodeEnv) {
860
+ const normalized = nodeEnv?.trim() || "";
861
+ const files = [];
862
+ if (normalized) {
863
+ files.push(`.env.${normalized}.local`);
864
+ }
865
+ if (normalized !== "test") {
866
+ files.push(".env.local");
867
+ }
868
+ if (normalized) {
869
+ files.push(`.env.${normalized}`);
870
+ }
871
+ files.push(".env");
872
+ return files;
873
+ }
874
+ function loadDotenvValues(rootDir, baseEnv) {
875
+ const values = {};
876
+ if (parseBool(baseEnv.OPENSTEER_DISABLE_DOTENV_AUTOLOAD) === true) {
877
+ return values;
878
+ }
879
+ const baseDir = import_path3.default.resolve(rootDir);
880
+ const nodeEnv = baseEnv.NODE_ENV?.trim() || "";
881
+ for (const filename of dotenvFileOrder(nodeEnv)) {
882
+ const filePath = import_path3.default.join(baseDir, filename);
883
+ if (!import_fs.default.existsSync(filePath)) continue;
884
+ try {
885
+ const raw = import_fs.default.readFileSync(filePath, "utf8");
886
+ const parsed = (0, import_dotenv.parse)(raw);
887
+ for (const [key, value] of Object.entries(parsed)) {
888
+ if (values[key] === void 0) {
889
+ values[key] = value;
890
+ }
891
+ }
892
+ } catch {
893
+ continue;
894
+ }
895
+ }
896
+ return values;
897
+ }
898
+ function resolveEnv(rootDir) {
899
+ const baseEnv = process.env;
900
+ const dotenvValues = loadDotenvValues(rootDir, baseEnv);
901
+ return {
902
+ ...dotenvValues,
903
+ ...baseEnv
904
+ };
905
+ }
858
906
  function hasOwn(config, key) {
859
907
  if (!config || typeof config !== "object") return false;
860
908
  return Object.prototype.hasOwnProperty.call(config, key);
@@ -983,16 +1031,13 @@ function parseCloudAnnounce(value, source) {
983
1031
  `Invalid ${source} value "${value}". Use "always", "off", or "tty".`
984
1032
  );
985
1033
  }
986
- function resolveOpensteerApiKey() {
987
- const value = process.env.OPENSTEER_API_KEY?.trim();
1034
+ function resolveOpensteerApiKey(env) {
1035
+ const value = env.OPENSTEER_API_KEY?.trim();
988
1036
  if (!value) return void 0;
989
1037
  return value;
990
1038
  }
991
- function resolveOpensteerAuthScheme() {
992
- return parseAuthScheme(
993
- process.env.OPENSTEER_AUTH_SCHEME,
994
- "OPENSTEER_AUTH_SCHEME"
995
- );
1039
+ function resolveOpensteerAuthScheme(env) {
1040
+ return parseAuthScheme(env.OPENSTEER_AUTH_SCHEME, "OPENSTEER_AUTH_SCHEME");
996
1041
  }
997
1042
  function normalizeCloudOptions(value) {
998
1043
  if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -1008,7 +1053,7 @@ function parseCloudEnabled(value, source) {
1008
1053
  `Invalid ${source} value "${String(value)}". Use true, false, or a cloud options object.`
1009
1054
  );
1010
1055
  }
1011
- function resolveCloudSelection(config) {
1056
+ function resolveCloudSelection(config, env = process.env) {
1012
1057
  const configCloud = parseCloudEnabled(config.cloud, "cloud");
1013
1058
  if (configCloud !== void 0) {
1014
1059
  return {
@@ -1016,10 +1061,7 @@ function resolveCloudSelection(config) {
1016
1061
  source: "config.cloud"
1017
1062
  };
1018
1063
  }
1019
- const envMode = parseRuntimeMode(
1020
- process.env.OPENSTEER_MODE,
1021
- "OPENSTEER_MODE"
1022
- );
1064
+ const envMode = parseRuntimeMode(env.OPENSTEER_MODE, "OPENSTEER_MODE");
1023
1065
  if (envMode) {
1024
1066
  return {
1025
1067
  cloud: envMode === "cloud",
@@ -1032,41 +1074,49 @@ function resolveCloudSelection(config) {
1032
1074
  };
1033
1075
  }
1034
1076
  function resolveConfig(input = {}) {
1035
- if (process.env.OPENSTEER_AI_MODEL) {
1077
+ const initialRootDir = input.storage?.rootDir ?? process.cwd();
1078
+ const runtimeDefaults = mergeDeep(DEFAULT_CONFIG, {
1079
+ storage: {
1080
+ rootDir: initialRootDir
1081
+ }
1082
+ });
1083
+ assertNoLegacyAiConfig("Opensteer constructor config", input);
1084
+ assertNoLegacyRuntimeConfig("Opensteer constructor config", input);
1085
+ const fileConfig = loadConfigFile(initialRootDir);
1086
+ assertNoLegacyAiConfig(".opensteer/config.json", fileConfig);
1087
+ assertNoLegacyRuntimeConfig(".opensteer/config.json", fileConfig);
1088
+ const fileRootDir = typeof fileConfig.storage?.rootDir === "string" ? fileConfig.storage.rootDir : void 0;
1089
+ const envRootDir = input.storage?.rootDir ?? fileRootDir ?? initialRootDir;
1090
+ const env = resolveEnv(envRootDir);
1091
+ if (env.OPENSTEER_AI_MODEL) {
1036
1092
  throw new Error(
1037
1093
  "OPENSTEER_AI_MODEL is no longer supported. Use OPENSTEER_MODEL instead."
1038
1094
  );
1039
1095
  }
1040
- if (process.env.OPENSTEER_RUNTIME != null) {
1096
+ if (env.OPENSTEER_RUNTIME != null) {
1041
1097
  throw new Error(
1042
1098
  "OPENSTEER_RUNTIME is no longer supported. Use OPENSTEER_MODE instead."
1043
1099
  );
1044
1100
  }
1045
- assertNoLegacyAiConfig("Opensteer constructor config", input);
1046
- assertNoLegacyRuntimeConfig("Opensteer constructor config", input);
1047
- const rootDir = input.storage?.rootDir ?? DEFAULT_CONFIG.storage.rootDir ?? process.cwd();
1048
- const fileConfig = loadConfigFile(rootDir);
1049
- assertNoLegacyAiConfig(".opensteer/config.json", fileConfig);
1050
- assertNoLegacyRuntimeConfig(".opensteer/config.json", fileConfig);
1051
1101
  const envConfig = {
1052
1102
  browser: {
1053
- headless: parseBool(process.env.OPENSTEER_HEADLESS),
1054
- executablePath: process.env.OPENSTEER_BROWSER_PATH || void 0,
1055
- slowMo: parseNumber(process.env.OPENSTEER_SLOW_MO),
1056
- connectUrl: process.env.OPENSTEER_CONNECT_URL || void 0,
1057
- channel: process.env.OPENSTEER_CHANNEL || void 0,
1058
- profileDir: process.env.OPENSTEER_PROFILE_DIR || void 0
1103
+ headless: parseBool(env.OPENSTEER_HEADLESS),
1104
+ executablePath: env.OPENSTEER_BROWSER_PATH || void 0,
1105
+ slowMo: parseNumber(env.OPENSTEER_SLOW_MO),
1106
+ connectUrl: env.OPENSTEER_CONNECT_URL || void 0,
1107
+ channel: env.OPENSTEER_CHANNEL || void 0,
1108
+ profileDir: env.OPENSTEER_PROFILE_DIR || void 0
1059
1109
  },
1060
- model: process.env.OPENSTEER_MODEL || void 0,
1061
- debug: parseBool(process.env.OPENSTEER_DEBUG)
1110
+ model: env.OPENSTEER_MODEL || void 0,
1111
+ debug: parseBool(env.OPENSTEER_DEBUG)
1062
1112
  };
1063
- const mergedWithFile = mergeDeep(DEFAULT_CONFIG, fileConfig);
1113
+ const mergedWithFile = mergeDeep(runtimeDefaults, fileConfig);
1064
1114
  const mergedWithEnv = mergeDeep(mergedWithFile, envConfig);
1065
1115
  const resolved = mergeDeep(mergedWithEnv, input);
1066
- const envApiKey = resolveOpensteerApiKey();
1067
- const envAuthScheme = resolveOpensteerAuthScheme();
1116
+ const envApiKey = resolveOpensteerApiKey(env);
1117
+ const envAuthScheme = resolveOpensteerAuthScheme(env);
1068
1118
  const envCloudAnnounce = parseCloudAnnounce(
1069
- process.env.OPENSTEER_REMOTE_ANNOUNCE,
1119
+ env.OPENSTEER_REMOTE_ANNOUNCE,
1070
1120
  "OPENSTEER_REMOTE_ANNOUNCE"
1071
1121
  );
1072
1122
  const inputCloudOptions = normalizeCloudOptions(input.cloud);
@@ -1083,7 +1133,7 @@ function resolveConfig(input = {}) {
1083
1133
  );
1084
1134
  const cloudSelection = resolveCloudSelection({
1085
1135
  cloud: resolved.cloud
1086
- });
1136
+ }, env);
1087
1137
  if (cloudSelection.cloud) {
1088
1138
  const resolvedCloud = normalizeCloudOptions(resolved.cloud) ?? {};
1089
1139
  const authScheme = inputAuthScheme ?? envAuthScheme ?? parseAuthScheme(resolvedCloud.authScheme, "cloud.authScheme") ?? "api-key";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Opensteer
3
- } from "../chunk-PIJI7FBH.js";
3
+ } from "../chunk-GQ7HNCM2.js";
4
4
  import "../chunk-3H5RRIMZ.js";
5
5
 
6
6
  // src/cli/server.ts
package/dist/index.cjs CHANGED
@@ -881,6 +881,7 @@ var BrowserPool = class {
881
881
  var import_fs = __toESM(require("fs"), 1);
882
882
  var import_path3 = __toESM(require("path"), 1);
883
883
  var import_url = require("url");
884
+ var import_dotenv = require("dotenv");
884
885
 
885
886
  // src/storage/namespace.ts
886
887
  var import_path2 = __toESM(require("path"), 1);
@@ -930,6 +931,53 @@ var DEFAULT_CONFIG = {
930
931
  model: "gpt-5.1",
931
932
  debug: false
932
933
  };
934
+ function dotenvFileOrder(nodeEnv) {
935
+ const normalized = nodeEnv?.trim() || "";
936
+ const files = [];
937
+ if (normalized) {
938
+ files.push(`.env.${normalized}.local`);
939
+ }
940
+ if (normalized !== "test") {
941
+ files.push(".env.local");
942
+ }
943
+ if (normalized) {
944
+ files.push(`.env.${normalized}`);
945
+ }
946
+ files.push(".env");
947
+ return files;
948
+ }
949
+ function loadDotenvValues(rootDir, baseEnv) {
950
+ const values = {};
951
+ if (parseBool(baseEnv.OPENSTEER_DISABLE_DOTENV_AUTOLOAD) === true) {
952
+ return values;
953
+ }
954
+ const baseDir = import_path3.default.resolve(rootDir);
955
+ const nodeEnv = baseEnv.NODE_ENV?.trim() || "";
956
+ for (const filename of dotenvFileOrder(nodeEnv)) {
957
+ const filePath = import_path3.default.join(baseDir, filename);
958
+ if (!import_fs.default.existsSync(filePath)) continue;
959
+ try {
960
+ const raw = import_fs.default.readFileSync(filePath, "utf8");
961
+ const parsed = (0, import_dotenv.parse)(raw);
962
+ for (const [key, value] of Object.entries(parsed)) {
963
+ if (values[key] === void 0) {
964
+ values[key] = value;
965
+ }
966
+ }
967
+ } catch {
968
+ continue;
969
+ }
970
+ }
971
+ return values;
972
+ }
973
+ function resolveEnv(rootDir) {
974
+ const baseEnv = process.env;
975
+ const dotenvValues = loadDotenvValues(rootDir, baseEnv);
976
+ return {
977
+ ...dotenvValues,
978
+ ...baseEnv
979
+ };
980
+ }
933
981
  function hasOwn(config, key) {
934
982
  if (!config || typeof config !== "object") return false;
935
983
  return Object.prototype.hasOwnProperty.call(config, key);
@@ -1058,16 +1106,13 @@ function parseCloudAnnounce(value, source) {
1058
1106
  `Invalid ${source} value "${value}". Use "always", "off", or "tty".`
1059
1107
  );
1060
1108
  }
1061
- function resolveOpensteerApiKey() {
1062
- const value = process.env.OPENSTEER_API_KEY?.trim();
1109
+ function resolveOpensteerApiKey(env) {
1110
+ const value = env.OPENSTEER_API_KEY?.trim();
1063
1111
  if (!value) return void 0;
1064
1112
  return value;
1065
1113
  }
1066
- function resolveOpensteerAuthScheme() {
1067
- return parseAuthScheme(
1068
- process.env.OPENSTEER_AUTH_SCHEME,
1069
- "OPENSTEER_AUTH_SCHEME"
1070
- );
1114
+ function resolveOpensteerAuthScheme(env) {
1115
+ return parseAuthScheme(env.OPENSTEER_AUTH_SCHEME, "OPENSTEER_AUTH_SCHEME");
1071
1116
  }
1072
1117
  function normalizeCloudOptions(value) {
1073
1118
  if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -1083,7 +1128,7 @@ function parseCloudEnabled(value, source) {
1083
1128
  `Invalid ${source} value "${String(value)}". Use true, false, or a cloud options object.`
1084
1129
  );
1085
1130
  }
1086
- function resolveCloudSelection(config) {
1131
+ function resolveCloudSelection(config, env = process.env) {
1087
1132
  const configCloud = parseCloudEnabled(config.cloud, "cloud");
1088
1133
  if (configCloud !== void 0) {
1089
1134
  return {
@@ -1091,10 +1136,7 @@ function resolveCloudSelection(config) {
1091
1136
  source: "config.cloud"
1092
1137
  };
1093
1138
  }
1094
- const envMode = parseRuntimeMode(
1095
- process.env.OPENSTEER_MODE,
1096
- "OPENSTEER_MODE"
1097
- );
1139
+ const envMode = parseRuntimeMode(env.OPENSTEER_MODE, "OPENSTEER_MODE");
1098
1140
  if (envMode) {
1099
1141
  return {
1100
1142
  cloud: envMode === "cloud",
@@ -1107,41 +1149,49 @@ function resolveCloudSelection(config) {
1107
1149
  };
1108
1150
  }
1109
1151
  function resolveConfig(input = {}) {
1110
- if (process.env.OPENSTEER_AI_MODEL) {
1152
+ const initialRootDir = input.storage?.rootDir ?? process.cwd();
1153
+ const runtimeDefaults = mergeDeep(DEFAULT_CONFIG, {
1154
+ storage: {
1155
+ rootDir: initialRootDir
1156
+ }
1157
+ });
1158
+ assertNoLegacyAiConfig("Opensteer constructor config", input);
1159
+ assertNoLegacyRuntimeConfig("Opensteer constructor config", input);
1160
+ const fileConfig = loadConfigFile(initialRootDir);
1161
+ assertNoLegacyAiConfig(".opensteer/config.json", fileConfig);
1162
+ assertNoLegacyRuntimeConfig(".opensteer/config.json", fileConfig);
1163
+ const fileRootDir = typeof fileConfig.storage?.rootDir === "string" ? fileConfig.storage.rootDir : void 0;
1164
+ const envRootDir = input.storage?.rootDir ?? fileRootDir ?? initialRootDir;
1165
+ const env = resolveEnv(envRootDir);
1166
+ if (env.OPENSTEER_AI_MODEL) {
1111
1167
  throw new Error(
1112
1168
  "OPENSTEER_AI_MODEL is no longer supported. Use OPENSTEER_MODEL instead."
1113
1169
  );
1114
1170
  }
1115
- if (process.env.OPENSTEER_RUNTIME != null) {
1171
+ if (env.OPENSTEER_RUNTIME != null) {
1116
1172
  throw new Error(
1117
1173
  "OPENSTEER_RUNTIME is no longer supported. Use OPENSTEER_MODE instead."
1118
1174
  );
1119
1175
  }
1120
- assertNoLegacyAiConfig("Opensteer constructor config", input);
1121
- assertNoLegacyRuntimeConfig("Opensteer constructor config", input);
1122
- const rootDir = input.storage?.rootDir ?? DEFAULT_CONFIG.storage.rootDir ?? process.cwd();
1123
- const fileConfig = loadConfigFile(rootDir);
1124
- assertNoLegacyAiConfig(".opensteer/config.json", fileConfig);
1125
- assertNoLegacyRuntimeConfig(".opensteer/config.json", fileConfig);
1126
1176
  const envConfig = {
1127
1177
  browser: {
1128
- headless: parseBool(process.env.OPENSTEER_HEADLESS),
1129
- executablePath: process.env.OPENSTEER_BROWSER_PATH || void 0,
1130
- slowMo: parseNumber(process.env.OPENSTEER_SLOW_MO),
1131
- connectUrl: process.env.OPENSTEER_CONNECT_URL || void 0,
1132
- channel: process.env.OPENSTEER_CHANNEL || void 0,
1133
- profileDir: process.env.OPENSTEER_PROFILE_DIR || void 0
1178
+ headless: parseBool(env.OPENSTEER_HEADLESS),
1179
+ executablePath: env.OPENSTEER_BROWSER_PATH || void 0,
1180
+ slowMo: parseNumber(env.OPENSTEER_SLOW_MO),
1181
+ connectUrl: env.OPENSTEER_CONNECT_URL || void 0,
1182
+ channel: env.OPENSTEER_CHANNEL || void 0,
1183
+ profileDir: env.OPENSTEER_PROFILE_DIR || void 0
1134
1184
  },
1135
- model: process.env.OPENSTEER_MODEL || void 0,
1136
- debug: parseBool(process.env.OPENSTEER_DEBUG)
1185
+ model: env.OPENSTEER_MODEL || void 0,
1186
+ debug: parseBool(env.OPENSTEER_DEBUG)
1137
1187
  };
1138
- const mergedWithFile = mergeDeep(DEFAULT_CONFIG, fileConfig);
1188
+ const mergedWithFile = mergeDeep(runtimeDefaults, fileConfig);
1139
1189
  const mergedWithEnv = mergeDeep(mergedWithFile, envConfig);
1140
1190
  const resolved = mergeDeep(mergedWithEnv, input);
1141
- const envApiKey = resolveOpensteerApiKey();
1142
- const envAuthScheme = resolveOpensteerAuthScheme();
1191
+ const envApiKey = resolveOpensteerApiKey(env);
1192
+ const envAuthScheme = resolveOpensteerAuthScheme(env);
1143
1193
  const envCloudAnnounce = parseCloudAnnounce(
1144
- process.env.OPENSTEER_REMOTE_ANNOUNCE,
1194
+ env.OPENSTEER_REMOTE_ANNOUNCE,
1145
1195
  "OPENSTEER_REMOTE_ANNOUNCE"
1146
1196
  );
1147
1197
  const inputCloudOptions = normalizeCloudOptions(input.cloud);
@@ -1158,7 +1208,7 @@ function resolveConfig(input = {}) {
1158
1208
  );
1159
1209
  const cloudSelection = resolveCloudSelection({
1160
1210
  cloud: resolved.cloud
1161
- });
1211
+ }, env);
1162
1212
  if (cloudSelection.cloud) {
1163
1213
  const resolvedCloud = normalizeCloudOptions(resolved.cloud) ?? {};
1164
1214
  const authScheme = inputAuthScheme ?? envAuthScheme ?? parseAuthScheme(resolvedCloud.authScheme, "cloud.authScheme") ?? "api-key";
package/dist/index.js CHANGED
@@ -68,7 +68,7 @@ import {
68
68
  switchTab,
69
69
  typeText,
70
70
  waitForVisualStability
71
- } from "./chunk-PIJI7FBH.js";
71
+ } from "./chunk-GQ7HNCM2.js";
72
72
  import {
73
73
  createResolveCallback
74
74
  } from "./chunk-SRJLH34D.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opensteer",
3
- "version": "0.4.7",
3
+ "version": "0.4.8",
4
4
  "description": "Open-source browser automation SDK with robust selectors and deterministic replay.",
5
5
  "license": "MIT",
6
6
  "type": "module",