opensteer 0.6.2 → 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-7RMY26CM.js → chunk-54KNQTOL.js} +172 -55
- package/dist/{chunk-WJI7TGBQ.js → chunk-6B6LOYU3.js} +1 -1
- package/dist/{chunk-F2VDVOJO.js → chunk-G6V2DJRN.js} +451 -609
- package/dist/chunk-K5CL76MG.js +81 -0
- package/dist/{chunk-WDRMHPWL.js → chunk-KPPOTU3D.js} +159 -180
- package/dist/cli/auth.cjs +186 -95
- 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 +1747 -1279
- 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 +759 -257
- package/dist/cli/server.js +69 -16
- package/dist/index.cjs +688 -238
- 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/cli/server.cjs
CHANGED
|
@@ -418,13 +418,19 @@ var init_extractor = __esm({
|
|
|
418
418
|
|
|
419
419
|
// src/cli/server.ts
|
|
420
420
|
var import_net = require("net");
|
|
421
|
-
var
|
|
421
|
+
var import_fs5 = require("fs");
|
|
422
422
|
|
|
423
423
|
// src/opensteer.ts
|
|
424
424
|
var import_crypto = require("crypto");
|
|
425
425
|
|
|
426
426
|
// src/browser/pool.ts
|
|
427
|
-
var
|
|
427
|
+
var import_node_child_process = require("child_process");
|
|
428
|
+
var import_node_fs = require("fs");
|
|
429
|
+
var import_promises = require("fs/promises");
|
|
430
|
+
var import_node_net = require("net");
|
|
431
|
+
var import_node_os = require("os");
|
|
432
|
+
var import_node_path = require("path");
|
|
433
|
+
var import_playwright = require("playwright");
|
|
428
434
|
|
|
429
435
|
// src/browser/cdp-proxy.ts
|
|
430
436
|
var import_ws = __toESM(require("ws"), 1);
|
|
@@ -763,209 +769,263 @@ function errorMessage(error) {
|
|
|
763
769
|
return error instanceof Error ? error.message : String(error);
|
|
764
770
|
}
|
|
765
771
|
|
|
766
|
-
// src/browser/chromium-profile.ts
|
|
767
|
-
var import_node_util = require("util");
|
|
768
|
-
var import_node_child_process2 = require("child_process");
|
|
769
|
-
var import_node_crypto = require("crypto");
|
|
770
|
-
var import_promises = require("fs/promises");
|
|
771
|
-
var import_node_fs = require("fs");
|
|
772
|
-
var import_node_path = require("path");
|
|
773
|
-
var import_node_os = require("os");
|
|
774
|
-
var import_playwright = require("playwright");
|
|
775
|
-
|
|
776
|
-
// src/auth/keychain-store.ts
|
|
777
|
-
var import_node_child_process = require("child_process");
|
|
778
|
-
|
|
779
772
|
// src/browser/chrome.ts
|
|
780
773
|
var import_os = require("os");
|
|
781
774
|
var import_path = require("path");
|
|
775
|
+
var import_fs = require("fs");
|
|
776
|
+
function detectChromePaths() {
|
|
777
|
+
const os = (0, import_os.platform)();
|
|
778
|
+
if (os === "darwin") {
|
|
779
|
+
const executable2 = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
|
|
780
|
+
return {
|
|
781
|
+
executable: (0, import_fs.existsSync)(executable2) ? executable2 : null,
|
|
782
|
+
defaultUserDataDir: (0, import_path.join)(
|
|
783
|
+
(0, import_os.homedir)(),
|
|
784
|
+
"Library",
|
|
785
|
+
"Application Support",
|
|
786
|
+
"Google",
|
|
787
|
+
"Chrome"
|
|
788
|
+
)
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
if (os === "win32") {
|
|
792
|
+
const executable2 = (0, import_path.join)(
|
|
793
|
+
process.env.PROGRAMFILES || "C:\\Program Files",
|
|
794
|
+
"Google",
|
|
795
|
+
"Chrome",
|
|
796
|
+
"Application",
|
|
797
|
+
"chrome.exe"
|
|
798
|
+
);
|
|
799
|
+
return {
|
|
800
|
+
executable: (0, import_fs.existsSync)(executable2) ? executable2 : null,
|
|
801
|
+
defaultUserDataDir: (0, import_path.join)(
|
|
802
|
+
process.env.LOCALAPPDATA || (0, import_path.join)((0, import_os.homedir)(), "AppData", "Local"),
|
|
803
|
+
"Google",
|
|
804
|
+
"Chrome",
|
|
805
|
+
"User Data"
|
|
806
|
+
)
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
const executable = "/usr/bin/google-chrome";
|
|
810
|
+
return {
|
|
811
|
+
executable: (0, import_fs.existsSync)(executable) ? executable : null,
|
|
812
|
+
defaultUserDataDir: (0, import_path.join)((0, import_os.homedir)(), ".config", "google-chrome")
|
|
813
|
+
};
|
|
814
|
+
}
|
|
782
815
|
function expandHome(p) {
|
|
783
816
|
if (p.startsWith("~/") || p === "~") {
|
|
784
817
|
return (0, import_path.join)((0, import_os.homedir)(), p.slice(1));
|
|
785
818
|
}
|
|
786
819
|
return p;
|
|
787
820
|
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
return (0, import_node_fs.statSync)(filePath).isDirectory();
|
|
794
|
-
} catch {
|
|
795
|
-
return false;
|
|
821
|
+
function listLocalChromeProfiles(userDataDir = detectChromePaths().defaultUserDataDir) {
|
|
822
|
+
const resolvedUserDataDir = expandHome(userDataDir);
|
|
823
|
+
const localStatePath = (0, import_path.join)(resolvedUserDataDir, "Local State");
|
|
824
|
+
if (!(0, import_fs.existsSync)(localStatePath)) {
|
|
825
|
+
return [];
|
|
796
826
|
}
|
|
797
|
-
}
|
|
798
|
-
function fileExists(filePath) {
|
|
799
827
|
try {
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
}
|
|
805
|
-
function resolveCookieDbPath(profileDir) {
|
|
806
|
-
const candidates = [(0, import_node_path.join)(profileDir, "Network", "Cookies"), (0, import_node_path.join)(profileDir, "Cookies")];
|
|
807
|
-
for (const candidate of candidates) {
|
|
808
|
-
if (fileExists(candidate)) {
|
|
809
|
-
return candidate;
|
|
828
|
+
const raw = JSON.parse((0, import_fs.readFileSync)(localStatePath, "utf-8"));
|
|
829
|
+
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;
|
|
830
|
+
if (!infoCache || typeof infoCache !== "object") {
|
|
831
|
+
return [];
|
|
810
832
|
}
|
|
833
|
+
return Object.entries(infoCache).map(([directory, info]) => {
|
|
834
|
+
const record = info && typeof info === "object" && !Array.isArray(info) ? info : {};
|
|
835
|
+
const name = typeof record.name === "string" && record.name.trim() ? record.name.trim() : directory;
|
|
836
|
+
return {
|
|
837
|
+
directory,
|
|
838
|
+
name
|
|
839
|
+
};
|
|
840
|
+
}).filter((profile) => profile.directory.trim().length > 0).sort(
|
|
841
|
+
(left, right) => left.directory.localeCompare(right.directory)
|
|
842
|
+
);
|
|
843
|
+
} catch {
|
|
844
|
+
return [];
|
|
811
845
|
}
|
|
812
|
-
return null;
|
|
813
|
-
}
|
|
814
|
-
function resolvePersistentChromiumLaunchProfile(inputPath) {
|
|
815
|
-
const expandedPath = expandHome(inputPath.trim());
|
|
816
|
-
if (!expandedPath) {
|
|
817
|
-
return {
|
|
818
|
-
userDataDir: inputPath
|
|
819
|
-
};
|
|
820
|
-
}
|
|
821
|
-
if (fileExists(expandedPath) && (0, import_node_path.basename)(expandedPath) === "Cookies") {
|
|
822
|
-
const directParent = (0, import_node_path.dirname)(expandedPath);
|
|
823
|
-
const profileDir = (0, import_node_path.basename)(directParent) === "Network" ? (0, import_node_path.dirname)(directParent) : directParent;
|
|
824
|
-
return {
|
|
825
|
-
userDataDir: (0, import_node_path.dirname)(profileDir),
|
|
826
|
-
profileDirectory: (0, import_node_path.basename)(profileDir)
|
|
827
|
-
};
|
|
828
|
-
}
|
|
829
|
-
if (directoryExists(expandedPath) && resolveCookieDbPath(expandedPath) && fileExists((0, import_node_path.join)((0, import_node_path.dirname)(expandedPath), "Local State"))) {
|
|
830
|
-
return {
|
|
831
|
-
userDataDir: (0, import_node_path.dirname)(expandedPath),
|
|
832
|
-
profileDirectory: (0, import_node_path.basename)(expandedPath)
|
|
833
|
-
};
|
|
834
|
-
}
|
|
835
|
-
return {
|
|
836
|
-
userDataDir: expandedPath
|
|
837
|
-
};
|
|
838
846
|
}
|
|
839
847
|
|
|
840
848
|
// src/browser/pool.ts
|
|
841
849
|
var BrowserPool = class {
|
|
842
850
|
browser = null;
|
|
843
|
-
persistentContext = null;
|
|
844
851
|
cdpProxy = null;
|
|
852
|
+
launchedProcess = null;
|
|
853
|
+
tempUserDataDir = null;
|
|
845
854
|
defaults;
|
|
846
855
|
constructor(defaults = {}) {
|
|
847
856
|
this.defaults = defaults;
|
|
848
857
|
}
|
|
849
858
|
async launch(options = {}) {
|
|
850
|
-
if (this.browser || this.cdpProxy) {
|
|
859
|
+
if (this.browser || this.cdpProxy || this.launchedProcess || this.tempUserDataDir) {
|
|
851
860
|
await this.close();
|
|
852
861
|
}
|
|
853
|
-
const
|
|
854
|
-
const
|
|
855
|
-
const
|
|
856
|
-
|
|
857
|
-
|
|
862
|
+
const mode = options.mode ?? this.defaults.mode ?? "chromium";
|
|
863
|
+
const cdpUrl = options.cdpUrl ?? this.defaults.cdpUrl;
|
|
864
|
+
const userDataDir = options.userDataDir ?? this.defaults.userDataDir;
|
|
865
|
+
const profileDirectory = options.profileDirectory ?? this.defaults.profileDirectory;
|
|
866
|
+
const executablePath = options.executablePath ?? this.defaults.executablePath;
|
|
867
|
+
if (cdpUrl) {
|
|
868
|
+
if (mode === "real") {
|
|
869
|
+
throw new Error(
|
|
870
|
+
'cdpUrl cannot be combined with mode "real". Use one browser launch path at a time.'
|
|
871
|
+
);
|
|
872
|
+
}
|
|
873
|
+
if (userDataDir || profileDirectory) {
|
|
874
|
+
throw new Error(
|
|
875
|
+
"userDataDir/profileDirectory cannot be combined with cdpUrl."
|
|
876
|
+
);
|
|
877
|
+
}
|
|
878
|
+
if (options.context && Object.keys(options.context).length > 0) {
|
|
879
|
+
throw new Error(
|
|
880
|
+
"context launch options are not supported when attaching over CDP."
|
|
881
|
+
);
|
|
882
|
+
}
|
|
883
|
+
return this.connectToRunning(cdpUrl, options.timeout);
|
|
858
884
|
}
|
|
859
|
-
if (
|
|
860
|
-
|
|
885
|
+
if (mode !== "real" && (userDataDir || profileDirectory)) {
|
|
886
|
+
throw new Error(
|
|
887
|
+
'userDataDir/profileDirectory require mode "real".'
|
|
888
|
+
);
|
|
861
889
|
}
|
|
862
|
-
if (
|
|
863
|
-
|
|
890
|
+
if (mode === "real") {
|
|
891
|
+
if (options.context && Object.keys(options.context).length > 0) {
|
|
892
|
+
throw new Error(
|
|
893
|
+
"context launch options are not supported for real-browser mode."
|
|
894
|
+
);
|
|
895
|
+
}
|
|
896
|
+
return this.launchOwnedRealBrowser({
|
|
897
|
+
...options,
|
|
898
|
+
executablePath,
|
|
899
|
+
userDataDir,
|
|
900
|
+
profileDirectory
|
|
901
|
+
});
|
|
864
902
|
}
|
|
865
903
|
return this.launchSandbox(options);
|
|
866
904
|
}
|
|
867
905
|
async close() {
|
|
868
906
|
const browser = this.browser;
|
|
869
|
-
const
|
|
907
|
+
const cdpProxy = this.cdpProxy;
|
|
908
|
+
const launchedProcess = this.launchedProcess;
|
|
909
|
+
const tempUserDataDir = this.tempUserDataDir;
|
|
870
910
|
this.browser = null;
|
|
871
|
-
this.
|
|
911
|
+
this.cdpProxy = null;
|
|
912
|
+
this.launchedProcess = null;
|
|
913
|
+
this.tempUserDataDir = null;
|
|
872
914
|
try {
|
|
873
|
-
if (
|
|
874
|
-
await
|
|
875
|
-
} else if (browser) {
|
|
876
|
-
await browser.close();
|
|
915
|
+
if (browser) {
|
|
916
|
+
await browser.close().catch(() => void 0);
|
|
877
917
|
}
|
|
878
918
|
} finally {
|
|
879
|
-
|
|
880
|
-
|
|
919
|
+
cdpProxy?.close();
|
|
920
|
+
await killProcessTree(launchedProcess);
|
|
921
|
+
if (tempUserDataDir) {
|
|
922
|
+
await (0, import_promises.rm)(tempUserDataDir, {
|
|
923
|
+
recursive: true,
|
|
924
|
+
force: true
|
|
925
|
+
}).catch(() => void 0);
|
|
926
|
+
}
|
|
881
927
|
}
|
|
882
928
|
}
|
|
883
|
-
async connectToRunning(
|
|
884
|
-
this.cdpProxy?.close();
|
|
885
|
-
this.cdpProxy = null;
|
|
929
|
+
async connectToRunning(cdpUrl, timeout) {
|
|
886
930
|
let browser = null;
|
|
931
|
+
let cdpProxy = null;
|
|
887
932
|
try {
|
|
888
|
-
const { browserWsUrl, targets } = await discoverTargets(
|
|
933
|
+
const { browserWsUrl, targets } = await discoverTargets(cdpUrl);
|
|
889
934
|
if (targets.length === 0) {
|
|
890
935
|
throw new Error(
|
|
891
936
|
"No page targets found. Is the browser running with an open window?"
|
|
892
937
|
);
|
|
893
938
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
browser = await import_playwright2.chromium.connectOverCDP(proxyWsUrl, {
|
|
939
|
+
cdpProxy = new CDPProxy(browserWsUrl, targets[0].id);
|
|
940
|
+
const proxyWsUrl = await cdpProxy.start();
|
|
941
|
+
browser = await import_playwright.chromium.connectOverCDP(proxyWsUrl, {
|
|
898
942
|
timeout: timeout ?? 3e4
|
|
899
943
|
});
|
|
900
944
|
this.browser = browser;
|
|
901
|
-
this.
|
|
902
|
-
const
|
|
903
|
-
if (contexts.length === 0) {
|
|
904
|
-
throw new Error(
|
|
905
|
-
"Connection succeeded but no browser contexts found. Is the browser running with an open window?"
|
|
906
|
-
);
|
|
907
|
-
}
|
|
908
|
-
const context = contexts[0];
|
|
909
|
-
const pages = context.pages();
|
|
910
|
-
const page = pages.length > 0 ? pages[0] : await context.newPage();
|
|
945
|
+
this.cdpProxy = cdpProxy;
|
|
946
|
+
const { context, page } = await pickBrowserContextAndPage(browser);
|
|
911
947
|
return { browser, context, page, isExternal: true };
|
|
912
948
|
} catch (error) {
|
|
913
949
|
if (browser) {
|
|
914
950
|
await browser.close().catch(() => void 0);
|
|
915
951
|
}
|
|
952
|
+
cdpProxy?.close();
|
|
916
953
|
this.browser = null;
|
|
917
|
-
this.persistentContext = null;
|
|
918
|
-
this.cdpProxy?.close();
|
|
919
954
|
this.cdpProxy = null;
|
|
920
955
|
throw error;
|
|
921
956
|
}
|
|
922
957
|
}
|
|
923
|
-
async
|
|
924
|
-
const
|
|
925
|
-
const
|
|
926
|
-
if (
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
launchProfile.userDataDir,
|
|
931
|
-
{
|
|
932
|
-
channel,
|
|
933
|
-
headless: options.headless ?? this.defaults.headless,
|
|
934
|
-
executablePath: options.executablePath ?? this.defaults.executablePath ?? void 0,
|
|
935
|
-
slowMo: options.slowMo ?? this.defaults.slowMo ?? 0,
|
|
936
|
-
timeout: options.timeout,
|
|
937
|
-
...options.context || {},
|
|
938
|
-
args
|
|
939
|
-
}
|
|
940
|
-
);
|
|
941
|
-
const browser = context.browser();
|
|
942
|
-
if (!browser) {
|
|
943
|
-
await context.close().catch(() => void 0);
|
|
944
|
-
throw new Error("Persistent browser launch did not expose a browser instance.");
|
|
958
|
+
async launchOwnedRealBrowser(options) {
|
|
959
|
+
const chromePaths = detectChromePaths();
|
|
960
|
+
const executablePath = options.executablePath ?? chromePaths.executable ?? void 0;
|
|
961
|
+
if (!executablePath) {
|
|
962
|
+
throw new Error(
|
|
963
|
+
"Chrome was not found. Set browser.executablePath or install Chrome in a supported location."
|
|
964
|
+
);
|
|
945
965
|
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
const
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
966
|
+
const sourceUserDataDir = expandHome(
|
|
967
|
+
options.userDataDir ?? chromePaths.defaultUserDataDir
|
|
968
|
+
);
|
|
969
|
+
const profileDirectory = options.profileDirectory ?? "Default";
|
|
970
|
+
const tempUserDataDir = await cloneProfileToTempDir(
|
|
971
|
+
sourceUserDataDir,
|
|
972
|
+
profileDirectory
|
|
973
|
+
);
|
|
974
|
+
const debugPort = await reserveDebugPort();
|
|
975
|
+
const headless = resolveLaunchHeadless(
|
|
976
|
+
"real",
|
|
977
|
+
options.headless,
|
|
978
|
+
this.defaults.headless
|
|
979
|
+
);
|
|
980
|
+
const launchArgs = buildRealBrowserLaunchArgs({
|
|
981
|
+
userDataDir: tempUserDataDir,
|
|
982
|
+
profileDirectory,
|
|
983
|
+
debugPort,
|
|
984
|
+
headless
|
|
959
985
|
});
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
986
|
+
const processHandle = (0, import_node_child_process.spawn)(executablePath, launchArgs, {
|
|
987
|
+
detached: process.platform !== "win32",
|
|
988
|
+
stdio: "ignore"
|
|
989
|
+
});
|
|
990
|
+
processHandle.unref();
|
|
991
|
+
let browser = null;
|
|
992
|
+
try {
|
|
993
|
+
const wsUrl = await resolveCdpWebSocketUrl(
|
|
994
|
+
`http://127.0.0.1:${debugPort}`,
|
|
995
|
+
options.timeout ?? 3e4
|
|
996
|
+
);
|
|
997
|
+
browser = await import_playwright.chromium.connectOverCDP(wsUrl, {
|
|
998
|
+
timeout: options.timeout ?? 3e4
|
|
999
|
+
});
|
|
1000
|
+
const { context, page } = await createOwnedBrowserContextAndPage(
|
|
1001
|
+
browser,
|
|
1002
|
+
{
|
|
1003
|
+
headless,
|
|
1004
|
+
initialUrl: options.initialUrl,
|
|
1005
|
+
timeoutMs: options.timeout ?? 3e4
|
|
1006
|
+
}
|
|
1007
|
+
);
|
|
1008
|
+
this.browser = browser;
|
|
1009
|
+
this.launchedProcess = processHandle;
|
|
1010
|
+
this.tempUserDataDir = tempUserDataDir;
|
|
1011
|
+
return { browser, context, page, isExternal: false };
|
|
1012
|
+
} catch (error) {
|
|
1013
|
+
await browser?.close().catch(() => void 0);
|
|
1014
|
+
await killProcessTree(processHandle);
|
|
1015
|
+
await (0, import_promises.rm)(tempUserDataDir, {
|
|
1016
|
+
recursive: true,
|
|
1017
|
+
force: true
|
|
1018
|
+
}).catch(() => void 0);
|
|
1019
|
+
throw error;
|
|
1020
|
+
}
|
|
965
1021
|
}
|
|
966
1022
|
async launchSandbox(options) {
|
|
967
|
-
const browser = await
|
|
968
|
-
headless:
|
|
1023
|
+
const browser = await import_playwright.chromium.launch({
|
|
1024
|
+
headless: resolveLaunchHeadless(
|
|
1025
|
+
"chromium",
|
|
1026
|
+
options.headless,
|
|
1027
|
+
this.defaults.headless
|
|
1028
|
+
),
|
|
969
1029
|
executablePath: options.executablePath ?? this.defaults.executablePath ?? void 0,
|
|
970
1030
|
slowMo: options.slowMo ?? this.defaults.slowMo ?? 0,
|
|
971
1031
|
timeout: options.timeout
|
|
@@ -973,13 +1033,271 @@ var BrowserPool = class {
|
|
|
973
1033
|
const context = await browser.newContext(options.context || {});
|
|
974
1034
|
const page = await context.newPage();
|
|
975
1035
|
this.browser = browser;
|
|
976
|
-
this.persistentContext = null;
|
|
977
1036
|
return { browser, context, page, isExternal: false };
|
|
978
1037
|
}
|
|
979
1038
|
};
|
|
1039
|
+
async function pickBrowserContextAndPage(browser) {
|
|
1040
|
+
const context = getPrimaryBrowserContext(browser);
|
|
1041
|
+
const pages = context.pages();
|
|
1042
|
+
const page = pages.find((candidate) => isInspectablePageUrl2(candidate.url())) || pages[0] || await context.newPage();
|
|
1043
|
+
return { context, page };
|
|
1044
|
+
}
|
|
1045
|
+
function resolveLaunchHeadless(mode, requestedHeadless, defaultHeadless) {
|
|
1046
|
+
if (requestedHeadless !== void 0) {
|
|
1047
|
+
return requestedHeadless;
|
|
1048
|
+
}
|
|
1049
|
+
if (defaultHeadless !== void 0) {
|
|
1050
|
+
return defaultHeadless;
|
|
1051
|
+
}
|
|
1052
|
+
return mode === "real";
|
|
1053
|
+
}
|
|
1054
|
+
async function createOwnedBrowserContextAndPage(browser, options) {
|
|
1055
|
+
const context = getPrimaryBrowserContext(browser);
|
|
1056
|
+
const page = await createOwnedBrowserPage(browser, context, options);
|
|
1057
|
+
return { context, page };
|
|
1058
|
+
}
|
|
1059
|
+
async function createOwnedBrowserPage(browser, context, options) {
|
|
1060
|
+
const targetUrl = options.initialUrl ?? "about:blank";
|
|
1061
|
+
const existingPages = new Set(context.pages());
|
|
1062
|
+
const browserSession = await browser.newBrowserCDPSession();
|
|
1063
|
+
try {
|
|
1064
|
+
const { targetId } = await browserSession.send("Target.createTarget", {
|
|
1065
|
+
url: targetUrl,
|
|
1066
|
+
newWindow: !options.headless
|
|
1067
|
+
});
|
|
1068
|
+
await browserSession.send("Target.activateTarget", { targetId }).catch(() => void 0);
|
|
1069
|
+
const page = await waitForOwnedBrowserPage(context, {
|
|
1070
|
+
existingPages,
|
|
1071
|
+
targetUrl,
|
|
1072
|
+
timeoutMs: options.timeoutMs
|
|
1073
|
+
});
|
|
1074
|
+
if (targetUrl !== "about:blank") {
|
|
1075
|
+
await page.waitForLoadState("domcontentloaded", {
|
|
1076
|
+
timeout: options.timeoutMs
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
await closeDisposableStartupTargets(browserSession, targetId);
|
|
1080
|
+
return page;
|
|
1081
|
+
} finally {
|
|
1082
|
+
await browserSession.detach().catch(() => void 0);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
async function closeDisposableStartupTargets(browserSession, preservedTargetId) {
|
|
1086
|
+
const response = await browserSession.send("Target.getTargets").catch(() => null);
|
|
1087
|
+
if (!response) {
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
for (const targetInfo of response.targetInfos) {
|
|
1091
|
+
if (targetInfo.targetId === preservedTargetId || targetInfo.type !== "page" || !isDisposableStartupPageUrl(targetInfo.url)) {
|
|
1092
|
+
continue;
|
|
1093
|
+
}
|
|
1094
|
+
await browserSession.send("Target.closeTarget", { targetId: targetInfo.targetId }).catch(() => void 0);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
async function waitForOwnedBrowserPage(context, options) {
|
|
1098
|
+
const deadline = Date.now() + options.timeoutMs;
|
|
1099
|
+
let fallbackPage = null;
|
|
1100
|
+
while (Date.now() < deadline) {
|
|
1101
|
+
for (const candidate of context.pages()) {
|
|
1102
|
+
if (options.existingPages.has(candidate)) {
|
|
1103
|
+
continue;
|
|
1104
|
+
}
|
|
1105
|
+
const url = candidate.url();
|
|
1106
|
+
if (!isInspectablePageUrl2(url)) {
|
|
1107
|
+
continue;
|
|
1108
|
+
}
|
|
1109
|
+
fallbackPage ??= candidate;
|
|
1110
|
+
if (options.targetUrl === "about:blank") {
|
|
1111
|
+
return candidate;
|
|
1112
|
+
}
|
|
1113
|
+
if (pageLooselyMatchesUrl(url, options.targetUrl)) {
|
|
1114
|
+
return candidate;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
await sleep(100);
|
|
1118
|
+
}
|
|
1119
|
+
if (fallbackPage) {
|
|
1120
|
+
return fallbackPage;
|
|
1121
|
+
}
|
|
1122
|
+
throw new Error(
|
|
1123
|
+
`Chrome created a target for ${options.targetUrl}, but Playwright did not expose the page in time.`
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
function getPrimaryBrowserContext(browser) {
|
|
1127
|
+
const contexts = browser.contexts();
|
|
1128
|
+
if (contexts.length === 0) {
|
|
1129
|
+
throw new Error(
|
|
1130
|
+
"Connection succeeded but no browser contexts were exposed."
|
|
1131
|
+
);
|
|
1132
|
+
}
|
|
1133
|
+
return contexts[0];
|
|
1134
|
+
}
|
|
1135
|
+
function isInspectablePageUrl2(url) {
|
|
1136
|
+
return url === "about:blank" || url.startsWith("http://") || url.startsWith("https://");
|
|
1137
|
+
}
|
|
1138
|
+
function isDisposableStartupPageUrl(url) {
|
|
1139
|
+
return url === "about:blank" || url === "chrome://newtab/" || url === "chrome://new-tab-page/";
|
|
1140
|
+
}
|
|
1141
|
+
function pageLooselyMatchesUrl(currentUrl, initialUrl) {
|
|
1142
|
+
try {
|
|
1143
|
+
const current = new URL(currentUrl);
|
|
1144
|
+
const requested = new URL(initialUrl);
|
|
1145
|
+
if (current.href === requested.href) {
|
|
1146
|
+
return true;
|
|
1147
|
+
}
|
|
1148
|
+
return current.hostname === requested.hostname && current.pathname === requested.pathname;
|
|
1149
|
+
} catch {
|
|
1150
|
+
return currentUrl === initialUrl;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
function normalizeDiscoveryUrl(cdpUrl) {
|
|
1154
|
+
let parsed;
|
|
1155
|
+
try {
|
|
1156
|
+
parsed = new URL(cdpUrl);
|
|
1157
|
+
} catch {
|
|
1158
|
+
throw new Error(
|
|
1159
|
+
`Invalid CDP URL "${cdpUrl}". Use an http(s) or ws(s) endpoint.`
|
|
1160
|
+
);
|
|
1161
|
+
}
|
|
1162
|
+
if (parsed.protocol === "ws:" || parsed.protocol === "wss:") {
|
|
1163
|
+
return parsed;
|
|
1164
|
+
}
|
|
1165
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
1166
|
+
throw new Error(
|
|
1167
|
+
`Unsupported CDP URL protocol "${parsed.protocol}". Use http(s) or ws(s).`
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
const normalized = new URL(parsed.toString());
|
|
1171
|
+
normalized.pathname = "/json/version";
|
|
1172
|
+
normalized.search = "";
|
|
1173
|
+
normalized.hash = "";
|
|
1174
|
+
return normalized;
|
|
1175
|
+
}
|
|
1176
|
+
async function resolveCdpWebSocketUrl(cdpUrl, timeoutMs) {
|
|
1177
|
+
if (cdpUrl.startsWith("ws://") || cdpUrl.startsWith("wss://")) {
|
|
1178
|
+
return cdpUrl;
|
|
1179
|
+
}
|
|
1180
|
+
const versionUrl = normalizeDiscoveryUrl(cdpUrl);
|
|
1181
|
+
const deadline = Date.now() + timeoutMs;
|
|
1182
|
+
let lastError = "CDP discovery did not respond.";
|
|
1183
|
+
while (Date.now() < deadline) {
|
|
1184
|
+
const remaining = Math.max(deadline - Date.now(), 1e3);
|
|
1185
|
+
try {
|
|
1186
|
+
const response = await fetch(versionUrl, {
|
|
1187
|
+
signal: AbortSignal.timeout(Math.min(remaining, 5e3))
|
|
1188
|
+
});
|
|
1189
|
+
if (!response.ok) {
|
|
1190
|
+
lastError = `${response.status} ${response.statusText}`;
|
|
1191
|
+
} else {
|
|
1192
|
+
const payload = await response.json();
|
|
1193
|
+
const wsUrl = payload && typeof payload === "object" && !Array.isArray(payload) && typeof payload.webSocketDebuggerUrl === "string" ? payload.webSocketDebuggerUrl : null;
|
|
1194
|
+
if (wsUrl && wsUrl.trim()) {
|
|
1195
|
+
return wsUrl;
|
|
1196
|
+
}
|
|
1197
|
+
lastError = "CDP discovery response did not include webSocketDebuggerUrl.";
|
|
1198
|
+
}
|
|
1199
|
+
} catch (error) {
|
|
1200
|
+
lastError = error instanceof Error ? error.message : "Unknown error";
|
|
1201
|
+
}
|
|
1202
|
+
await sleep(100);
|
|
1203
|
+
}
|
|
1204
|
+
throw new Error(
|
|
1205
|
+
`Failed to resolve a CDP websocket URL from ${versionUrl.toString()}: ${lastError}`
|
|
1206
|
+
);
|
|
1207
|
+
}
|
|
1208
|
+
async function reserveDebugPort() {
|
|
1209
|
+
return await new Promise((resolve, reject) => {
|
|
1210
|
+
const server2 = (0, import_node_net.createServer)();
|
|
1211
|
+
server2.unref();
|
|
1212
|
+
server2.on("error", reject);
|
|
1213
|
+
server2.listen(0, "127.0.0.1", () => {
|
|
1214
|
+
const address = server2.address();
|
|
1215
|
+
if (!address || typeof address === "string") {
|
|
1216
|
+
server2.close();
|
|
1217
|
+
reject(new Error("Failed to reserve a local debug port."));
|
|
1218
|
+
return;
|
|
1219
|
+
}
|
|
1220
|
+
server2.close((error) => {
|
|
1221
|
+
if (error) {
|
|
1222
|
+
reject(error);
|
|
1223
|
+
return;
|
|
1224
|
+
}
|
|
1225
|
+
resolve(address.port);
|
|
1226
|
+
});
|
|
1227
|
+
});
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1230
|
+
async function cloneProfileToTempDir(userDataDir, profileDirectory) {
|
|
1231
|
+
const resolvedUserDataDir = expandHome(userDataDir);
|
|
1232
|
+
const tempUserDataDir = await (0, import_promises.mkdtemp)(
|
|
1233
|
+
(0, import_node_path.join)((0, import_node_os.tmpdir)(), "opensteer-real-browser-")
|
|
1234
|
+
);
|
|
1235
|
+
const sourceProfileDir = (0, import_node_path.join)(resolvedUserDataDir, profileDirectory);
|
|
1236
|
+
const targetProfileDir = (0, import_node_path.join)(tempUserDataDir, profileDirectory);
|
|
1237
|
+
if ((0, import_node_fs.existsSync)(sourceProfileDir)) {
|
|
1238
|
+
await (0, import_promises.cp)(sourceProfileDir, targetProfileDir, {
|
|
1239
|
+
recursive: true
|
|
1240
|
+
});
|
|
1241
|
+
} else {
|
|
1242
|
+
await (0, import_promises.mkdir)(targetProfileDir, {
|
|
1243
|
+
recursive: true
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
const localStatePath = (0, import_node_path.join)(resolvedUserDataDir, "Local State");
|
|
1247
|
+
if ((0, import_node_fs.existsSync)(localStatePath)) {
|
|
1248
|
+
await (0, import_promises.copyFile)(localStatePath, (0, import_node_path.join)(tempUserDataDir, "Local State"));
|
|
1249
|
+
}
|
|
1250
|
+
return tempUserDataDir;
|
|
1251
|
+
}
|
|
1252
|
+
function buildRealBrowserLaunchArgs(options) {
|
|
1253
|
+
const args = [
|
|
1254
|
+
`--user-data-dir=${options.userDataDir}`,
|
|
1255
|
+
`--profile-directory=${options.profileDirectory}`,
|
|
1256
|
+
`--remote-debugging-port=${options.debugPort}`,
|
|
1257
|
+
"--no-first-run",
|
|
1258
|
+
"--no-default-browser-check",
|
|
1259
|
+
"--disable-background-networking",
|
|
1260
|
+
"--disable-sync",
|
|
1261
|
+
"--disable-popup-blocking"
|
|
1262
|
+
];
|
|
1263
|
+
if (options.headless) {
|
|
1264
|
+
args.push("--headless=new");
|
|
1265
|
+
}
|
|
1266
|
+
return args;
|
|
1267
|
+
}
|
|
1268
|
+
async function killProcessTree(processHandle) {
|
|
1269
|
+
if (!processHandle || processHandle.pid == null || processHandle.exitCode !== null) {
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1272
|
+
if (process.platform === "win32") {
|
|
1273
|
+
await new Promise((resolve) => {
|
|
1274
|
+
const killer = (0, import_node_child_process.spawn)(
|
|
1275
|
+
"taskkill",
|
|
1276
|
+
["/pid", String(processHandle.pid), "/t", "/f"],
|
|
1277
|
+
{
|
|
1278
|
+
stdio: "ignore"
|
|
1279
|
+
}
|
|
1280
|
+
);
|
|
1281
|
+
killer.on("error", () => resolve());
|
|
1282
|
+
killer.on("exit", () => resolve());
|
|
1283
|
+
});
|
|
1284
|
+
return;
|
|
1285
|
+
}
|
|
1286
|
+
try {
|
|
1287
|
+
process.kill(-processHandle.pid, "SIGKILL");
|
|
1288
|
+
} catch {
|
|
1289
|
+
try {
|
|
1290
|
+
processHandle.kill("SIGKILL");
|
|
1291
|
+
} catch {
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
async function sleep(ms) {
|
|
1296
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
1297
|
+
}
|
|
980
1298
|
|
|
981
1299
|
// src/config.ts
|
|
982
|
-
var
|
|
1300
|
+
var import_fs2 = __toESM(require("fs"), 1);
|
|
983
1301
|
var import_path3 = __toESM(require("path"), 1);
|
|
984
1302
|
var import_url = require("url");
|
|
985
1303
|
var import_dotenv = require("dotenv");
|
|
@@ -1210,6 +1528,65 @@ function toJsonSafeValue(value, seen) {
|
|
|
1210
1528
|
return void 0;
|
|
1211
1529
|
}
|
|
1212
1530
|
|
|
1531
|
+
// src/cloud/credential-selection.ts
|
|
1532
|
+
function selectCloudCredential(options) {
|
|
1533
|
+
const apiKey = normalizeNonEmptyString(options.apiKey);
|
|
1534
|
+
const accessToken = normalizeNonEmptyString(options.accessToken);
|
|
1535
|
+
if (apiKey) {
|
|
1536
|
+
if (options.authScheme === "bearer") {
|
|
1537
|
+
return {
|
|
1538
|
+
apiKey,
|
|
1539
|
+
authScheme: "bearer",
|
|
1540
|
+
kind: "access-token",
|
|
1541
|
+
token: apiKey,
|
|
1542
|
+
compatibilityBearerApiKey: true
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
return {
|
|
1546
|
+
apiKey,
|
|
1547
|
+
authScheme: "api-key",
|
|
1548
|
+
kind: "api-key",
|
|
1549
|
+
token: apiKey
|
|
1550
|
+
};
|
|
1551
|
+
}
|
|
1552
|
+
if (accessToken) {
|
|
1553
|
+
return {
|
|
1554
|
+
accessToken,
|
|
1555
|
+
authScheme: "bearer",
|
|
1556
|
+
kind: "access-token",
|
|
1557
|
+
token: accessToken
|
|
1558
|
+
};
|
|
1559
|
+
}
|
|
1560
|
+
return null;
|
|
1561
|
+
}
|
|
1562
|
+
function selectCloudCredentialByPrecedence(layers, authScheme) {
|
|
1563
|
+
for (const layer of layers) {
|
|
1564
|
+
const hasApiKey = layer.hasApiKey ?? Object.prototype.hasOwnProperty.call(layer, "apiKey");
|
|
1565
|
+
const hasAccessToken = layer.hasAccessToken ?? Object.prototype.hasOwnProperty.call(layer, "accessToken");
|
|
1566
|
+
if (!hasApiKey && !hasAccessToken) {
|
|
1567
|
+
continue;
|
|
1568
|
+
}
|
|
1569
|
+
return {
|
|
1570
|
+
source: layer.source,
|
|
1571
|
+
apiKey: layer.apiKey,
|
|
1572
|
+
accessToken: layer.accessToken,
|
|
1573
|
+
hasApiKey,
|
|
1574
|
+
hasAccessToken,
|
|
1575
|
+
credential: selectCloudCredential({
|
|
1576
|
+
apiKey: layer.apiKey,
|
|
1577
|
+
accessToken: layer.accessToken,
|
|
1578
|
+
authScheme: layer.authScheme ?? authScheme
|
|
1579
|
+
})
|
|
1580
|
+
};
|
|
1581
|
+
}
|
|
1582
|
+
return null;
|
|
1583
|
+
}
|
|
1584
|
+
function normalizeNonEmptyString(value) {
|
|
1585
|
+
if (typeof value !== "string") return void 0;
|
|
1586
|
+
const normalized = value.trim();
|
|
1587
|
+
return normalized.length ? normalized : void 0;
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1213
1590
|
// src/storage/namespace.ts
|
|
1214
1591
|
var import_path2 = __toESM(require("path"), 1);
|
|
1215
1592
|
var DEFAULT_NAMESPACE = "default";
|
|
@@ -1248,9 +1625,10 @@ var DEFAULT_CONFIG = {
|
|
|
1248
1625
|
headless: false,
|
|
1249
1626
|
executablePath: void 0,
|
|
1250
1627
|
slowMo: 0,
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1628
|
+
mode: void 0,
|
|
1629
|
+
cdpUrl: void 0,
|
|
1630
|
+
userDataDir: void 0,
|
|
1631
|
+
profileDirectory: void 0
|
|
1254
1632
|
},
|
|
1255
1633
|
storage: {
|
|
1256
1634
|
rootDir: process.cwd()
|
|
@@ -1287,9 +1665,9 @@ function loadDotenvValues(rootDir, baseEnv, options = {}) {
|
|
|
1287
1665
|
const nodeEnv = baseEnv.NODE_ENV?.trim() || "";
|
|
1288
1666
|
for (const filename of dotenvFileOrder(nodeEnv)) {
|
|
1289
1667
|
const filePath = import_path3.default.join(baseDir, filename);
|
|
1290
|
-
if (!
|
|
1668
|
+
if (!import_fs2.default.existsSync(filePath)) continue;
|
|
1291
1669
|
try {
|
|
1292
|
-
const raw =
|
|
1670
|
+
const raw = import_fs2.default.readFileSync(filePath, "utf8");
|
|
1293
1671
|
const parsed = (0, import_dotenv.parse)(raw);
|
|
1294
1672
|
for (const [key, value] of Object.entries(parsed)) {
|
|
1295
1673
|
if (values[key] === void 0) {
|
|
@@ -1359,9 +1737,9 @@ function assertNoLegacyRuntimeConfig(source, config) {
|
|
|
1359
1737
|
}
|
|
1360
1738
|
function loadConfigFile(rootDir, options = {}) {
|
|
1361
1739
|
const configPath = import_path3.default.join(rootDir, ".opensteer", "config.json");
|
|
1362
|
-
if (!
|
|
1740
|
+
if (!import_fs2.default.existsSync(configPath)) return {};
|
|
1363
1741
|
try {
|
|
1364
|
-
const raw =
|
|
1742
|
+
const raw = import_fs2.default.readFileSync(configPath, "utf8");
|
|
1365
1743
|
return JSON.parse(raw);
|
|
1366
1744
|
} catch (error) {
|
|
1367
1745
|
const message = extractErrorMessage(
|
|
@@ -1534,11 +1912,6 @@ function normalizeCloudOptions(value) {
|
|
|
1534
1912
|
}
|
|
1535
1913
|
return value;
|
|
1536
1914
|
}
|
|
1537
|
-
function normalizeNonEmptyString(value) {
|
|
1538
|
-
if (typeof value !== "string") return void 0;
|
|
1539
|
-
const normalized = value.trim();
|
|
1540
|
-
return normalized.length ? normalized : void 0;
|
|
1541
|
-
}
|
|
1542
1915
|
function parseCloudEnabled(value, source) {
|
|
1543
1916
|
if (value == null) return void 0;
|
|
1544
1917
|
if (typeof value === "boolean") return value;
|
|
@@ -1547,6 +1920,18 @@ function parseCloudEnabled(value, source) {
|
|
|
1547
1920
|
`Invalid ${source} value "${String(value)}". Use true, false, or a cloud options object.`
|
|
1548
1921
|
);
|
|
1549
1922
|
}
|
|
1923
|
+
function resolveCloudCredentialFields(selectedLayer) {
|
|
1924
|
+
const credential = selectedLayer?.credential;
|
|
1925
|
+
if (!credential) return {};
|
|
1926
|
+
if (credential.kind === "api-key" || credential.compatibilityBearerApiKey === true && selectedLayer?.source !== "env") {
|
|
1927
|
+
return {
|
|
1928
|
+
apiKey: credential.token
|
|
1929
|
+
};
|
|
1930
|
+
}
|
|
1931
|
+
return {
|
|
1932
|
+
accessToken: credential.token
|
|
1933
|
+
};
|
|
1934
|
+
}
|
|
1550
1935
|
function resolveCloudSelection(config, env = process.env) {
|
|
1551
1936
|
const configCloud = parseCloudEnabled(config.cloud, "cloud");
|
|
1552
1937
|
if (configCloud !== void 0) {
|
|
@@ -1583,7 +1968,12 @@ function resolveConfigWithEnv(input = {}, options = {}) {
|
|
|
1583
1968
|
});
|
|
1584
1969
|
assertNoLegacyAiConfig(".opensteer/config.json", fileConfig);
|
|
1585
1970
|
assertNoLegacyRuntimeConfig(".opensteer/config.json", fileConfig);
|
|
1971
|
+
const fileCloudOptions = normalizeCloudOptions(fileConfig.cloud);
|
|
1972
|
+
const fileHasCloudApiKey = hasOwn(fileCloudOptions, "apiKey");
|
|
1973
|
+
const fileHasCloudAccessToken = hasOwn(fileCloudOptions, "accessToken");
|
|
1586
1974
|
const fileRootDir = typeof fileConfig.storage?.rootDir === "string" ? fileConfig.storage.rootDir : void 0;
|
|
1975
|
+
assertNoRemovedBrowserConfig(input.browser, "Opensteer constructor config");
|
|
1976
|
+
assertNoRemovedBrowserConfig(fileConfig.browser, ".opensteer/config.json");
|
|
1587
1977
|
const envRootDir = input.storage?.rootDir ?? fileRootDir ?? initialRootDir;
|
|
1588
1978
|
const env = resolveEnv(envRootDir, {
|
|
1589
1979
|
debug: debugHint,
|
|
@@ -1599,14 +1989,30 @@ function resolveConfigWithEnv(input = {}, options = {}) {
|
|
|
1599
1989
|
"OPENSTEER_RUNTIME is no longer supported. Use OPENSTEER_MODE instead."
|
|
1600
1990
|
);
|
|
1601
1991
|
}
|
|
1992
|
+
if (env.OPENSTEER_CONNECT_URL != null) {
|
|
1993
|
+
throw new Error(
|
|
1994
|
+
"OPENSTEER_CONNECT_URL is no longer supported. Use OPENSTEER_CDP_URL instead."
|
|
1995
|
+
);
|
|
1996
|
+
}
|
|
1997
|
+
if (env.OPENSTEER_CHANNEL != null) {
|
|
1998
|
+
throw new Error(
|
|
1999
|
+
"OPENSTEER_CHANNEL is no longer supported. Use OPENSTEER_BROWSER plus OPENSTEER_BROWSER_PATH when needed."
|
|
2000
|
+
);
|
|
2001
|
+
}
|
|
2002
|
+
if (env.OPENSTEER_PROFILE_DIR != null) {
|
|
2003
|
+
throw new Error(
|
|
2004
|
+
"OPENSTEER_PROFILE_DIR is no longer supported. Use OPENSTEER_USER_DATA_DIR and OPENSTEER_PROFILE_DIRECTORY instead."
|
|
2005
|
+
);
|
|
2006
|
+
}
|
|
1602
2007
|
const envConfig = {
|
|
1603
2008
|
browser: {
|
|
1604
2009
|
headless: parseBool(env.OPENSTEER_HEADLESS),
|
|
1605
2010
|
executablePath: env.OPENSTEER_BROWSER_PATH || void 0,
|
|
1606
2011
|
slowMo: parseNumber(env.OPENSTEER_SLOW_MO),
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
2012
|
+
mode: env.OPENSTEER_BROWSER === "real" || env.OPENSTEER_BROWSER === "chromium" ? env.OPENSTEER_BROWSER : void 0,
|
|
2013
|
+
cdpUrl: env.OPENSTEER_CDP_URL || void 0,
|
|
2014
|
+
userDataDir: env.OPENSTEER_USER_DATA_DIR || void 0,
|
|
2015
|
+
profileDirectory: env.OPENSTEER_PROFILE_DIRECTORY || void 0
|
|
1610
2016
|
},
|
|
1611
2017
|
cursor: {
|
|
1612
2018
|
enabled: parseBool(env.OPENSTEER_CURSOR)
|
|
@@ -1617,17 +2023,38 @@ function resolveConfigWithEnv(input = {}, options = {}) {
|
|
|
1617
2023
|
const mergedWithFile = mergeDeep(runtimeDefaults, fileConfig);
|
|
1618
2024
|
const mergedWithEnv = mergeDeep(mergedWithFile, envConfig);
|
|
1619
2025
|
const resolved = mergeDeep(mergedWithEnv, input);
|
|
2026
|
+
const browserHeadlessExplicit = input.browser?.headless !== void 0 || fileConfig.browser?.headless !== void 0 || envConfig.browser?.headless !== void 0;
|
|
2027
|
+
if (!browserHeadlessExplicit && resolved.browser?.mode === "real") {
|
|
2028
|
+
resolved.browser = {
|
|
2029
|
+
...resolved.browser,
|
|
2030
|
+
headless: true
|
|
2031
|
+
};
|
|
2032
|
+
}
|
|
2033
|
+
function assertNoRemovedBrowserConfig(value, source) {
|
|
2034
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2035
|
+
return;
|
|
2036
|
+
}
|
|
2037
|
+
const record = value;
|
|
2038
|
+
if (record.connectUrl !== void 0) {
|
|
2039
|
+
throw new Error(
|
|
2040
|
+
`${source}.browser.connectUrl is no longer supported. Use browser.cdpUrl instead.`
|
|
2041
|
+
);
|
|
2042
|
+
}
|
|
2043
|
+
if (record.channel !== void 0) {
|
|
2044
|
+
throw new Error(
|
|
2045
|
+
`${source}.browser.channel is no longer supported. Use browser.mode plus browser.executablePath instead.`
|
|
2046
|
+
);
|
|
2047
|
+
}
|
|
2048
|
+
if (record.profileDir !== void 0) {
|
|
2049
|
+
throw new Error(
|
|
2050
|
+
`${source}.browser.profileDir is no longer supported. Use browser.userDataDir and browser.profileDirectory instead.`
|
|
2051
|
+
);
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
1620
2054
|
const envApiKey = resolveOpensteerApiKey(env);
|
|
1621
2055
|
const envAccessTokenRaw = resolveOpensteerAccessToken(env);
|
|
1622
2056
|
const envBaseUrl = resolveOpensteerBaseUrl(env);
|
|
1623
2057
|
const envAuthScheme = resolveOpensteerAuthScheme(env);
|
|
1624
|
-
if (envApiKey && envAccessTokenRaw) {
|
|
1625
|
-
throw new Error(
|
|
1626
|
-
"OPENSTEER_API_KEY and OPENSTEER_ACCESS_TOKEN are mutually exclusive. Set only one."
|
|
1627
|
-
);
|
|
1628
|
-
}
|
|
1629
|
-
const envAccessToken = envAccessTokenRaw || (envAuthScheme === "bearer" ? envApiKey : void 0);
|
|
1630
|
-
const envApiCredential = envAuthScheme === "bearer" && !envAccessTokenRaw ? void 0 : envApiKey;
|
|
1631
2058
|
const envCloudProfileId = resolveOpensteerCloudProfileId(env);
|
|
1632
2059
|
const envCloudProfileReuseIfActive = resolveOpensteerCloudProfileReuseIfActive(env);
|
|
1633
2060
|
const envCloudAnnounce = parseCloudAnnounce(
|
|
@@ -1656,11 +2083,6 @@ function resolveConfigWithEnv(input = {}, options = {}) {
|
|
|
1656
2083
|
const inputHasCloudBaseUrl = Boolean(
|
|
1657
2084
|
inputCloudOptions && Object.prototype.hasOwnProperty.call(inputCloudOptions, "baseUrl")
|
|
1658
2085
|
);
|
|
1659
|
-
if (normalizeNonEmptyString(inputCloudOptions?.apiKey) && normalizeNonEmptyString(inputCloudOptions?.accessToken)) {
|
|
1660
|
-
throw new Error(
|
|
1661
|
-
"cloud.apiKey and cloud.accessToken are mutually exclusive. Set only one."
|
|
1662
|
-
);
|
|
1663
|
-
}
|
|
1664
2086
|
const cloudSelection = resolveCloudSelection({
|
|
1665
2087
|
cloud: resolved.cloud
|
|
1666
2088
|
}, env);
|
|
@@ -1671,11 +2093,6 @@ function resolveConfigWithEnv(input = {}, options = {}) {
|
|
|
1671
2093
|
accessToken: resolvedCloudAccessTokenRaw,
|
|
1672
2094
|
...resolvedCloudRest
|
|
1673
2095
|
} = resolvedCloud;
|
|
1674
|
-
if (normalizeNonEmptyString(resolvedCloudApiKeyRaw) && normalizeNonEmptyString(resolvedCloudAccessTokenRaw)) {
|
|
1675
|
-
throw new Error(
|
|
1676
|
-
"Cloud config cannot include both apiKey and accessToken at the same time."
|
|
1677
|
-
);
|
|
1678
|
-
}
|
|
1679
2096
|
const resolvedCloudBrowserProfile = normalizeCloudBrowserProfileOptions(
|
|
1680
2097
|
resolvedCloud.browserProfile,
|
|
1681
2098
|
"resolved.cloud.browserProfile"
|
|
@@ -1687,25 +2104,40 @@ function resolveConfigWithEnv(input = {}, options = {}) {
|
|
|
1687
2104
|
const browserProfile = inputCloudBrowserProfile ?? envCloudBrowserProfile ?? resolvedCloudBrowserProfile;
|
|
1688
2105
|
let authScheme = inputAuthScheme ?? envAuthScheme ?? parseAuthScheme(resolvedCloud.authScheme, "cloud.authScheme") ?? "api-key";
|
|
1689
2106
|
const announce = inputCloudAnnounce ?? envCloudAnnounce ?? parseCloudAnnounce(resolvedCloud.announce, "cloud.announce") ?? "always";
|
|
1690
|
-
const
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
2107
|
+
const selectedCredentialLayer = selectCloudCredentialByPrecedence(
|
|
2108
|
+
[
|
|
2109
|
+
{
|
|
2110
|
+
source: "input",
|
|
2111
|
+
apiKey: inputCloudOptions?.apiKey,
|
|
2112
|
+
accessToken: inputCloudOptions?.accessToken,
|
|
2113
|
+
hasApiKey: inputHasCloudApiKey,
|
|
2114
|
+
hasAccessToken: inputHasCloudAccessToken
|
|
2115
|
+
},
|
|
2116
|
+
{
|
|
2117
|
+
source: "env",
|
|
2118
|
+
apiKey: envApiKey,
|
|
2119
|
+
accessToken: envAccessTokenRaw,
|
|
2120
|
+
hasApiKey: envApiKey !== void 0,
|
|
2121
|
+
hasAccessToken: envAccessTokenRaw !== void 0
|
|
2122
|
+
},
|
|
2123
|
+
{
|
|
2124
|
+
source: "file",
|
|
2125
|
+
apiKey: fileCloudOptions?.apiKey,
|
|
2126
|
+
accessToken: fileCloudOptions?.accessToken,
|
|
2127
|
+
hasApiKey: fileHasCloudApiKey,
|
|
2128
|
+
hasAccessToken: fileHasCloudAccessToken
|
|
2129
|
+
}
|
|
2130
|
+
],
|
|
2131
|
+
authScheme
|
|
2132
|
+
);
|
|
2133
|
+
const { apiKey, accessToken } = resolveCloudCredentialFields(selectedCredentialLayer);
|
|
1702
2134
|
if (accessToken) {
|
|
1703
2135
|
authScheme = "bearer";
|
|
1704
2136
|
}
|
|
1705
2137
|
resolved.cloud = {
|
|
1706
2138
|
...resolvedCloudRest,
|
|
1707
|
-
...
|
|
1708
|
-
...
|
|
2139
|
+
...apiKey ? { apiKey } : selectedCredentialLayer?.hasApiKey && !accessToken ? { apiKey: selectedCredentialLayer.apiKey } : {},
|
|
2140
|
+
...accessToken ? { accessToken } : selectedCredentialLayer?.hasAccessToken && !apiKey ? { accessToken: selectedCredentialLayer.accessToken } : {},
|
|
1709
2141
|
authScheme,
|
|
1710
2142
|
announce,
|
|
1711
2143
|
...browserProfile ? { browserProfile } : {}
|
|
@@ -2134,7 +2566,7 @@ var StealthCdpRuntime = class _StealthCdpRuntime {
|
|
|
2134
2566
|
TRANSIENT_CONTEXT_RETRY_DELAY_MS,
|
|
2135
2567
|
Math.max(0, deadline - Date.now())
|
|
2136
2568
|
);
|
|
2137
|
-
await
|
|
2569
|
+
await sleep2(retryDelay);
|
|
2138
2570
|
}
|
|
2139
2571
|
}
|
|
2140
2572
|
}
|
|
@@ -2167,7 +2599,7 @@ var StealthCdpRuntime = class _StealthCdpRuntime {
|
|
|
2167
2599
|
() => ({ kind: "resolved" }),
|
|
2168
2600
|
(error) => ({ kind: "rejected", error })
|
|
2169
2601
|
);
|
|
2170
|
-
const timeoutPromise =
|
|
2602
|
+
const timeoutPromise = sleep2(
|
|
2171
2603
|
timeout + FRAME_EVALUATE_GRACE_MS
|
|
2172
2604
|
).then(() => ({ kind: "timeout" }));
|
|
2173
2605
|
const result = await Promise.race([
|
|
@@ -2309,14 +2741,14 @@ function isIgnorableFrameError(error) {
|
|
|
2309
2741
|
const message = error.message;
|
|
2310
2742
|
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");
|
|
2311
2743
|
}
|
|
2312
|
-
function
|
|
2744
|
+
function sleep2(ms) {
|
|
2313
2745
|
return new Promise((resolve) => {
|
|
2314
2746
|
setTimeout(resolve, ms);
|
|
2315
2747
|
});
|
|
2316
2748
|
}
|
|
2317
2749
|
|
|
2318
2750
|
// src/storage/local.ts
|
|
2319
|
-
var
|
|
2751
|
+
var import_fs3 = __toESM(require("fs"), 1);
|
|
2320
2752
|
var import_path4 = __toESM(require("path"), 1);
|
|
2321
2753
|
|
|
2322
2754
|
// src/storage/registry.ts
|
|
@@ -2360,14 +2792,14 @@ var LocalSelectorStorage = class {
|
|
|
2360
2792
|
return import_path4.default.join(this.getNamespaceDir(), this.getSelectorFileName(id));
|
|
2361
2793
|
}
|
|
2362
2794
|
ensureDirs() {
|
|
2363
|
-
|
|
2795
|
+
import_fs3.default.mkdirSync(this.getNamespaceDir(), { recursive: true });
|
|
2364
2796
|
}
|
|
2365
2797
|
loadRegistry() {
|
|
2366
2798
|
this.ensureDirs();
|
|
2367
2799
|
const file = this.getRegistryPath();
|
|
2368
|
-
if (!
|
|
2800
|
+
if (!import_fs3.default.existsSync(file)) return createEmptyRegistry(this.namespace);
|
|
2369
2801
|
try {
|
|
2370
|
-
const raw =
|
|
2802
|
+
const raw = import_fs3.default.readFileSync(file, "utf8");
|
|
2371
2803
|
return JSON.parse(raw);
|
|
2372
2804
|
} catch (error) {
|
|
2373
2805
|
const message = extractErrorMessage(
|
|
@@ -2384,16 +2816,16 @@ var LocalSelectorStorage = class {
|
|
|
2384
2816
|
}
|
|
2385
2817
|
saveRegistry(registry) {
|
|
2386
2818
|
this.ensureDirs();
|
|
2387
|
-
|
|
2819
|
+
import_fs3.default.writeFileSync(
|
|
2388
2820
|
this.getRegistryPath(),
|
|
2389
2821
|
JSON.stringify(registry, null, 2)
|
|
2390
2822
|
);
|
|
2391
2823
|
}
|
|
2392
2824
|
readSelector(id) {
|
|
2393
2825
|
const file = this.getSelectorPath(id);
|
|
2394
|
-
if (!
|
|
2826
|
+
if (!import_fs3.default.existsSync(file)) return null;
|
|
2395
2827
|
try {
|
|
2396
|
-
const raw =
|
|
2828
|
+
const raw = import_fs3.default.readFileSync(file, "utf8");
|
|
2397
2829
|
return JSON.parse(raw);
|
|
2398
2830
|
} catch (error) {
|
|
2399
2831
|
const message = extractErrorMessage(
|
|
@@ -2410,15 +2842,15 @@ var LocalSelectorStorage = class {
|
|
|
2410
2842
|
}
|
|
2411
2843
|
writeSelector(payload) {
|
|
2412
2844
|
this.ensureDirs();
|
|
2413
|
-
|
|
2845
|
+
import_fs3.default.writeFileSync(
|
|
2414
2846
|
this.getSelectorPath(payload.id),
|
|
2415
2847
|
JSON.stringify(payload, null, 2)
|
|
2416
2848
|
);
|
|
2417
2849
|
}
|
|
2418
2850
|
clearNamespace() {
|
|
2419
2851
|
const dir = this.getNamespaceDir();
|
|
2420
|
-
if (!
|
|
2421
|
-
|
|
2852
|
+
if (!import_fs3.default.existsSync(dir)) return;
|
|
2853
|
+
import_fs3.default.rmSync(dir, { recursive: true, force: true });
|
|
2422
2854
|
}
|
|
2423
2855
|
};
|
|
2424
2856
|
|
|
@@ -6862,7 +7294,7 @@ var AdaptiveNetworkTracker = class {
|
|
|
6862
7294
|
this.idleSince = 0;
|
|
6863
7295
|
}
|
|
6864
7296
|
const remaining = Math.max(1, options.deadline - now);
|
|
6865
|
-
await
|
|
7297
|
+
await sleep3(Math.min(NETWORK_POLL_MS, remaining));
|
|
6866
7298
|
}
|
|
6867
7299
|
}
|
|
6868
7300
|
handleRequestStarted = (request) => {
|
|
@@ -6907,7 +7339,7 @@ var AdaptiveNetworkTracker = class {
|
|
|
6907
7339
|
return false;
|
|
6908
7340
|
}
|
|
6909
7341
|
};
|
|
6910
|
-
async function
|
|
7342
|
+
async function sleep3(ms) {
|
|
6911
7343
|
await new Promise((resolve) => {
|
|
6912
7344
|
setTimeout(resolve, ms);
|
|
6913
7345
|
});
|
|
@@ -8384,15 +8816,15 @@ function withTokenQuery(wsUrl, token) {
|
|
|
8384
8816
|
}
|
|
8385
8817
|
|
|
8386
8818
|
// src/cloud/local-cache-sync.ts
|
|
8387
|
-
var
|
|
8819
|
+
var import_fs4 = __toESM(require("fs"), 1);
|
|
8388
8820
|
var import_path5 = __toESM(require("path"), 1);
|
|
8389
8821
|
function collectLocalSelectorCacheEntries(storage, options = {}) {
|
|
8390
8822
|
const debug = options.debug === true;
|
|
8391
8823
|
const namespace = storage.getNamespace();
|
|
8392
8824
|
const namespaceDir = storage.getNamespaceDir();
|
|
8393
|
-
if (!
|
|
8825
|
+
if (!import_fs4.default.existsSync(namespaceDir)) return [];
|
|
8394
8826
|
const entries = [];
|
|
8395
|
-
const fileNames =
|
|
8827
|
+
const fileNames = import_fs4.default.readdirSync(namespaceDir);
|
|
8396
8828
|
for (const fileName of fileNames) {
|
|
8397
8829
|
if (fileName === "index.json" || !fileName.endsWith(".json")) continue;
|
|
8398
8830
|
const filePath = import_path5.default.join(namespaceDir, fileName);
|
|
@@ -8423,7 +8855,7 @@ function collectLocalSelectorCacheEntries(storage, options = {}) {
|
|
|
8423
8855
|
}
|
|
8424
8856
|
function readSelectorFile(filePath, debug) {
|
|
8425
8857
|
try {
|
|
8426
|
-
const raw =
|
|
8858
|
+
const raw = import_fs4.default.readFileSync(filePath, "utf8");
|
|
8427
8859
|
return JSON.parse(raw);
|
|
8428
8860
|
} catch (error) {
|
|
8429
8861
|
const message = extractErrorMessage(
|
|
@@ -8518,13 +8950,13 @@ function dedupeNewest(entries) {
|
|
|
8518
8950
|
}
|
|
8519
8951
|
|
|
8520
8952
|
// src/cloud/cdp-client.ts
|
|
8521
|
-
var
|
|
8953
|
+
var import_playwright2 = require("playwright");
|
|
8522
8954
|
var CloudCdpClient = class {
|
|
8523
8955
|
async connect(args) {
|
|
8524
8956
|
const endpoint = withTokenQuery2(args.wsUrl, args.token);
|
|
8525
8957
|
let browser;
|
|
8526
8958
|
try {
|
|
8527
|
-
browser = await
|
|
8959
|
+
browser = await import_playwright2.chromium.connectOverCDP(endpoint);
|
|
8528
8960
|
} catch (error) {
|
|
8529
8961
|
const message = error instanceof Error ? error.message : "Failed to connect to cloud CDP endpoint.";
|
|
8530
8962
|
throw new OpensteerCloudError("CLOUD_TRANSPORT_ERROR", message);
|
|
@@ -10317,7 +10749,7 @@ async function executeAgentAction(page, action) {
|
|
|
10317
10749
|
}
|
|
10318
10750
|
case "wait": {
|
|
10319
10751
|
const ms = numberOr(action.timeMs, action.time_ms, 1e3);
|
|
10320
|
-
await
|
|
10752
|
+
await sleep4(ms);
|
|
10321
10753
|
return;
|
|
10322
10754
|
}
|
|
10323
10755
|
case "goto": {
|
|
@@ -10482,7 +10914,7 @@ async function pressKeyCombo(page, combo) {
|
|
|
10482
10914
|
}
|
|
10483
10915
|
}
|
|
10484
10916
|
}
|
|
10485
|
-
function
|
|
10917
|
+
function sleep4(ms) {
|
|
10486
10918
|
return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
|
|
10487
10919
|
}
|
|
10488
10920
|
|
|
@@ -10513,7 +10945,7 @@ var OpensteerCuaAgentHandler = class {
|
|
|
10513
10945
|
if (isMutatingAgentAction(action)) {
|
|
10514
10946
|
this.onMutatingAction?.(action);
|
|
10515
10947
|
}
|
|
10516
|
-
await
|
|
10948
|
+
await sleep5(this.config.waitBetweenActionsMs);
|
|
10517
10949
|
});
|
|
10518
10950
|
try {
|
|
10519
10951
|
const result = await this.client.execute({
|
|
@@ -10575,7 +11007,7 @@ var OpensteerCuaAgentHandler = class {
|
|
|
10575
11007
|
await this.cursorController.preview({ x, y }, "agent");
|
|
10576
11008
|
}
|
|
10577
11009
|
};
|
|
10578
|
-
function
|
|
11010
|
+
function sleep5(ms) {
|
|
10579
11011
|
return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
|
|
10580
11012
|
}
|
|
10581
11013
|
|
|
@@ -11011,7 +11443,7 @@ var CursorController = class {
|
|
|
11011
11443
|
for (const step of motion.points) {
|
|
11012
11444
|
await this.renderer.move(step, this.style);
|
|
11013
11445
|
if (motion.stepDelayMs > 0) {
|
|
11014
|
-
await
|
|
11446
|
+
await sleep6(motion.stepDelayMs);
|
|
11015
11447
|
}
|
|
11016
11448
|
}
|
|
11017
11449
|
if (shouldPulse(intent)) {
|
|
@@ -11169,7 +11601,7 @@ function clamp2(value, min, max) {
|
|
|
11169
11601
|
function shouldPulse(intent) {
|
|
11170
11602
|
return intent === "click" || intent === "dblclick" || intent === "rightclick" || intent === "agent";
|
|
11171
11603
|
}
|
|
11172
|
-
function
|
|
11604
|
+
function sleep6(ms) {
|
|
11173
11605
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
11174
11606
|
}
|
|
11175
11607
|
|
|
@@ -11220,30 +11652,20 @@ var Opensteer = class _Opensteer {
|
|
|
11220
11652
|
this.pool = new BrowserPool(resolved.browser || {});
|
|
11221
11653
|
if (cloudSelection.cloud) {
|
|
11222
11654
|
const cloudConfig = resolved.cloud && typeof resolved.cloud === "object" ? resolved.cloud : void 0;
|
|
11223
|
-
const
|
|
11224
|
-
|
|
11225
|
-
|
|
11226
|
-
|
|
11227
|
-
|
|
11228
|
-
);
|
|
11229
|
-
}
|
|
11230
|
-
let credential = "";
|
|
11231
|
-
let authScheme = cloudConfig?.authScheme ?? "api-key";
|
|
11232
|
-
if (accessToken) {
|
|
11233
|
-
credential = accessToken;
|
|
11234
|
-
authScheme = "bearer";
|
|
11235
|
-
} else if (apiKey) {
|
|
11236
|
-
credential = apiKey;
|
|
11237
|
-
}
|
|
11655
|
+
const credential = selectCloudCredential({
|
|
11656
|
+
apiKey: cloudConfig?.apiKey,
|
|
11657
|
+
accessToken: cloudConfig?.accessToken,
|
|
11658
|
+
authScheme: cloudConfig?.authScheme
|
|
11659
|
+
});
|
|
11238
11660
|
if (!credential) {
|
|
11239
11661
|
throw new Error(
|
|
11240
11662
|
"Cloud mode requires credentials via cloud.apiKey/cloud.accessToken or OPENSTEER_API_KEY/OPENSTEER_ACCESS_TOKEN."
|
|
11241
11663
|
);
|
|
11242
11664
|
}
|
|
11243
11665
|
this.cloud = createCloudRuntimeState(
|
|
11244
|
-
credential,
|
|
11666
|
+
credential.token,
|
|
11245
11667
|
cloudConfig?.baseUrl,
|
|
11246
|
-
authScheme
|
|
11668
|
+
credential.authScheme
|
|
11247
11669
|
);
|
|
11248
11670
|
} else {
|
|
11249
11671
|
this.cloud = null;
|
|
@@ -11564,9 +11986,11 @@ var Opensteer = class _Opensteer {
|
|
|
11564
11986
|
}
|
|
11565
11987
|
const session2 = await this.pool.launch({
|
|
11566
11988
|
...options,
|
|
11567
|
-
|
|
11568
|
-
|
|
11569
|
-
|
|
11989
|
+
mode: options.mode ?? this.config.browser?.mode,
|
|
11990
|
+
cdpUrl: options.cdpUrl ?? this.config.browser?.cdpUrl,
|
|
11991
|
+
userDataDir: options.userDataDir ?? this.config.browser?.userDataDir,
|
|
11992
|
+
profileDirectory: options.profileDirectory ?? this.config.browser?.profileDirectory,
|
|
11993
|
+
executablePath: options.executablePath ?? this.config.browser?.executablePath
|
|
11570
11994
|
});
|
|
11571
11995
|
this.browser = session2.browser;
|
|
11572
11996
|
this.contextRef = session2.context;
|
|
@@ -11597,6 +12021,32 @@ var Opensteer = class _Opensteer {
|
|
|
11597
12021
|
instance2.snapshotCache = null;
|
|
11598
12022
|
return instance2;
|
|
11599
12023
|
}
|
|
12024
|
+
static listLocalProfiles(userDataDir) {
|
|
12025
|
+
return listLocalChromeProfiles(userDataDir);
|
|
12026
|
+
}
|
|
12027
|
+
static fromSystemChrome(browser = {}, config = {}) {
|
|
12028
|
+
const chromePaths = detectChromePaths();
|
|
12029
|
+
const executablePath = browser.executablePath ?? config.browser?.executablePath ?? chromePaths.executable ?? void 0;
|
|
12030
|
+
if (!executablePath) {
|
|
12031
|
+
throw new Error(
|
|
12032
|
+
"Chrome was not found. Pass executablePath explicitly or install Chrome in a supported location."
|
|
12033
|
+
);
|
|
12034
|
+
}
|
|
12035
|
+
const userDataDir = browser.userDataDir ?? config.browser?.userDataDir ?? chromePaths.defaultUserDataDir;
|
|
12036
|
+
const autoDetectedProfiles = listLocalChromeProfiles(userDataDir);
|
|
12037
|
+
const profileDirectory = browser.profileDirectory ?? config.browser?.profileDirectory ?? autoDetectedProfiles[0]?.directory ?? "Default";
|
|
12038
|
+
return new _Opensteer({
|
|
12039
|
+
...config,
|
|
12040
|
+
browser: {
|
|
12041
|
+
...config.browser || {},
|
|
12042
|
+
mode: "real",
|
|
12043
|
+
headless: browser.headless ?? config.browser?.headless ?? true,
|
|
12044
|
+
executablePath,
|
|
12045
|
+
userDataDir,
|
|
12046
|
+
profileDirectory
|
|
12047
|
+
}
|
|
12048
|
+
});
|
|
12049
|
+
}
|
|
11600
12050
|
async close() {
|
|
11601
12051
|
this.snapshotCache = null;
|
|
11602
12052
|
if (this.cloud) {
|
|
@@ -14330,10 +14780,12 @@ function buildServerOpenConfig(options) {
|
|
|
14330
14780
|
enabled: options.cursorEnabled
|
|
14331
14781
|
},
|
|
14332
14782
|
browser: {
|
|
14333
|
-
headless: options.headless
|
|
14334
|
-
|
|
14335
|
-
|
|
14336
|
-
|
|
14783
|
+
headless: options.headless,
|
|
14784
|
+
mode: options.mode,
|
|
14785
|
+
cdpUrl: options.cdpUrl,
|
|
14786
|
+
userDataDir: options.userDataDir,
|
|
14787
|
+
profileDirectory: options.profileDirectory,
|
|
14788
|
+
executablePath: options.executablePath
|
|
14337
14789
|
}
|
|
14338
14790
|
};
|
|
14339
14791
|
if (!options.cloudAuth) {
|
|
@@ -14385,6 +14837,19 @@ function normalizeAuthScheme(value) {
|
|
|
14385
14837
|
);
|
|
14386
14838
|
}
|
|
14387
14839
|
|
|
14840
|
+
// src/cli/open-browser-config.ts
|
|
14841
|
+
function resolveCliBrowserRequestConfig(options) {
|
|
14842
|
+
const mode = options.browser ?? (options.profileDirectory || options.userDataDir || options.executablePath ? "real" : void 0);
|
|
14843
|
+
return {
|
|
14844
|
+
mode,
|
|
14845
|
+
headless: options.headless ?? (mode === "real" ? true : void 0),
|
|
14846
|
+
cdpUrl: options.cdpUrl,
|
|
14847
|
+
profileDirectory: options.profileDirectory,
|
|
14848
|
+
userDataDir: options.userDataDir,
|
|
14849
|
+
executablePath: options.executablePath
|
|
14850
|
+
};
|
|
14851
|
+
}
|
|
14852
|
+
|
|
14388
14853
|
// src/cli/server.ts
|
|
14389
14854
|
var instance = null;
|
|
14390
14855
|
var launchPromise = null;
|
|
@@ -14459,15 +14924,15 @@ var socketPath = getSocketPath(session);
|
|
|
14459
14924
|
var pidPath = getPidPath(session);
|
|
14460
14925
|
function cleanup() {
|
|
14461
14926
|
try {
|
|
14462
|
-
(0,
|
|
14927
|
+
(0, import_fs5.unlinkSync)(socketPath);
|
|
14463
14928
|
} catch {
|
|
14464
14929
|
}
|
|
14465
14930
|
try {
|
|
14466
|
-
(0,
|
|
14931
|
+
(0, import_fs5.unlinkSync)(pidPath);
|
|
14467
14932
|
} catch {
|
|
14468
14933
|
}
|
|
14469
14934
|
try {
|
|
14470
|
-
(0,
|
|
14935
|
+
(0, import_fs5.unlinkSync)(getMetadataPath(session));
|
|
14471
14936
|
} catch {
|
|
14472
14937
|
}
|
|
14473
14938
|
}
|
|
@@ -14545,9 +15010,11 @@ async function handleRequest(request, socket) {
|
|
|
14545
15010
|
try {
|
|
14546
15011
|
const url = args.url;
|
|
14547
15012
|
const headless = args.headless;
|
|
14548
|
-
const
|
|
14549
|
-
const
|
|
14550
|
-
const
|
|
15013
|
+
const browser = args.browser === "real" || args.browser === "chromium" ? args.browser : args.browser === void 0 ? void 0 : null;
|
|
15014
|
+
const cdpUrl = args["cdp-url"];
|
|
15015
|
+
const profileDirectory = args.profile;
|
|
15016
|
+
const userDataDir = args["user-data-dir"];
|
|
15017
|
+
const executablePath = args["browser-path"];
|
|
14551
15018
|
const cloudProfileId = typeof args["cloud-profile-id"] === "string" ? args["cloud-profile-id"].trim() : void 0;
|
|
14552
15019
|
const cloudProfileReuseIfActive = typeof args["cloud-profile-reuse-if-active"] === "boolean" ? args["cloud-profile-reuse-if-active"] : void 0;
|
|
14553
15020
|
const requestedCloudProfileBinding = normalizeCloudProfileBinding({
|
|
@@ -14562,6 +15029,21 @@ async function handleRequest(request, socket) {
|
|
|
14562
15029
|
"--cloud-profile-reuse-if-active requires --cloud-profile-id."
|
|
14563
15030
|
);
|
|
14564
15031
|
}
|
|
15032
|
+
if (browser === null) {
|
|
15033
|
+
throw new Error(
|
|
15034
|
+
'--browser must be either "chromium" or "real".'
|
|
15035
|
+
);
|
|
15036
|
+
}
|
|
15037
|
+
if (browser === "chromium" && (profileDirectory || userDataDir || executablePath)) {
|
|
15038
|
+
throw new Error(
|
|
15039
|
+
"--profile, --user-data-dir, and --browser-path require --browser real."
|
|
15040
|
+
);
|
|
15041
|
+
}
|
|
15042
|
+
if (cdpUrl && browser === "real") {
|
|
15043
|
+
throw new Error(
|
|
15044
|
+
"--cdp-url cannot be combined with --browser real."
|
|
15045
|
+
);
|
|
15046
|
+
}
|
|
14565
15047
|
const requestedCursor = normalizeCursorFlag(args.cursor);
|
|
14566
15048
|
const requestedName = typeof args.name === "string" && args.name.trim().length > 0 ? sanitizeNamespace(args.name) : null;
|
|
14567
15049
|
if (requestedCursor !== null) {
|
|
@@ -14603,26 +15085,45 @@ async function handleRequest(request, socket) {
|
|
|
14603
15085
|
invalidateInstance();
|
|
14604
15086
|
}
|
|
14605
15087
|
}
|
|
15088
|
+
const requestedBrowserConfig = resolveCliBrowserRequestConfig({
|
|
15089
|
+
browser: browser ?? void 0,
|
|
15090
|
+
headless,
|
|
15091
|
+
cdpUrl,
|
|
15092
|
+
profileDirectory,
|
|
15093
|
+
userDataDir,
|
|
15094
|
+
executablePath
|
|
15095
|
+
});
|
|
14606
15096
|
if (instance && !launchPromise) {
|
|
14607
15097
|
assertCompatibleCloudProfileBinding(
|
|
14608
15098
|
logicalSession,
|
|
14609
15099
|
cloudProfileBinding,
|
|
14610
15100
|
requestedCloudProfileBinding
|
|
14611
15101
|
);
|
|
15102
|
+
const existingBrowserConfig = instance.getConfig().browser || {};
|
|
15103
|
+
const existingBrowserRecord = existingBrowserConfig;
|
|
15104
|
+
const mismatch = Object.entries(requestedBrowserConfig).find(
|
|
15105
|
+
([key, value]) => value !== void 0 && existingBrowserRecord[key] !== value
|
|
15106
|
+
);
|
|
15107
|
+
if (mismatch) {
|
|
15108
|
+
const [key, value] = mismatch;
|
|
15109
|
+
throw new Error(
|
|
15110
|
+
`Session '${logicalSession}' is already bound to browser setting "${key}"=${JSON.stringify(existingBrowserRecord[key])}. Requested ${JSON.stringify(value)} does not match. Use the same browser flags for this session or start a different --session.`
|
|
15111
|
+
);
|
|
15112
|
+
}
|
|
14612
15113
|
}
|
|
15114
|
+
let shouldLaunchInitialUrl = false;
|
|
14613
15115
|
if (!instance) {
|
|
14614
15116
|
instance = new Opensteer(
|
|
14615
15117
|
buildServerOpenConfig({
|
|
14616
15118
|
scopeDir,
|
|
14617
15119
|
name: activeNamespace,
|
|
14618
15120
|
cursorEnabled: effectiveCursorEnabled,
|
|
14619
|
-
|
|
14620
|
-
connectUrl,
|
|
14621
|
-
channel,
|
|
14622
|
-
profileDir,
|
|
15121
|
+
...requestedBrowserConfig,
|
|
14623
15122
|
cloudAuth: cloudAuthOverride
|
|
14624
15123
|
})
|
|
14625
15124
|
);
|
|
15125
|
+
const resolvedBrowserConfig = instance.getConfig().browser || {};
|
|
15126
|
+
shouldLaunchInitialUrl = Boolean(url) && resolvedBrowserConfig.mode === "real" && !resolvedBrowserConfig.cdpUrl;
|
|
14626
15127
|
const nextCloudProfileBinding = resolveSessionCloudProfileBinding(
|
|
14627
15128
|
instance.getConfig(),
|
|
14628
15129
|
requestedCloudProfileBinding
|
|
@@ -14634,12 +15135,13 @@ async function handleRequest(request, socket) {
|
|
|
14634
15135
|
);
|
|
14635
15136
|
}
|
|
14636
15137
|
launchPromise = instance.launch({
|
|
14637
|
-
|
|
15138
|
+
initialUrl: shouldLaunchInitialUrl ? url : void 0,
|
|
15139
|
+
...requestedBrowserConfig,
|
|
14638
15140
|
cloudBrowserProfile: cloudProfileId ? {
|
|
14639
15141
|
profileId: cloudProfileId,
|
|
14640
15142
|
reuseIfActive: cloudProfileReuseIfActive
|
|
14641
15143
|
} : void 0,
|
|
14642
|
-
timeout:
|
|
15144
|
+
timeout: cdpUrl ? 12e4 : 3e4
|
|
14643
15145
|
});
|
|
14644
15146
|
try {
|
|
14645
15147
|
await launchPromise;
|
|
@@ -14657,7 +15159,7 @@ async function handleRequest(request, socket) {
|
|
|
14657
15159
|
} else if (requestedCursor !== null) {
|
|
14658
15160
|
instance.setCursorEnabled(requestedCursor);
|
|
14659
15161
|
}
|
|
14660
|
-
if (url) {
|
|
15162
|
+
if (url && !shouldLaunchInitialUrl) {
|
|
14661
15163
|
await instance.goto(url);
|
|
14662
15164
|
}
|
|
14663
15165
|
sendResponse(socket, {
|
|
@@ -14787,8 +15289,8 @@ async function handleRequest(request, socket) {
|
|
|
14787
15289
|
);
|
|
14788
15290
|
}
|
|
14789
15291
|
}
|
|
14790
|
-
if ((0,
|
|
14791
|
-
(0,
|
|
15292
|
+
if ((0, import_fs5.existsSync)(socketPath)) {
|
|
15293
|
+
(0, import_fs5.unlinkSync)(socketPath);
|
|
14792
15294
|
}
|
|
14793
15295
|
var server = (0, import_net.createServer)((socket) => {
|
|
14794
15296
|
let buffer = "";
|
|
@@ -14818,7 +15320,7 @@ var server = (0, import_net.createServer)((socket) => {
|
|
|
14818
15320
|
});
|
|
14819
15321
|
});
|
|
14820
15322
|
server.listen(socketPath, () => {
|
|
14821
|
-
(0,
|
|
15323
|
+
(0, import_fs5.writeFileSync)(pidPath, String(process.pid));
|
|
14822
15324
|
if (process.send) {
|
|
14823
15325
|
process.send("ready");
|
|
14824
15326
|
}
|