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/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");
|
|
@@ -1307,9 +1625,10 @@ var DEFAULT_CONFIG = {
|
|
|
1307
1625
|
headless: false,
|
|
1308
1626
|
executablePath: void 0,
|
|
1309
1627
|
slowMo: 0,
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1628
|
+
mode: void 0,
|
|
1629
|
+
cdpUrl: void 0,
|
|
1630
|
+
userDataDir: void 0,
|
|
1631
|
+
profileDirectory: void 0
|
|
1313
1632
|
},
|
|
1314
1633
|
storage: {
|
|
1315
1634
|
rootDir: process.cwd()
|
|
@@ -1346,9 +1665,9 @@ function loadDotenvValues(rootDir, baseEnv, options = {}) {
|
|
|
1346
1665
|
const nodeEnv = baseEnv.NODE_ENV?.trim() || "";
|
|
1347
1666
|
for (const filename of dotenvFileOrder(nodeEnv)) {
|
|
1348
1667
|
const filePath = import_path3.default.join(baseDir, filename);
|
|
1349
|
-
if (!
|
|
1668
|
+
if (!import_fs2.default.existsSync(filePath)) continue;
|
|
1350
1669
|
try {
|
|
1351
|
-
const raw =
|
|
1670
|
+
const raw = import_fs2.default.readFileSync(filePath, "utf8");
|
|
1352
1671
|
const parsed = (0, import_dotenv.parse)(raw);
|
|
1353
1672
|
for (const [key, value] of Object.entries(parsed)) {
|
|
1354
1673
|
if (values[key] === void 0) {
|
|
@@ -1418,9 +1737,9 @@ function assertNoLegacyRuntimeConfig(source, config) {
|
|
|
1418
1737
|
}
|
|
1419
1738
|
function loadConfigFile(rootDir, options = {}) {
|
|
1420
1739
|
const configPath = import_path3.default.join(rootDir, ".opensteer", "config.json");
|
|
1421
|
-
if (!
|
|
1740
|
+
if (!import_fs2.default.existsSync(configPath)) return {};
|
|
1422
1741
|
try {
|
|
1423
|
-
const raw =
|
|
1742
|
+
const raw = import_fs2.default.readFileSync(configPath, "utf8");
|
|
1424
1743
|
return JSON.parse(raw);
|
|
1425
1744
|
} catch (error) {
|
|
1426
1745
|
const message = extractErrorMessage(
|
|
@@ -1653,6 +1972,8 @@ function resolveConfigWithEnv(input = {}, options = {}) {
|
|
|
1653
1972
|
const fileHasCloudApiKey = hasOwn(fileCloudOptions, "apiKey");
|
|
1654
1973
|
const fileHasCloudAccessToken = hasOwn(fileCloudOptions, "accessToken");
|
|
1655
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");
|
|
1656
1977
|
const envRootDir = input.storage?.rootDir ?? fileRootDir ?? initialRootDir;
|
|
1657
1978
|
const env = resolveEnv(envRootDir, {
|
|
1658
1979
|
debug: debugHint,
|
|
@@ -1668,14 +1989,30 @@ function resolveConfigWithEnv(input = {}, options = {}) {
|
|
|
1668
1989
|
"OPENSTEER_RUNTIME is no longer supported. Use OPENSTEER_MODE instead."
|
|
1669
1990
|
);
|
|
1670
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
|
+
}
|
|
1671
2007
|
const envConfig = {
|
|
1672
2008
|
browser: {
|
|
1673
2009
|
headless: parseBool(env.OPENSTEER_HEADLESS),
|
|
1674
2010
|
executablePath: env.OPENSTEER_BROWSER_PATH || void 0,
|
|
1675
2011
|
slowMo: parseNumber(env.OPENSTEER_SLOW_MO),
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
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
|
|
1679
2016
|
},
|
|
1680
2017
|
cursor: {
|
|
1681
2018
|
enabled: parseBool(env.OPENSTEER_CURSOR)
|
|
@@ -1686,6 +2023,34 @@ function resolveConfigWithEnv(input = {}, options = {}) {
|
|
|
1686
2023
|
const mergedWithFile = mergeDeep(runtimeDefaults, fileConfig);
|
|
1687
2024
|
const mergedWithEnv = mergeDeep(mergedWithFile, envConfig);
|
|
1688
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
|
+
}
|
|
1689
2054
|
const envApiKey = resolveOpensteerApiKey(env);
|
|
1690
2055
|
const envAccessTokenRaw = resolveOpensteerAccessToken(env);
|
|
1691
2056
|
const envBaseUrl = resolveOpensteerBaseUrl(env);
|
|
@@ -2201,7 +2566,7 @@ var StealthCdpRuntime = class _StealthCdpRuntime {
|
|
|
2201
2566
|
TRANSIENT_CONTEXT_RETRY_DELAY_MS,
|
|
2202
2567
|
Math.max(0, deadline - Date.now())
|
|
2203
2568
|
);
|
|
2204
|
-
await
|
|
2569
|
+
await sleep2(retryDelay);
|
|
2205
2570
|
}
|
|
2206
2571
|
}
|
|
2207
2572
|
}
|
|
@@ -2234,7 +2599,7 @@ var StealthCdpRuntime = class _StealthCdpRuntime {
|
|
|
2234
2599
|
() => ({ kind: "resolved" }),
|
|
2235
2600
|
(error) => ({ kind: "rejected", error })
|
|
2236
2601
|
);
|
|
2237
|
-
const timeoutPromise =
|
|
2602
|
+
const timeoutPromise = sleep2(
|
|
2238
2603
|
timeout + FRAME_EVALUATE_GRACE_MS
|
|
2239
2604
|
).then(() => ({ kind: "timeout" }));
|
|
2240
2605
|
const result = await Promise.race([
|
|
@@ -2376,14 +2741,14 @@ function isIgnorableFrameError(error) {
|
|
|
2376
2741
|
const message = error.message;
|
|
2377
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");
|
|
2378
2743
|
}
|
|
2379
|
-
function
|
|
2744
|
+
function sleep2(ms) {
|
|
2380
2745
|
return new Promise((resolve) => {
|
|
2381
2746
|
setTimeout(resolve, ms);
|
|
2382
2747
|
});
|
|
2383
2748
|
}
|
|
2384
2749
|
|
|
2385
2750
|
// src/storage/local.ts
|
|
2386
|
-
var
|
|
2751
|
+
var import_fs3 = __toESM(require("fs"), 1);
|
|
2387
2752
|
var import_path4 = __toESM(require("path"), 1);
|
|
2388
2753
|
|
|
2389
2754
|
// src/storage/registry.ts
|
|
@@ -2427,14 +2792,14 @@ var LocalSelectorStorage = class {
|
|
|
2427
2792
|
return import_path4.default.join(this.getNamespaceDir(), this.getSelectorFileName(id));
|
|
2428
2793
|
}
|
|
2429
2794
|
ensureDirs() {
|
|
2430
|
-
|
|
2795
|
+
import_fs3.default.mkdirSync(this.getNamespaceDir(), { recursive: true });
|
|
2431
2796
|
}
|
|
2432
2797
|
loadRegistry() {
|
|
2433
2798
|
this.ensureDirs();
|
|
2434
2799
|
const file = this.getRegistryPath();
|
|
2435
|
-
if (!
|
|
2800
|
+
if (!import_fs3.default.existsSync(file)) return createEmptyRegistry(this.namespace);
|
|
2436
2801
|
try {
|
|
2437
|
-
const raw =
|
|
2802
|
+
const raw = import_fs3.default.readFileSync(file, "utf8");
|
|
2438
2803
|
return JSON.parse(raw);
|
|
2439
2804
|
} catch (error) {
|
|
2440
2805
|
const message = extractErrorMessage(
|
|
@@ -2451,16 +2816,16 @@ var LocalSelectorStorage = class {
|
|
|
2451
2816
|
}
|
|
2452
2817
|
saveRegistry(registry) {
|
|
2453
2818
|
this.ensureDirs();
|
|
2454
|
-
|
|
2819
|
+
import_fs3.default.writeFileSync(
|
|
2455
2820
|
this.getRegistryPath(),
|
|
2456
2821
|
JSON.stringify(registry, null, 2)
|
|
2457
2822
|
);
|
|
2458
2823
|
}
|
|
2459
2824
|
readSelector(id) {
|
|
2460
2825
|
const file = this.getSelectorPath(id);
|
|
2461
|
-
if (!
|
|
2826
|
+
if (!import_fs3.default.existsSync(file)) return null;
|
|
2462
2827
|
try {
|
|
2463
|
-
const raw =
|
|
2828
|
+
const raw = import_fs3.default.readFileSync(file, "utf8");
|
|
2464
2829
|
return JSON.parse(raw);
|
|
2465
2830
|
} catch (error) {
|
|
2466
2831
|
const message = extractErrorMessage(
|
|
@@ -2477,15 +2842,15 @@ var LocalSelectorStorage = class {
|
|
|
2477
2842
|
}
|
|
2478
2843
|
writeSelector(payload) {
|
|
2479
2844
|
this.ensureDirs();
|
|
2480
|
-
|
|
2845
|
+
import_fs3.default.writeFileSync(
|
|
2481
2846
|
this.getSelectorPath(payload.id),
|
|
2482
2847
|
JSON.stringify(payload, null, 2)
|
|
2483
2848
|
);
|
|
2484
2849
|
}
|
|
2485
2850
|
clearNamespace() {
|
|
2486
2851
|
const dir = this.getNamespaceDir();
|
|
2487
|
-
if (!
|
|
2488
|
-
|
|
2852
|
+
if (!import_fs3.default.existsSync(dir)) return;
|
|
2853
|
+
import_fs3.default.rmSync(dir, { recursive: true, force: true });
|
|
2489
2854
|
}
|
|
2490
2855
|
};
|
|
2491
2856
|
|
|
@@ -6929,7 +7294,7 @@ var AdaptiveNetworkTracker = class {
|
|
|
6929
7294
|
this.idleSince = 0;
|
|
6930
7295
|
}
|
|
6931
7296
|
const remaining = Math.max(1, options.deadline - now);
|
|
6932
|
-
await
|
|
7297
|
+
await sleep3(Math.min(NETWORK_POLL_MS, remaining));
|
|
6933
7298
|
}
|
|
6934
7299
|
}
|
|
6935
7300
|
handleRequestStarted = (request) => {
|
|
@@ -6974,7 +7339,7 @@ var AdaptiveNetworkTracker = class {
|
|
|
6974
7339
|
return false;
|
|
6975
7340
|
}
|
|
6976
7341
|
};
|
|
6977
|
-
async function
|
|
7342
|
+
async function sleep3(ms) {
|
|
6978
7343
|
await new Promise((resolve) => {
|
|
6979
7344
|
setTimeout(resolve, ms);
|
|
6980
7345
|
});
|
|
@@ -8451,15 +8816,15 @@ function withTokenQuery(wsUrl, token) {
|
|
|
8451
8816
|
}
|
|
8452
8817
|
|
|
8453
8818
|
// src/cloud/local-cache-sync.ts
|
|
8454
|
-
var
|
|
8819
|
+
var import_fs4 = __toESM(require("fs"), 1);
|
|
8455
8820
|
var import_path5 = __toESM(require("path"), 1);
|
|
8456
8821
|
function collectLocalSelectorCacheEntries(storage, options = {}) {
|
|
8457
8822
|
const debug = options.debug === true;
|
|
8458
8823
|
const namespace = storage.getNamespace();
|
|
8459
8824
|
const namespaceDir = storage.getNamespaceDir();
|
|
8460
|
-
if (!
|
|
8825
|
+
if (!import_fs4.default.existsSync(namespaceDir)) return [];
|
|
8461
8826
|
const entries = [];
|
|
8462
|
-
const fileNames =
|
|
8827
|
+
const fileNames = import_fs4.default.readdirSync(namespaceDir);
|
|
8463
8828
|
for (const fileName of fileNames) {
|
|
8464
8829
|
if (fileName === "index.json" || !fileName.endsWith(".json")) continue;
|
|
8465
8830
|
const filePath = import_path5.default.join(namespaceDir, fileName);
|
|
@@ -8490,7 +8855,7 @@ function collectLocalSelectorCacheEntries(storage, options = {}) {
|
|
|
8490
8855
|
}
|
|
8491
8856
|
function readSelectorFile(filePath, debug) {
|
|
8492
8857
|
try {
|
|
8493
|
-
const raw =
|
|
8858
|
+
const raw = import_fs4.default.readFileSync(filePath, "utf8");
|
|
8494
8859
|
return JSON.parse(raw);
|
|
8495
8860
|
} catch (error) {
|
|
8496
8861
|
const message = extractErrorMessage(
|
|
@@ -8585,13 +8950,13 @@ function dedupeNewest(entries) {
|
|
|
8585
8950
|
}
|
|
8586
8951
|
|
|
8587
8952
|
// src/cloud/cdp-client.ts
|
|
8588
|
-
var
|
|
8953
|
+
var import_playwright2 = require("playwright");
|
|
8589
8954
|
var CloudCdpClient = class {
|
|
8590
8955
|
async connect(args) {
|
|
8591
8956
|
const endpoint = withTokenQuery2(args.wsUrl, args.token);
|
|
8592
8957
|
let browser;
|
|
8593
8958
|
try {
|
|
8594
|
-
browser = await
|
|
8959
|
+
browser = await import_playwright2.chromium.connectOverCDP(endpoint);
|
|
8595
8960
|
} catch (error) {
|
|
8596
8961
|
const message = error instanceof Error ? error.message : "Failed to connect to cloud CDP endpoint.";
|
|
8597
8962
|
throw new OpensteerCloudError("CLOUD_TRANSPORT_ERROR", message);
|
|
@@ -10384,7 +10749,7 @@ async function executeAgentAction(page, action) {
|
|
|
10384
10749
|
}
|
|
10385
10750
|
case "wait": {
|
|
10386
10751
|
const ms = numberOr(action.timeMs, action.time_ms, 1e3);
|
|
10387
|
-
await
|
|
10752
|
+
await sleep4(ms);
|
|
10388
10753
|
return;
|
|
10389
10754
|
}
|
|
10390
10755
|
case "goto": {
|
|
@@ -10549,7 +10914,7 @@ async function pressKeyCombo(page, combo) {
|
|
|
10549
10914
|
}
|
|
10550
10915
|
}
|
|
10551
10916
|
}
|
|
10552
|
-
function
|
|
10917
|
+
function sleep4(ms) {
|
|
10553
10918
|
return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
|
|
10554
10919
|
}
|
|
10555
10920
|
|
|
@@ -10580,7 +10945,7 @@ var OpensteerCuaAgentHandler = class {
|
|
|
10580
10945
|
if (isMutatingAgentAction(action)) {
|
|
10581
10946
|
this.onMutatingAction?.(action);
|
|
10582
10947
|
}
|
|
10583
|
-
await
|
|
10948
|
+
await sleep5(this.config.waitBetweenActionsMs);
|
|
10584
10949
|
});
|
|
10585
10950
|
try {
|
|
10586
10951
|
const result = await this.client.execute({
|
|
@@ -10642,7 +11007,7 @@ var OpensteerCuaAgentHandler = class {
|
|
|
10642
11007
|
await this.cursorController.preview({ x, y }, "agent");
|
|
10643
11008
|
}
|
|
10644
11009
|
};
|
|
10645
|
-
function
|
|
11010
|
+
function sleep5(ms) {
|
|
10646
11011
|
return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
|
|
10647
11012
|
}
|
|
10648
11013
|
|
|
@@ -11078,7 +11443,7 @@ var CursorController = class {
|
|
|
11078
11443
|
for (const step of motion.points) {
|
|
11079
11444
|
await this.renderer.move(step, this.style);
|
|
11080
11445
|
if (motion.stepDelayMs > 0) {
|
|
11081
|
-
await
|
|
11446
|
+
await sleep6(motion.stepDelayMs);
|
|
11082
11447
|
}
|
|
11083
11448
|
}
|
|
11084
11449
|
if (shouldPulse(intent)) {
|
|
@@ -11236,7 +11601,7 @@ function clamp2(value, min, max) {
|
|
|
11236
11601
|
function shouldPulse(intent) {
|
|
11237
11602
|
return intent === "click" || intent === "dblclick" || intent === "rightclick" || intent === "agent";
|
|
11238
11603
|
}
|
|
11239
|
-
function
|
|
11604
|
+
function sleep6(ms) {
|
|
11240
11605
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
11241
11606
|
}
|
|
11242
11607
|
|
|
@@ -11621,9 +11986,11 @@ var Opensteer = class _Opensteer {
|
|
|
11621
11986
|
}
|
|
11622
11987
|
const session2 = await this.pool.launch({
|
|
11623
11988
|
...options,
|
|
11624
|
-
|
|
11625
|
-
|
|
11626
|
-
|
|
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
|
|
11627
11994
|
});
|
|
11628
11995
|
this.browser = session2.browser;
|
|
11629
11996
|
this.contextRef = session2.context;
|
|
@@ -11654,6 +12021,32 @@ var Opensteer = class _Opensteer {
|
|
|
11654
12021
|
instance2.snapshotCache = null;
|
|
11655
12022
|
return instance2;
|
|
11656
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
|
+
}
|
|
11657
12050
|
async close() {
|
|
11658
12051
|
this.snapshotCache = null;
|
|
11659
12052
|
if (this.cloud) {
|
|
@@ -14387,10 +14780,12 @@ function buildServerOpenConfig(options) {
|
|
|
14387
14780
|
enabled: options.cursorEnabled
|
|
14388
14781
|
},
|
|
14389
14782
|
browser: {
|
|
14390
|
-
headless: options.headless
|
|
14391
|
-
|
|
14392
|
-
|
|
14393
|
-
|
|
14783
|
+
headless: options.headless,
|
|
14784
|
+
mode: options.mode,
|
|
14785
|
+
cdpUrl: options.cdpUrl,
|
|
14786
|
+
userDataDir: options.userDataDir,
|
|
14787
|
+
profileDirectory: options.profileDirectory,
|
|
14788
|
+
executablePath: options.executablePath
|
|
14394
14789
|
}
|
|
14395
14790
|
};
|
|
14396
14791
|
if (!options.cloudAuth) {
|
|
@@ -14442,6 +14837,19 @@ function normalizeAuthScheme(value) {
|
|
|
14442
14837
|
);
|
|
14443
14838
|
}
|
|
14444
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
|
+
|
|
14445
14853
|
// src/cli/server.ts
|
|
14446
14854
|
var instance = null;
|
|
14447
14855
|
var launchPromise = null;
|
|
@@ -14516,15 +14924,15 @@ var socketPath = getSocketPath(session);
|
|
|
14516
14924
|
var pidPath = getPidPath(session);
|
|
14517
14925
|
function cleanup() {
|
|
14518
14926
|
try {
|
|
14519
|
-
(0,
|
|
14927
|
+
(0, import_fs5.unlinkSync)(socketPath);
|
|
14520
14928
|
} catch {
|
|
14521
14929
|
}
|
|
14522
14930
|
try {
|
|
14523
|
-
(0,
|
|
14931
|
+
(0, import_fs5.unlinkSync)(pidPath);
|
|
14524
14932
|
} catch {
|
|
14525
14933
|
}
|
|
14526
14934
|
try {
|
|
14527
|
-
(0,
|
|
14935
|
+
(0, import_fs5.unlinkSync)(getMetadataPath(session));
|
|
14528
14936
|
} catch {
|
|
14529
14937
|
}
|
|
14530
14938
|
}
|
|
@@ -14602,9 +15010,11 @@ async function handleRequest(request, socket) {
|
|
|
14602
15010
|
try {
|
|
14603
15011
|
const url = args.url;
|
|
14604
15012
|
const headless = args.headless;
|
|
14605
|
-
const
|
|
14606
|
-
const
|
|
14607
|
-
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"];
|
|
14608
15018
|
const cloudProfileId = typeof args["cloud-profile-id"] === "string" ? args["cloud-profile-id"].trim() : void 0;
|
|
14609
15019
|
const cloudProfileReuseIfActive = typeof args["cloud-profile-reuse-if-active"] === "boolean" ? args["cloud-profile-reuse-if-active"] : void 0;
|
|
14610
15020
|
const requestedCloudProfileBinding = normalizeCloudProfileBinding({
|
|
@@ -14619,6 +15029,21 @@ async function handleRequest(request, socket) {
|
|
|
14619
15029
|
"--cloud-profile-reuse-if-active requires --cloud-profile-id."
|
|
14620
15030
|
);
|
|
14621
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
|
+
}
|
|
14622
15047
|
const requestedCursor = normalizeCursorFlag(args.cursor);
|
|
14623
15048
|
const requestedName = typeof args.name === "string" && args.name.trim().length > 0 ? sanitizeNamespace(args.name) : null;
|
|
14624
15049
|
if (requestedCursor !== null) {
|
|
@@ -14660,26 +15085,45 @@ async function handleRequest(request, socket) {
|
|
|
14660
15085
|
invalidateInstance();
|
|
14661
15086
|
}
|
|
14662
15087
|
}
|
|
15088
|
+
const requestedBrowserConfig = resolveCliBrowserRequestConfig({
|
|
15089
|
+
browser: browser ?? void 0,
|
|
15090
|
+
headless,
|
|
15091
|
+
cdpUrl,
|
|
15092
|
+
profileDirectory,
|
|
15093
|
+
userDataDir,
|
|
15094
|
+
executablePath
|
|
15095
|
+
});
|
|
14663
15096
|
if (instance && !launchPromise) {
|
|
14664
15097
|
assertCompatibleCloudProfileBinding(
|
|
14665
15098
|
logicalSession,
|
|
14666
15099
|
cloudProfileBinding,
|
|
14667
15100
|
requestedCloudProfileBinding
|
|
14668
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
|
+
}
|
|
14669
15113
|
}
|
|
15114
|
+
let shouldLaunchInitialUrl = false;
|
|
14670
15115
|
if (!instance) {
|
|
14671
15116
|
instance = new Opensteer(
|
|
14672
15117
|
buildServerOpenConfig({
|
|
14673
15118
|
scopeDir,
|
|
14674
15119
|
name: activeNamespace,
|
|
14675
15120
|
cursorEnabled: effectiveCursorEnabled,
|
|
14676
|
-
|
|
14677
|
-
connectUrl,
|
|
14678
|
-
channel,
|
|
14679
|
-
profileDir,
|
|
15121
|
+
...requestedBrowserConfig,
|
|
14680
15122
|
cloudAuth: cloudAuthOverride
|
|
14681
15123
|
})
|
|
14682
15124
|
);
|
|
15125
|
+
const resolvedBrowserConfig = instance.getConfig().browser || {};
|
|
15126
|
+
shouldLaunchInitialUrl = Boolean(url) && resolvedBrowserConfig.mode === "real" && !resolvedBrowserConfig.cdpUrl;
|
|
14683
15127
|
const nextCloudProfileBinding = resolveSessionCloudProfileBinding(
|
|
14684
15128
|
instance.getConfig(),
|
|
14685
15129
|
requestedCloudProfileBinding
|
|
@@ -14691,12 +15135,13 @@ async function handleRequest(request, socket) {
|
|
|
14691
15135
|
);
|
|
14692
15136
|
}
|
|
14693
15137
|
launchPromise = instance.launch({
|
|
14694
|
-
|
|
15138
|
+
initialUrl: shouldLaunchInitialUrl ? url : void 0,
|
|
15139
|
+
...requestedBrowserConfig,
|
|
14695
15140
|
cloudBrowserProfile: cloudProfileId ? {
|
|
14696
15141
|
profileId: cloudProfileId,
|
|
14697
15142
|
reuseIfActive: cloudProfileReuseIfActive
|
|
14698
15143
|
} : void 0,
|
|
14699
|
-
timeout:
|
|
15144
|
+
timeout: cdpUrl ? 12e4 : 3e4
|
|
14700
15145
|
});
|
|
14701
15146
|
try {
|
|
14702
15147
|
await launchPromise;
|
|
@@ -14714,7 +15159,7 @@ async function handleRequest(request, socket) {
|
|
|
14714
15159
|
} else if (requestedCursor !== null) {
|
|
14715
15160
|
instance.setCursorEnabled(requestedCursor);
|
|
14716
15161
|
}
|
|
14717
|
-
if (url) {
|
|
15162
|
+
if (url && !shouldLaunchInitialUrl) {
|
|
14718
15163
|
await instance.goto(url);
|
|
14719
15164
|
}
|
|
14720
15165
|
sendResponse(socket, {
|
|
@@ -14844,8 +15289,8 @@ async function handleRequest(request, socket) {
|
|
|
14844
15289
|
);
|
|
14845
15290
|
}
|
|
14846
15291
|
}
|
|
14847
|
-
if ((0,
|
|
14848
|
-
(0,
|
|
15292
|
+
if ((0, import_fs5.existsSync)(socketPath)) {
|
|
15293
|
+
(0, import_fs5.unlinkSync)(socketPath);
|
|
14849
15294
|
}
|
|
14850
15295
|
var server = (0, import_net.createServer)((socket) => {
|
|
14851
15296
|
let buffer = "";
|
|
@@ -14875,7 +15320,7 @@ var server = (0, import_net.createServer)((socket) => {
|
|
|
14875
15320
|
});
|
|
14876
15321
|
});
|
|
14877
15322
|
server.listen(socketPath, () => {
|
|
14878
|
-
(0,
|
|
15323
|
+
(0, import_fs5.writeFileSync)(pidPath, String(process.pid));
|
|
14879
15324
|
if (process.send) {
|
|
14880
15325
|
process.send("ready");
|
|
14881
15326
|
}
|