opensteer 0.4.13 → 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.
- package/README.md +5 -0
- package/bin/opensteer.mjs +42 -6
- package/dist/{chunk-QTGJO7RC.js → chunk-QHZFY3ZK.js} +9 -0
- package/dist/{chunk-V4OOJO4S.js → chunk-SPHS6YWD.js} +1 -1
- package/dist/{chunk-JSH3VLMH.js → chunk-WXUXG76V.js} +404 -47
- package/dist/{chunk-UIUDSWZV.js → chunk-YIQDOALV.js} +1 -1
- package/dist/cli/server.cjs +497 -67
- package/dist/cli/server.js +90 -24
- package/dist/{extractor-I6TJPTXV.js → extractor-CZFCFUME.js} +2 -2
- package/dist/index.cjs +410 -45
- package/dist/index.d.cts +8 -2
- package/dist/index.d.ts +8 -2
- package/dist/index.js +4 -4
- package/dist/{resolver-HVZJQZ32.js → resolver-ZREUOOTV.js} +2 -2
- package/package.json +3 -3
|
@@ -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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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."
|
|
@@ -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-
|
|
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-
|
|
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
|
-
|
|
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(() =>
|
|
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
|
-
(
|
|
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
|
-
() =>
|
|
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(() =>
|
|
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(() =>
|
|
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
|
-
|
|
10673
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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,
|