opensteer 0.5.4 → 0.5.6

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/dist/index.cjs CHANGED
@@ -118,7 +118,7 @@ function resolveProviderInfo(modelStr) {
118
118
  );
119
119
  }
120
120
  }
121
- return { pkg: "@ai-sdk/openai", providerFn: "openai" };
121
+ return OPENAI_PROVIDER_INFO;
122
122
  }
123
123
  function stripProviderPrefix(modelStr) {
124
124
  const slash = modelStr.indexOf("/");
@@ -129,42 +129,101 @@ function stripProviderPrefix(modelStr) {
129
129
  }
130
130
  return modelStr;
131
131
  }
132
- async function getModelProvider(modelStr) {
133
- const { pkg, providerFn } = resolveProviderInfo(modelStr);
132
+ function normalizeEnvValue(value) {
133
+ if (typeof value !== "string") return void 0;
134
+ const trimmed = value.trim();
135
+ return trimmed.length ? trimmed : void 0;
136
+ }
137
+ function buildFactoryOptions(provider, env) {
138
+ const apiKey = normalizeEnvValue(env[provider.apiKeyEnvVar]);
139
+ if (!apiKey) {
140
+ throw new Error(
141
+ `API key is missing in the resolved Opensteer environment. Set ${provider.apiKeyEnvVar} in your runtime environment or .env file under storage.rootDir.`
142
+ );
143
+ }
144
+ const baseURL = provider.baseUrlEnvVar ? normalizeEnvValue(env[provider.baseUrlEnvVar]) : void 0;
145
+ return {
146
+ apiKey,
147
+ ...baseURL ? { baseURL } : {}
148
+ };
149
+ }
150
+ async function getModelProvider(modelStr, options = {}) {
151
+ const info = resolveProviderInfo(modelStr);
134
152
  let mod;
135
153
  try {
136
- mod = await import(pkg);
154
+ mod = await import(info.pkg);
137
155
  } catch {
138
156
  throw new Error(
139
- `To use AI resolution with model '${modelStr}', install 'ai' and '${pkg}' with your package manager.`
157
+ `To use AI resolution with model '${modelStr}', install 'ai' and '${info.pkg}' with your package manager.`
140
158
  );
141
159
  }
142
- const provider = mod[providerFn];
143
- if (typeof provider !== "function") {
160
+ const providerExportName = options.env ? info.factoryFn : info.providerFn;
161
+ const providerExport = mod[providerExportName];
162
+ if (typeof providerExport !== "function") {
144
163
  throw new Error(
145
- `Provider '${providerFn}' not found in '${pkg}'. Ensure you have the latest version installed.`
164
+ `Provider '${providerExportName}' not found in '${info.pkg}'. Ensure you have the latest version installed.`
146
165
  );
147
166
  }
148
167
  const modelId = stripProviderPrefix(modelStr);
168
+ const provider = options.env != null ? providerExport(
169
+ buildFactoryOptions(info, options.env)
170
+ ) : providerExport;
171
+ if (typeof provider !== "function") {
172
+ throw new Error(
173
+ `Provider '${providerExportName}' from '${info.pkg}' did not return a model factory function.`
174
+ );
175
+ }
149
176
  return provider(modelId);
150
177
  }
151
- var PROVIDER_MAP;
178
+ var OPENAI_PROVIDER_INFO, ANTHROPIC_PROVIDER_INFO, GOOGLE_PROVIDER_INFO, XAI_PROVIDER_INFO, GROQ_PROVIDER_INFO, PROVIDER_MAP;
152
179
  var init_model = __esm({
153
180
  "src/ai/model.ts"() {
154
181
  "use strict";
182
+ OPENAI_PROVIDER_INFO = {
183
+ pkg: "@ai-sdk/openai",
184
+ providerFn: "openai",
185
+ factoryFn: "createOpenAI",
186
+ apiKeyEnvVar: "OPENAI_API_KEY",
187
+ baseUrlEnvVar: "OPENAI_BASE_URL"
188
+ };
189
+ ANTHROPIC_PROVIDER_INFO = {
190
+ pkg: "@ai-sdk/anthropic",
191
+ providerFn: "anthropic",
192
+ factoryFn: "createAnthropic",
193
+ apiKeyEnvVar: "ANTHROPIC_API_KEY",
194
+ baseUrlEnvVar: "ANTHROPIC_BASE_URL"
195
+ };
196
+ GOOGLE_PROVIDER_INFO = {
197
+ pkg: "@ai-sdk/google",
198
+ providerFn: "google",
199
+ factoryFn: "createGoogleGenerativeAI",
200
+ apiKeyEnvVar: "GOOGLE_GENERATIVE_AI_API_KEY"
201
+ };
202
+ XAI_PROVIDER_INFO = {
203
+ pkg: "@ai-sdk/xai",
204
+ providerFn: "xai",
205
+ factoryFn: "createXai",
206
+ apiKeyEnvVar: "XAI_API_KEY"
207
+ };
208
+ GROQ_PROVIDER_INFO = {
209
+ pkg: "@ai-sdk/groq",
210
+ providerFn: "groq",
211
+ factoryFn: "createGroq",
212
+ apiKeyEnvVar: "GROQ_API_KEY"
213
+ };
155
214
  PROVIDER_MAP = {
156
- "openai/": { pkg: "@ai-sdk/openai", providerFn: "openai" },
157
- "anthropic/": { pkg: "@ai-sdk/anthropic", providerFn: "anthropic" },
158
- "google/": { pkg: "@ai-sdk/google", providerFn: "google" },
159
- "xai/": { pkg: "@ai-sdk/xai", providerFn: "xai" },
160
- "gpt-": { pkg: "@ai-sdk/openai", providerFn: "openai" },
161
- "o1-": { pkg: "@ai-sdk/openai", providerFn: "openai" },
162
- "o3-": { pkg: "@ai-sdk/openai", providerFn: "openai" },
163
- "o4-": { pkg: "@ai-sdk/openai", providerFn: "openai" },
164
- "claude-": { pkg: "@ai-sdk/anthropic", providerFn: "anthropic" },
165
- "gemini-": { pkg: "@ai-sdk/google", providerFn: "google" },
166
- "grok-": { pkg: "@ai-sdk/xai", providerFn: "xai" },
167
- "groq/": { pkg: "@ai-sdk/groq", providerFn: "groq" }
215
+ "openai/": OPENAI_PROVIDER_INFO,
216
+ "anthropic/": ANTHROPIC_PROVIDER_INFO,
217
+ "google/": GOOGLE_PROVIDER_INFO,
218
+ "xai/": XAI_PROVIDER_INFO,
219
+ "gpt-": OPENAI_PROVIDER_INFO,
220
+ "o1-": OPENAI_PROVIDER_INFO,
221
+ "o3-": OPENAI_PROVIDER_INFO,
222
+ "o4-": OPENAI_PROVIDER_INFO,
223
+ "claude-": ANTHROPIC_PROVIDER_INFO,
224
+ "gemini-": GOOGLE_PROVIDER_INFO,
225
+ "grok-": XAI_PROVIDER_INFO,
226
+ "groq/": GROQ_PROVIDER_INFO
168
227
  };
169
228
  }
170
229
  });
@@ -245,6 +304,7 @@ __export(resolver_exports, {
245
304
  function createResolveCallback(model, options) {
246
305
  const temperature = options?.temperature ?? 1;
247
306
  const maxTokens = options?.maxTokens ?? null;
307
+ const env = options?.env;
248
308
  return async (args) => {
249
309
  let generateObject;
250
310
  let z;
@@ -263,7 +323,7 @@ function createResolveCallback(model, options) {
263
323
  `To use AI resolution with model '${model}', install 'zod' with your package manager.`
264
324
  );
265
325
  }
266
- const modelProvider = await getModelProvider(model);
326
+ const modelProvider = await getModelProvider(model, { env });
267
327
  const schema = z.object({
268
328
  element: z.number().describe(
269
329
  "Counter number of the matching element, or -1 if no match"
@@ -303,6 +363,7 @@ __export(extractor_exports, {
303
363
  function createExtractCallback(model, options) {
304
364
  const temperature = options?.temperature ?? 1;
305
365
  const maxTokens = options?.maxTokens ?? null;
366
+ const env = options?.env;
306
367
  return async (args) => {
307
368
  let generateText;
308
369
  try {
@@ -313,7 +374,7 @@ function createExtractCallback(model, options) {
313
374
  `To use AI extraction with model '${model}', install 'ai' with your package manager.`
314
375
  );
315
376
  }
316
- const modelProvider = await getModelProvider(model);
377
+ const modelProvider = await getModelProvider(model, { env });
317
378
  const request = {
318
379
  model: modelProvider,
319
380
  system: buildExtractSystemPrompt(),
@@ -384,6 +445,7 @@ __export(index_exports, {
384
445
  OpensteerAgentProviderError: () => OpensteerAgentProviderError,
385
446
  OpensteerCloudError: () => OpensteerCloudError,
386
447
  OpensteerCuaAgentHandler: () => OpensteerCuaAgentHandler,
448
+ buildArrayFieldPathCandidates: () => buildArrayFieldPathCandidates,
387
449
  buildElementPathFromHandle: () => buildElementPathFromHandle,
388
450
  buildElementPathFromSelector: () => buildElementPathFromSelector,
389
451
  buildPathSelectorHint: () => buildPathSelectorHint,
@@ -429,6 +491,7 @@ __export(index_exports, {
429
491
  performSelect: () => performSelect,
430
492
  prepareSnapshot: () => prepareSnapshot,
431
493
  pressKey: () => pressKey,
494
+ queryAllByElementPath: () => queryAllByElementPath,
432
495
  resolveAgentConfig: () => resolveAgentConfig,
433
496
  resolveCounterElement: () => resolveCounterElement,
434
497
  resolveCountersBatch: () => resolveCountersBatch,
@@ -1429,7 +1492,7 @@ function resolveCloudSelection(config, env = process.env) {
1429
1492
  source: "default"
1430
1493
  };
1431
1494
  }
1432
- function resolveConfig(input = {}) {
1495
+ function resolveConfigWithEnv(input = {}) {
1433
1496
  const processEnv = process.env;
1434
1497
  const debugHint = typeof input.debug === "boolean" ? input.debug : parseBool(processEnv.OPENSTEER_DEBUG) === true;
1435
1498
  const initialRootDir = input.storage?.rootDir ?? process.cwd();
@@ -1522,7 +1585,10 @@ function resolveConfig(input = {}) {
1522
1585
  baseUrl: envBaseUrl
1523
1586
  };
1524
1587
  }
1525
- return resolved;
1588
+ return {
1589
+ config: resolved,
1590
+ env
1591
+ };
1526
1592
  }
1527
1593
  function resolveNamespace(config, rootDir) {
1528
1594
  if (config.name && config.name.trim()) {
@@ -7127,7 +7193,9 @@ function minimizePathMatchClauses(path5, mode) {
7127
7193
  (clause) => clause?.kind === "position"
7128
7194
  );
7129
7195
  let keptPositions = [];
7130
- if (!attrClauses.length) {
7196
+ if (mode === "field") {
7197
+ keptPositions = pickMinimalPositionClauses(positionClauses);
7198
+ } else if (!attrClauses.length) {
7131
7199
  keptPositions = pickMinimalPositionClauses(positionClauses);
7132
7200
  } else if (mode === "item-root" && !isLast) {
7133
7201
  keptPositions = [];
@@ -7221,7 +7289,7 @@ function relaxPathForSingleSample(path5, mode) {
7221
7289
  const match = (node.match || []).filter((clause) => {
7222
7290
  if (!clause || typeof clause !== "object") return false;
7223
7291
  if (clause.kind === "position") {
7224
- if (mode === "field") return false;
7292
+ if (mode === "field") return true;
7225
7293
  return !isLast;
7226
7294
  }
7227
7295
  const key = String(clause.key || "").trim().toLowerCase();
@@ -7772,6 +7840,196 @@ function clonePersistedExtractNode(node) {
7772
7840
  return JSON.parse(JSON.stringify(node));
7773
7841
  }
7774
7842
 
7843
+ // src/extraction/array-field-validation.ts
7844
+ async function stripRedundantPositionClauses(payload, page) {
7845
+ const cloned = structuredClone(payload);
7846
+ await processObjectNode(cloned, page);
7847
+ return cloned;
7848
+ }
7849
+ async function processNode(node, page) {
7850
+ if (isPersistedArrayNode(node)) {
7851
+ await processArrayNode(node, page);
7852
+ return;
7853
+ }
7854
+ if (isPersistedObjectNode(node)) {
7855
+ await processObjectNode(node, page);
7856
+ }
7857
+ }
7858
+ async function processObjectNode(node, page) {
7859
+ for (const child of Object.values(node)) {
7860
+ await processNode(child, page);
7861
+ }
7862
+ }
7863
+ async function processArrayNode(node, page) {
7864
+ for (const variant of node.$array.variants) {
7865
+ try {
7866
+ await pruneVariantPositions(variant, page);
7867
+ } catch {
7868
+ }
7869
+ await processNode(variant.item, page);
7870
+ }
7871
+ }
7872
+ function collectValueNodes(node) {
7873
+ if (isPersistedValueNode(node)) {
7874
+ return [
7875
+ {
7876
+ path: node.$path,
7877
+ replacePath(path5) {
7878
+ node.$path = path5;
7879
+ }
7880
+ }
7881
+ ];
7882
+ }
7883
+ if (!isPersistedObjectNode(node)) return [];
7884
+ const refs = [];
7885
+ const visit = (current) => {
7886
+ for (const [key, child] of Object.entries(current)) {
7887
+ if (isPersistedValueNode(child)) {
7888
+ refs.push({
7889
+ path: child.$path,
7890
+ replacePath(path5) {
7891
+ const next = current[key];
7892
+ if (!isPersistedValueNode(next)) return;
7893
+ next.$path = path5;
7894
+ }
7895
+ });
7896
+ continue;
7897
+ }
7898
+ if (isPersistedObjectNode(child)) {
7899
+ visit(child);
7900
+ }
7901
+ }
7902
+ };
7903
+ visit(node);
7904
+ return refs;
7905
+ }
7906
+ function hasPositionClause(path5) {
7907
+ return path5.nodes.some(
7908
+ (node) => (node.match || []).some((clause) => clause.kind === "position")
7909
+ );
7910
+ }
7911
+ function areArraysEqual(left, right) {
7912
+ if (left.length !== right.length) return false;
7913
+ for (let i = 0; i < left.length; i++) {
7914
+ if (left[i] !== right[i]) return false;
7915
+ }
7916
+ return true;
7917
+ }
7918
+ async function pruneVariantPositions(variant, page) {
7919
+ const refs = collectValueNodes(variant.item).filter(
7920
+ (ref) => hasPositionClause(ref.path)
7921
+ );
7922
+ if (!refs.length) return;
7923
+ const plans = refs.map((ref, index) => {
7924
+ const withPos = buildArrayFieldPathCandidates(ref.path);
7925
+ const strippedPath = stripPositionClauses2(ref.path);
7926
+ const withoutPos = buildArrayFieldPathCandidates(strippedPath);
7927
+ if (areArraysEqual(withPos, withoutPos)) return null;
7928
+ const plan = {
7929
+ key: String(index),
7930
+ strippedPath,
7931
+ replacePath: ref.replacePath,
7932
+ selectors: {
7933
+ withPos,
7934
+ withoutPos
7935
+ }
7936
+ };
7937
+ return plan;
7938
+ }).filter((plan) => !!plan);
7939
+ if (!plans.length) return;
7940
+ const selectorMap = {};
7941
+ for (const plan of plans) {
7942
+ selectorMap[plan.key] = plan.selectors;
7943
+ }
7944
+ const validatedKeys = new Set(plans.map((plan) => plan.key));
7945
+ const items = await queryAllByElementPath(page, variant.itemParentPath);
7946
+ if (!items.length) return;
7947
+ try {
7948
+ for (const item of items) {
7949
+ const results = await validateRowSelectors(item, selectorMap);
7950
+ const failedKeys = [];
7951
+ for (const key of validatedKeys) {
7952
+ if (results[key] === true) continue;
7953
+ failedKeys.push(key);
7954
+ }
7955
+ for (const key of failedKeys) {
7956
+ validatedKeys.delete(key);
7957
+ }
7958
+ if (!validatedKeys.size) break;
7959
+ }
7960
+ } finally {
7961
+ await Promise.all(
7962
+ items.map(async (item) => {
7963
+ try {
7964
+ await item.dispose();
7965
+ } catch {
7966
+ }
7967
+ })
7968
+ );
7969
+ }
7970
+ for (const plan of plans) {
7971
+ if (!validatedKeys.has(plan.key)) continue;
7972
+ plan.replacePath(plan.strippedPath);
7973
+ }
7974
+ }
7975
+ async function validateRowSelectors(item, fields) {
7976
+ return item.evaluate((element, selectorMap) => {
7977
+ const tryFirst = (root, selectors) => {
7978
+ let fallback = null;
7979
+ for (const selector of selectors) {
7980
+ if (!selector) continue;
7981
+ let matches = [];
7982
+ try {
7983
+ matches = Array.from(root.querySelectorAll(selector));
7984
+ } catch {
7985
+ matches = [];
7986
+ }
7987
+ if (!matches.length) continue;
7988
+ if (matches.length === 1) {
7989
+ return matches[0];
7990
+ }
7991
+ if (!fallback) {
7992
+ fallback = matches[0];
7993
+ }
7994
+ }
7995
+ return fallback;
7996
+ };
7997
+ const out = {};
7998
+ for (const [key, selectors] of Object.entries(selectorMap)) {
7999
+ const original = tryFirst(element, selectors.withPos);
8000
+ if (!original) {
8001
+ out[key] = false;
8002
+ continue;
8003
+ }
8004
+ let strippedUnique = null;
8005
+ for (const selector of selectors.withoutPos) {
8006
+ if (!selector) continue;
8007
+ let matches = [];
8008
+ try {
8009
+ matches = Array.from(element.querySelectorAll(selector));
8010
+ } catch {
8011
+ matches = [];
8012
+ }
8013
+ if (matches.length === 1) {
8014
+ strippedUnique = matches[0];
8015
+ break;
8016
+ }
8017
+ }
8018
+ out[key] = strippedUnique === original;
8019
+ }
8020
+ return out;
8021
+ }, fields);
8022
+ }
8023
+ function stripPositionClauses2(path5) {
8024
+ return {
8025
+ context: path5.context,
8026
+ nodes: path5.nodes.map((node) => ({
8027
+ ...node,
8028
+ match: (node.match || []).filter((clause) => clause.kind !== "position")
8029
+ }))
8030
+ };
8031
+ }
8032
+
7775
8033
  // src/cloud/contracts.ts
7776
8034
  var cloudSessionContractVersion = "v3";
7777
8035
 
@@ -10174,6 +10432,7 @@ var CLOUD_INTERACTION_METHODS = /* @__PURE__ */ new Set([
10174
10432
  ]);
10175
10433
  var Opensteer = class _Opensteer {
10176
10434
  config;
10435
+ runtimeEnv;
10177
10436
  aiResolve;
10178
10437
  aiExtract;
10179
10438
  namespace;
@@ -10187,14 +10446,17 @@ var Opensteer = class _Opensteer {
10187
10446
  snapshotCache = null;
10188
10447
  agentExecutionInFlight = false;
10189
10448
  constructor(config = {}) {
10190
- const resolved = resolveConfig(config);
10449
+ const resolvedRuntime = resolveConfigWithEnv(config);
10450
+ const resolved = resolvedRuntime.config;
10451
+ const runtimeEnv = resolvedRuntime.env;
10191
10452
  const cloudSelection = resolveCloudSelection({
10192
10453
  cloud: resolved.cloud
10193
- });
10454
+ }, runtimeEnv);
10194
10455
  const model = resolved.model;
10195
10456
  this.config = resolved;
10196
- this.aiResolve = this.createLazyResolveCallback(model);
10197
- this.aiExtract = this.createLazyExtractCallback(model);
10457
+ this.runtimeEnv = runtimeEnv;
10458
+ this.aiResolve = this.createLazyResolveCallback(model, runtimeEnv);
10459
+ this.aiExtract = this.createLazyExtractCallback(model, runtimeEnv);
10198
10460
  const rootDir = resolved.storage?.rootDir || process.cwd();
10199
10461
  this.namespace = resolveNamespace(resolved, rootDir);
10200
10462
  this.storage = new LocalSelectorStorage(rootDir, this.namespace, {
@@ -10226,13 +10488,13 @@ var Opensteer = class _Opensteer {
10226
10488
  `[opensteer] ${context}: ${normalized.message}${codeSuffix}`
10227
10489
  );
10228
10490
  }
10229
- createLazyResolveCallback(model) {
10491
+ createLazyResolveCallback(model, env) {
10230
10492
  let resolverPromise = null;
10231
10493
  return async (...args) => {
10232
10494
  try {
10233
10495
  if (!resolverPromise) {
10234
10496
  resolverPromise = Promise.resolve().then(() => (init_resolver(), resolver_exports)).then(
10235
- (m) => m.createResolveCallback(model)
10497
+ (m) => m.createResolveCallback(model, { env })
10236
10498
  );
10237
10499
  }
10238
10500
  const resolver = await resolverPromise;
@@ -10243,13 +10505,13 @@ var Opensteer = class _Opensteer {
10243
10505
  }
10244
10506
  };
10245
10507
  }
10246
- createLazyExtractCallback(model) {
10508
+ createLazyExtractCallback(model, env) {
10247
10509
  let extractorPromise = null;
10248
10510
  const extract = async (args) => {
10249
10511
  try {
10250
10512
  if (!extractorPromise) {
10251
10513
  extractorPromise = Promise.resolve().then(() => (init_extractor(), extractor_exports)).then(
10252
- (m) => m.createExtractCallback(model)
10514
+ (m) => m.createExtractCallback(model, { env })
10253
10515
  );
10254
10516
  }
10255
10517
  const extractor = await extractorPromise;
@@ -10526,10 +10788,11 @@ var Opensteer = class _Opensteer {
10526
10788
  this.snapshotCache = null;
10527
10789
  }
10528
10790
  static from(page, config = {}) {
10529
- const resolvedConfig = resolveConfig(config);
10791
+ const resolvedRuntime = resolveConfigWithEnv(config);
10792
+ const resolvedConfig = resolvedRuntime.config;
10530
10793
  const cloudSelection = resolveCloudSelection({
10531
10794
  cloud: resolvedConfig.cloud
10532
- });
10795
+ }, resolvedRuntime.env);
10533
10796
  if (cloudSelection.cloud) {
10534
10797
  throw cloudUnsupportedMethodError(
10535
10798
  "Opensteer.from(page)",
@@ -11504,7 +11767,7 @@ var Opensteer = class _Opensteer {
11504
11767
  const data = await this.extractFields(fields);
11505
11768
  if (storageKey && schemaHash && (!stored || stored.schemaHash !== schemaHash)) {
11506
11769
  const persistedFields = await this.resolveFieldTargetsToPersistableFields(fields);
11507
- this.persistExtractPaths(
11770
+ await this.persistExtractPaths(
11508
11771
  storageKey,
11509
11772
  options.description,
11510
11773
  persistedFields,
@@ -11538,13 +11801,12 @@ var Opensteer = class _Opensteer {
11538
11801
  const resolvedFields = await this.resolveFieldTargetsToPersistableFields(fields);
11539
11802
  let persisted = false;
11540
11803
  if (storageKey) {
11541
- this.persistExtractPaths(
11804
+ persisted = await this.persistExtractPaths(
11542
11805
  storageKey,
11543
11806
  options.description,
11544
11807
  resolvedFields,
11545
11808
  schemaHash
11546
11809
  );
11547
- persisted = true;
11548
11810
  }
11549
11811
  return {
11550
11812
  namespace: this.storage.getNamespace(),
@@ -11576,7 +11838,8 @@ var Opensteer = class _Opensteer {
11576
11838
  agent(config) {
11577
11839
  const resolvedAgentConfig = resolveAgentConfig({
11578
11840
  agentConfig: config,
11579
- fallbackModel: this.config.model
11841
+ fallbackModel: this.config.model,
11842
+ env: this.runtimeEnv
11580
11843
  });
11581
11844
  return {
11582
11845
  execute: async (instructionOrOptions) => {
@@ -12005,7 +12268,7 @@ var Opensteer = class _Opensteer {
12005
12268
  this.storage.saveRegistry(registry);
12006
12269
  return true;
12007
12270
  }
12008
- persistExtractPaths(id, description, fields, schemaHash) {
12271
+ async persistExtractPaths(id, description, fields, schemaHash) {
12009
12272
  const now = Date.now();
12010
12273
  const safeFile = this.storage.getSelectorFileName(id);
12011
12274
  const existing = this.storage.readSelector(id);
@@ -12026,11 +12289,19 @@ var Opensteer = class _Opensteer {
12026
12289
  }
12027
12290
  );
12028
12291
  const persistedPayload = buildPersistedExtractPayload(normalizedFields);
12292
+ let validatedPayload = persistedPayload;
12293
+ try {
12294
+ validatedPayload = await stripRedundantPositionClauses(
12295
+ persistedPayload,
12296
+ this.page
12297
+ );
12298
+ } catch {
12299
+ }
12029
12300
  const payload = {
12030
12301
  id,
12031
12302
  method: "extract",
12032
12303
  description: description || "Extraction paths",
12033
- path: persistedPayload,
12304
+ path: validatedPayload,
12034
12305
  schemaHash,
12035
12306
  metadata: {
12036
12307
  createdAt,
@@ -12718,6 +12989,7 @@ init_model();
12718
12989
  OpensteerAgentProviderError,
12719
12990
  OpensteerCloudError,
12720
12991
  OpensteerCuaAgentHandler,
12992
+ buildArrayFieldPathCandidates,
12721
12993
  buildElementPathFromHandle,
12722
12994
  buildElementPathFromSelector,
12723
12995
  buildPathSelectorHint,
@@ -12763,6 +13035,7 @@ init_model();
12763
13035
  performSelect,
12764
13036
  prepareSnapshot,
12765
13037
  pressKey,
13038
+ queryAllByElementPath,
12766
13039
  resolveAgentConfig,
12767
13040
  resolveCounterElement,
12768
13041
  resolveCountersBatch,
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as playwright from 'playwright';
2
- import { BrowserContextOptions, Page, BrowserContext, Cookie, ElementHandle, Browser } from 'playwright';
2
+ import { BrowserContextOptions, Page, BrowserContext, ElementHandle, Cookie, Browser } from 'playwright';
3
3
 
4
4
  type MatchOperator = 'exact' | 'startsWith' | 'contains';
5
5
  interface AttributeMatchClause {
@@ -362,6 +362,7 @@ declare class LocalSelectorStorage {
362
362
 
363
363
  declare class Opensteer {
364
364
  private readonly config;
365
+ private readonly runtimeEnv;
365
366
  private readonly aiResolve;
366
367
  private readonly aiExtract;
367
368
  private readonly namespace;
@@ -524,6 +525,8 @@ declare function extractWithPaths(page: Page, fields: FieldSelector[]): Promise<
524
525
  declare function extractArrayWithPaths(page: Page, array: ArraySelector): Promise<Array<Record<string, unknown>>>;
525
526
  declare function extractArrayRowsWithPaths(page: Page, array: ArraySelector): Promise<Array<ArrayExtractedRow>>;
526
527
  declare function countArrayItemsWithPath(page: Page, itemParentPath: ElementPath): Promise<number>;
528
+ declare function queryAllByElementPath(page: Page, path: ElementPath): Promise<ElementHandle<Element>[]>;
529
+ declare function buildArrayFieldPathCandidates(path: ElementPath): string[];
527
530
 
528
531
  declare function listTabs(context: BrowserContext, activePage: Page): Promise<TabInfo[]>;
529
532
  declare function createTab(context: BrowserContext, url?: string, gotoOptions?: GotoOptions): Promise<{
@@ -644,17 +647,23 @@ declare function resolveCountersBatch(page: Page, requests: CounterRequest[]): P
644
647
  declare function normalizeNamespace(input?: string): string;
645
648
  declare function resolveNamespaceDir(rootDir: string, namespace: string): string;
646
649
 
650
+ type RuntimeEnv = Record<string, string | undefined>;
651
+
647
652
  declare function createResolveCallback(model: string, options?: {
648
653
  temperature?: number;
649
654
  maxTokens?: number | null;
655
+ env?: RuntimeEnv;
650
656
  }): AiResolveCallback;
651
657
 
652
658
  declare function createExtractCallback(model: string, options?: {
653
659
  temperature?: number;
654
660
  maxTokens?: number | null;
661
+ env?: RuntimeEnv;
655
662
  }): AiExtractCallback;
656
663
 
657
- declare function getModelProvider(modelStr: string): Promise<unknown>;
664
+ declare function getModelProvider(modelStr: string, options?: {
665
+ env?: RuntimeEnv;
666
+ }): Promise<unknown>;
658
667
 
659
668
  interface AiModelConfig {
660
669
  model: string;
@@ -865,7 +874,7 @@ interface ResolvedAgentConfig {
865
874
  declare function resolveAgentConfig(args: {
866
875
  agentConfig: OpensteerAgentConfig;
867
876
  fallbackModel?: string;
868
- env?: NodeJS.ProcessEnv;
877
+ env?: RuntimeEnv;
869
878
  }): ResolvedAgentConfig;
870
879
  declare function createCuaClient(config: ResolvedAgentConfig): CuaClient;
871
880
 
@@ -890,4 +899,4 @@ declare class OpensteerCuaAgentHandler {
890
899
  private maybeRenderCursor;
891
900
  }
892
901
 
893
- export { type ActionExecutionResult, type ActionFailure, type ActionFailureBlocker, type ActionFailureClassificationSource, type ActionFailureCode, type ActionFailureDetails, type ActionResult, type ActionWaitOptions, ActionWsClient, type AiExtractArgs, type AiExtractCallback, type AiExtractResult, type AiModelConfig, type AiResolveArgs, type AiResolveCallback, type AiResolveCallbackResult, type AiResolveResult, type ArrayExtractedRow, type ArrayRowMetadata, type ArraySelector, type AttributeMatchClause, type BaseActionOptions, type BoundingBox, type ClickOptions, type CloudActionFailure, type CloudActionFailureDetails, type CloudActionMethod, type CloudActionRequest, type CloudActionResponse, type CloudActionSuccess, CloudCdpClient, type CloudCdpConnectArgs, type CloudCdpConnection, type CloudErrorCode, type CloudSelectorCacheImportEntry, type CloudSelectorCacheImportRequest, type CloudSelectorCacheImportResponse, CloudSessionClient, type CloudSessionContractVersion, type CloudSessionCreateRequest, type CloudSessionCreateResponse, type CloudSessionSourceType, type CloudSessionSummary, type ContextHop, type CookieParam, type CounterRequest, CounterResolutionError, type CounterResolutionErrorCode, type DomPath, type ElementPath, ElementPathError, type ElementPathErrorCode, type ExtractFromPlanOptions, type ExtractOptions, type ExtractSchema, type ExtractSchemaField, type ExtractSchemaValue, type ExtractionFieldPlan, type ExtractionPlan, type ExtractionRunResult, type FieldSelector, type FileUploadOptions, type GotoOptions, type HoverOptions, type InputOptions, type LaunchOptions, LocalSelectorStorage, type MarkInteractivityOptions, type MatchClause, type MatchOperator, OPENSTEER_HIDDEN_ATTR, OPENSTEER_INTERACTIVE_ATTR, OPENSTEER_SCROLLABLE_ATTR, OS_BOUNDARY_ATTR, OS_IFRAME_BOUNDARY_TAG, OS_NODE_ID_ATTR, OS_SHADOW_BOUNDARY_TAG, OS_UNAVAILABLE_ATTR, Opensteer, OpensteerActionError, type OpensteerAgentAction, OpensteerAgentActionError, OpensteerAgentApiError, OpensteerAgentBusyError, type OpensteerAgentConfig, OpensteerAgentConfigError, OpensteerAgentError, type OpensteerAgentExecuteOptions, OpensteerAgentExecutionError, type OpensteerAgentInstance, type OpensteerAgentMode, type OpensteerAgentModelConfig, type OpensteerAgentProvider, OpensteerAgentProviderError, type OpensteerAgentResult, type OpensteerAgentUsage, type OpensteerAuthScheme, type OpensteerBrowserConfig, type OpensteerCloudAnnouncePolicy, type OpensteerCloudConfig, OpensteerCloudError, type OpensteerCloudOptions, type OpensteerConfig, OpensteerCuaAgentHandler, type OpensteerStorageConfig, type PathNode, type PathNodePosition, type PositionMatchClause, type PreparedSnapshot, type RegistryEntry, type ResolvedElementPath, type ScreenshotOptions, type ScrollOptions, type SelectOptions, type SelectorFile, type SelectorRegistry, type SerializeOptions, type SerializedNodeMeta, type SerializedPageHTML, type SnapshotMode, type SnapshotOptions, type StateResult, type TabInfo, buildElementPathFromHandle, buildElementPathFromSelector, buildPathSelectorHint, cleanForAction, cleanForClickable, cleanForExtraction, cleanForFull, cleanForScrollable, clearCookies, cloneElementPath, closeTab, cloudNotLaunchedError, cloudSessionContractVersion, cloudUnsupportedMethodError, collectLocalSelectorCacheEntries, countArrayItemsWithPath, createCuaClient, createEmptyRegistry, createExtractCallback, createResolveCallback, createTab, exportCookies, extractArrayRowsWithPaths, extractArrayWithPaths, extractWithPaths, getCookies, getElementAttributes, getElementBoundingBox, getElementText, getElementValue, getModelProvider, getPageHtml, getPageTitle, importCookies, listTabs, markInteractiveElements, normalizeNamespace, performClick, performFileUpload, performHover, performInput, performScroll, performSelect, prepareSnapshot, pressKey, resolveAgentConfig, resolveCounterElement, resolveCountersBatch, resolveElementPath, resolveNamespaceDir, sanitizeElementPath, serializePageHTML, setCookie, switchTab, typeText, waitForVisualStability };
902
+ export { type ActionExecutionResult, type ActionFailure, type ActionFailureBlocker, type ActionFailureClassificationSource, type ActionFailureCode, type ActionFailureDetails, type ActionResult, type ActionWaitOptions, ActionWsClient, type AiExtractArgs, type AiExtractCallback, type AiExtractResult, type AiModelConfig, type AiResolveArgs, type AiResolveCallback, type AiResolveCallbackResult, type AiResolveResult, type ArrayExtractedRow, type ArrayRowMetadata, type ArraySelector, type AttributeMatchClause, type BaseActionOptions, type BoundingBox, type ClickOptions, type CloudActionFailure, type CloudActionFailureDetails, type CloudActionMethod, type CloudActionRequest, type CloudActionResponse, type CloudActionSuccess, CloudCdpClient, type CloudCdpConnectArgs, type CloudCdpConnection, type CloudErrorCode, type CloudSelectorCacheImportEntry, type CloudSelectorCacheImportRequest, type CloudSelectorCacheImportResponse, CloudSessionClient, type CloudSessionContractVersion, type CloudSessionCreateRequest, type CloudSessionCreateResponse, type CloudSessionSourceType, type CloudSessionSummary, type ContextHop, type CookieParam, type CounterRequest, CounterResolutionError, type CounterResolutionErrorCode, type DomPath, type ElementPath, ElementPathError, type ElementPathErrorCode, type ExtractFromPlanOptions, type ExtractOptions, type ExtractSchema, type ExtractSchemaField, type ExtractSchemaValue, type ExtractionFieldPlan, type ExtractionPlan, type ExtractionRunResult, type FieldSelector, type FileUploadOptions, type GotoOptions, type HoverOptions, type InputOptions, type LaunchOptions, LocalSelectorStorage, type MarkInteractivityOptions, type MatchClause, type MatchOperator, OPENSTEER_HIDDEN_ATTR, OPENSTEER_INTERACTIVE_ATTR, OPENSTEER_SCROLLABLE_ATTR, OS_BOUNDARY_ATTR, OS_IFRAME_BOUNDARY_TAG, OS_NODE_ID_ATTR, OS_SHADOW_BOUNDARY_TAG, OS_UNAVAILABLE_ATTR, Opensteer, OpensteerActionError, type OpensteerAgentAction, OpensteerAgentActionError, OpensteerAgentApiError, OpensteerAgentBusyError, type OpensteerAgentConfig, OpensteerAgentConfigError, OpensteerAgentError, type OpensteerAgentExecuteOptions, OpensteerAgentExecutionError, type OpensteerAgentInstance, type OpensteerAgentMode, type OpensteerAgentModelConfig, type OpensteerAgentProvider, OpensteerAgentProviderError, type OpensteerAgentResult, type OpensteerAgentUsage, type OpensteerAuthScheme, type OpensteerBrowserConfig, type OpensteerCloudAnnouncePolicy, type OpensteerCloudConfig, OpensteerCloudError, type OpensteerCloudOptions, type OpensteerConfig, OpensteerCuaAgentHandler, type OpensteerStorageConfig, type PathNode, type PathNodePosition, type PositionMatchClause, type PreparedSnapshot, type RegistryEntry, type ResolvedElementPath, type ScreenshotOptions, type ScrollOptions, type SelectOptions, type SelectorFile, type SelectorRegistry, type SerializeOptions, type SerializedNodeMeta, type SerializedPageHTML, type SnapshotMode, type SnapshotOptions, type StateResult, type TabInfo, buildArrayFieldPathCandidates, buildElementPathFromHandle, buildElementPathFromSelector, buildPathSelectorHint, cleanForAction, cleanForClickable, cleanForExtraction, cleanForFull, cleanForScrollable, clearCookies, cloneElementPath, closeTab, cloudNotLaunchedError, cloudSessionContractVersion, cloudUnsupportedMethodError, collectLocalSelectorCacheEntries, countArrayItemsWithPath, createCuaClient, createEmptyRegistry, createExtractCallback, createResolveCallback, createTab, exportCookies, extractArrayRowsWithPaths, extractArrayWithPaths, extractWithPaths, getCookies, getElementAttributes, getElementBoundingBox, getElementText, getElementValue, getModelProvider, getPageHtml, getPageTitle, importCookies, listTabs, markInteractiveElements, normalizeNamespace, performClick, performFileUpload, performHover, performInput, performScroll, performSelect, prepareSnapshot, pressKey, queryAllByElementPath, resolveAgentConfig, resolveCounterElement, resolveCountersBatch, resolveElementPath, resolveNamespaceDir, sanitizeElementPath, serializePageHTML, setCookie, switchTab, typeText, waitForVisualStability };