opensteer 0.4.12 → 0.4.14

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.
@@ -2,6 +2,232 @@ import {
2
2
  flattenExtractionDataToFieldPlan
3
3
  } from "./chunk-3H5RRIMZ.js";
4
4
 
5
+ // src/error-normalization.ts
6
+ function extractErrorMessage(error, fallback = "Unknown error.") {
7
+ if (error instanceof Error) {
8
+ const message = error.message.trim();
9
+ if (message) return message;
10
+ const name = error.name.trim();
11
+ if (name) return name;
12
+ }
13
+ if (typeof error === "string" && error.trim()) {
14
+ return error.trim();
15
+ }
16
+ const record = asRecord(error);
17
+ const recordMessage = toNonEmptyString(record?.message) || toNonEmptyString(record?.error);
18
+ if (recordMessage) {
19
+ return recordMessage;
20
+ }
21
+ return fallback;
22
+ }
23
+ function normalizeError(error, fallback = "Unknown error.", maxCauseDepth = 2) {
24
+ const seen = /* @__PURE__ */ new WeakSet();
25
+ return normalizeErrorInternal(error, fallback, maxCauseDepth, seen);
26
+ }
27
+ function normalizeErrorInternal(error, fallback, depthRemaining, seen) {
28
+ const record = asRecord(error);
29
+ if (record) {
30
+ if (seen.has(record)) {
31
+ return {
32
+ message: extractErrorMessage(error, fallback)
33
+ };
34
+ }
35
+ seen.add(record);
36
+ }
37
+ const message = extractErrorMessage(error, fallback);
38
+ const code = extractCode(error);
39
+ const name = extractName(error);
40
+ const details = extractDetails(error);
41
+ if (depthRemaining <= 0) {
42
+ return compactErrorInfo({
43
+ message,
44
+ ...code ? { code } : {},
45
+ ...name ? { name } : {},
46
+ ...details ? { details } : {}
47
+ });
48
+ }
49
+ const cause = extractCause(error);
50
+ if (!cause) {
51
+ return compactErrorInfo({
52
+ message,
53
+ ...code ? { code } : {},
54
+ ...name ? { name } : {},
55
+ ...details ? { details } : {}
56
+ });
57
+ }
58
+ const normalizedCause = normalizeErrorInternal(
59
+ cause,
60
+ "Caused by an unknown error.",
61
+ depthRemaining - 1,
62
+ seen
63
+ );
64
+ return compactErrorInfo({
65
+ message,
66
+ ...code ? { code } : {},
67
+ ...name ? { name } : {},
68
+ ...details ? { details } : {},
69
+ cause: normalizedCause
70
+ });
71
+ }
72
+ function compactErrorInfo(info) {
73
+ const safeDetails = toJsonSafeRecord(info.details);
74
+ return {
75
+ message: info.message,
76
+ ...info.code ? { code: info.code } : {},
77
+ ...info.name ? { name: info.name } : {},
78
+ ...safeDetails ? { details: safeDetails } : {},
79
+ ...info.cause ? { cause: info.cause } : {}
80
+ };
81
+ }
82
+ function extractCode(error) {
83
+ const record = asRecord(error);
84
+ const raw = record?.code;
85
+ if (typeof raw === "string" && raw.trim()) {
86
+ return raw.trim();
87
+ }
88
+ if (typeof raw === "number" && Number.isFinite(raw)) {
89
+ return String(raw);
90
+ }
91
+ return void 0;
92
+ }
93
+ function extractName(error) {
94
+ if (error instanceof Error && error.name.trim()) {
95
+ return error.name.trim();
96
+ }
97
+ const record = asRecord(error);
98
+ return toNonEmptyString(record?.name);
99
+ }
100
+ function extractDetails(error) {
101
+ const record = asRecord(error);
102
+ if (!record) return void 0;
103
+ const details = {};
104
+ const rawDetails = asRecord(record.details);
105
+ if (rawDetails) {
106
+ Object.assign(details, rawDetails);
107
+ }
108
+ const action = toNonEmptyString(record.action);
109
+ if (action) {
110
+ details.action = action;
111
+ }
112
+ const selectorUsed = toNonEmptyString(record.selectorUsed);
113
+ if (selectorUsed) {
114
+ details.selectorUsed = selectorUsed;
115
+ }
116
+ if (typeof record.status === "number" && Number.isFinite(record.status)) {
117
+ details.status = record.status;
118
+ }
119
+ const failure = asRecord(record.failure);
120
+ if (failure) {
121
+ const failureCode = toNonEmptyString(failure.code);
122
+ const classificationSource = toNonEmptyString(
123
+ failure.classificationSource
124
+ );
125
+ const failureDetails = asRecord(failure.details);
126
+ if (failureCode || classificationSource || failureDetails) {
127
+ details.actionFailure = {
128
+ ...failureCode ? { code: failureCode } : {},
129
+ ...classificationSource ? { classificationSource } : {},
130
+ ...failureDetails ? { details: failureDetails } : {}
131
+ };
132
+ }
133
+ }
134
+ return Object.keys(details).length ? details : void 0;
135
+ }
136
+ function extractCause(error) {
137
+ if (error instanceof Error) {
138
+ return error.cause;
139
+ }
140
+ const record = asRecord(error);
141
+ return record?.cause;
142
+ }
143
+ function asRecord(value) {
144
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
145
+ return null;
146
+ }
147
+ return value;
148
+ }
149
+ function toNonEmptyString(value) {
150
+ if (typeof value !== "string") return void 0;
151
+ const normalized = value.trim();
152
+ return normalized.length ? normalized : void 0;
153
+ }
154
+ function toJsonSafeRecord(value) {
155
+ if (!value) return void 0;
156
+ const sanitized = toJsonSafeValue(value, /* @__PURE__ */ new WeakSet());
157
+ if (!sanitized || typeof sanitized !== "object" || Array.isArray(sanitized)) {
158
+ return void 0;
159
+ }
160
+ const record = sanitized;
161
+ return Object.keys(record).length > 0 ? record : void 0;
162
+ }
163
+ function toJsonSafeValue(value, seen) {
164
+ if (value === null) return null;
165
+ if (typeof value === "string" || typeof value === "boolean") {
166
+ return value;
167
+ }
168
+ if (typeof value === "number") {
169
+ return Number.isFinite(value) ? value : null;
170
+ }
171
+ if (typeof value === "bigint") {
172
+ return value.toString();
173
+ }
174
+ if (value === void 0 || typeof value === "function" || typeof value === "symbol") {
175
+ return void 0;
176
+ }
177
+ if (value instanceof Date) {
178
+ return Number.isNaN(value.getTime()) ? null : value.toISOString();
179
+ }
180
+ if (Array.isArray(value)) {
181
+ if (seen.has(value)) return "[Circular]";
182
+ seen.add(value);
183
+ const output = value.map((item) => {
184
+ const next = toJsonSafeValue(item, seen);
185
+ return next === void 0 ? null : next;
186
+ });
187
+ seen.delete(value);
188
+ return output;
189
+ }
190
+ if (value instanceof Set) {
191
+ if (seen.has(value)) return "[Circular]";
192
+ seen.add(value);
193
+ const output = Array.from(value, (item) => {
194
+ const next = toJsonSafeValue(item, seen);
195
+ return next === void 0 ? null : next;
196
+ });
197
+ seen.delete(value);
198
+ return output;
199
+ }
200
+ if (value instanceof Map) {
201
+ if (seen.has(value)) return "[Circular]";
202
+ seen.add(value);
203
+ const output = {};
204
+ for (const [key, item] of value.entries()) {
205
+ const normalizedKey = String(key);
206
+ const next = toJsonSafeValue(item, seen);
207
+ if (next !== void 0) {
208
+ output[normalizedKey] = next;
209
+ }
210
+ }
211
+ seen.delete(value);
212
+ return output;
213
+ }
214
+ if (typeof value === "object") {
215
+ const objectValue = value;
216
+ if (seen.has(objectValue)) return "[Circular]";
217
+ seen.add(objectValue);
218
+ const output = {};
219
+ for (const [key, item] of Object.entries(objectValue)) {
220
+ const next = toJsonSafeValue(item, seen);
221
+ if (next !== void 0) {
222
+ output[key] = next;
223
+ }
224
+ }
225
+ seen.delete(objectValue);
226
+ return output;
227
+ }
228
+ return void 0;
229
+ }
230
+
5
231
  // src/storage/namespace.ts
6
232
  import path from "path";
7
233
  var DEFAULT_NAMESPACE = "default";
@@ -607,9 +833,11 @@ import path2 from "path";
607
833
  var LocalSelectorStorage = class {
608
834
  rootDir;
609
835
  namespace;
610
- constructor(rootDir, namespace) {
836
+ debug;
837
+ constructor(rootDir, namespace, options = {}) {
611
838
  this.rootDir = rootDir;
612
839
  this.namespace = normalizeNamespace(namespace);
840
+ this.debug = options.debug === true;
613
841
  }
614
842
  getRootDir() {
615
843
  return this.rootDir;
@@ -643,7 +871,16 @@ var LocalSelectorStorage = class {
643
871
  try {
644
872
  const raw = fs.readFileSync(file, "utf8");
645
873
  return JSON.parse(raw);
646
- } catch {
874
+ } catch (error) {
875
+ const message = extractErrorMessage(
876
+ error,
877
+ "Unable to parse selector registry JSON."
878
+ );
879
+ if (this.debug) {
880
+ console.warn(
881
+ `[opensteer] failed to read selector registry "${file}": ${message}`
882
+ );
883
+ }
647
884
  return createEmptyRegistry(this.namespace);
648
885
  }
649
886
  }
@@ -660,7 +897,16 @@ var LocalSelectorStorage = class {
660
897
  try {
661
898
  const raw = fs.readFileSync(file, "utf8");
662
899
  return JSON.parse(raw);
663
- } catch {
900
+ } catch (error) {
901
+ const message = extractErrorMessage(
902
+ error,
903
+ "Unable to parse selector file JSON."
904
+ );
905
+ if (this.debug) {
906
+ console.warn(
907
+ `[opensteer] failed to read selector file "${file}": ${message}`
908
+ );
909
+ }
664
910
  return null;
665
911
  }
666
912
  }
@@ -3906,7 +4152,7 @@ function defaultActionFailureMessage(action) {
3906
4152
  function classifyActionFailure(input) {
3907
4153
  const typed = classifyTypedError(input.error);
3908
4154
  if (typed) return typed;
3909
- const message = extractErrorMessage(input.error, input.fallbackMessage);
4155
+ const message = extractErrorMessage2(input.error, input.fallbackMessage);
3910
4156
  const fromCallLog = classifyFromPlaywrightMessage(message, input.probe);
3911
4157
  if (fromCallLog) return fromCallLog;
3912
4158
  const fromProbe = classifyFromProbe(input.probe);
@@ -3915,7 +4161,7 @@ function classifyActionFailure(input) {
3915
4161
  if (fromHeuristic) return fromHeuristic;
3916
4162
  return buildFailure({
3917
4163
  code: "UNKNOWN",
3918
- message: ensureMessage(input.fallbackMessage, "Action failed."),
4164
+ message: ensureMessage(message, input.fallbackMessage),
3919
4165
  classificationSource: "unknown"
3920
4166
  });
3921
4167
  }
@@ -4175,13 +4421,22 @@ function defaultRetryableForCode(code) {
4175
4421
  return true;
4176
4422
  }
4177
4423
  }
4178
- function extractErrorMessage(error, fallbackMessage) {
4424
+ function extractErrorMessage2(error, fallbackMessage) {
4179
4425
  if (error instanceof Error && error.message.trim()) {
4180
4426
  return error.message;
4181
4427
  }
4182
4428
  if (typeof error === "string" && error.trim()) {
4183
4429
  return error.trim();
4184
4430
  }
4431
+ if (error && typeof error === "object" && !Array.isArray(error)) {
4432
+ const record = error;
4433
+ if (typeof record.message === "string" && record.message.trim()) {
4434
+ return record.message.trim();
4435
+ }
4436
+ if (typeof record.error === "string" && record.error.trim()) {
4437
+ return record.error.trim();
4438
+ }
4439
+ }
4185
4440
  return ensureMessage(fallbackMessage, "Action failed.");
4186
4441
  }
4187
4442
  function ensureMessage(value, fallback) {
@@ -5140,7 +5395,8 @@ function withTokenQuery(wsUrl, token) {
5140
5395
  // src/cloud/local-cache-sync.ts
5141
5396
  import fs2 from "fs";
5142
5397
  import path3 from "path";
5143
- function collectLocalSelectorCacheEntries(storage) {
5398
+ function collectLocalSelectorCacheEntries(storage, options = {}) {
5399
+ const debug = options.debug === true;
5144
5400
  const namespace = storage.getNamespace();
5145
5401
  const namespaceDir = storage.getNamespaceDir();
5146
5402
  if (!fs2.existsSync(namespaceDir)) return [];
@@ -5149,7 +5405,7 @@ function collectLocalSelectorCacheEntries(storage) {
5149
5405
  for (const fileName of fileNames) {
5150
5406
  if (fileName === "index.json" || !fileName.endsWith(".json")) continue;
5151
5407
  const filePath = path3.join(namespaceDir, fileName);
5152
- const selector = readSelectorFile(filePath);
5408
+ const selector = readSelectorFile(filePath, debug);
5153
5409
  if (!selector) continue;
5154
5410
  const descriptionHash = normalizeDescriptionHash(selector.id);
5155
5411
  const method = normalizeMethod(selector.method);
@@ -5174,11 +5430,20 @@ function collectLocalSelectorCacheEntries(storage) {
5174
5430
  }
5175
5431
  return dedupeNewest(entries);
5176
5432
  }
5177
- function readSelectorFile(filePath) {
5433
+ function readSelectorFile(filePath, debug) {
5178
5434
  try {
5179
5435
  const raw = fs2.readFileSync(filePath, "utf8");
5180
5436
  return JSON.parse(raw);
5181
- } catch {
5437
+ } catch (error) {
5438
+ const message = extractErrorMessage(
5439
+ error,
5440
+ "Unable to parse selector cache file JSON."
5441
+ );
5442
+ if (debug) {
5443
+ console.warn(
5444
+ `[opensteer] failed to read local selector cache file "${filePath}": ${message}`
5445
+ );
5446
+ }
5182
5447
  return null;
5183
5448
  }
5184
5449
  }
@@ -7824,11 +8089,12 @@ function dotenvFileOrder(nodeEnv) {
7824
8089
  files.push(".env");
7825
8090
  return files;
7826
8091
  }
7827
- function loadDotenvValues(rootDir, baseEnv) {
8092
+ function loadDotenvValues(rootDir, baseEnv, options = {}) {
7828
8093
  const values = {};
7829
8094
  if (parseBool(baseEnv.OPENSTEER_DISABLE_DOTENV_AUTOLOAD) === true) {
7830
8095
  return values;
7831
8096
  }
8097
+ const debug = options.debug ?? parseBool(baseEnv.OPENSTEER_DEBUG) === true;
7832
8098
  const baseDir = path4.resolve(rootDir);
7833
8099
  const nodeEnv = baseEnv.NODE_ENV?.trim() || "";
7834
8100
  for (const filename of dotenvFileOrder(nodeEnv)) {
@@ -7842,15 +8108,24 @@ function loadDotenvValues(rootDir, baseEnv) {
7842
8108
  values[key] = value;
7843
8109
  }
7844
8110
  }
7845
- } catch {
8111
+ } catch (error) {
8112
+ const message = extractErrorMessage(
8113
+ error,
8114
+ "Unable to read or parse dotenv file."
8115
+ );
8116
+ if (debug) {
8117
+ console.warn(
8118
+ `[opensteer] failed to load dotenv file "${filePath}": ${message}`
8119
+ );
8120
+ }
7846
8121
  continue;
7847
8122
  }
7848
8123
  }
7849
8124
  return values;
7850
8125
  }
7851
- function resolveEnv(rootDir) {
8126
+ function resolveEnv(rootDir, options = {}) {
7852
8127
  const baseEnv = process.env;
7853
- const dotenvValues = loadDotenvValues(rootDir, baseEnv);
8128
+ const dotenvValues = loadDotenvValues(rootDir, baseEnv, options);
7854
8129
  return {
7855
8130
  ...dotenvValues,
7856
8131
  ...baseEnv
@@ -7894,13 +8169,22 @@ function assertNoLegacyRuntimeConfig(source, config) {
7894
8169
  );
7895
8170
  }
7896
8171
  }
7897
- function loadConfigFile(rootDir) {
8172
+ function loadConfigFile(rootDir, options = {}) {
7898
8173
  const configPath = path4.join(rootDir, ".opensteer", "config.json");
7899
8174
  if (!fs3.existsSync(configPath)) return {};
7900
8175
  try {
7901
8176
  const raw = fs3.readFileSync(configPath, "utf8");
7902
8177
  return JSON.parse(raw);
7903
- } catch {
8178
+ } catch (error) {
8179
+ const message = extractErrorMessage(
8180
+ error,
8181
+ "Unable to read or parse config file."
8182
+ );
8183
+ if (options.debug) {
8184
+ console.warn(
8185
+ `[opensteer] failed to load config file "${configPath}": ${message}`
8186
+ );
8187
+ }
7904
8188
  return {};
7905
8189
  }
7906
8190
  }
@@ -8032,6 +8316,8 @@ function resolveCloudSelection(config, env = process.env) {
8032
8316
  };
8033
8317
  }
8034
8318
  function resolveConfig(input = {}) {
8319
+ const processEnv = process.env;
8320
+ const debugHint = typeof input.debug === "boolean" ? input.debug : parseBool(processEnv.OPENSTEER_DEBUG) === true;
8035
8321
  const initialRootDir = input.storage?.rootDir ?? process.cwd();
8036
8322
  const runtimeDefaults = mergeDeep(DEFAULT_CONFIG, {
8037
8323
  storage: {
@@ -8040,12 +8326,16 @@ function resolveConfig(input = {}) {
8040
8326
  });
8041
8327
  assertNoLegacyAiConfig("Opensteer constructor config", input);
8042
8328
  assertNoLegacyRuntimeConfig("Opensteer constructor config", input);
8043
- const fileConfig = loadConfigFile(initialRootDir);
8329
+ const fileConfig = loadConfigFile(initialRootDir, {
8330
+ debug: debugHint
8331
+ });
8044
8332
  assertNoLegacyAiConfig(".opensteer/config.json", fileConfig);
8045
8333
  assertNoLegacyRuntimeConfig(".opensteer/config.json", fileConfig);
8046
8334
  const fileRootDir = typeof fileConfig.storage?.rootDir === "string" ? fileConfig.storage.rootDir : void 0;
8047
8335
  const envRootDir = input.storage?.rootDir ?? fileRootDir ?? initialRootDir;
8048
- const env = resolveEnv(envRootDir);
8336
+ const env = resolveEnv(envRootDir, {
8337
+ debug: debugHint
8338
+ });
8049
8339
  if (env.OPENSTEER_AI_MODEL) {
8050
8340
  throw new Error(
8051
8341
  "OPENSTEER_AI_MODEL is no longer supported. Use OPENSTEER_MODEL instead."
@@ -9511,7 +9801,7 @@ function clonePersistedExtractNode(node) {
9511
9801
  }
9512
9802
 
9513
9803
  // src/cloud/runtime.ts
9514
- var DEFAULT_CLOUD_BASE_URL = "https://remote.opensteer.com";
9804
+ var DEFAULT_CLOUD_BASE_URL = "https://api.opensteer.com";
9515
9805
  function createCloudRuntimeState(key, baseUrl = resolveCloudBaseUrl(), authScheme = "api-key") {
9516
9806
  const normalizedBaseUrl = normalizeCloudBaseUrl(baseUrl);
9517
9807
  return {
@@ -9579,7 +9869,9 @@ var Opensteer = class _Opensteer {
9579
9869
  this.aiExtract = this.createLazyExtractCallback(model);
9580
9870
  const rootDir = resolved.storage?.rootDir || process.cwd();
9581
9871
  this.namespace = resolveNamespace(resolved, rootDir);
9582
- this.storage = new LocalSelectorStorage(rootDir, this.namespace);
9872
+ this.storage = new LocalSelectorStorage(rootDir, this.namespace, {
9873
+ debug: Boolean(resolved.debug)
9874
+ });
9583
9875
  this.pool = new BrowserPool(resolved.browser || {});
9584
9876
  if (cloudSelection.cloud) {
9585
9877
  const cloudConfig = resolved.cloud && typeof resolved.cloud === "object" ? resolved.cloud : void 0;
@@ -9598,12 +9890,20 @@ var Opensteer = class _Opensteer {
9598
9890
  this.cloud = null;
9599
9891
  }
9600
9892
  }
9893
+ logDebugError(context, error) {
9894
+ if (!this.config.debug) return;
9895
+ const normalized = normalizeError(error, "Unknown error.");
9896
+ const codeSuffix = normalized.code && normalized.code.trim() ? ` [${normalized.code.trim()}]` : "";
9897
+ console.warn(
9898
+ `[opensteer] ${context}: ${normalized.message}${codeSuffix}`
9899
+ );
9900
+ }
9601
9901
  createLazyResolveCallback(model) {
9602
9902
  let resolverPromise = null;
9603
9903
  return async (...args) => {
9604
9904
  try {
9605
9905
  if (!resolverPromise) {
9606
- resolverPromise = import("./resolver-HVZJQZ32.js").then(
9906
+ resolverPromise = import("./resolver-ZREUOOTV.js").then(
9607
9907
  (m) => m.createResolveCallback(model)
9608
9908
  );
9609
9909
  }
@@ -9620,7 +9920,7 @@ var Opensteer = class _Opensteer {
9620
9920
  const extract = async (args) => {
9621
9921
  try {
9622
9922
  if (!extractorPromise) {
9623
- extractorPromise = import("./extractor-I6TJPTXV.js").then(
9923
+ extractorPromise = import("./extractor-CZFCFUME.js").then(
9624
9924
  (m) => m.createExtractCallback(model)
9625
9925
  );
9626
9926
  }
@@ -9688,7 +9988,8 @@ var Opensteer = class _Opensteer {
9688
9988
  let tabs;
9689
9989
  try {
9690
9990
  tabs = await this.invokeCloudAction("tabs", {});
9691
- } catch {
9991
+ } catch (error) {
9992
+ this.logDebugError("cloud page reference sync (tabs lookup) failed", error);
9692
9993
  return;
9693
9994
  }
9694
9995
  if (!tabs.length) {
@@ -9826,12 +10127,7 @@ var Opensteer = class _Opensteer {
9826
10127
  try {
9827
10128
  await this.syncLocalSelectorCacheToCloud();
9828
10129
  } catch (error) {
9829
- if (this.config.debug) {
9830
- const message = error instanceof Error ? error.message : String(error);
9831
- console.warn(
9832
- `[opensteer] cloud selector cache sync failed: ${message}`
9833
- );
9834
- }
10130
+ this.logDebugError("cloud selector cache sync failed", error);
9835
10131
  }
9836
10132
  localRunId = this.cloud.localRunId || buildLocalRunId(this.namespace);
9837
10133
  this.cloud.localRunId = localRunId;
@@ -9863,7 +10159,12 @@ var Opensteer = class _Opensteer {
9863
10159
  this.cloud.actionClient = actionClient;
9864
10160
  this.cloud.sessionId = sessionId;
9865
10161
  this.cloud.cloudSessionUrl = session2.cloudSessionUrl;
9866
- await this.syncCloudPageRef().catch(() => void 0);
10162
+ await this.syncCloudPageRef().catch((error) => {
10163
+ this.logDebugError(
10164
+ "cloud page reference sync after launch failed",
10165
+ error
10166
+ );
10167
+ });
9867
10168
  this.announceCloudSession({
9868
10169
  sessionId: session2.sessionId,
9869
10170
  workspaceId: session2.cloudSession.workspaceId,
@@ -9950,7 +10251,9 @@ var Opensteer = class _Opensteer {
9950
10251
  }
9951
10252
  async syncLocalSelectorCacheToCloud() {
9952
10253
  if (!this.cloud) return;
9953
- const entries = collectLocalSelectorCacheEntries(this.storage);
10254
+ const entries = collectLocalSelectorCacheEntries(this.storage, {
10255
+ debug: Boolean(this.config.debug)
10256
+ });
9954
10257
  if (!entries.length) return;
9955
10258
  await this.cloud.sessionClient.importSelectorCache({
9956
10259
  entries
@@ -9959,9 +10262,12 @@ var Opensteer = class _Opensteer {
9959
10262
  async goto(url, options) {
9960
10263
  if (this.cloud) {
9961
10264
  await this.invokeCloudActionAndResetCache("goto", { url, options });
9962
- await this.syncCloudPageRef({ expectedUrl: url }).catch(
9963
- () => void 0
9964
- );
10265
+ await this.syncCloudPageRef({ expectedUrl: url }).catch((error) => {
10266
+ this.logDebugError(
10267
+ "cloud page reference sync after goto failed",
10268
+ error
10269
+ );
10270
+ });
9965
10271
  return;
9966
10272
  }
9967
10273
  const { waitUntil = "domcontentloaded", ...rest } = options ?? {};
@@ -10473,7 +10779,12 @@ var Opensteer = class _Opensteer {
10473
10779
  }
10474
10780
  );
10475
10781
  await this.syncCloudPageRef({ expectedUrl: result.url }).catch(
10476
- () => void 0
10782
+ (error) => {
10783
+ this.logDebugError(
10784
+ "cloud page reference sync after newTab failed",
10785
+ error
10786
+ );
10787
+ }
10477
10788
  );
10478
10789
  return result;
10479
10790
  }
@@ -10485,7 +10796,12 @@ var Opensteer = class _Opensteer {
10485
10796
  async switchTab(index) {
10486
10797
  if (this.cloud) {
10487
10798
  await this.invokeCloudActionAndResetCache("switchTab", { index });
10488
- await this.syncCloudPageRef().catch(() => void 0);
10799
+ await this.syncCloudPageRef().catch((error) => {
10800
+ this.logDebugError(
10801
+ "cloud page reference sync after switchTab failed",
10802
+ error
10803
+ );
10804
+ });
10489
10805
  return;
10490
10806
  }
10491
10807
  const page = await switchTab(this.context, index);
@@ -10495,7 +10811,12 @@ var Opensteer = class _Opensteer {
10495
10811
  async closeTab(index) {
10496
10812
  if (this.cloud) {
10497
10813
  await this.invokeCloudActionAndResetCache("closeTab", { index });
10498
- await this.syncCloudPageRef().catch(() => void 0);
10814
+ await this.syncCloudPageRef().catch((error) => {
10815
+ this.logDebugError(
10816
+ "cloud page reference sync after closeTab failed",
10817
+ error
10818
+ );
10819
+ });
10499
10820
  return;
10500
10821
  }
10501
10822
  const newPage = await closeTab(this.context, this.page, index);
@@ -10669,8 +10990,12 @@ var Opensteer = class _Opensteer {
10669
10990
  }
10670
10991
  return await counterFn(handle);
10671
10992
  } catch (err) {
10672
- const message = err instanceof Error ? err.message : `${method} failed.`;
10673
- throw new Error(message);
10993
+ if (err instanceof Error) {
10994
+ throw err;
10995
+ }
10996
+ throw new Error(
10997
+ `${method} failed. ${extractErrorMessage(err, "Unknown error.")}`
10998
+ );
10674
10999
  } finally {
10675
11000
  await handle.dispose();
10676
11001
  }
@@ -10797,6 +11122,9 @@ var Opensteer = class _Opensteer {
10797
11122
  if (this.cloud) {
10798
11123
  return await this.invokeCloudAction("extract", options);
10799
11124
  }
11125
+ if (options.schema !== void 0) {
11126
+ assertValidExtractSchemaRoot(options.schema);
11127
+ }
10800
11128
  const storageKey = this.resolveStorageKey(options.description);
10801
11129
  const schemaHash = options.schema ? computeSchemaHash(options.schema) : null;
10802
11130
  const stored = storageKey ? this.storage.readSelector(storageKey) : null;
@@ -10805,7 +11133,7 @@ var Opensteer = class _Opensteer {
10805
11133
  try {
10806
11134
  payload = normalizePersistedExtractPayload(stored.path);
10807
11135
  } catch (err) {
10808
- const message = err instanceof Error ? err.message : "Unknown error";
11136
+ const message = extractErrorMessage(err, "Unknown error.");
10809
11137
  const selectorFile = storageKey ? this.storage.getSelectorPath(storageKey) : "unknown selector file";
10810
11138
  throw new Error(
10811
11139
  `Cached extraction selector is invalid for the current schema at "${selectorFile}". Delete the cached selector and rerun extraction. ${message}`
@@ -10822,7 +11150,16 @@ var Opensteer = class _Opensteer {
10822
11150
  fields.push(...schemaFields);
10823
11151
  }
10824
11152
  if (!fields.length) {
10825
- const planResult = await this.parseAiExtractPlan(options);
11153
+ let planResult;
11154
+ try {
11155
+ planResult = await this.parseAiExtractPlan(options);
11156
+ } catch (error) {
11157
+ const message = extractErrorMessage(error, "Unknown error.");
11158
+ const contextMessage = options.schema ? "Schema extraction did not resolve deterministic field targets, so Opensteer attempted AI extraction planning." : "Opensteer attempted AI extraction planning.";
11159
+ throw new Error(`${contextMessage} ${message}`, {
11160
+ cause: error
11161
+ });
11162
+ }
10826
11163
  if (planResult.fields.length) {
10827
11164
  fields.push(...planResult.fields);
10828
11165
  } else if (planResult.data !== void 0) {
@@ -11484,12 +11821,6 @@ var Opensteer = class _Opensteer {
11484
11821
  };
11485
11822
  }
11486
11823
  async buildFieldTargetsFromSchema(schema) {
11487
- if (!schema || typeof schema !== "object") {
11488
- return [];
11489
- }
11490
- if (Array.isArray(schema)) {
11491
- return [];
11492
- }
11493
11824
  const fields = [];
11494
11825
  await this.collectFieldTargetsFromSchemaObject(
11495
11826
  schema,
@@ -11563,6 +11894,10 @@ var Opensteer = class _Opensteer {
11563
11894
  path: path5,
11564
11895
  attribute: normalized.attribute
11565
11896
  });
11897
+ } else {
11898
+ throw new Error(
11899
+ `Extraction schema field "${fieldKey}" uses selector "${normalized.selector}", but no matching element path could be built from the current page snapshot.`
11900
+ );
11566
11901
  }
11567
11902
  return;
11568
11903
  }
@@ -11913,13 +12248,28 @@ function countNonNullLeaves(value) {
11913
12248
  function isPrimitiveLike(value) {
11914
12249
  return value == null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
11915
12250
  }
12251
+ function assertValidExtractSchemaRoot(schema) {
12252
+ if (!schema || typeof schema !== "object") {
12253
+ throw new Error(
12254
+ "Invalid extraction schema: expected a JSON object at the top level."
12255
+ );
12256
+ }
12257
+ if (Array.isArray(schema)) {
12258
+ throw new Error(
12259
+ 'Invalid extraction schema: top-level arrays are not supported. Wrap array fields in an object (for example {"items":[...]}).'
12260
+ );
12261
+ }
12262
+ }
11916
12263
  function parseAiExtractResponse(response) {
11917
12264
  if (typeof response === "string") {
11918
12265
  const trimmed = stripCodeFence(response);
11919
12266
  try {
11920
12267
  return JSON.parse(trimmed);
11921
12268
  } catch {
11922
- throw new Error("LLM extraction returned a non-JSON string.");
12269
+ const preview = summarizeForError(trimmed);
12270
+ throw new Error(
12271
+ `LLM extraction returned a non-JSON response.${preview ? ` Preview: "${preview}"` : ""}`
12272
+ );
11923
12273
  }
11924
12274
  }
11925
12275
  if (response && typeof response === "object") {
@@ -11944,6 +12294,12 @@ function stripCodeFence(input) {
11944
12294
  if (lastFence === -1) return withoutHeader.trim();
11945
12295
  return withoutHeader.slice(0, lastFence).trim();
11946
12296
  }
12297
+ function summarizeForError(input, maxLength = 180) {
12298
+ const compact = input.replace(/\s+/g, " ").trim();
12299
+ if (!compact) return "";
12300
+ if (compact.length <= maxLength) return compact;
12301
+ return `${compact.slice(0, maxLength)}...`;
12302
+ }
11947
12303
  function getScrollDelta2(options) {
11948
12304
  const amount = typeof options.amount === "number" ? options.amount : 600;
11949
12305
  const absoluteAmount = Math.abs(amount);
@@ -11970,6 +12326,7 @@ function buildLocalRunId(namespace) {
11970
12326
  }
11971
12327
 
11972
12328
  export {
12329
+ normalizeError,
11973
12330
  normalizeNamespace,
11974
12331
  resolveNamespaceDir,
11975
12332
  waitForVisualStability,