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
package/dist/cli/server.cjs
CHANGED
|
@@ -108,6 +108,15 @@ function resolveProviderInfo(modelStr) {
|
|
|
108
108
|
return info;
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
|
+
const slash = modelStr.indexOf("/");
|
|
112
|
+
if (slash > 0) {
|
|
113
|
+
const provider = modelStr.slice(0, slash).trim().toLowerCase();
|
|
114
|
+
if (provider) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
`Unsupported model provider prefix "${provider}" in model "${modelStr}". Use one of: openai, anthropic, google, xai, groq.`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
111
120
|
return { pkg: "@ai-sdk/openai", providerFn: "openai" };
|
|
112
121
|
}
|
|
113
122
|
function stripProviderPrefix(modelStr) {
|
|
@@ -821,6 +830,232 @@ var import_path3 = __toESM(require("path"), 1);
|
|
|
821
830
|
var import_url = require("url");
|
|
822
831
|
var import_dotenv = require("dotenv");
|
|
823
832
|
|
|
833
|
+
// src/error-normalization.ts
|
|
834
|
+
function extractErrorMessage(error, fallback = "Unknown error.") {
|
|
835
|
+
if (error instanceof Error) {
|
|
836
|
+
const message = error.message.trim();
|
|
837
|
+
if (message) return message;
|
|
838
|
+
const name = error.name.trim();
|
|
839
|
+
if (name) return name;
|
|
840
|
+
}
|
|
841
|
+
if (typeof error === "string" && error.trim()) {
|
|
842
|
+
return error.trim();
|
|
843
|
+
}
|
|
844
|
+
const record = asRecord(error);
|
|
845
|
+
const recordMessage = toNonEmptyString(record?.message) || toNonEmptyString(record?.error);
|
|
846
|
+
if (recordMessage) {
|
|
847
|
+
return recordMessage;
|
|
848
|
+
}
|
|
849
|
+
return fallback;
|
|
850
|
+
}
|
|
851
|
+
function normalizeError(error, fallback = "Unknown error.", maxCauseDepth = 2) {
|
|
852
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
853
|
+
return normalizeErrorInternal(error, fallback, maxCauseDepth, seen);
|
|
854
|
+
}
|
|
855
|
+
function normalizeErrorInternal(error, fallback, depthRemaining, seen) {
|
|
856
|
+
const record = asRecord(error);
|
|
857
|
+
if (record) {
|
|
858
|
+
if (seen.has(record)) {
|
|
859
|
+
return {
|
|
860
|
+
message: extractErrorMessage(error, fallback)
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
seen.add(record);
|
|
864
|
+
}
|
|
865
|
+
const message = extractErrorMessage(error, fallback);
|
|
866
|
+
const code = extractCode(error);
|
|
867
|
+
const name = extractName(error);
|
|
868
|
+
const details = extractDetails(error);
|
|
869
|
+
if (depthRemaining <= 0) {
|
|
870
|
+
return compactErrorInfo({
|
|
871
|
+
message,
|
|
872
|
+
...code ? { code } : {},
|
|
873
|
+
...name ? { name } : {},
|
|
874
|
+
...details ? { details } : {}
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
const cause = extractCause(error);
|
|
878
|
+
if (!cause) {
|
|
879
|
+
return compactErrorInfo({
|
|
880
|
+
message,
|
|
881
|
+
...code ? { code } : {},
|
|
882
|
+
...name ? { name } : {},
|
|
883
|
+
...details ? { details } : {}
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
const normalizedCause = normalizeErrorInternal(
|
|
887
|
+
cause,
|
|
888
|
+
"Caused by an unknown error.",
|
|
889
|
+
depthRemaining - 1,
|
|
890
|
+
seen
|
|
891
|
+
);
|
|
892
|
+
return compactErrorInfo({
|
|
893
|
+
message,
|
|
894
|
+
...code ? { code } : {},
|
|
895
|
+
...name ? { name } : {},
|
|
896
|
+
...details ? { details } : {},
|
|
897
|
+
cause: normalizedCause
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
function compactErrorInfo(info) {
|
|
901
|
+
const safeDetails = toJsonSafeRecord(info.details);
|
|
902
|
+
return {
|
|
903
|
+
message: info.message,
|
|
904
|
+
...info.code ? { code: info.code } : {},
|
|
905
|
+
...info.name ? { name: info.name } : {},
|
|
906
|
+
...safeDetails ? { details: safeDetails } : {},
|
|
907
|
+
...info.cause ? { cause: info.cause } : {}
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
function extractCode(error) {
|
|
911
|
+
const record = asRecord(error);
|
|
912
|
+
const raw = record?.code;
|
|
913
|
+
if (typeof raw === "string" && raw.trim()) {
|
|
914
|
+
return raw.trim();
|
|
915
|
+
}
|
|
916
|
+
if (typeof raw === "number" && Number.isFinite(raw)) {
|
|
917
|
+
return String(raw);
|
|
918
|
+
}
|
|
919
|
+
return void 0;
|
|
920
|
+
}
|
|
921
|
+
function extractName(error) {
|
|
922
|
+
if (error instanceof Error && error.name.trim()) {
|
|
923
|
+
return error.name.trim();
|
|
924
|
+
}
|
|
925
|
+
const record = asRecord(error);
|
|
926
|
+
return toNonEmptyString(record?.name);
|
|
927
|
+
}
|
|
928
|
+
function extractDetails(error) {
|
|
929
|
+
const record = asRecord(error);
|
|
930
|
+
if (!record) return void 0;
|
|
931
|
+
const details = {};
|
|
932
|
+
const rawDetails = asRecord(record.details);
|
|
933
|
+
if (rawDetails) {
|
|
934
|
+
Object.assign(details, rawDetails);
|
|
935
|
+
}
|
|
936
|
+
const action = toNonEmptyString(record.action);
|
|
937
|
+
if (action) {
|
|
938
|
+
details.action = action;
|
|
939
|
+
}
|
|
940
|
+
const selectorUsed = toNonEmptyString(record.selectorUsed);
|
|
941
|
+
if (selectorUsed) {
|
|
942
|
+
details.selectorUsed = selectorUsed;
|
|
943
|
+
}
|
|
944
|
+
if (typeof record.status === "number" && Number.isFinite(record.status)) {
|
|
945
|
+
details.status = record.status;
|
|
946
|
+
}
|
|
947
|
+
const failure = asRecord(record.failure);
|
|
948
|
+
if (failure) {
|
|
949
|
+
const failureCode = toNonEmptyString(failure.code);
|
|
950
|
+
const classificationSource = toNonEmptyString(
|
|
951
|
+
failure.classificationSource
|
|
952
|
+
);
|
|
953
|
+
const failureDetails = asRecord(failure.details);
|
|
954
|
+
if (failureCode || classificationSource || failureDetails) {
|
|
955
|
+
details.actionFailure = {
|
|
956
|
+
...failureCode ? { code: failureCode } : {},
|
|
957
|
+
...classificationSource ? { classificationSource } : {},
|
|
958
|
+
...failureDetails ? { details: failureDetails } : {}
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
return Object.keys(details).length ? details : void 0;
|
|
963
|
+
}
|
|
964
|
+
function extractCause(error) {
|
|
965
|
+
if (error instanceof Error) {
|
|
966
|
+
return error.cause;
|
|
967
|
+
}
|
|
968
|
+
const record = asRecord(error);
|
|
969
|
+
return record?.cause;
|
|
970
|
+
}
|
|
971
|
+
function asRecord(value) {
|
|
972
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
973
|
+
return null;
|
|
974
|
+
}
|
|
975
|
+
return value;
|
|
976
|
+
}
|
|
977
|
+
function toNonEmptyString(value) {
|
|
978
|
+
if (typeof value !== "string") return void 0;
|
|
979
|
+
const normalized = value.trim();
|
|
980
|
+
return normalized.length ? normalized : void 0;
|
|
981
|
+
}
|
|
982
|
+
function toJsonSafeRecord(value) {
|
|
983
|
+
if (!value) return void 0;
|
|
984
|
+
const sanitized = toJsonSafeValue(value, /* @__PURE__ */ new WeakSet());
|
|
985
|
+
if (!sanitized || typeof sanitized !== "object" || Array.isArray(sanitized)) {
|
|
986
|
+
return void 0;
|
|
987
|
+
}
|
|
988
|
+
const record = sanitized;
|
|
989
|
+
return Object.keys(record).length > 0 ? record : void 0;
|
|
990
|
+
}
|
|
991
|
+
function toJsonSafeValue(value, seen) {
|
|
992
|
+
if (value === null) return null;
|
|
993
|
+
if (typeof value === "string" || typeof value === "boolean") {
|
|
994
|
+
return value;
|
|
995
|
+
}
|
|
996
|
+
if (typeof value === "number") {
|
|
997
|
+
return Number.isFinite(value) ? value : null;
|
|
998
|
+
}
|
|
999
|
+
if (typeof value === "bigint") {
|
|
1000
|
+
return value.toString();
|
|
1001
|
+
}
|
|
1002
|
+
if (value === void 0 || typeof value === "function" || typeof value === "symbol") {
|
|
1003
|
+
return void 0;
|
|
1004
|
+
}
|
|
1005
|
+
if (value instanceof Date) {
|
|
1006
|
+
return Number.isNaN(value.getTime()) ? null : value.toISOString();
|
|
1007
|
+
}
|
|
1008
|
+
if (Array.isArray(value)) {
|
|
1009
|
+
if (seen.has(value)) return "[Circular]";
|
|
1010
|
+
seen.add(value);
|
|
1011
|
+
const output = value.map((item) => {
|
|
1012
|
+
const next = toJsonSafeValue(item, seen);
|
|
1013
|
+
return next === void 0 ? null : next;
|
|
1014
|
+
});
|
|
1015
|
+
seen.delete(value);
|
|
1016
|
+
return output;
|
|
1017
|
+
}
|
|
1018
|
+
if (value instanceof Set) {
|
|
1019
|
+
if (seen.has(value)) return "[Circular]";
|
|
1020
|
+
seen.add(value);
|
|
1021
|
+
const output = Array.from(value, (item) => {
|
|
1022
|
+
const next = toJsonSafeValue(item, seen);
|
|
1023
|
+
return next === void 0 ? null : next;
|
|
1024
|
+
});
|
|
1025
|
+
seen.delete(value);
|
|
1026
|
+
return output;
|
|
1027
|
+
}
|
|
1028
|
+
if (value instanceof Map) {
|
|
1029
|
+
if (seen.has(value)) return "[Circular]";
|
|
1030
|
+
seen.add(value);
|
|
1031
|
+
const output = {};
|
|
1032
|
+
for (const [key, item] of value.entries()) {
|
|
1033
|
+
const normalizedKey = String(key);
|
|
1034
|
+
const next = toJsonSafeValue(item, seen);
|
|
1035
|
+
if (next !== void 0) {
|
|
1036
|
+
output[normalizedKey] = next;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
seen.delete(value);
|
|
1040
|
+
return output;
|
|
1041
|
+
}
|
|
1042
|
+
if (typeof value === "object") {
|
|
1043
|
+
const objectValue = value;
|
|
1044
|
+
if (seen.has(objectValue)) return "[Circular]";
|
|
1045
|
+
seen.add(objectValue);
|
|
1046
|
+
const output = {};
|
|
1047
|
+
for (const [key, item] of Object.entries(objectValue)) {
|
|
1048
|
+
const next = toJsonSafeValue(item, seen);
|
|
1049
|
+
if (next !== void 0) {
|
|
1050
|
+
output[key] = next;
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
seen.delete(objectValue);
|
|
1054
|
+
return output;
|
|
1055
|
+
}
|
|
1056
|
+
return void 0;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
824
1059
|
// src/storage/namespace.ts
|
|
825
1060
|
var import_path2 = __toESM(require("path"), 1);
|
|
826
1061
|
var DEFAULT_NAMESPACE = "default";
|
|
@@ -884,11 +1119,12 @@ function dotenvFileOrder(nodeEnv) {
|
|
|
884
1119
|
files.push(".env");
|
|
885
1120
|
return files;
|
|
886
1121
|
}
|
|
887
|
-
function loadDotenvValues(rootDir, baseEnv) {
|
|
1122
|
+
function loadDotenvValues(rootDir, baseEnv, options = {}) {
|
|
888
1123
|
const values = {};
|
|
889
1124
|
if (parseBool(baseEnv.OPENSTEER_DISABLE_DOTENV_AUTOLOAD) === true) {
|
|
890
1125
|
return values;
|
|
891
1126
|
}
|
|
1127
|
+
const debug = options.debug ?? parseBool(baseEnv.OPENSTEER_DEBUG) === true;
|
|
892
1128
|
const baseDir = import_path3.default.resolve(rootDir);
|
|
893
1129
|
const nodeEnv = baseEnv.NODE_ENV?.trim() || "";
|
|
894
1130
|
for (const filename of dotenvFileOrder(nodeEnv)) {
|
|
@@ -902,15 +1138,24 @@ function loadDotenvValues(rootDir, baseEnv) {
|
|
|
902
1138
|
values[key] = value;
|
|
903
1139
|
}
|
|
904
1140
|
}
|
|
905
|
-
} catch {
|
|
1141
|
+
} catch (error) {
|
|
1142
|
+
const message = extractErrorMessage(
|
|
1143
|
+
error,
|
|
1144
|
+
"Unable to read or parse dotenv file."
|
|
1145
|
+
);
|
|
1146
|
+
if (debug) {
|
|
1147
|
+
console.warn(
|
|
1148
|
+
`[opensteer] failed to load dotenv file "${filePath}": ${message}`
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
906
1151
|
continue;
|
|
907
1152
|
}
|
|
908
1153
|
}
|
|
909
1154
|
return values;
|
|
910
1155
|
}
|
|
911
|
-
function resolveEnv(rootDir) {
|
|
1156
|
+
function resolveEnv(rootDir, options = {}) {
|
|
912
1157
|
const baseEnv = process.env;
|
|
913
|
-
const dotenvValues = loadDotenvValues(rootDir, baseEnv);
|
|
1158
|
+
const dotenvValues = loadDotenvValues(rootDir, baseEnv, options);
|
|
914
1159
|
return {
|
|
915
1160
|
...dotenvValues,
|
|
916
1161
|
...baseEnv
|
|
@@ -954,13 +1199,22 @@ function assertNoLegacyRuntimeConfig(source, config) {
|
|
|
954
1199
|
);
|
|
955
1200
|
}
|
|
956
1201
|
}
|
|
957
|
-
function loadConfigFile(rootDir) {
|
|
1202
|
+
function loadConfigFile(rootDir, options = {}) {
|
|
958
1203
|
const configPath = import_path3.default.join(rootDir, ".opensteer", "config.json");
|
|
959
1204
|
if (!import_fs.default.existsSync(configPath)) return {};
|
|
960
1205
|
try {
|
|
961
1206
|
const raw = import_fs.default.readFileSync(configPath, "utf8");
|
|
962
1207
|
return JSON.parse(raw);
|
|
963
|
-
} catch {
|
|
1208
|
+
} catch (error) {
|
|
1209
|
+
const message = extractErrorMessage(
|
|
1210
|
+
error,
|
|
1211
|
+
"Unable to read or parse config file."
|
|
1212
|
+
);
|
|
1213
|
+
if (options.debug) {
|
|
1214
|
+
console.warn(
|
|
1215
|
+
`[opensteer] failed to load config file "${configPath}": ${message}`
|
|
1216
|
+
);
|
|
1217
|
+
}
|
|
964
1218
|
return {};
|
|
965
1219
|
}
|
|
966
1220
|
}
|
|
@@ -1092,6 +1346,8 @@ function resolveCloudSelection(config, env = process.env) {
|
|
|
1092
1346
|
};
|
|
1093
1347
|
}
|
|
1094
1348
|
function resolveConfig(input = {}) {
|
|
1349
|
+
const processEnv = process.env;
|
|
1350
|
+
const debugHint = typeof input.debug === "boolean" ? input.debug : parseBool(processEnv.OPENSTEER_DEBUG) === true;
|
|
1095
1351
|
const initialRootDir = input.storage?.rootDir ?? process.cwd();
|
|
1096
1352
|
const runtimeDefaults = mergeDeep(DEFAULT_CONFIG, {
|
|
1097
1353
|
storage: {
|
|
@@ -1100,12 +1356,16 @@ function resolveConfig(input = {}) {
|
|
|
1100
1356
|
});
|
|
1101
1357
|
assertNoLegacyAiConfig("Opensteer constructor config", input);
|
|
1102
1358
|
assertNoLegacyRuntimeConfig("Opensteer constructor config", input);
|
|
1103
|
-
const fileConfig = loadConfigFile(initialRootDir
|
|
1359
|
+
const fileConfig = loadConfigFile(initialRootDir, {
|
|
1360
|
+
debug: debugHint
|
|
1361
|
+
});
|
|
1104
1362
|
assertNoLegacyAiConfig(".opensteer/config.json", fileConfig);
|
|
1105
1363
|
assertNoLegacyRuntimeConfig(".opensteer/config.json", fileConfig);
|
|
1106
1364
|
const fileRootDir = typeof fileConfig.storage?.rootDir === "string" ? fileConfig.storage.rootDir : void 0;
|
|
1107
1365
|
const envRootDir = input.storage?.rootDir ?? fileRootDir ?? initialRootDir;
|
|
1108
|
-
const env = resolveEnv(envRootDir
|
|
1366
|
+
const env = resolveEnv(envRootDir, {
|
|
1367
|
+
debug: debugHint
|
|
1368
|
+
});
|
|
1109
1369
|
if (env.OPENSTEER_AI_MODEL) {
|
|
1110
1370
|
throw new Error(
|
|
1111
1371
|
"OPENSTEER_AI_MODEL is no longer supported. Use OPENSTEER_MODEL instead."
|
|
@@ -1789,9 +2049,11 @@ function createEmptyRegistry(name) {
|
|
|
1789
2049
|
var LocalSelectorStorage = class {
|
|
1790
2050
|
rootDir;
|
|
1791
2051
|
namespace;
|
|
1792
|
-
|
|
2052
|
+
debug;
|
|
2053
|
+
constructor(rootDir, namespace, options = {}) {
|
|
1793
2054
|
this.rootDir = rootDir;
|
|
1794
2055
|
this.namespace = normalizeNamespace(namespace);
|
|
2056
|
+
this.debug = options.debug === true;
|
|
1795
2057
|
}
|
|
1796
2058
|
getRootDir() {
|
|
1797
2059
|
return this.rootDir;
|
|
@@ -1825,7 +2087,16 @@ var LocalSelectorStorage = class {
|
|
|
1825
2087
|
try {
|
|
1826
2088
|
const raw = import_fs2.default.readFileSync(file, "utf8");
|
|
1827
2089
|
return JSON.parse(raw);
|
|
1828
|
-
} catch {
|
|
2090
|
+
} catch (error) {
|
|
2091
|
+
const message = extractErrorMessage(
|
|
2092
|
+
error,
|
|
2093
|
+
"Unable to parse selector registry JSON."
|
|
2094
|
+
);
|
|
2095
|
+
if (this.debug) {
|
|
2096
|
+
console.warn(
|
|
2097
|
+
`[opensteer] failed to read selector registry "${file}": ${message}`
|
|
2098
|
+
);
|
|
2099
|
+
}
|
|
1829
2100
|
return createEmptyRegistry(this.namespace);
|
|
1830
2101
|
}
|
|
1831
2102
|
}
|
|
@@ -1842,7 +2113,16 @@ var LocalSelectorStorage = class {
|
|
|
1842
2113
|
try {
|
|
1843
2114
|
const raw = import_fs2.default.readFileSync(file, "utf8");
|
|
1844
2115
|
return JSON.parse(raw);
|
|
1845
|
-
} catch {
|
|
2116
|
+
} catch (error) {
|
|
2117
|
+
const message = extractErrorMessage(
|
|
2118
|
+
error,
|
|
2119
|
+
"Unable to parse selector file JSON."
|
|
2120
|
+
);
|
|
2121
|
+
if (this.debug) {
|
|
2122
|
+
console.warn(
|
|
2123
|
+
`[opensteer] failed to read selector file "${file}": ${message}`
|
|
2124
|
+
);
|
|
2125
|
+
}
|
|
1846
2126
|
return null;
|
|
1847
2127
|
}
|
|
1848
2128
|
}
|
|
@@ -5092,7 +5372,7 @@ function defaultActionFailureMessage(action) {
|
|
|
5092
5372
|
function classifyActionFailure(input) {
|
|
5093
5373
|
const typed = classifyTypedError(input.error);
|
|
5094
5374
|
if (typed) return typed;
|
|
5095
|
-
const message =
|
|
5375
|
+
const message = extractErrorMessage2(input.error, input.fallbackMessage);
|
|
5096
5376
|
const fromCallLog = classifyFromPlaywrightMessage(message, input.probe);
|
|
5097
5377
|
if (fromCallLog) return fromCallLog;
|
|
5098
5378
|
const fromProbe = classifyFromProbe(input.probe);
|
|
@@ -5101,7 +5381,7 @@ function classifyActionFailure(input) {
|
|
|
5101
5381
|
if (fromHeuristic) return fromHeuristic;
|
|
5102
5382
|
return buildFailure({
|
|
5103
5383
|
code: "UNKNOWN",
|
|
5104
|
-
message: ensureMessage(input.fallbackMessage
|
|
5384
|
+
message: ensureMessage(message, input.fallbackMessage),
|
|
5105
5385
|
classificationSource: "unknown"
|
|
5106
5386
|
});
|
|
5107
5387
|
}
|
|
@@ -5361,13 +5641,22 @@ function defaultRetryableForCode(code) {
|
|
|
5361
5641
|
return true;
|
|
5362
5642
|
}
|
|
5363
5643
|
}
|
|
5364
|
-
function
|
|
5644
|
+
function extractErrorMessage2(error, fallbackMessage) {
|
|
5365
5645
|
if (error instanceof Error && error.message.trim()) {
|
|
5366
5646
|
return error.message;
|
|
5367
5647
|
}
|
|
5368
5648
|
if (typeof error === "string" && error.trim()) {
|
|
5369
5649
|
return error.trim();
|
|
5370
5650
|
}
|
|
5651
|
+
if (error && typeof error === "object" && !Array.isArray(error)) {
|
|
5652
|
+
const record = error;
|
|
5653
|
+
if (typeof record.message === "string" && record.message.trim()) {
|
|
5654
|
+
return record.message.trim();
|
|
5655
|
+
}
|
|
5656
|
+
if (typeof record.error === "string" && record.error.trim()) {
|
|
5657
|
+
return record.error.trim();
|
|
5658
|
+
}
|
|
5659
|
+
}
|
|
5371
5660
|
return ensureMessage(fallbackMessage, "Action failed.");
|
|
5372
5661
|
}
|
|
5373
5662
|
function ensureMessage(value, fallback) {
|
|
@@ -7677,7 +7966,8 @@ function withTokenQuery(wsUrl, token) {
|
|
|
7677
7966
|
// src/cloud/local-cache-sync.ts
|
|
7678
7967
|
var import_fs3 = __toESM(require("fs"), 1);
|
|
7679
7968
|
var import_path5 = __toESM(require("path"), 1);
|
|
7680
|
-
function collectLocalSelectorCacheEntries(storage) {
|
|
7969
|
+
function collectLocalSelectorCacheEntries(storage, options = {}) {
|
|
7970
|
+
const debug = options.debug === true;
|
|
7681
7971
|
const namespace = storage.getNamespace();
|
|
7682
7972
|
const namespaceDir = storage.getNamespaceDir();
|
|
7683
7973
|
if (!import_fs3.default.existsSync(namespaceDir)) return [];
|
|
@@ -7686,7 +7976,7 @@ function collectLocalSelectorCacheEntries(storage) {
|
|
|
7686
7976
|
for (const fileName of fileNames) {
|
|
7687
7977
|
if (fileName === "index.json" || !fileName.endsWith(".json")) continue;
|
|
7688
7978
|
const filePath = import_path5.default.join(namespaceDir, fileName);
|
|
7689
|
-
const selector = readSelectorFile(filePath);
|
|
7979
|
+
const selector = readSelectorFile(filePath, debug);
|
|
7690
7980
|
if (!selector) continue;
|
|
7691
7981
|
const descriptionHash = normalizeDescriptionHash(selector.id);
|
|
7692
7982
|
const method = normalizeMethod(selector.method);
|
|
@@ -7711,11 +8001,20 @@ function collectLocalSelectorCacheEntries(storage) {
|
|
|
7711
8001
|
}
|
|
7712
8002
|
return dedupeNewest(entries);
|
|
7713
8003
|
}
|
|
7714
|
-
function readSelectorFile(filePath) {
|
|
8004
|
+
function readSelectorFile(filePath, debug) {
|
|
7715
8005
|
try {
|
|
7716
8006
|
const raw = import_fs3.default.readFileSync(filePath, "utf8");
|
|
7717
8007
|
return JSON.parse(raw);
|
|
7718
|
-
} catch {
|
|
8008
|
+
} catch (error) {
|
|
8009
|
+
const message = extractErrorMessage(
|
|
8010
|
+
error,
|
|
8011
|
+
"Unable to parse selector cache file JSON."
|
|
8012
|
+
);
|
|
8013
|
+
if (debug) {
|
|
8014
|
+
console.warn(
|
|
8015
|
+
`[opensteer] failed to read local selector cache file "${filePath}": ${message}`
|
|
8016
|
+
);
|
|
8017
|
+
}
|
|
7719
8018
|
return null;
|
|
7720
8019
|
}
|
|
7721
8020
|
}
|
|
@@ -9923,7 +10222,9 @@ var Opensteer = class _Opensteer {
|
|
|
9923
10222
|
this.aiExtract = this.createLazyExtractCallback(model);
|
|
9924
10223
|
const rootDir = resolved.storage?.rootDir || process.cwd();
|
|
9925
10224
|
this.namespace = resolveNamespace(resolved, rootDir);
|
|
9926
|
-
this.storage = new LocalSelectorStorage(rootDir, this.namespace
|
|
10225
|
+
this.storage = new LocalSelectorStorage(rootDir, this.namespace, {
|
|
10226
|
+
debug: Boolean(resolved.debug)
|
|
10227
|
+
});
|
|
9927
10228
|
this.pool = new BrowserPool(resolved.browser || {});
|
|
9928
10229
|
if (cloudSelection.cloud) {
|
|
9929
10230
|
const cloudConfig = resolved.cloud && typeof resolved.cloud === "object" ? resolved.cloud : void 0;
|
|
@@ -9942,6 +10243,14 @@ var Opensteer = class _Opensteer {
|
|
|
9942
10243
|
this.cloud = null;
|
|
9943
10244
|
}
|
|
9944
10245
|
}
|
|
10246
|
+
logDebugError(context, error) {
|
|
10247
|
+
if (!this.config.debug) return;
|
|
10248
|
+
const normalized = normalizeError(error, "Unknown error.");
|
|
10249
|
+
const codeSuffix = normalized.code && normalized.code.trim() ? ` [${normalized.code.trim()}]` : "";
|
|
10250
|
+
console.warn(
|
|
10251
|
+
`[opensteer] ${context}: ${normalized.message}${codeSuffix}`
|
|
10252
|
+
);
|
|
10253
|
+
}
|
|
9945
10254
|
createLazyResolveCallback(model) {
|
|
9946
10255
|
let resolverPromise = null;
|
|
9947
10256
|
return async (...args) => {
|
|
@@ -10032,7 +10341,8 @@ var Opensteer = class _Opensteer {
|
|
|
10032
10341
|
let tabs;
|
|
10033
10342
|
try {
|
|
10034
10343
|
tabs = await this.invokeCloudAction("tabs", {});
|
|
10035
|
-
} catch {
|
|
10344
|
+
} catch (error) {
|
|
10345
|
+
this.logDebugError("cloud page reference sync (tabs lookup) failed", error);
|
|
10036
10346
|
return;
|
|
10037
10347
|
}
|
|
10038
10348
|
if (!tabs.length) {
|
|
@@ -10170,12 +10480,7 @@ var Opensteer = class _Opensteer {
|
|
|
10170
10480
|
try {
|
|
10171
10481
|
await this.syncLocalSelectorCacheToCloud();
|
|
10172
10482
|
} catch (error) {
|
|
10173
|
-
|
|
10174
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
10175
|
-
console.warn(
|
|
10176
|
-
`[opensteer] cloud selector cache sync failed: ${message}`
|
|
10177
|
-
);
|
|
10178
|
-
}
|
|
10483
|
+
this.logDebugError("cloud selector cache sync failed", error);
|
|
10179
10484
|
}
|
|
10180
10485
|
localRunId = this.cloud.localRunId || buildLocalRunId(this.namespace);
|
|
10181
10486
|
this.cloud.localRunId = localRunId;
|
|
@@ -10207,7 +10512,12 @@ var Opensteer = class _Opensteer {
|
|
|
10207
10512
|
this.cloud.actionClient = actionClient;
|
|
10208
10513
|
this.cloud.sessionId = sessionId;
|
|
10209
10514
|
this.cloud.cloudSessionUrl = session3.cloudSessionUrl;
|
|
10210
|
-
await this.syncCloudPageRef().catch(() =>
|
|
10515
|
+
await this.syncCloudPageRef().catch((error) => {
|
|
10516
|
+
this.logDebugError(
|
|
10517
|
+
"cloud page reference sync after launch failed",
|
|
10518
|
+
error
|
|
10519
|
+
);
|
|
10520
|
+
});
|
|
10211
10521
|
this.announceCloudSession({
|
|
10212
10522
|
sessionId: session3.sessionId,
|
|
10213
10523
|
workspaceId: session3.cloudSession.workspaceId,
|
|
@@ -10294,7 +10604,9 @@ var Opensteer = class _Opensteer {
|
|
|
10294
10604
|
}
|
|
10295
10605
|
async syncLocalSelectorCacheToCloud() {
|
|
10296
10606
|
if (!this.cloud) return;
|
|
10297
|
-
const entries = collectLocalSelectorCacheEntries(this.storage
|
|
10607
|
+
const entries = collectLocalSelectorCacheEntries(this.storage, {
|
|
10608
|
+
debug: Boolean(this.config.debug)
|
|
10609
|
+
});
|
|
10298
10610
|
if (!entries.length) return;
|
|
10299
10611
|
await this.cloud.sessionClient.importSelectorCache({
|
|
10300
10612
|
entries
|
|
@@ -10303,9 +10615,12 @@ var Opensteer = class _Opensteer {
|
|
|
10303
10615
|
async goto(url, options) {
|
|
10304
10616
|
if (this.cloud) {
|
|
10305
10617
|
await this.invokeCloudActionAndResetCache("goto", { url, options });
|
|
10306
|
-
await this.syncCloudPageRef({ expectedUrl: url }).catch(
|
|
10307
|
-
(
|
|
10308
|
-
|
|
10618
|
+
await this.syncCloudPageRef({ expectedUrl: url }).catch((error) => {
|
|
10619
|
+
this.logDebugError(
|
|
10620
|
+
"cloud page reference sync after goto failed",
|
|
10621
|
+
error
|
|
10622
|
+
);
|
|
10623
|
+
});
|
|
10309
10624
|
return;
|
|
10310
10625
|
}
|
|
10311
10626
|
const { waitUntil = "domcontentloaded", ...rest } = options ?? {};
|
|
@@ -10817,7 +11132,12 @@ var Opensteer = class _Opensteer {
|
|
|
10817
11132
|
}
|
|
10818
11133
|
);
|
|
10819
11134
|
await this.syncCloudPageRef({ expectedUrl: result.url }).catch(
|
|
10820
|
-
() =>
|
|
11135
|
+
(error) => {
|
|
11136
|
+
this.logDebugError(
|
|
11137
|
+
"cloud page reference sync after newTab failed",
|
|
11138
|
+
error
|
|
11139
|
+
);
|
|
11140
|
+
}
|
|
10821
11141
|
);
|
|
10822
11142
|
return result;
|
|
10823
11143
|
}
|
|
@@ -10829,7 +11149,12 @@ var Opensteer = class _Opensteer {
|
|
|
10829
11149
|
async switchTab(index) {
|
|
10830
11150
|
if (this.cloud) {
|
|
10831
11151
|
await this.invokeCloudActionAndResetCache("switchTab", { index });
|
|
10832
|
-
await this.syncCloudPageRef().catch(() =>
|
|
11152
|
+
await this.syncCloudPageRef().catch((error) => {
|
|
11153
|
+
this.logDebugError(
|
|
11154
|
+
"cloud page reference sync after switchTab failed",
|
|
11155
|
+
error
|
|
11156
|
+
);
|
|
11157
|
+
});
|
|
10833
11158
|
return;
|
|
10834
11159
|
}
|
|
10835
11160
|
const page = await switchTab(this.context, index);
|
|
@@ -10839,7 +11164,12 @@ var Opensteer = class _Opensteer {
|
|
|
10839
11164
|
async closeTab(index) {
|
|
10840
11165
|
if (this.cloud) {
|
|
10841
11166
|
await this.invokeCloudActionAndResetCache("closeTab", { index });
|
|
10842
|
-
await this.syncCloudPageRef().catch(() =>
|
|
11167
|
+
await this.syncCloudPageRef().catch((error) => {
|
|
11168
|
+
this.logDebugError(
|
|
11169
|
+
"cloud page reference sync after closeTab failed",
|
|
11170
|
+
error
|
|
11171
|
+
);
|
|
11172
|
+
});
|
|
10843
11173
|
return;
|
|
10844
11174
|
}
|
|
10845
11175
|
const newPage = await closeTab(this.context, this.page, index);
|
|
@@ -11013,8 +11343,12 @@ var Opensteer = class _Opensteer {
|
|
|
11013
11343
|
}
|
|
11014
11344
|
return await counterFn(handle);
|
|
11015
11345
|
} catch (err) {
|
|
11016
|
-
|
|
11017
|
-
|
|
11346
|
+
if (err instanceof Error) {
|
|
11347
|
+
throw err;
|
|
11348
|
+
}
|
|
11349
|
+
throw new Error(
|
|
11350
|
+
`${method} failed. ${extractErrorMessage(err, "Unknown error.")}`
|
|
11351
|
+
);
|
|
11018
11352
|
} finally {
|
|
11019
11353
|
await handle.dispose();
|
|
11020
11354
|
}
|
|
@@ -11141,6 +11475,9 @@ var Opensteer = class _Opensteer {
|
|
|
11141
11475
|
if (this.cloud) {
|
|
11142
11476
|
return await this.invokeCloudAction("extract", options);
|
|
11143
11477
|
}
|
|
11478
|
+
if (options.schema !== void 0) {
|
|
11479
|
+
assertValidExtractSchemaRoot(options.schema);
|
|
11480
|
+
}
|
|
11144
11481
|
const storageKey = this.resolveStorageKey(options.description);
|
|
11145
11482
|
const schemaHash = options.schema ? computeSchemaHash(options.schema) : null;
|
|
11146
11483
|
const stored = storageKey ? this.storage.readSelector(storageKey) : null;
|
|
@@ -11149,7 +11486,7 @@ var Opensteer = class _Opensteer {
|
|
|
11149
11486
|
try {
|
|
11150
11487
|
payload = normalizePersistedExtractPayload(stored.path);
|
|
11151
11488
|
} catch (err) {
|
|
11152
|
-
const message = err
|
|
11489
|
+
const message = extractErrorMessage(err, "Unknown error.");
|
|
11153
11490
|
const selectorFile = storageKey ? this.storage.getSelectorPath(storageKey) : "unknown selector file";
|
|
11154
11491
|
throw new Error(
|
|
11155
11492
|
`Cached extraction selector is invalid for the current schema at "${selectorFile}". Delete the cached selector and rerun extraction. ${message}`
|
|
@@ -11166,7 +11503,16 @@ var Opensteer = class _Opensteer {
|
|
|
11166
11503
|
fields.push(...schemaFields);
|
|
11167
11504
|
}
|
|
11168
11505
|
if (!fields.length) {
|
|
11169
|
-
|
|
11506
|
+
let planResult;
|
|
11507
|
+
try {
|
|
11508
|
+
planResult = await this.parseAiExtractPlan(options);
|
|
11509
|
+
} catch (error) {
|
|
11510
|
+
const message = extractErrorMessage(error, "Unknown error.");
|
|
11511
|
+
const contextMessage = options.schema ? "Schema extraction did not resolve deterministic field targets, so Opensteer attempted AI extraction planning." : "Opensteer attempted AI extraction planning.";
|
|
11512
|
+
throw new Error(`${contextMessage} ${message}`, {
|
|
11513
|
+
cause: error
|
|
11514
|
+
});
|
|
11515
|
+
}
|
|
11170
11516
|
if (planResult.fields.length) {
|
|
11171
11517
|
fields.push(...planResult.fields);
|
|
11172
11518
|
} else if (planResult.data !== void 0) {
|
|
@@ -11828,12 +12174,6 @@ var Opensteer = class _Opensteer {
|
|
|
11828
12174
|
};
|
|
11829
12175
|
}
|
|
11830
12176
|
async buildFieldTargetsFromSchema(schema) {
|
|
11831
|
-
if (!schema || typeof schema !== "object") {
|
|
11832
|
-
return [];
|
|
11833
|
-
}
|
|
11834
|
-
if (Array.isArray(schema)) {
|
|
11835
|
-
return [];
|
|
11836
|
-
}
|
|
11837
12177
|
const fields = [];
|
|
11838
12178
|
await this.collectFieldTargetsFromSchemaObject(
|
|
11839
12179
|
schema,
|
|
@@ -11907,6 +12247,10 @@ var Opensteer = class _Opensteer {
|
|
|
11907
12247
|
path: path5,
|
|
11908
12248
|
attribute: normalized.attribute
|
|
11909
12249
|
});
|
|
12250
|
+
} else {
|
|
12251
|
+
throw new Error(
|
|
12252
|
+
`Extraction schema field "${fieldKey}" uses selector "${normalized.selector}", but no matching element path could be built from the current page snapshot.`
|
|
12253
|
+
);
|
|
11910
12254
|
}
|
|
11911
12255
|
return;
|
|
11912
12256
|
}
|
|
@@ -12257,13 +12601,28 @@ function countNonNullLeaves(value) {
|
|
|
12257
12601
|
function isPrimitiveLike(value) {
|
|
12258
12602
|
return value == null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
12259
12603
|
}
|
|
12604
|
+
function assertValidExtractSchemaRoot(schema) {
|
|
12605
|
+
if (!schema || typeof schema !== "object") {
|
|
12606
|
+
throw new Error(
|
|
12607
|
+
"Invalid extraction schema: expected a JSON object at the top level."
|
|
12608
|
+
);
|
|
12609
|
+
}
|
|
12610
|
+
if (Array.isArray(schema)) {
|
|
12611
|
+
throw new Error(
|
|
12612
|
+
'Invalid extraction schema: top-level arrays are not supported. Wrap array fields in an object (for example {"items":[...]}).'
|
|
12613
|
+
);
|
|
12614
|
+
}
|
|
12615
|
+
}
|
|
12260
12616
|
function parseAiExtractResponse(response) {
|
|
12261
12617
|
if (typeof response === "string") {
|
|
12262
12618
|
const trimmed = stripCodeFence2(response);
|
|
12263
12619
|
try {
|
|
12264
12620
|
return JSON.parse(trimmed);
|
|
12265
12621
|
} catch {
|
|
12266
|
-
|
|
12622
|
+
const preview = summarizeForError(trimmed);
|
|
12623
|
+
throw new Error(
|
|
12624
|
+
`LLM extraction returned a non-JSON response.${preview ? ` Preview: "${preview}"` : ""}`
|
|
12625
|
+
);
|
|
12267
12626
|
}
|
|
12268
12627
|
}
|
|
12269
12628
|
if (response && typeof response === "object") {
|
|
@@ -12288,6 +12647,12 @@ function stripCodeFence2(input) {
|
|
|
12288
12647
|
if (lastFence === -1) return withoutHeader.trim();
|
|
12289
12648
|
return withoutHeader.slice(0, lastFence).trim();
|
|
12290
12649
|
}
|
|
12650
|
+
function summarizeForError(input, maxLength = 180) {
|
|
12651
|
+
const compact = input.replace(/\s+/g, " ").trim();
|
|
12652
|
+
if (!compact) return "";
|
|
12653
|
+
if (compact.length <= maxLength) return compact;
|
|
12654
|
+
return `${compact.slice(0, maxLength)}...`;
|
|
12655
|
+
}
|
|
12291
12656
|
function getScrollDelta2(options) {
|
|
12292
12657
|
const amount = typeof options.amount === "number" ? options.amount : 600;
|
|
12293
12658
|
const absoluteAmount = Math.abs(amount);
|
|
@@ -12627,7 +12992,16 @@ function enqueueRequest(request, socket) {
|
|
|
12627
12992
|
void handleRequest(request, socket);
|
|
12628
12993
|
return;
|
|
12629
12994
|
}
|
|
12630
|
-
requestQueue = requestQueue.then(() => handleRequest(request, socket)).catch(() => {
|
|
12995
|
+
requestQueue = requestQueue.then(() => handleRequest(request, socket)).catch((error) => {
|
|
12996
|
+
sendResponse(
|
|
12997
|
+
socket,
|
|
12998
|
+
buildErrorResponse(
|
|
12999
|
+
request.id,
|
|
13000
|
+
error,
|
|
13001
|
+
"Unexpected server error while handling request.",
|
|
13002
|
+
"CLI_INTERNAL_ERROR"
|
|
13003
|
+
)
|
|
13004
|
+
);
|
|
12631
13005
|
});
|
|
12632
13006
|
}
|
|
12633
13007
|
async function handleRequest(request, socket) {
|
|
@@ -12636,7 +13010,11 @@ async function handleRequest(request, socket) {
|
|
|
12636
13010
|
sendResponse(socket, {
|
|
12637
13011
|
id,
|
|
12638
13012
|
ok: false,
|
|
12639
|
-
error: `Session '${session}' is shutting down
|
|
13013
|
+
error: `Session '${session}' is shutting down.`,
|
|
13014
|
+
errorInfo: {
|
|
13015
|
+
message: `Session '${session}' is shutting down.`,
|
|
13016
|
+
code: "SESSION_SHUTTING_DOWN"
|
|
13017
|
+
}
|
|
12640
13018
|
});
|
|
12641
13019
|
return;
|
|
12642
13020
|
}
|
|
@@ -12644,7 +13022,11 @@ async function handleRequest(request, socket) {
|
|
|
12644
13022
|
sendResponse(socket, {
|
|
12645
13023
|
id,
|
|
12646
13024
|
ok: false,
|
|
12647
|
-
error: `Session '${session}' is shutting down. Retry your command
|
|
13025
|
+
error: `Session '${session}' is shutting down. Retry your command.`,
|
|
13026
|
+
errorInfo: {
|
|
13027
|
+
message: `Session '${session}' is shutting down. Retry your command.`,
|
|
13028
|
+
code: "SESSION_SHUTTING_DOWN"
|
|
13029
|
+
}
|
|
12648
13030
|
});
|
|
12649
13031
|
return;
|
|
12650
13032
|
}
|
|
@@ -12660,7 +13042,16 @@ async function handleRequest(request, socket) {
|
|
|
12660
13042
|
sendResponse(socket, {
|
|
12661
13043
|
id,
|
|
12662
13044
|
ok: false,
|
|
12663
|
-
error: `Session '${session}' is already bound to selector namespace '${selectorNamespace}'. Requested '${requestedName}' does not match. Use the same --name for this session or start a different --session
|
|
13045
|
+
error: `Session '${session}' is already bound to selector namespace '${selectorNamespace}'. Requested '${requestedName}' does not match. Use the same --name for this session or start a different --session.`,
|
|
13046
|
+
errorInfo: {
|
|
13047
|
+
message: `Session '${session}' is already bound to selector namespace '${selectorNamespace}'. Requested '${requestedName}' does not match. Use the same --name for this session or start a different --session.`,
|
|
13048
|
+
code: "SESSION_NAMESPACE_MISMATCH",
|
|
13049
|
+
details: {
|
|
13050
|
+
session,
|
|
13051
|
+
activeNamespace: selectorNamespace,
|
|
13052
|
+
requestedNamespace: requestedName
|
|
13053
|
+
}
|
|
13054
|
+
}
|
|
12664
13055
|
});
|
|
12665
13056
|
return;
|
|
12666
13057
|
}
|
|
@@ -12718,11 +13109,10 @@ async function handleRequest(request, socket) {
|
|
|
12718
13109
|
}
|
|
12719
13110
|
});
|
|
12720
13111
|
} catch (err) {
|
|
12721
|
-
sendResponse(
|
|
12722
|
-
|
|
12723
|
-
|
|
12724
|
-
|
|
12725
|
-
});
|
|
13112
|
+
sendResponse(
|
|
13113
|
+
socket,
|
|
13114
|
+
buildErrorResponse(id, err, "Failed to open browser session.")
|
|
13115
|
+
);
|
|
12726
13116
|
}
|
|
12727
13117
|
return;
|
|
12728
13118
|
}
|
|
@@ -12738,11 +13128,10 @@ async function handleRequest(request, socket) {
|
|
|
12738
13128
|
result: { sessionClosed: true }
|
|
12739
13129
|
});
|
|
12740
13130
|
} catch (err) {
|
|
12741
|
-
sendResponse(
|
|
12742
|
-
|
|
12743
|
-
|
|
12744
|
-
|
|
12745
|
-
});
|
|
13131
|
+
sendResponse(
|
|
13132
|
+
socket,
|
|
13133
|
+
buildErrorResponse(id, err, "Failed to close browser session.")
|
|
13134
|
+
);
|
|
12746
13135
|
}
|
|
12747
13136
|
beginShutdown();
|
|
12748
13137
|
return;
|
|
@@ -12755,7 +13144,14 @@ async function handleRequest(request, socket) {
|
|
|
12755
13144
|
sendResponse(socket, {
|
|
12756
13145
|
id,
|
|
12757
13146
|
ok: false,
|
|
12758
|
-
error: `No browser session in session '${session}'. Call 'opensteer open --session ${session}' first, or use 'opensteer sessions' to list active sessions
|
|
13147
|
+
error: `No browser session in session '${session}'. Call 'opensteer open --session ${session}' first, or use 'opensteer sessions' to list active sessions.`,
|
|
13148
|
+
errorInfo: {
|
|
13149
|
+
message: `No browser session in session '${session}'. Call 'opensteer open --session ${session}' first, or use 'opensteer sessions' to list active sessions.`,
|
|
13150
|
+
code: "SESSION_NOT_OPEN",
|
|
13151
|
+
details: {
|
|
13152
|
+
session
|
|
13153
|
+
}
|
|
13154
|
+
}
|
|
12759
13155
|
});
|
|
12760
13156
|
return;
|
|
12761
13157
|
}
|
|
@@ -12764,7 +13160,14 @@ async function handleRequest(request, socket) {
|
|
|
12764
13160
|
sendResponse(socket, {
|
|
12765
13161
|
id,
|
|
12766
13162
|
ok: false,
|
|
12767
|
-
error: `Unknown command: ${command}
|
|
13163
|
+
error: `Unknown command: ${command}`,
|
|
13164
|
+
errorInfo: {
|
|
13165
|
+
message: `Unknown command: ${command}`,
|
|
13166
|
+
code: "UNKNOWN_COMMAND",
|
|
13167
|
+
details: {
|
|
13168
|
+
command
|
|
13169
|
+
}
|
|
13170
|
+
}
|
|
12768
13171
|
});
|
|
12769
13172
|
return;
|
|
12770
13173
|
}
|
|
@@ -12772,11 +13175,12 @@ async function handleRequest(request, socket) {
|
|
|
12772
13175
|
const result = await handler(instance, args);
|
|
12773
13176
|
sendResponse(socket, { id, ok: true, result });
|
|
12774
13177
|
} catch (err) {
|
|
12775
|
-
sendResponse(
|
|
12776
|
-
|
|
12777
|
-
|
|
12778
|
-
|
|
12779
|
-
|
|
13178
|
+
sendResponse(
|
|
13179
|
+
socket,
|
|
13180
|
+
buildErrorResponse(id, err, `Command "${command}" failed.`, void 0, {
|
|
13181
|
+
command
|
|
13182
|
+
})
|
|
13183
|
+
);
|
|
12780
13184
|
}
|
|
12781
13185
|
}
|
|
12782
13186
|
if ((0, import_fs4.existsSync)(socketPath)) {
|
|
@@ -12797,7 +13201,11 @@ var server = (0, import_net.createServer)((socket) => {
|
|
|
12797
13201
|
sendResponse(socket, {
|
|
12798
13202
|
id: 0,
|
|
12799
13203
|
ok: false,
|
|
12800
|
-
error: "Invalid JSON request"
|
|
13204
|
+
error: "Invalid JSON request",
|
|
13205
|
+
errorInfo: {
|
|
13206
|
+
message: "Invalid JSON request",
|
|
13207
|
+
code: "INVALID_JSON_REQUEST"
|
|
13208
|
+
}
|
|
12801
13209
|
});
|
|
12802
13210
|
}
|
|
12803
13211
|
}
|
|
@@ -12836,3 +13244,25 @@ async function shutdown() {
|
|
|
12836
13244
|
}
|
|
12837
13245
|
process.on("SIGTERM", shutdown);
|
|
12838
13246
|
process.on("SIGINT", shutdown);
|
|
13247
|
+
function buildErrorResponse(id, error, fallbackMessage, fallbackCode, details) {
|
|
13248
|
+
const normalized = normalizeError(error, fallbackMessage);
|
|
13249
|
+
let mergedDetails;
|
|
13250
|
+
if (normalized.details || details) {
|
|
13251
|
+
mergedDetails = {
|
|
13252
|
+
...normalized.details || {},
|
|
13253
|
+
...details || {}
|
|
13254
|
+
};
|
|
13255
|
+
}
|
|
13256
|
+
return {
|
|
13257
|
+
id,
|
|
13258
|
+
ok: false,
|
|
13259
|
+
error: normalized.message,
|
|
13260
|
+
errorInfo: {
|
|
13261
|
+
message: normalized.message,
|
|
13262
|
+
...normalized.code || fallbackCode ? { code: normalized.code || fallbackCode } : {},
|
|
13263
|
+
...normalized.name ? { name: normalized.name } : {},
|
|
13264
|
+
...mergedDetails ? { details: mergedDetails } : {},
|
|
13265
|
+
...normalized.cause ? { cause: normalized.cause } : {}
|
|
13266
|
+
}
|
|
13267
|
+
};
|
|
13268
|
+
}
|