opensteer 0.6.3 → 0.6.4
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/bin/opensteer.mjs +94 -8
- package/dist/{browser-profile-client-DK9qa_Dj.d.cts → browser-profile-client-D6PuRefA.d.cts} +1 -1
- package/dist/{browser-profile-client-CaL-mwqs.d.ts → browser-profile-client-OUHaODro.d.ts} +1 -1
- package/dist/{chunk-SCNX4NN3.js → chunk-54KNQTOL.js} +141 -2
- package/dist/{chunk-FTKWQ6X3.js → chunk-6B6LOYU3.js} +1 -1
- package/dist/{chunk-3OMXCBPD.js → chunk-G6V2DJRN.js} +442 -591
- package/dist/chunk-K5CL76MG.js +81 -0
- package/dist/{chunk-KE35RQOJ.js → chunk-KPPOTU3D.js} +53 -144
- package/dist/cli/auth.cjs +53 -6
- package/dist/cli/auth.d.cts +1 -1
- package/dist/cli/auth.d.ts +1 -1
- package/dist/cli/auth.js +2 -2
- package/dist/cli/local-profile.cjs +197 -0
- package/dist/cli/local-profile.d.cts +18 -0
- package/dist/cli/local-profile.d.ts +18 -0
- package/dist/cli/local-profile.js +97 -0
- package/dist/cli/profile.cjs +2844 -2412
- package/dist/cli/profile.d.cts +2 -2
- package/dist/cli/profile.d.ts +2 -2
- package/dist/cli/profile.js +469 -7
- package/dist/cli/server.cjs +649 -204
- package/dist/cli/server.js +69 -16
- package/dist/index.cjs +578 -185
- package/dist/index.d.cts +7 -5
- package/dist/index.d.ts +7 -5
- package/dist/index.js +4 -3
- package/dist/{types-BxiRblC7.d.cts → types-BWItZPl_.d.cts} +31 -13
- package/dist/{types-BxiRblC7.d.ts → types-BWItZPl_.d.ts} +31 -13
- package/package.json +2 -2
- package/skills/opensteer/SKILL.md +34 -14
- package/skills/opensteer/references/cli-reference.md +1 -1
- package/skills/opensteer/references/examples.md +5 -3
- package/skills/opensteer/references/sdk-reference.md +16 -14
package/dist/index.cjs
CHANGED
|
@@ -515,7 +515,13 @@ module.exports = __toCommonJS(index_exports);
|
|
|
515
515
|
var import_crypto = require("crypto");
|
|
516
516
|
|
|
517
517
|
// src/browser/pool.ts
|
|
518
|
-
var
|
|
518
|
+
var import_node_child_process = require("child_process");
|
|
519
|
+
var import_node_fs = require("fs");
|
|
520
|
+
var import_promises = require("fs/promises");
|
|
521
|
+
var import_node_net = require("net");
|
|
522
|
+
var import_node_os = require("os");
|
|
523
|
+
var import_node_path = require("path");
|
|
524
|
+
var import_playwright = require("playwright");
|
|
519
525
|
|
|
520
526
|
// src/browser/cdp-proxy.ts
|
|
521
527
|
var import_ws = __toESM(require("ws"), 1);
|
|
@@ -854,209 +860,263 @@ function errorMessage(error) {
|
|
|
854
860
|
return error instanceof Error ? error.message : String(error);
|
|
855
861
|
}
|
|
856
862
|
|
|
857
|
-
// src/browser/chromium-profile.ts
|
|
858
|
-
var import_node_util = require("util");
|
|
859
|
-
var import_node_child_process2 = require("child_process");
|
|
860
|
-
var import_node_crypto = require("crypto");
|
|
861
|
-
var import_promises = require("fs/promises");
|
|
862
|
-
var import_node_fs = require("fs");
|
|
863
|
-
var import_node_path = require("path");
|
|
864
|
-
var import_node_os = require("os");
|
|
865
|
-
var import_playwright = require("playwright");
|
|
866
|
-
|
|
867
|
-
// src/auth/keychain-store.ts
|
|
868
|
-
var import_node_child_process = require("child_process");
|
|
869
|
-
|
|
870
863
|
// src/browser/chrome.ts
|
|
871
864
|
var import_os = require("os");
|
|
872
865
|
var import_path = require("path");
|
|
866
|
+
var import_fs = require("fs");
|
|
867
|
+
function detectChromePaths() {
|
|
868
|
+
const os = (0, import_os.platform)();
|
|
869
|
+
if (os === "darwin") {
|
|
870
|
+
const executable2 = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
|
|
871
|
+
return {
|
|
872
|
+
executable: (0, import_fs.existsSync)(executable2) ? executable2 : null,
|
|
873
|
+
defaultUserDataDir: (0, import_path.join)(
|
|
874
|
+
(0, import_os.homedir)(),
|
|
875
|
+
"Library",
|
|
876
|
+
"Application Support",
|
|
877
|
+
"Google",
|
|
878
|
+
"Chrome"
|
|
879
|
+
)
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
if (os === "win32") {
|
|
883
|
+
const executable2 = (0, import_path.join)(
|
|
884
|
+
process.env.PROGRAMFILES || "C:\\Program Files",
|
|
885
|
+
"Google",
|
|
886
|
+
"Chrome",
|
|
887
|
+
"Application",
|
|
888
|
+
"chrome.exe"
|
|
889
|
+
);
|
|
890
|
+
return {
|
|
891
|
+
executable: (0, import_fs.existsSync)(executable2) ? executable2 : null,
|
|
892
|
+
defaultUserDataDir: (0, import_path.join)(
|
|
893
|
+
process.env.LOCALAPPDATA || (0, import_path.join)((0, import_os.homedir)(), "AppData", "Local"),
|
|
894
|
+
"Google",
|
|
895
|
+
"Chrome",
|
|
896
|
+
"User Data"
|
|
897
|
+
)
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
const executable = "/usr/bin/google-chrome";
|
|
901
|
+
return {
|
|
902
|
+
executable: (0, import_fs.existsSync)(executable) ? executable : null,
|
|
903
|
+
defaultUserDataDir: (0, import_path.join)((0, import_os.homedir)(), ".config", "google-chrome")
|
|
904
|
+
};
|
|
905
|
+
}
|
|
873
906
|
function expandHome(p) {
|
|
874
907
|
if (p.startsWith("~/") || p === "~") {
|
|
875
908
|
return (0, import_path.join)((0, import_os.homedir)(), p.slice(1));
|
|
876
909
|
}
|
|
877
910
|
return p;
|
|
878
911
|
}
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
return (0, import_node_fs.statSync)(filePath).isDirectory();
|
|
885
|
-
} catch {
|
|
886
|
-
return false;
|
|
912
|
+
function listLocalChromeProfiles(userDataDir = detectChromePaths().defaultUserDataDir) {
|
|
913
|
+
const resolvedUserDataDir = expandHome(userDataDir);
|
|
914
|
+
const localStatePath = (0, import_path.join)(resolvedUserDataDir, "Local State");
|
|
915
|
+
if (!(0, import_fs.existsSync)(localStatePath)) {
|
|
916
|
+
return [];
|
|
887
917
|
}
|
|
888
|
-
}
|
|
889
|
-
function fileExists(filePath) {
|
|
890
918
|
try {
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
}
|
|
896
|
-
function resolveCookieDbPath(profileDir) {
|
|
897
|
-
const candidates = [(0, import_node_path.join)(profileDir, "Network", "Cookies"), (0, import_node_path.join)(profileDir, "Cookies")];
|
|
898
|
-
for (const candidate of candidates) {
|
|
899
|
-
if (fileExists(candidate)) {
|
|
900
|
-
return candidate;
|
|
919
|
+
const raw = JSON.parse((0, import_fs.readFileSync)(localStatePath, "utf-8"));
|
|
920
|
+
const infoCache = raw && typeof raw === "object" && !Array.isArray(raw) && raw.profile && typeof raw.profile === "object" && !Array.isArray(raw.profile) ? raw.profile.info_cache : void 0;
|
|
921
|
+
if (!infoCache || typeof infoCache !== "object") {
|
|
922
|
+
return [];
|
|
901
923
|
}
|
|
924
|
+
return Object.entries(infoCache).map(([directory, info]) => {
|
|
925
|
+
const record = info && typeof info === "object" && !Array.isArray(info) ? info : {};
|
|
926
|
+
const name = typeof record.name === "string" && record.name.trim() ? record.name.trim() : directory;
|
|
927
|
+
return {
|
|
928
|
+
directory,
|
|
929
|
+
name
|
|
930
|
+
};
|
|
931
|
+
}).filter((profile) => profile.directory.trim().length > 0).sort(
|
|
932
|
+
(left, right) => left.directory.localeCompare(right.directory)
|
|
933
|
+
);
|
|
934
|
+
} catch {
|
|
935
|
+
return [];
|
|
902
936
|
}
|
|
903
|
-
return null;
|
|
904
|
-
}
|
|
905
|
-
function resolvePersistentChromiumLaunchProfile(inputPath) {
|
|
906
|
-
const expandedPath = expandHome(inputPath.trim());
|
|
907
|
-
if (!expandedPath) {
|
|
908
|
-
return {
|
|
909
|
-
userDataDir: inputPath
|
|
910
|
-
};
|
|
911
|
-
}
|
|
912
|
-
if (fileExists(expandedPath) && (0, import_node_path.basename)(expandedPath) === "Cookies") {
|
|
913
|
-
const directParent = (0, import_node_path.dirname)(expandedPath);
|
|
914
|
-
const profileDir = (0, import_node_path.basename)(directParent) === "Network" ? (0, import_node_path.dirname)(directParent) : directParent;
|
|
915
|
-
return {
|
|
916
|
-
userDataDir: (0, import_node_path.dirname)(profileDir),
|
|
917
|
-
profileDirectory: (0, import_node_path.basename)(profileDir)
|
|
918
|
-
};
|
|
919
|
-
}
|
|
920
|
-
if (directoryExists(expandedPath) && resolveCookieDbPath(expandedPath) && fileExists((0, import_node_path.join)((0, import_node_path.dirname)(expandedPath), "Local State"))) {
|
|
921
|
-
return {
|
|
922
|
-
userDataDir: (0, import_node_path.dirname)(expandedPath),
|
|
923
|
-
profileDirectory: (0, import_node_path.basename)(expandedPath)
|
|
924
|
-
};
|
|
925
|
-
}
|
|
926
|
-
return {
|
|
927
|
-
userDataDir: expandedPath
|
|
928
|
-
};
|
|
929
937
|
}
|
|
930
938
|
|
|
931
939
|
// src/browser/pool.ts
|
|
932
940
|
var BrowserPool = class {
|
|
933
941
|
browser = null;
|
|
934
|
-
persistentContext = null;
|
|
935
942
|
cdpProxy = null;
|
|
943
|
+
launchedProcess = null;
|
|
944
|
+
tempUserDataDir = null;
|
|
936
945
|
defaults;
|
|
937
946
|
constructor(defaults = {}) {
|
|
938
947
|
this.defaults = defaults;
|
|
939
948
|
}
|
|
940
949
|
async launch(options = {}) {
|
|
941
|
-
if (this.browser || this.cdpProxy) {
|
|
950
|
+
if (this.browser || this.cdpProxy || this.launchedProcess || this.tempUserDataDir) {
|
|
942
951
|
await this.close();
|
|
943
952
|
}
|
|
944
|
-
const
|
|
945
|
-
const
|
|
946
|
-
const
|
|
947
|
-
|
|
948
|
-
|
|
953
|
+
const mode = options.mode ?? this.defaults.mode ?? "chromium";
|
|
954
|
+
const cdpUrl = options.cdpUrl ?? this.defaults.cdpUrl;
|
|
955
|
+
const userDataDir = options.userDataDir ?? this.defaults.userDataDir;
|
|
956
|
+
const profileDirectory = options.profileDirectory ?? this.defaults.profileDirectory;
|
|
957
|
+
const executablePath = options.executablePath ?? this.defaults.executablePath;
|
|
958
|
+
if (cdpUrl) {
|
|
959
|
+
if (mode === "real") {
|
|
960
|
+
throw new Error(
|
|
961
|
+
'cdpUrl cannot be combined with mode "real". Use one browser launch path at a time.'
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
if (userDataDir || profileDirectory) {
|
|
965
|
+
throw new Error(
|
|
966
|
+
"userDataDir/profileDirectory cannot be combined with cdpUrl."
|
|
967
|
+
);
|
|
968
|
+
}
|
|
969
|
+
if (options.context && Object.keys(options.context).length > 0) {
|
|
970
|
+
throw new Error(
|
|
971
|
+
"context launch options are not supported when attaching over CDP."
|
|
972
|
+
);
|
|
973
|
+
}
|
|
974
|
+
return this.connectToRunning(cdpUrl, options.timeout);
|
|
949
975
|
}
|
|
950
|
-
if (
|
|
951
|
-
|
|
976
|
+
if (mode !== "real" && (userDataDir || profileDirectory)) {
|
|
977
|
+
throw new Error(
|
|
978
|
+
'userDataDir/profileDirectory require mode "real".'
|
|
979
|
+
);
|
|
952
980
|
}
|
|
953
|
-
if (
|
|
954
|
-
|
|
981
|
+
if (mode === "real") {
|
|
982
|
+
if (options.context && Object.keys(options.context).length > 0) {
|
|
983
|
+
throw new Error(
|
|
984
|
+
"context launch options are not supported for real-browser mode."
|
|
985
|
+
);
|
|
986
|
+
}
|
|
987
|
+
return this.launchOwnedRealBrowser({
|
|
988
|
+
...options,
|
|
989
|
+
executablePath,
|
|
990
|
+
userDataDir,
|
|
991
|
+
profileDirectory
|
|
992
|
+
});
|
|
955
993
|
}
|
|
956
994
|
return this.launchSandbox(options);
|
|
957
995
|
}
|
|
958
996
|
async close() {
|
|
959
997
|
const browser = this.browser;
|
|
960
|
-
const
|
|
998
|
+
const cdpProxy = this.cdpProxy;
|
|
999
|
+
const launchedProcess = this.launchedProcess;
|
|
1000
|
+
const tempUserDataDir = this.tempUserDataDir;
|
|
961
1001
|
this.browser = null;
|
|
962
|
-
this.
|
|
1002
|
+
this.cdpProxy = null;
|
|
1003
|
+
this.launchedProcess = null;
|
|
1004
|
+
this.tempUserDataDir = null;
|
|
963
1005
|
try {
|
|
964
|
-
if (
|
|
965
|
-
await
|
|
966
|
-
} else if (browser) {
|
|
967
|
-
await browser.close();
|
|
1006
|
+
if (browser) {
|
|
1007
|
+
await browser.close().catch(() => void 0);
|
|
968
1008
|
}
|
|
969
1009
|
} finally {
|
|
970
|
-
|
|
971
|
-
|
|
1010
|
+
cdpProxy?.close();
|
|
1011
|
+
await killProcessTree(launchedProcess);
|
|
1012
|
+
if (tempUserDataDir) {
|
|
1013
|
+
await (0, import_promises.rm)(tempUserDataDir, {
|
|
1014
|
+
recursive: true,
|
|
1015
|
+
force: true
|
|
1016
|
+
}).catch(() => void 0);
|
|
1017
|
+
}
|
|
972
1018
|
}
|
|
973
1019
|
}
|
|
974
|
-
async connectToRunning(
|
|
975
|
-
this.cdpProxy?.close();
|
|
976
|
-
this.cdpProxy = null;
|
|
1020
|
+
async connectToRunning(cdpUrl, timeout) {
|
|
977
1021
|
let browser = null;
|
|
1022
|
+
let cdpProxy = null;
|
|
978
1023
|
try {
|
|
979
|
-
const { browserWsUrl, targets } = await discoverTargets(
|
|
1024
|
+
const { browserWsUrl, targets } = await discoverTargets(cdpUrl);
|
|
980
1025
|
if (targets.length === 0) {
|
|
981
1026
|
throw new Error(
|
|
982
1027
|
"No page targets found. Is the browser running with an open window?"
|
|
983
1028
|
);
|
|
984
1029
|
}
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
browser = await import_playwright2.chromium.connectOverCDP(proxyWsUrl, {
|
|
1030
|
+
cdpProxy = new CDPProxy(browserWsUrl, targets[0].id);
|
|
1031
|
+
const proxyWsUrl = await cdpProxy.start();
|
|
1032
|
+
browser = await import_playwright.chromium.connectOverCDP(proxyWsUrl, {
|
|
989
1033
|
timeout: timeout ?? 3e4
|
|
990
1034
|
});
|
|
991
1035
|
this.browser = browser;
|
|
992
|
-
this.
|
|
993
|
-
const
|
|
994
|
-
if (contexts.length === 0) {
|
|
995
|
-
throw new Error(
|
|
996
|
-
"Connection succeeded but no browser contexts found. Is the browser running with an open window?"
|
|
997
|
-
);
|
|
998
|
-
}
|
|
999
|
-
const context = contexts[0];
|
|
1000
|
-
const pages = context.pages();
|
|
1001
|
-
const page = pages.length > 0 ? pages[0] : await context.newPage();
|
|
1036
|
+
this.cdpProxy = cdpProxy;
|
|
1037
|
+
const { context, page } = await pickBrowserContextAndPage(browser);
|
|
1002
1038
|
return { browser, context, page, isExternal: true };
|
|
1003
1039
|
} catch (error) {
|
|
1004
1040
|
if (browser) {
|
|
1005
1041
|
await browser.close().catch(() => void 0);
|
|
1006
1042
|
}
|
|
1043
|
+
cdpProxy?.close();
|
|
1007
1044
|
this.browser = null;
|
|
1008
|
-
this.persistentContext = null;
|
|
1009
|
-
this.cdpProxy?.close();
|
|
1010
1045
|
this.cdpProxy = null;
|
|
1011
1046
|
throw error;
|
|
1012
1047
|
}
|
|
1013
1048
|
}
|
|
1014
|
-
async
|
|
1015
|
-
const
|
|
1016
|
-
const
|
|
1017
|
-
if (
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
launchProfile.userDataDir,
|
|
1022
|
-
{
|
|
1023
|
-
channel,
|
|
1024
|
-
headless: options.headless ?? this.defaults.headless,
|
|
1025
|
-
executablePath: options.executablePath ?? this.defaults.executablePath ?? void 0,
|
|
1026
|
-
slowMo: options.slowMo ?? this.defaults.slowMo ?? 0,
|
|
1027
|
-
timeout: options.timeout,
|
|
1028
|
-
...options.context || {},
|
|
1029
|
-
args
|
|
1030
|
-
}
|
|
1031
|
-
);
|
|
1032
|
-
const browser = context.browser();
|
|
1033
|
-
if (!browser) {
|
|
1034
|
-
await context.close().catch(() => void 0);
|
|
1035
|
-
throw new Error("Persistent browser launch did not expose a browser instance.");
|
|
1049
|
+
async launchOwnedRealBrowser(options) {
|
|
1050
|
+
const chromePaths = detectChromePaths();
|
|
1051
|
+
const executablePath = options.executablePath ?? chromePaths.executable ?? void 0;
|
|
1052
|
+
if (!executablePath) {
|
|
1053
|
+
throw new Error(
|
|
1054
|
+
"Chrome was not found. Set browser.executablePath or install Chrome in a supported location."
|
|
1055
|
+
);
|
|
1036
1056
|
}
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
const
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1057
|
+
const sourceUserDataDir = expandHome(
|
|
1058
|
+
options.userDataDir ?? chromePaths.defaultUserDataDir
|
|
1059
|
+
);
|
|
1060
|
+
const profileDirectory = options.profileDirectory ?? "Default";
|
|
1061
|
+
const tempUserDataDir = await cloneProfileToTempDir(
|
|
1062
|
+
sourceUserDataDir,
|
|
1063
|
+
profileDirectory
|
|
1064
|
+
);
|
|
1065
|
+
const debugPort = await reserveDebugPort();
|
|
1066
|
+
const headless = resolveLaunchHeadless(
|
|
1067
|
+
"real",
|
|
1068
|
+
options.headless,
|
|
1069
|
+
this.defaults.headless
|
|
1070
|
+
);
|
|
1071
|
+
const launchArgs = buildRealBrowserLaunchArgs({
|
|
1072
|
+
userDataDir: tempUserDataDir,
|
|
1073
|
+
profileDirectory,
|
|
1074
|
+
debugPort,
|
|
1075
|
+
headless
|
|
1050
1076
|
});
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1077
|
+
const processHandle = (0, import_node_child_process.spawn)(executablePath, launchArgs, {
|
|
1078
|
+
detached: process.platform !== "win32",
|
|
1079
|
+
stdio: "ignore"
|
|
1080
|
+
});
|
|
1081
|
+
processHandle.unref();
|
|
1082
|
+
let browser = null;
|
|
1083
|
+
try {
|
|
1084
|
+
const wsUrl = await resolveCdpWebSocketUrl(
|
|
1085
|
+
`http://127.0.0.1:${debugPort}`,
|
|
1086
|
+
options.timeout ?? 3e4
|
|
1087
|
+
);
|
|
1088
|
+
browser = await import_playwright.chromium.connectOverCDP(wsUrl, {
|
|
1089
|
+
timeout: options.timeout ?? 3e4
|
|
1090
|
+
});
|
|
1091
|
+
const { context, page } = await createOwnedBrowserContextAndPage(
|
|
1092
|
+
browser,
|
|
1093
|
+
{
|
|
1094
|
+
headless,
|
|
1095
|
+
initialUrl: options.initialUrl,
|
|
1096
|
+
timeoutMs: options.timeout ?? 3e4
|
|
1097
|
+
}
|
|
1098
|
+
);
|
|
1099
|
+
this.browser = browser;
|
|
1100
|
+
this.launchedProcess = processHandle;
|
|
1101
|
+
this.tempUserDataDir = tempUserDataDir;
|
|
1102
|
+
return { browser, context, page, isExternal: false };
|
|
1103
|
+
} catch (error) {
|
|
1104
|
+
await browser?.close().catch(() => void 0);
|
|
1105
|
+
await killProcessTree(processHandle);
|
|
1106
|
+
await (0, import_promises.rm)(tempUserDataDir, {
|
|
1107
|
+
recursive: true,
|
|
1108
|
+
force: true
|
|
1109
|
+
}).catch(() => void 0);
|
|
1110
|
+
throw error;
|
|
1111
|
+
}
|
|
1056
1112
|
}
|
|
1057
1113
|
async launchSandbox(options) {
|
|
1058
|
-
const browser = await
|
|
1059
|
-
headless:
|
|
1114
|
+
const browser = await import_playwright.chromium.launch({
|
|
1115
|
+
headless: resolveLaunchHeadless(
|
|
1116
|
+
"chromium",
|
|
1117
|
+
options.headless,
|
|
1118
|
+
this.defaults.headless
|
|
1119
|
+
),
|
|
1060
1120
|
executablePath: options.executablePath ?? this.defaults.executablePath ?? void 0,
|
|
1061
1121
|
slowMo: options.slowMo ?? this.defaults.slowMo ?? 0,
|
|
1062
1122
|
timeout: options.timeout
|
|
@@ -1064,13 +1124,271 @@ var BrowserPool = class {
|
|
|
1064
1124
|
const context = await browser.newContext(options.context || {});
|
|
1065
1125
|
const page = await context.newPage();
|
|
1066
1126
|
this.browser = browser;
|
|
1067
|
-
this.persistentContext = null;
|
|
1068
1127
|
return { browser, context, page, isExternal: false };
|
|
1069
1128
|
}
|
|
1070
1129
|
};
|
|
1130
|
+
async function pickBrowserContextAndPage(browser) {
|
|
1131
|
+
const context = getPrimaryBrowserContext(browser);
|
|
1132
|
+
const pages = context.pages();
|
|
1133
|
+
const page = pages.find((candidate) => isInspectablePageUrl2(candidate.url())) || pages[0] || await context.newPage();
|
|
1134
|
+
return { context, page };
|
|
1135
|
+
}
|
|
1136
|
+
function resolveLaunchHeadless(mode, requestedHeadless, defaultHeadless) {
|
|
1137
|
+
if (requestedHeadless !== void 0) {
|
|
1138
|
+
return requestedHeadless;
|
|
1139
|
+
}
|
|
1140
|
+
if (defaultHeadless !== void 0) {
|
|
1141
|
+
return defaultHeadless;
|
|
1142
|
+
}
|
|
1143
|
+
return mode === "real";
|
|
1144
|
+
}
|
|
1145
|
+
async function createOwnedBrowserContextAndPage(browser, options) {
|
|
1146
|
+
const context = getPrimaryBrowserContext(browser);
|
|
1147
|
+
const page = await createOwnedBrowserPage(browser, context, options);
|
|
1148
|
+
return { context, page };
|
|
1149
|
+
}
|
|
1150
|
+
async function createOwnedBrowserPage(browser, context, options) {
|
|
1151
|
+
const targetUrl = options.initialUrl ?? "about:blank";
|
|
1152
|
+
const existingPages = new Set(context.pages());
|
|
1153
|
+
const browserSession = await browser.newBrowserCDPSession();
|
|
1154
|
+
try {
|
|
1155
|
+
const { targetId } = await browserSession.send("Target.createTarget", {
|
|
1156
|
+
url: targetUrl,
|
|
1157
|
+
newWindow: !options.headless
|
|
1158
|
+
});
|
|
1159
|
+
await browserSession.send("Target.activateTarget", { targetId }).catch(() => void 0);
|
|
1160
|
+
const page = await waitForOwnedBrowserPage(context, {
|
|
1161
|
+
existingPages,
|
|
1162
|
+
targetUrl,
|
|
1163
|
+
timeoutMs: options.timeoutMs
|
|
1164
|
+
});
|
|
1165
|
+
if (targetUrl !== "about:blank") {
|
|
1166
|
+
await page.waitForLoadState("domcontentloaded", {
|
|
1167
|
+
timeout: options.timeoutMs
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1170
|
+
await closeDisposableStartupTargets(browserSession, targetId);
|
|
1171
|
+
return page;
|
|
1172
|
+
} finally {
|
|
1173
|
+
await browserSession.detach().catch(() => void 0);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
async function closeDisposableStartupTargets(browserSession, preservedTargetId) {
|
|
1177
|
+
const response = await browserSession.send("Target.getTargets").catch(() => null);
|
|
1178
|
+
if (!response) {
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
for (const targetInfo of response.targetInfos) {
|
|
1182
|
+
if (targetInfo.targetId === preservedTargetId || targetInfo.type !== "page" || !isDisposableStartupPageUrl(targetInfo.url)) {
|
|
1183
|
+
continue;
|
|
1184
|
+
}
|
|
1185
|
+
await browserSession.send("Target.closeTarget", { targetId: targetInfo.targetId }).catch(() => void 0);
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
async function waitForOwnedBrowserPage(context, options) {
|
|
1189
|
+
const deadline = Date.now() + options.timeoutMs;
|
|
1190
|
+
let fallbackPage = null;
|
|
1191
|
+
while (Date.now() < deadline) {
|
|
1192
|
+
for (const candidate of context.pages()) {
|
|
1193
|
+
if (options.existingPages.has(candidate)) {
|
|
1194
|
+
continue;
|
|
1195
|
+
}
|
|
1196
|
+
const url = candidate.url();
|
|
1197
|
+
if (!isInspectablePageUrl2(url)) {
|
|
1198
|
+
continue;
|
|
1199
|
+
}
|
|
1200
|
+
fallbackPage ??= candidate;
|
|
1201
|
+
if (options.targetUrl === "about:blank") {
|
|
1202
|
+
return candidate;
|
|
1203
|
+
}
|
|
1204
|
+
if (pageLooselyMatchesUrl(url, options.targetUrl)) {
|
|
1205
|
+
return candidate;
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
await sleep(100);
|
|
1209
|
+
}
|
|
1210
|
+
if (fallbackPage) {
|
|
1211
|
+
return fallbackPage;
|
|
1212
|
+
}
|
|
1213
|
+
throw new Error(
|
|
1214
|
+
`Chrome created a target for ${options.targetUrl}, but Playwright did not expose the page in time.`
|
|
1215
|
+
);
|
|
1216
|
+
}
|
|
1217
|
+
function getPrimaryBrowserContext(browser) {
|
|
1218
|
+
const contexts = browser.contexts();
|
|
1219
|
+
if (contexts.length === 0) {
|
|
1220
|
+
throw new Error(
|
|
1221
|
+
"Connection succeeded but no browser contexts were exposed."
|
|
1222
|
+
);
|
|
1223
|
+
}
|
|
1224
|
+
return contexts[0];
|
|
1225
|
+
}
|
|
1226
|
+
function isInspectablePageUrl2(url) {
|
|
1227
|
+
return url === "about:blank" || url.startsWith("http://") || url.startsWith("https://");
|
|
1228
|
+
}
|
|
1229
|
+
function isDisposableStartupPageUrl(url) {
|
|
1230
|
+
return url === "about:blank" || url === "chrome://newtab/" || url === "chrome://new-tab-page/";
|
|
1231
|
+
}
|
|
1232
|
+
function pageLooselyMatchesUrl(currentUrl, initialUrl) {
|
|
1233
|
+
try {
|
|
1234
|
+
const current = new URL(currentUrl);
|
|
1235
|
+
const requested = new URL(initialUrl);
|
|
1236
|
+
if (current.href === requested.href) {
|
|
1237
|
+
return true;
|
|
1238
|
+
}
|
|
1239
|
+
return current.hostname === requested.hostname && current.pathname === requested.pathname;
|
|
1240
|
+
} catch {
|
|
1241
|
+
return currentUrl === initialUrl;
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
function normalizeDiscoveryUrl(cdpUrl) {
|
|
1245
|
+
let parsed;
|
|
1246
|
+
try {
|
|
1247
|
+
parsed = new URL(cdpUrl);
|
|
1248
|
+
} catch {
|
|
1249
|
+
throw new Error(
|
|
1250
|
+
`Invalid CDP URL "${cdpUrl}". Use an http(s) or ws(s) endpoint.`
|
|
1251
|
+
);
|
|
1252
|
+
}
|
|
1253
|
+
if (parsed.protocol === "ws:" || parsed.protocol === "wss:") {
|
|
1254
|
+
return parsed;
|
|
1255
|
+
}
|
|
1256
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
1257
|
+
throw new Error(
|
|
1258
|
+
`Unsupported CDP URL protocol "${parsed.protocol}". Use http(s) or ws(s).`
|
|
1259
|
+
);
|
|
1260
|
+
}
|
|
1261
|
+
const normalized = new URL(parsed.toString());
|
|
1262
|
+
normalized.pathname = "/json/version";
|
|
1263
|
+
normalized.search = "";
|
|
1264
|
+
normalized.hash = "";
|
|
1265
|
+
return normalized;
|
|
1266
|
+
}
|
|
1267
|
+
async function resolveCdpWebSocketUrl(cdpUrl, timeoutMs) {
|
|
1268
|
+
if (cdpUrl.startsWith("ws://") || cdpUrl.startsWith("wss://")) {
|
|
1269
|
+
return cdpUrl;
|
|
1270
|
+
}
|
|
1271
|
+
const versionUrl = normalizeDiscoveryUrl(cdpUrl);
|
|
1272
|
+
const deadline = Date.now() + timeoutMs;
|
|
1273
|
+
let lastError = "CDP discovery did not respond.";
|
|
1274
|
+
while (Date.now() < deadline) {
|
|
1275
|
+
const remaining = Math.max(deadline - Date.now(), 1e3);
|
|
1276
|
+
try {
|
|
1277
|
+
const response = await fetch(versionUrl, {
|
|
1278
|
+
signal: AbortSignal.timeout(Math.min(remaining, 5e3))
|
|
1279
|
+
});
|
|
1280
|
+
if (!response.ok) {
|
|
1281
|
+
lastError = `${response.status} ${response.statusText}`;
|
|
1282
|
+
} else {
|
|
1283
|
+
const payload = await response.json();
|
|
1284
|
+
const wsUrl = payload && typeof payload === "object" && !Array.isArray(payload) && typeof payload.webSocketDebuggerUrl === "string" ? payload.webSocketDebuggerUrl : null;
|
|
1285
|
+
if (wsUrl && wsUrl.trim()) {
|
|
1286
|
+
return wsUrl;
|
|
1287
|
+
}
|
|
1288
|
+
lastError = "CDP discovery response did not include webSocketDebuggerUrl.";
|
|
1289
|
+
}
|
|
1290
|
+
} catch (error) {
|
|
1291
|
+
lastError = error instanceof Error ? error.message : "Unknown error";
|
|
1292
|
+
}
|
|
1293
|
+
await sleep(100);
|
|
1294
|
+
}
|
|
1295
|
+
throw new Error(
|
|
1296
|
+
`Failed to resolve a CDP websocket URL from ${versionUrl.toString()}: ${lastError}`
|
|
1297
|
+
);
|
|
1298
|
+
}
|
|
1299
|
+
async function reserveDebugPort() {
|
|
1300
|
+
return await new Promise((resolve, reject) => {
|
|
1301
|
+
const server = (0, import_node_net.createServer)();
|
|
1302
|
+
server.unref();
|
|
1303
|
+
server.on("error", reject);
|
|
1304
|
+
server.listen(0, "127.0.0.1", () => {
|
|
1305
|
+
const address = server.address();
|
|
1306
|
+
if (!address || typeof address === "string") {
|
|
1307
|
+
server.close();
|
|
1308
|
+
reject(new Error("Failed to reserve a local debug port."));
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
server.close((error) => {
|
|
1312
|
+
if (error) {
|
|
1313
|
+
reject(error);
|
|
1314
|
+
return;
|
|
1315
|
+
}
|
|
1316
|
+
resolve(address.port);
|
|
1317
|
+
});
|
|
1318
|
+
});
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1321
|
+
async function cloneProfileToTempDir(userDataDir, profileDirectory) {
|
|
1322
|
+
const resolvedUserDataDir = expandHome(userDataDir);
|
|
1323
|
+
const tempUserDataDir = await (0, import_promises.mkdtemp)(
|
|
1324
|
+
(0, import_node_path.join)((0, import_node_os.tmpdir)(), "opensteer-real-browser-")
|
|
1325
|
+
);
|
|
1326
|
+
const sourceProfileDir = (0, import_node_path.join)(resolvedUserDataDir, profileDirectory);
|
|
1327
|
+
const targetProfileDir = (0, import_node_path.join)(tempUserDataDir, profileDirectory);
|
|
1328
|
+
if ((0, import_node_fs.existsSync)(sourceProfileDir)) {
|
|
1329
|
+
await (0, import_promises.cp)(sourceProfileDir, targetProfileDir, {
|
|
1330
|
+
recursive: true
|
|
1331
|
+
});
|
|
1332
|
+
} else {
|
|
1333
|
+
await (0, import_promises.mkdir)(targetProfileDir, {
|
|
1334
|
+
recursive: true
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
const localStatePath = (0, import_node_path.join)(resolvedUserDataDir, "Local State");
|
|
1338
|
+
if ((0, import_node_fs.existsSync)(localStatePath)) {
|
|
1339
|
+
await (0, import_promises.copyFile)(localStatePath, (0, import_node_path.join)(tempUserDataDir, "Local State"));
|
|
1340
|
+
}
|
|
1341
|
+
return tempUserDataDir;
|
|
1342
|
+
}
|
|
1343
|
+
function buildRealBrowserLaunchArgs(options) {
|
|
1344
|
+
const args = [
|
|
1345
|
+
`--user-data-dir=${options.userDataDir}`,
|
|
1346
|
+
`--profile-directory=${options.profileDirectory}`,
|
|
1347
|
+
`--remote-debugging-port=${options.debugPort}`,
|
|
1348
|
+
"--no-first-run",
|
|
1349
|
+
"--no-default-browser-check",
|
|
1350
|
+
"--disable-background-networking",
|
|
1351
|
+
"--disable-sync",
|
|
1352
|
+
"--disable-popup-blocking"
|
|
1353
|
+
];
|
|
1354
|
+
if (options.headless) {
|
|
1355
|
+
args.push("--headless=new");
|
|
1356
|
+
}
|
|
1357
|
+
return args;
|
|
1358
|
+
}
|
|
1359
|
+
async function killProcessTree(processHandle) {
|
|
1360
|
+
if (!processHandle || processHandle.pid == null || processHandle.exitCode !== null) {
|
|
1361
|
+
return;
|
|
1362
|
+
}
|
|
1363
|
+
if (process.platform === "win32") {
|
|
1364
|
+
await new Promise((resolve) => {
|
|
1365
|
+
const killer = (0, import_node_child_process.spawn)(
|
|
1366
|
+
"taskkill",
|
|
1367
|
+
["/pid", String(processHandle.pid), "/t", "/f"],
|
|
1368
|
+
{
|
|
1369
|
+
stdio: "ignore"
|
|
1370
|
+
}
|
|
1371
|
+
);
|
|
1372
|
+
killer.on("error", () => resolve());
|
|
1373
|
+
killer.on("exit", () => resolve());
|
|
1374
|
+
});
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
try {
|
|
1378
|
+
process.kill(-processHandle.pid, "SIGKILL");
|
|
1379
|
+
} catch {
|
|
1380
|
+
try {
|
|
1381
|
+
processHandle.kill("SIGKILL");
|
|
1382
|
+
} catch {
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
async function sleep(ms) {
|
|
1387
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
1388
|
+
}
|
|
1071
1389
|
|
|
1072
1390
|
// src/config.ts
|
|
1073
|
-
var
|
|
1391
|
+
var import_fs2 = __toESM(require("fs"), 1);
|
|
1074
1392
|
var import_path3 = __toESM(require("path"), 1);
|
|
1075
1393
|
var import_url = require("url");
|
|
1076
1394
|
var import_dotenv = require("dotenv");
|
|
@@ -1398,9 +1716,10 @@ var DEFAULT_CONFIG = {
|
|
|
1398
1716
|
headless: false,
|
|
1399
1717
|
executablePath: void 0,
|
|
1400
1718
|
slowMo: 0,
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1719
|
+
mode: void 0,
|
|
1720
|
+
cdpUrl: void 0,
|
|
1721
|
+
userDataDir: void 0,
|
|
1722
|
+
profileDirectory: void 0
|
|
1404
1723
|
},
|
|
1405
1724
|
storage: {
|
|
1406
1725
|
rootDir: process.cwd()
|
|
@@ -1437,9 +1756,9 @@ function loadDotenvValues(rootDir, baseEnv, options = {}) {
|
|
|
1437
1756
|
const nodeEnv = baseEnv.NODE_ENV?.trim() || "";
|
|
1438
1757
|
for (const filename of dotenvFileOrder(nodeEnv)) {
|
|
1439
1758
|
const filePath = import_path3.default.join(baseDir, filename);
|
|
1440
|
-
if (!
|
|
1759
|
+
if (!import_fs2.default.existsSync(filePath)) continue;
|
|
1441
1760
|
try {
|
|
1442
|
-
const raw =
|
|
1761
|
+
const raw = import_fs2.default.readFileSync(filePath, "utf8");
|
|
1443
1762
|
const parsed = (0, import_dotenv.parse)(raw);
|
|
1444
1763
|
for (const [key, value] of Object.entries(parsed)) {
|
|
1445
1764
|
if (values[key] === void 0) {
|
|
@@ -1509,9 +1828,9 @@ function assertNoLegacyRuntimeConfig(source, config) {
|
|
|
1509
1828
|
}
|
|
1510
1829
|
function loadConfigFile(rootDir, options = {}) {
|
|
1511
1830
|
const configPath = import_path3.default.join(rootDir, ".opensteer", "config.json");
|
|
1512
|
-
if (!
|
|
1831
|
+
if (!import_fs2.default.existsSync(configPath)) return {};
|
|
1513
1832
|
try {
|
|
1514
|
-
const raw =
|
|
1833
|
+
const raw = import_fs2.default.readFileSync(configPath, "utf8");
|
|
1515
1834
|
return JSON.parse(raw);
|
|
1516
1835
|
} catch (error) {
|
|
1517
1836
|
const message = extractErrorMessage(
|
|
@@ -1744,6 +2063,8 @@ function resolveConfigWithEnv(input = {}, options = {}) {
|
|
|
1744
2063
|
const fileHasCloudApiKey = hasOwn(fileCloudOptions, "apiKey");
|
|
1745
2064
|
const fileHasCloudAccessToken = hasOwn(fileCloudOptions, "accessToken");
|
|
1746
2065
|
const fileRootDir = typeof fileConfig.storage?.rootDir === "string" ? fileConfig.storage.rootDir : void 0;
|
|
2066
|
+
assertNoRemovedBrowserConfig(input.browser, "Opensteer constructor config");
|
|
2067
|
+
assertNoRemovedBrowserConfig(fileConfig.browser, ".opensteer/config.json");
|
|
1747
2068
|
const envRootDir = input.storage?.rootDir ?? fileRootDir ?? initialRootDir;
|
|
1748
2069
|
const env = resolveEnv(envRootDir, {
|
|
1749
2070
|
debug: debugHint,
|
|
@@ -1759,14 +2080,30 @@ function resolveConfigWithEnv(input = {}, options = {}) {
|
|
|
1759
2080
|
"OPENSTEER_RUNTIME is no longer supported. Use OPENSTEER_MODE instead."
|
|
1760
2081
|
);
|
|
1761
2082
|
}
|
|
2083
|
+
if (env.OPENSTEER_CONNECT_URL != null) {
|
|
2084
|
+
throw new Error(
|
|
2085
|
+
"OPENSTEER_CONNECT_URL is no longer supported. Use OPENSTEER_CDP_URL instead."
|
|
2086
|
+
);
|
|
2087
|
+
}
|
|
2088
|
+
if (env.OPENSTEER_CHANNEL != null) {
|
|
2089
|
+
throw new Error(
|
|
2090
|
+
"OPENSTEER_CHANNEL is no longer supported. Use OPENSTEER_BROWSER plus OPENSTEER_BROWSER_PATH when needed."
|
|
2091
|
+
);
|
|
2092
|
+
}
|
|
2093
|
+
if (env.OPENSTEER_PROFILE_DIR != null) {
|
|
2094
|
+
throw new Error(
|
|
2095
|
+
"OPENSTEER_PROFILE_DIR is no longer supported. Use OPENSTEER_USER_DATA_DIR and OPENSTEER_PROFILE_DIRECTORY instead."
|
|
2096
|
+
);
|
|
2097
|
+
}
|
|
1762
2098
|
const envConfig = {
|
|
1763
2099
|
browser: {
|
|
1764
2100
|
headless: parseBool(env.OPENSTEER_HEADLESS),
|
|
1765
2101
|
executablePath: env.OPENSTEER_BROWSER_PATH || void 0,
|
|
1766
2102
|
slowMo: parseNumber(env.OPENSTEER_SLOW_MO),
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
2103
|
+
mode: env.OPENSTEER_BROWSER === "real" || env.OPENSTEER_BROWSER === "chromium" ? env.OPENSTEER_BROWSER : void 0,
|
|
2104
|
+
cdpUrl: env.OPENSTEER_CDP_URL || void 0,
|
|
2105
|
+
userDataDir: env.OPENSTEER_USER_DATA_DIR || void 0,
|
|
2106
|
+
profileDirectory: env.OPENSTEER_PROFILE_DIRECTORY || void 0
|
|
1770
2107
|
},
|
|
1771
2108
|
cursor: {
|
|
1772
2109
|
enabled: parseBool(env.OPENSTEER_CURSOR)
|
|
@@ -1777,6 +2114,34 @@ function resolveConfigWithEnv(input = {}, options = {}) {
|
|
|
1777
2114
|
const mergedWithFile = mergeDeep(runtimeDefaults, fileConfig);
|
|
1778
2115
|
const mergedWithEnv = mergeDeep(mergedWithFile, envConfig);
|
|
1779
2116
|
const resolved = mergeDeep(mergedWithEnv, input);
|
|
2117
|
+
const browserHeadlessExplicit = input.browser?.headless !== void 0 || fileConfig.browser?.headless !== void 0 || envConfig.browser?.headless !== void 0;
|
|
2118
|
+
if (!browserHeadlessExplicit && resolved.browser?.mode === "real") {
|
|
2119
|
+
resolved.browser = {
|
|
2120
|
+
...resolved.browser,
|
|
2121
|
+
headless: true
|
|
2122
|
+
};
|
|
2123
|
+
}
|
|
2124
|
+
function assertNoRemovedBrowserConfig(value, source) {
|
|
2125
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2126
|
+
return;
|
|
2127
|
+
}
|
|
2128
|
+
const record = value;
|
|
2129
|
+
if (record.connectUrl !== void 0) {
|
|
2130
|
+
throw new Error(
|
|
2131
|
+
`${source}.browser.connectUrl is no longer supported. Use browser.cdpUrl instead.`
|
|
2132
|
+
);
|
|
2133
|
+
}
|
|
2134
|
+
if (record.channel !== void 0) {
|
|
2135
|
+
throw new Error(
|
|
2136
|
+
`${source}.browser.channel is no longer supported. Use browser.mode plus browser.executablePath instead.`
|
|
2137
|
+
);
|
|
2138
|
+
}
|
|
2139
|
+
if (record.profileDir !== void 0) {
|
|
2140
|
+
throw new Error(
|
|
2141
|
+
`${source}.browser.profileDir is no longer supported. Use browser.userDataDir and browser.profileDirectory instead.`
|
|
2142
|
+
);
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
1780
2145
|
const envApiKey = resolveOpensteerApiKey(env);
|
|
1781
2146
|
const envAccessTokenRaw = resolveOpensteerAccessToken(env);
|
|
1782
2147
|
const envBaseUrl = resolveOpensteerBaseUrl(env);
|
|
@@ -2292,7 +2657,7 @@ var StealthCdpRuntime = class _StealthCdpRuntime {
|
|
|
2292
2657
|
TRANSIENT_CONTEXT_RETRY_DELAY_MS,
|
|
2293
2658
|
Math.max(0, deadline - Date.now())
|
|
2294
2659
|
);
|
|
2295
|
-
await
|
|
2660
|
+
await sleep2(retryDelay);
|
|
2296
2661
|
}
|
|
2297
2662
|
}
|
|
2298
2663
|
}
|
|
@@ -2325,7 +2690,7 @@ var StealthCdpRuntime = class _StealthCdpRuntime {
|
|
|
2325
2690
|
() => ({ kind: "resolved" }),
|
|
2326
2691
|
(error) => ({ kind: "rejected", error })
|
|
2327
2692
|
);
|
|
2328
|
-
const timeoutPromise =
|
|
2693
|
+
const timeoutPromise = sleep2(
|
|
2329
2694
|
timeout + FRAME_EVALUATE_GRACE_MS
|
|
2330
2695
|
).then(() => ({ kind: "timeout" }));
|
|
2331
2696
|
const result = await Promise.race([
|
|
@@ -2467,14 +2832,14 @@ function isIgnorableFrameError(error) {
|
|
|
2467
2832
|
const message = error.message;
|
|
2468
2833
|
return message.includes("Frame was detached") || message.includes("Target page, context or browser has been closed") || isTransientExecutionContextError(error) || message.includes("No frame for given id found");
|
|
2469
2834
|
}
|
|
2470
|
-
function
|
|
2835
|
+
function sleep2(ms) {
|
|
2471
2836
|
return new Promise((resolve) => {
|
|
2472
2837
|
setTimeout(resolve, ms);
|
|
2473
2838
|
});
|
|
2474
2839
|
}
|
|
2475
2840
|
|
|
2476
2841
|
// src/storage/local.ts
|
|
2477
|
-
var
|
|
2842
|
+
var import_fs3 = __toESM(require("fs"), 1);
|
|
2478
2843
|
var import_path4 = __toESM(require("path"), 1);
|
|
2479
2844
|
|
|
2480
2845
|
// src/storage/registry.ts
|
|
@@ -2518,14 +2883,14 @@ var LocalSelectorStorage = class {
|
|
|
2518
2883
|
return import_path4.default.join(this.getNamespaceDir(), this.getSelectorFileName(id));
|
|
2519
2884
|
}
|
|
2520
2885
|
ensureDirs() {
|
|
2521
|
-
|
|
2886
|
+
import_fs3.default.mkdirSync(this.getNamespaceDir(), { recursive: true });
|
|
2522
2887
|
}
|
|
2523
2888
|
loadRegistry() {
|
|
2524
2889
|
this.ensureDirs();
|
|
2525
2890
|
const file = this.getRegistryPath();
|
|
2526
|
-
if (!
|
|
2891
|
+
if (!import_fs3.default.existsSync(file)) return createEmptyRegistry(this.namespace);
|
|
2527
2892
|
try {
|
|
2528
|
-
const raw =
|
|
2893
|
+
const raw = import_fs3.default.readFileSync(file, "utf8");
|
|
2529
2894
|
return JSON.parse(raw);
|
|
2530
2895
|
} catch (error) {
|
|
2531
2896
|
const message = extractErrorMessage(
|
|
@@ -2542,16 +2907,16 @@ var LocalSelectorStorage = class {
|
|
|
2542
2907
|
}
|
|
2543
2908
|
saveRegistry(registry) {
|
|
2544
2909
|
this.ensureDirs();
|
|
2545
|
-
|
|
2910
|
+
import_fs3.default.writeFileSync(
|
|
2546
2911
|
this.getRegistryPath(),
|
|
2547
2912
|
JSON.stringify(registry, null, 2)
|
|
2548
2913
|
);
|
|
2549
2914
|
}
|
|
2550
2915
|
readSelector(id) {
|
|
2551
2916
|
const file = this.getSelectorPath(id);
|
|
2552
|
-
if (!
|
|
2917
|
+
if (!import_fs3.default.existsSync(file)) return null;
|
|
2553
2918
|
try {
|
|
2554
|
-
const raw =
|
|
2919
|
+
const raw = import_fs3.default.readFileSync(file, "utf8");
|
|
2555
2920
|
return JSON.parse(raw);
|
|
2556
2921
|
} catch (error) {
|
|
2557
2922
|
const message = extractErrorMessage(
|
|
@@ -2568,15 +2933,15 @@ var LocalSelectorStorage = class {
|
|
|
2568
2933
|
}
|
|
2569
2934
|
writeSelector(payload) {
|
|
2570
2935
|
this.ensureDirs();
|
|
2571
|
-
|
|
2936
|
+
import_fs3.default.writeFileSync(
|
|
2572
2937
|
this.getSelectorPath(payload.id),
|
|
2573
2938
|
JSON.stringify(payload, null, 2)
|
|
2574
2939
|
);
|
|
2575
2940
|
}
|
|
2576
2941
|
clearNamespace() {
|
|
2577
2942
|
const dir = this.getNamespaceDir();
|
|
2578
|
-
if (!
|
|
2579
|
-
|
|
2943
|
+
if (!import_fs3.default.existsSync(dir)) return;
|
|
2944
|
+
import_fs3.default.rmSync(dir, { recursive: true, force: true });
|
|
2580
2945
|
}
|
|
2581
2946
|
};
|
|
2582
2947
|
|
|
@@ -7030,7 +7395,7 @@ var AdaptiveNetworkTracker = class {
|
|
|
7030
7395
|
this.idleSince = 0;
|
|
7031
7396
|
}
|
|
7032
7397
|
const remaining = Math.max(1, options.deadline - now);
|
|
7033
|
-
await
|
|
7398
|
+
await sleep3(Math.min(NETWORK_POLL_MS, remaining));
|
|
7034
7399
|
}
|
|
7035
7400
|
}
|
|
7036
7401
|
handleRequestStarted = (request) => {
|
|
@@ -7075,7 +7440,7 @@ var AdaptiveNetworkTracker = class {
|
|
|
7075
7440
|
return false;
|
|
7076
7441
|
}
|
|
7077
7442
|
};
|
|
7078
|
-
async function
|
|
7443
|
+
async function sleep3(ms) {
|
|
7079
7444
|
await new Promise((resolve) => {
|
|
7080
7445
|
setTimeout(resolve, ms);
|
|
7081
7446
|
});
|
|
@@ -8552,15 +8917,15 @@ function withTokenQuery(wsUrl, token) {
|
|
|
8552
8917
|
}
|
|
8553
8918
|
|
|
8554
8919
|
// src/cloud/local-cache-sync.ts
|
|
8555
|
-
var
|
|
8920
|
+
var import_fs4 = __toESM(require("fs"), 1);
|
|
8556
8921
|
var import_path5 = __toESM(require("path"), 1);
|
|
8557
8922
|
function collectLocalSelectorCacheEntries(storage, options = {}) {
|
|
8558
8923
|
const debug = options.debug === true;
|
|
8559
8924
|
const namespace = storage.getNamespace();
|
|
8560
8925
|
const namespaceDir = storage.getNamespaceDir();
|
|
8561
|
-
if (!
|
|
8926
|
+
if (!import_fs4.default.existsSync(namespaceDir)) return [];
|
|
8562
8927
|
const entries = [];
|
|
8563
|
-
const fileNames =
|
|
8928
|
+
const fileNames = import_fs4.default.readdirSync(namespaceDir);
|
|
8564
8929
|
for (const fileName of fileNames) {
|
|
8565
8930
|
if (fileName === "index.json" || !fileName.endsWith(".json")) continue;
|
|
8566
8931
|
const filePath = import_path5.default.join(namespaceDir, fileName);
|
|
@@ -8591,7 +8956,7 @@ function collectLocalSelectorCacheEntries(storage, options = {}) {
|
|
|
8591
8956
|
}
|
|
8592
8957
|
function readSelectorFile(filePath, debug) {
|
|
8593
8958
|
try {
|
|
8594
|
-
const raw =
|
|
8959
|
+
const raw = import_fs4.default.readFileSync(filePath, "utf8");
|
|
8595
8960
|
return JSON.parse(raw);
|
|
8596
8961
|
} catch (error) {
|
|
8597
8962
|
const message = extractErrorMessage(
|
|
@@ -8686,13 +9051,13 @@ function dedupeNewest(entries) {
|
|
|
8686
9051
|
}
|
|
8687
9052
|
|
|
8688
9053
|
// src/cloud/cdp-client.ts
|
|
8689
|
-
var
|
|
9054
|
+
var import_playwright2 = require("playwright");
|
|
8690
9055
|
var CloudCdpClient = class {
|
|
8691
9056
|
async connect(args) {
|
|
8692
9057
|
const endpoint = withTokenQuery2(args.wsUrl, args.token);
|
|
8693
9058
|
let browser;
|
|
8694
9059
|
try {
|
|
8695
|
-
browser = await
|
|
9060
|
+
browser = await import_playwright2.chromium.connectOverCDP(endpoint);
|
|
8696
9061
|
} catch (error) {
|
|
8697
9062
|
const message = error instanceof Error ? error.message : "Failed to connect to cloud CDP endpoint.";
|
|
8698
9063
|
throw new OpensteerCloudError("CLOUD_TRANSPORT_ERROR", message);
|
|
@@ -10485,7 +10850,7 @@ async function executeAgentAction(page, action) {
|
|
|
10485
10850
|
}
|
|
10486
10851
|
case "wait": {
|
|
10487
10852
|
const ms = numberOr(action.timeMs, action.time_ms, 1e3);
|
|
10488
|
-
await
|
|
10853
|
+
await sleep4(ms);
|
|
10489
10854
|
return;
|
|
10490
10855
|
}
|
|
10491
10856
|
case "goto": {
|
|
@@ -10650,7 +11015,7 @@ async function pressKeyCombo(page, combo) {
|
|
|
10650
11015
|
}
|
|
10651
11016
|
}
|
|
10652
11017
|
}
|
|
10653
|
-
function
|
|
11018
|
+
function sleep4(ms) {
|
|
10654
11019
|
return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
|
|
10655
11020
|
}
|
|
10656
11021
|
|
|
@@ -10681,7 +11046,7 @@ var OpensteerCuaAgentHandler = class {
|
|
|
10681
11046
|
if (isMutatingAgentAction(action)) {
|
|
10682
11047
|
this.onMutatingAction?.(action);
|
|
10683
11048
|
}
|
|
10684
|
-
await
|
|
11049
|
+
await sleep5(this.config.waitBetweenActionsMs);
|
|
10685
11050
|
});
|
|
10686
11051
|
try {
|
|
10687
11052
|
const result = await this.client.execute({
|
|
@@ -10743,7 +11108,7 @@ var OpensteerCuaAgentHandler = class {
|
|
|
10743
11108
|
await this.cursorController.preview({ x, y }, "agent");
|
|
10744
11109
|
}
|
|
10745
11110
|
};
|
|
10746
|
-
function
|
|
11111
|
+
function sleep5(ms) {
|
|
10747
11112
|
return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
|
|
10748
11113
|
}
|
|
10749
11114
|
|
|
@@ -11179,7 +11544,7 @@ var CursorController = class {
|
|
|
11179
11544
|
for (const step of motion.points) {
|
|
11180
11545
|
await this.renderer.move(step, this.style);
|
|
11181
11546
|
if (motion.stepDelayMs > 0) {
|
|
11182
|
-
await
|
|
11547
|
+
await sleep6(motion.stepDelayMs);
|
|
11183
11548
|
}
|
|
11184
11549
|
}
|
|
11185
11550
|
if (shouldPulse(intent)) {
|
|
@@ -11337,7 +11702,7 @@ function clamp2(value, min, max) {
|
|
|
11337
11702
|
function shouldPulse(intent) {
|
|
11338
11703
|
return intent === "click" || intent === "dblclick" || intent === "rightclick" || intent === "agent";
|
|
11339
11704
|
}
|
|
11340
|
-
function
|
|
11705
|
+
function sleep6(ms) {
|
|
11341
11706
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
11342
11707
|
}
|
|
11343
11708
|
|
|
@@ -11722,9 +12087,11 @@ var Opensteer = class _Opensteer {
|
|
|
11722
12087
|
}
|
|
11723
12088
|
const session = await this.pool.launch({
|
|
11724
12089
|
...options,
|
|
11725
|
-
|
|
11726
|
-
|
|
11727
|
-
|
|
12090
|
+
mode: options.mode ?? this.config.browser?.mode,
|
|
12091
|
+
cdpUrl: options.cdpUrl ?? this.config.browser?.cdpUrl,
|
|
12092
|
+
userDataDir: options.userDataDir ?? this.config.browser?.userDataDir,
|
|
12093
|
+
profileDirectory: options.profileDirectory ?? this.config.browser?.profileDirectory,
|
|
12094
|
+
executablePath: options.executablePath ?? this.config.browser?.executablePath
|
|
11728
12095
|
});
|
|
11729
12096
|
this.browser = session.browser;
|
|
11730
12097
|
this.contextRef = session.context;
|
|
@@ -11755,6 +12122,32 @@ var Opensteer = class _Opensteer {
|
|
|
11755
12122
|
instance.snapshotCache = null;
|
|
11756
12123
|
return instance;
|
|
11757
12124
|
}
|
|
12125
|
+
static listLocalProfiles(userDataDir) {
|
|
12126
|
+
return listLocalChromeProfiles(userDataDir);
|
|
12127
|
+
}
|
|
12128
|
+
static fromSystemChrome(browser = {}, config = {}) {
|
|
12129
|
+
const chromePaths = detectChromePaths();
|
|
12130
|
+
const executablePath = browser.executablePath ?? config.browser?.executablePath ?? chromePaths.executable ?? void 0;
|
|
12131
|
+
if (!executablePath) {
|
|
12132
|
+
throw new Error(
|
|
12133
|
+
"Chrome was not found. Pass executablePath explicitly or install Chrome in a supported location."
|
|
12134
|
+
);
|
|
12135
|
+
}
|
|
12136
|
+
const userDataDir = browser.userDataDir ?? config.browser?.userDataDir ?? chromePaths.defaultUserDataDir;
|
|
12137
|
+
const autoDetectedProfiles = listLocalChromeProfiles(userDataDir);
|
|
12138
|
+
const profileDirectory = browser.profileDirectory ?? config.browser?.profileDirectory ?? autoDetectedProfiles[0]?.directory ?? "Default";
|
|
12139
|
+
return new _Opensteer({
|
|
12140
|
+
...config,
|
|
12141
|
+
browser: {
|
|
12142
|
+
...config.browser || {},
|
|
12143
|
+
mode: "real",
|
|
12144
|
+
headless: browser.headless ?? config.browser?.headless ?? true,
|
|
12145
|
+
executablePath,
|
|
12146
|
+
userDataDir,
|
|
12147
|
+
profileDirectory
|
|
12148
|
+
}
|
|
12149
|
+
});
|
|
12150
|
+
}
|
|
11758
12151
|
async close() {
|
|
11759
12152
|
this.snapshotCache = null;
|
|
11760
12153
|
if (this.cloud) {
|
|
@@ -14245,7 +14638,7 @@ var CdpOverlayCursorRenderer = class {
|
|
|
14245
14638
|
outlineColor: toProtocolRgba(pulseOutline)
|
|
14246
14639
|
});
|
|
14247
14640
|
});
|
|
14248
|
-
await
|
|
14641
|
+
await sleep7(PULSE_DELAY_MS);
|
|
14249
14642
|
await this.move(point, style);
|
|
14250
14643
|
}
|
|
14251
14644
|
async clear() {
|
|
@@ -14369,7 +14762,7 @@ function clampAlpha(value) {
|
|
|
14369
14762
|
function roundPointValue(value) {
|
|
14370
14763
|
return Math.round(value * 100) / 100;
|
|
14371
14764
|
}
|
|
14372
|
-
function
|
|
14765
|
+
function sleep7(ms) {
|
|
14373
14766
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
14374
14767
|
}
|
|
14375
14768
|
// Annotate the CommonJS export names for ESM import in node:
|