opensteer 0.6.6 → 0.6.7

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.
@@ -425,7 +425,7 @@ __export(profile_exports, {
425
425
  });
426
426
  module.exports = __toCommonJS(profile_exports);
427
427
  var import_node_path4 = __toESM(require("path"), 1);
428
- var import_promises4 = require("readline/promises");
428
+ var import_promises5 = require("readline/promises");
429
429
 
430
430
  // src/config.ts
431
431
  var import_fs = __toESM(require("fs"), 1);
@@ -1324,11 +1324,8 @@ var import_crypto = require("crypto");
1324
1324
 
1325
1325
  // src/browser/pool.ts
1326
1326
  var import_node_child_process = require("child_process");
1327
- var import_node_fs = require("fs");
1328
- var import_promises = require("fs/promises");
1327
+ var import_promises2 = require("fs/promises");
1329
1328
  var import_node_net = require("net");
1330
- var import_node_os = require("os");
1331
- var import_node_path = require("path");
1332
1329
  var import_playwright = require("playwright");
1333
1330
 
1334
1331
  // src/browser/cdp-proxy.ts
@@ -1821,18 +1818,282 @@ function listLocalChromeProfiles(userDataDir = detectChromePaths().defaultUserDa
1821
1818
  }
1822
1819
  }
1823
1820
 
1821
+ // src/browser/persistent-profile.ts
1822
+ var import_node_crypto = require("crypto");
1823
+ var import_node_fs = require("fs");
1824
+ var import_promises = require("fs/promises");
1825
+ var import_node_os = require("os");
1826
+ var import_node_path = require("path");
1827
+ var OPENSTEER_META_FILE = ".opensteer-meta.json";
1828
+ var PROCESS_STARTED_AT_MS = Math.floor(Date.now() - process.uptime() * 1e3);
1829
+ var PROCESS_START_TIME_TOLERANCE_MS = 1e3;
1830
+ var CHROME_SINGLETON_ENTRIES = /* @__PURE__ */ new Set([
1831
+ "SingletonCookie",
1832
+ "SingletonLock",
1833
+ "SingletonSocket",
1834
+ "DevToolsActivePort",
1835
+ "lockfile"
1836
+ ]);
1837
+ var COPY_SKIP_ENTRIES = /* @__PURE__ */ new Set([
1838
+ ...CHROME_SINGLETON_ENTRIES,
1839
+ OPENSTEER_META_FILE
1840
+ ]);
1841
+ var SKIPPED_ROOT_DIRECTORIES = /* @__PURE__ */ new Set([
1842
+ "Crash Reports",
1843
+ "Crashpad",
1844
+ "BrowserMetrics",
1845
+ "GrShaderCache",
1846
+ "ShaderCache",
1847
+ "GraphiteDawnCache",
1848
+ "component_crx_cache",
1849
+ "Crowd Deny",
1850
+ "hyphen-data",
1851
+ "OnDeviceHeadSuggestModel",
1852
+ "OptimizationGuidePredictionModels",
1853
+ "Segmentation Platform",
1854
+ "SmartCardDeviceNames",
1855
+ "WidevineCdm",
1856
+ "pnacl"
1857
+ ]);
1858
+ async function getOrCreatePersistentProfile(sourceUserDataDir, profileDirectory, profilesRootDir = defaultPersistentProfilesRootDir()) {
1859
+ const resolvedSourceUserDataDir = expandHome(sourceUserDataDir);
1860
+ const targetUserDataDir = (0, import_node_path.join)(
1861
+ expandHome(profilesRootDir),
1862
+ buildPersistentProfileKey(resolvedSourceUserDataDir, profileDirectory)
1863
+ );
1864
+ const sourceProfileDir = (0, import_node_path.join)(resolvedSourceUserDataDir, profileDirectory);
1865
+ const metadata = buildPersistentProfileMetadata(
1866
+ resolvedSourceUserDataDir,
1867
+ profileDirectory
1868
+ );
1869
+ await (0, import_promises.mkdir)((0, import_node_path.dirname)(targetUserDataDir), { recursive: true });
1870
+ await cleanOrphanedTempDirs(
1871
+ (0, import_node_path.dirname)(targetUserDataDir),
1872
+ (0, import_node_path.basename)(targetUserDataDir)
1873
+ );
1874
+ if (!(0, import_node_fs.existsSync)(sourceProfileDir)) {
1875
+ throw new Error(
1876
+ `Chrome profile "${profileDirectory}" was not found in "${resolvedSourceUserDataDir}".`
1877
+ );
1878
+ }
1879
+ const created = await createPersistentProfileClone(
1880
+ resolvedSourceUserDataDir,
1881
+ sourceProfileDir,
1882
+ targetUserDataDir,
1883
+ profileDirectory,
1884
+ metadata
1885
+ );
1886
+ await ensurePersistentProfileMetadata(targetUserDataDir, metadata);
1887
+ return {
1888
+ created,
1889
+ userDataDir: targetUserDataDir
1890
+ };
1891
+ }
1892
+ async function clearPersistentProfileSingletons(userDataDir) {
1893
+ await Promise.all(
1894
+ [...CHROME_SINGLETON_ENTRIES].map(
1895
+ (entry) => (0, import_promises.rm)((0, import_node_path.join)(userDataDir, entry), {
1896
+ force: true,
1897
+ recursive: true
1898
+ }).catch(() => void 0)
1899
+ )
1900
+ );
1901
+ }
1902
+ function buildPersistentProfileKey(sourceUserDataDir, profileDirectory) {
1903
+ const hash = (0, import_node_crypto.createHash)("sha256").update(`${sourceUserDataDir}\0${profileDirectory}`).digest("hex").slice(0, 16);
1904
+ const sourceLabel = sanitizePathSegment((0, import_node_path.basename)(sourceUserDataDir) || "user-data");
1905
+ const profileLabel = sanitizePathSegment(profileDirectory || "Default");
1906
+ return `${sourceLabel}-${profileLabel}-${hash}`;
1907
+ }
1908
+ function defaultPersistentProfilesRootDir() {
1909
+ return (0, import_node_path.join)((0, import_node_os.homedir)(), ".opensteer", "real-browser-profiles");
1910
+ }
1911
+ function sanitizePathSegment(value) {
1912
+ const sanitized = value.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/-+/g, "-");
1913
+ return sanitized.replace(/^-|-$/g, "") || "profile";
1914
+ }
1915
+ function isProfileDirectory(userDataDir, entry) {
1916
+ return (0, import_node_fs.existsSync)((0, import_node_path.join)(userDataDir, entry, "Preferences"));
1917
+ }
1918
+ async function copyRootLevelEntries(sourceUserDataDir, targetUserDataDir, targetProfileDirectory) {
1919
+ let entries;
1920
+ try {
1921
+ entries = await (0, import_promises.readdir)(sourceUserDataDir);
1922
+ } catch {
1923
+ return;
1924
+ }
1925
+ const copyTasks = [];
1926
+ for (const entry of entries) {
1927
+ if (COPY_SKIP_ENTRIES.has(entry)) continue;
1928
+ if (entry === targetProfileDirectory) continue;
1929
+ const sourcePath = (0, import_node_path.join)(sourceUserDataDir, entry);
1930
+ const targetPath = (0, import_node_path.join)(targetUserDataDir, entry);
1931
+ if ((0, import_node_fs.existsSync)(targetPath)) continue;
1932
+ let entryStat;
1933
+ try {
1934
+ entryStat = await (0, import_promises.stat)(sourcePath);
1935
+ } catch {
1936
+ continue;
1937
+ }
1938
+ if (entryStat.isFile()) {
1939
+ copyTasks.push((0, import_promises.copyFile)(sourcePath, targetPath).catch(() => void 0));
1940
+ } else if (entryStat.isDirectory()) {
1941
+ if (isProfileDirectory(sourceUserDataDir, entry)) continue;
1942
+ if (SKIPPED_ROOT_DIRECTORIES.has(entry)) continue;
1943
+ copyTasks.push(
1944
+ (0, import_promises.cp)(sourcePath, targetPath, { recursive: true }).catch(
1945
+ () => void 0
1946
+ )
1947
+ );
1948
+ }
1949
+ }
1950
+ await Promise.all(copyTasks);
1951
+ }
1952
+ async function writePersistentProfileMetadata(userDataDir, metadata) {
1953
+ await (0, import_promises.writeFile)(
1954
+ (0, import_node_path.join)(userDataDir, OPENSTEER_META_FILE),
1955
+ JSON.stringify(metadata, null, 2)
1956
+ );
1957
+ }
1958
+ function buildPersistentProfileMetadata(sourceUserDataDir, profileDirectory) {
1959
+ return {
1960
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1961
+ profileDirectory,
1962
+ source: sourceUserDataDir
1963
+ };
1964
+ }
1965
+ async function createPersistentProfileClone(sourceUserDataDir, sourceProfileDir, targetUserDataDir, profileDirectory, metadata) {
1966
+ if ((0, import_node_fs.existsSync)(targetUserDataDir)) {
1967
+ return false;
1968
+ }
1969
+ const tempUserDataDir = await (0, import_promises.mkdtemp)(
1970
+ buildPersistentProfileTempDirPrefix(targetUserDataDir)
1971
+ );
1972
+ let published = false;
1973
+ try {
1974
+ await (0, import_promises.cp)(sourceProfileDir, (0, import_node_path.join)(tempUserDataDir, profileDirectory), {
1975
+ recursive: true
1976
+ });
1977
+ await copyRootLevelEntries(
1978
+ sourceUserDataDir,
1979
+ tempUserDataDir,
1980
+ profileDirectory
1981
+ );
1982
+ await writePersistentProfileMetadata(tempUserDataDir, metadata);
1983
+ try {
1984
+ await (0, import_promises.rename)(tempUserDataDir, targetUserDataDir);
1985
+ } catch (error) {
1986
+ if (wasProfilePublishedByAnotherProcess(error, targetUserDataDir)) {
1987
+ return false;
1988
+ }
1989
+ throw error;
1990
+ }
1991
+ published = true;
1992
+ return true;
1993
+ } finally {
1994
+ if (!published) {
1995
+ await (0, import_promises.rm)(tempUserDataDir, {
1996
+ recursive: true,
1997
+ force: true
1998
+ }).catch(() => void 0);
1999
+ }
2000
+ }
2001
+ }
2002
+ async function ensurePersistentProfileMetadata(userDataDir, metadata) {
2003
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(userDataDir, OPENSTEER_META_FILE))) {
2004
+ return;
2005
+ }
2006
+ await writePersistentProfileMetadata(userDataDir, metadata);
2007
+ }
2008
+ function wasProfilePublishedByAnotherProcess(error, targetUserDataDir) {
2009
+ const code = typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : void 0;
2010
+ return (0, import_node_fs.existsSync)(targetUserDataDir) && (code === "EEXIST" || code === "ENOTEMPTY" || code === "EPERM");
2011
+ }
2012
+ function buildPersistentProfileTempDirPrefix(targetUserDataDir) {
2013
+ return (0, import_node_path.join)(
2014
+ (0, import_node_path.dirname)(targetUserDataDir),
2015
+ `${(0, import_node_path.basename)(targetUserDataDir)}-tmp-${process.pid}-${PROCESS_STARTED_AT_MS}-`
2016
+ );
2017
+ }
2018
+ async function cleanOrphanedTempDirs(profilesDir, targetBaseName) {
2019
+ let entries;
2020
+ try {
2021
+ entries = await (0, import_promises.readdir)(profilesDir, {
2022
+ encoding: "utf8",
2023
+ withFileTypes: true
2024
+ });
2025
+ } catch {
2026
+ return;
2027
+ }
2028
+ const tempDirPrefix = `${targetBaseName}-tmp-`;
2029
+ await Promise.all(
2030
+ entries.map(async (entry) => {
2031
+ if (!entry.isDirectory() || !entry.name.startsWith(tempDirPrefix)) {
2032
+ return;
2033
+ }
2034
+ if (isTempDirOwnedByLiveProcess(entry.name, tempDirPrefix)) {
2035
+ return;
2036
+ }
2037
+ await (0, import_promises.rm)((0, import_node_path.join)(profilesDir, entry.name), {
2038
+ recursive: true,
2039
+ force: true
2040
+ }).catch(() => void 0);
2041
+ })
2042
+ );
2043
+ }
2044
+ function isTempDirOwnedByLiveProcess(tempDirName, tempDirPrefix) {
2045
+ const owner = parseTempDirOwner(tempDirName, tempDirPrefix);
2046
+ if (!owner) {
2047
+ return false;
2048
+ }
2049
+ if (owner.pid === process.pid && Math.abs(owner.processStartedAtMs - PROCESS_STARTED_AT_MS) <= PROCESS_START_TIME_TOLERANCE_MS) {
2050
+ return true;
2051
+ }
2052
+ return isProcessRunning(owner.pid);
2053
+ }
2054
+ function parseTempDirOwner(tempDirName, tempDirPrefix) {
2055
+ const remainder = tempDirName.slice(tempDirPrefix.length);
2056
+ const firstDashIndex = remainder.indexOf("-");
2057
+ const secondDashIndex = firstDashIndex === -1 ? -1 : remainder.indexOf("-", firstDashIndex + 1);
2058
+ if (firstDashIndex === -1 || secondDashIndex === -1) {
2059
+ return null;
2060
+ }
2061
+ const pid = Number.parseInt(remainder.slice(0, firstDashIndex), 10);
2062
+ const processStartedAtMs = Number.parseInt(
2063
+ remainder.slice(firstDashIndex + 1, secondDashIndex),
2064
+ 10
2065
+ );
2066
+ if (!Number.isInteger(pid) || pid <= 0) {
2067
+ return null;
2068
+ }
2069
+ if (!Number.isInteger(processStartedAtMs) || processStartedAtMs <= 0) {
2070
+ return null;
2071
+ }
2072
+ return { pid, processStartedAtMs };
2073
+ }
2074
+ function isProcessRunning(pid) {
2075
+ try {
2076
+ process.kill(pid, 0);
2077
+ return true;
2078
+ } catch (error) {
2079
+ const code = typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : void 0;
2080
+ return code !== "ESRCH";
2081
+ }
2082
+ }
2083
+
1824
2084
  // src/browser/pool.ts
1825
2085
  var BrowserPool = class {
1826
2086
  browser = null;
1827
2087
  cdpProxy = null;
1828
2088
  launchedProcess = null;
1829
- tempUserDataDir = null;
2089
+ managedUserDataDir = null;
2090
+ persistentProfile = false;
1830
2091
  defaults;
1831
2092
  constructor(defaults = {}) {
1832
2093
  this.defaults = defaults;
1833
2094
  }
1834
2095
  async launch(options = {}) {
1835
- if (this.browser || this.cdpProxy || this.launchedProcess || this.tempUserDataDir) {
2096
+ if (this.browser || this.cdpProxy || this.launchedProcess || this.managedUserDataDir) {
1836
2097
  await this.close();
1837
2098
  }
1838
2099
  const mode = options.mode ?? this.defaults.mode ?? "chromium";
@@ -1882,11 +2143,13 @@ var BrowserPool = class {
1882
2143
  const browser = this.browser;
1883
2144
  const cdpProxy = this.cdpProxy;
1884
2145
  const launchedProcess = this.launchedProcess;
1885
- const tempUserDataDir = this.tempUserDataDir;
2146
+ const managedUserDataDir = this.managedUserDataDir;
2147
+ const persistentProfile = this.persistentProfile;
1886
2148
  this.browser = null;
1887
2149
  this.cdpProxy = null;
1888
2150
  this.launchedProcess = null;
1889
- this.tempUserDataDir = null;
2151
+ this.managedUserDataDir = null;
2152
+ this.persistentProfile = false;
1890
2153
  try {
1891
2154
  if (browser) {
1892
2155
  await browser.close().catch(() => void 0);
@@ -1894,8 +2157,8 @@ var BrowserPool = class {
1894
2157
  } finally {
1895
2158
  cdpProxy?.close();
1896
2159
  await killProcessTree(launchedProcess);
1897
- if (tempUserDataDir) {
1898
- await (0, import_promises.rm)(tempUserDataDir, {
2160
+ if (managedUserDataDir && !persistentProfile) {
2161
+ await (0, import_promises2.rm)(managedUserDataDir, {
1899
2162
  recursive: true,
1900
2163
  force: true
1901
2164
  }).catch(() => void 0);
@@ -1944,10 +2207,11 @@ var BrowserPool = class {
1944
2207
  options.userDataDir ?? chromePaths.defaultUserDataDir
1945
2208
  );
1946
2209
  const profileDirectory = options.profileDirectory ?? "Default";
1947
- const tempUserDataDir = await cloneProfileToTempDir(
2210
+ const persistentProfile = await getOrCreatePersistentProfile(
1948
2211
  sourceUserDataDir,
1949
2212
  profileDirectory
1950
2213
  );
2214
+ await clearPersistentProfileSingletons(persistentProfile.userDataDir);
1951
2215
  const debugPort = await reserveDebugPort();
1952
2216
  const headless = resolveLaunchHeadless(
1953
2217
  "real",
@@ -1955,7 +2219,7 @@ var BrowserPool = class {
1955
2219
  this.defaults.headless
1956
2220
  );
1957
2221
  const launchArgs = buildRealBrowserLaunchArgs({
1958
- userDataDir: tempUserDataDir,
2222
+ userDataDir: persistentProfile.userDataDir,
1959
2223
  profileDirectory,
1960
2224
  debugPort,
1961
2225
  headless
@@ -1975,24 +2239,22 @@ var BrowserPool = class {
1975
2239
  timeout: options.timeout ?? 3e4
1976
2240
  });
1977
2241
  const { context, page } = await createOwnedBrowserContextAndPage(
1978
- browser,
1979
- {
1980
- headless,
1981
- initialUrl: options.initialUrl,
1982
- timeoutMs: options.timeout ?? 3e4
1983
- }
2242
+ browser
1984
2243
  );
2244
+ if (options.initialUrl) {
2245
+ await page.goto(options.initialUrl, {
2246
+ waitUntil: "domcontentloaded",
2247
+ timeout: options.timeout ?? 3e4
2248
+ });
2249
+ }
1985
2250
  this.browser = browser;
1986
2251
  this.launchedProcess = processHandle;
1987
- this.tempUserDataDir = tempUserDataDir;
2252
+ this.managedUserDataDir = persistentProfile.userDataDir;
2253
+ this.persistentProfile = true;
1988
2254
  return { browser, context, page, isExternal: false };
1989
2255
  } catch (error) {
1990
2256
  await browser?.close().catch(() => void 0);
1991
2257
  await killProcessTree(processHandle);
1992
- await (0, import_promises.rm)(tempUserDataDir, {
1993
- recursive: true,
1994
- force: true
1995
- }).catch(() => void 0);
1996
2258
  throw error;
1997
2259
  }
1998
2260
  }
@@ -2015,8 +2277,7 @@ var BrowserPool = class {
2015
2277
  };
2016
2278
  async function pickBrowserContextAndPage(browser) {
2017
2279
  const context = getPrimaryBrowserContext(browser);
2018
- const pages = context.pages();
2019
- const page = pages.find((candidate) => isInspectablePageUrl2(candidate.url())) || pages[0] || await context.newPage();
2280
+ const page = await getAttachedPageOrCreate(context);
2020
2281
  return { context, page };
2021
2282
  }
2022
2283
  function resolveLaunchHeadless(mode, requestedHeadless, defaultHeadless) {
@@ -2028,77 +2289,31 @@ function resolveLaunchHeadless(mode, requestedHeadless, defaultHeadless) {
2028
2289
  }
2029
2290
  return mode === "real";
2030
2291
  }
2031
- async function createOwnedBrowserContextAndPage(browser, options) {
2292
+ async function createOwnedBrowserContextAndPage(browser) {
2032
2293
  const context = getPrimaryBrowserContext(browser);
2033
- const page = await createOwnedBrowserPage(browser, context, options);
2294
+ const page = await getExistingPageOrCreate(context);
2034
2295
  return { context, page };
2035
2296
  }
2036
- async function createOwnedBrowserPage(browser, context, options) {
2037
- const targetUrl = options.initialUrl ?? "about:blank";
2038
- const existingPages = new Set(context.pages());
2039
- const browserSession = await browser.newBrowserCDPSession();
2040
- try {
2041
- const { targetId } = await browserSession.send("Target.createTarget", {
2042
- url: targetUrl,
2043
- newWindow: !options.headless
2044
- });
2045
- await browserSession.send("Target.activateTarget", { targetId }).catch(() => void 0);
2046
- const page = await waitForOwnedBrowserPage(context, {
2047
- existingPages,
2048
- targetUrl,
2049
- timeoutMs: options.timeoutMs
2050
- });
2051
- if (targetUrl !== "about:blank") {
2052
- await page.waitForLoadState("domcontentloaded", {
2053
- timeout: options.timeoutMs
2054
- });
2055
- }
2056
- await closeDisposableStartupTargets(browserSession, targetId);
2057
- return page;
2058
- } finally {
2059
- await browserSession.detach().catch(() => void 0);
2297
+ async function getAttachedPageOrCreate(context) {
2298
+ const pages = context.pages();
2299
+ const inspectablePage = pages.find(
2300
+ (candidate) => isInspectablePageUrl2(safePageUrl(candidate))
2301
+ );
2302
+ if (inspectablePage) {
2303
+ return inspectablePage;
2060
2304
  }
2061
- }
2062
- async function closeDisposableStartupTargets(browserSession, preservedTargetId) {
2063
- const response = await browserSession.send("Target.getTargets").catch(() => null);
2064
- if (!response) {
2065
- return;
2066
- }
2067
- for (const targetInfo of response.targetInfos) {
2068
- if (targetInfo.targetId === preservedTargetId || targetInfo.type !== "page" || !isDisposableStartupPageUrl(targetInfo.url)) {
2069
- continue;
2070
- }
2071
- await browserSession.send("Target.closeTarget", { targetId: targetInfo.targetId }).catch(() => void 0);
2305
+ const attachedPage = pages[0];
2306
+ if (attachedPage) {
2307
+ return attachedPage;
2072
2308
  }
2309
+ return await context.newPage();
2073
2310
  }
2074
- async function waitForOwnedBrowserPage(context, options) {
2075
- const deadline = Date.now() + options.timeoutMs;
2076
- let fallbackPage = null;
2077
- while (Date.now() < deadline) {
2078
- for (const candidate of context.pages()) {
2079
- if (options.existingPages.has(candidate)) {
2080
- continue;
2081
- }
2082
- const url = candidate.url();
2083
- if (!isInspectablePageUrl2(url)) {
2084
- continue;
2085
- }
2086
- fallbackPage ??= candidate;
2087
- if (options.targetUrl === "about:blank") {
2088
- return candidate;
2089
- }
2090
- if (pageLooselyMatchesUrl(url, options.targetUrl)) {
2091
- return candidate;
2092
- }
2093
- }
2094
- await sleep(100);
2095
- }
2096
- if (fallbackPage) {
2097
- return fallbackPage;
2311
+ async function getExistingPageOrCreate(context) {
2312
+ const existingPage = context.pages()[0];
2313
+ if (existingPage) {
2314
+ return existingPage;
2098
2315
  }
2099
- throw new Error(
2100
- `Chrome created a target for ${options.targetUrl}, but Playwright did not expose the page in time.`
2101
- );
2316
+ return await context.newPage();
2102
2317
  }
2103
2318
  function getPrimaryBrowserContext(browser) {
2104
2319
  const contexts = browser.contexts();
@@ -2112,19 +2327,11 @@ function getPrimaryBrowserContext(browser) {
2112
2327
  function isInspectablePageUrl2(url) {
2113
2328
  return url === "about:blank" || url.startsWith("http://") || url.startsWith("https://");
2114
2329
  }
2115
- function isDisposableStartupPageUrl(url) {
2116
- return url === "about:blank" || url === "chrome://newtab/" || url === "chrome://new-tab-page/";
2117
- }
2118
- function pageLooselyMatchesUrl(currentUrl, initialUrl) {
2330
+ function safePageUrl(page) {
2119
2331
  try {
2120
- const current = new URL(currentUrl);
2121
- const requested = new URL(initialUrl);
2122
- if (current.href === requested.href) {
2123
- return true;
2124
- }
2125
- return current.hostname === requested.hostname && current.pathname === requested.pathname;
2332
+ return page.url();
2126
2333
  } catch {
2127
- return currentUrl === initialUrl;
2334
+ return "";
2128
2335
  }
2129
2336
  }
2130
2337
  function normalizeDiscoveryUrl(cdpUrl) {
@@ -2204,38 +2411,12 @@ async function reserveDebugPort() {
2204
2411
  });
2205
2412
  });
2206
2413
  }
2207
- async function cloneProfileToTempDir(userDataDir, profileDirectory) {
2208
- const resolvedUserDataDir = expandHome(userDataDir);
2209
- const tempUserDataDir = await (0, import_promises.mkdtemp)(
2210
- (0, import_node_path.join)((0, import_node_os.tmpdir)(), "opensteer-real-browser-")
2211
- );
2212
- const sourceProfileDir = (0, import_node_path.join)(resolvedUserDataDir, profileDirectory);
2213
- const targetProfileDir = (0, import_node_path.join)(tempUserDataDir, profileDirectory);
2214
- if ((0, import_node_fs.existsSync)(sourceProfileDir)) {
2215
- await (0, import_promises.cp)(sourceProfileDir, targetProfileDir, {
2216
- recursive: true
2217
- });
2218
- } else {
2219
- await (0, import_promises.mkdir)(targetProfileDir, {
2220
- recursive: true
2221
- });
2222
- }
2223
- const localStatePath = (0, import_node_path.join)(resolvedUserDataDir, "Local State");
2224
- if ((0, import_node_fs.existsSync)(localStatePath)) {
2225
- await (0, import_promises.copyFile)(localStatePath, (0, import_node_path.join)(tempUserDataDir, "Local State"));
2226
- }
2227
- return tempUserDataDir;
2228
- }
2229
2414
  function buildRealBrowserLaunchArgs(options) {
2230
2415
  const args = [
2231
2416
  `--user-data-dir=${options.userDataDir}`,
2232
2417
  `--profile-directory=${options.profileDirectory}`,
2233
2418
  `--remote-debugging-port=${options.debugPort}`,
2234
- "--no-first-run",
2235
- "--no-default-browser-check",
2236
- "--disable-background-networking",
2237
- "--disable-sync",
2238
- "--disable-popup-blocking"
2419
+ "--disable-blink-features=AutomationControlled"
2239
2420
  ];
2240
2421
  if (options.headless) {
2241
2422
  args.push("--headless=new");
@@ -7053,7 +7234,7 @@ async function closeTab(context, activePage, index) {
7053
7234
  }
7054
7235
 
7055
7236
  // src/actions/cookies.ts
7056
- var import_promises2 = require("fs/promises");
7237
+ var import_promises3 = require("fs/promises");
7057
7238
  async function getCookies(context, url) {
7058
7239
  return context.cookies(url ? [url] : void 0);
7059
7240
  }
@@ -7065,10 +7246,10 @@ async function clearCookies(context) {
7065
7246
  }
7066
7247
  async function exportCookies(context, filePath, url) {
7067
7248
  const cookies = await context.cookies(url ? [url] : void 0);
7068
- await (0, import_promises2.writeFile)(filePath, JSON.stringify(cookies, null, 2), "utf-8");
7249
+ await (0, import_promises3.writeFile)(filePath, JSON.stringify(cookies, null, 2), "utf-8");
7069
7250
  }
7070
7251
  async function importCookies(context, filePath) {
7071
- const raw = await (0, import_promises2.readFile)(filePath, "utf-8");
7252
+ const raw = await (0, import_promises3.readFile)(filePath, "utf-8");
7072
7253
  const cookies = JSON.parse(raw);
7073
7254
  await context.addCookies(cookies);
7074
7255
  }
@@ -7819,31 +8000,31 @@ function buildVariantDescriptorFromCluster(descriptors) {
7819
8000
  const keyStats = /* @__PURE__ */ new Map();
7820
8001
  for (const descriptor of descriptors) {
7821
8002
  for (const field of descriptor.fields) {
7822
- const stat = keyStats.get(field.path) || {
8003
+ const stat2 = keyStats.get(field.path) || {
7823
8004
  indices: /* @__PURE__ */ new Set(),
7824
8005
  pathNodes: [],
7825
8006
  attributes: [],
7826
8007
  sources: []
7827
8008
  };
7828
- stat.indices.add(descriptor.index);
8009
+ stat2.indices.add(descriptor.index);
7829
8010
  if (isPersistedValueNode(field.node)) {
7830
- stat.pathNodes.push(field.node.$path);
7831
- stat.attributes.push(field.node.attribute);
8011
+ stat2.pathNodes.push(field.node.$path);
8012
+ stat2.attributes.push(field.node.attribute);
7832
8013
  } else if (isPersistedSourceNode(field.node)) {
7833
- stat.sources.push("current_url");
8014
+ stat2.sources.push("current_url");
7834
8015
  }
7835
- keyStats.set(field.path, stat);
8016
+ keyStats.set(field.path, stat2);
7836
8017
  }
7837
8018
  }
7838
8019
  const mergedFields = [];
7839
- for (const [fieldPath, stat] of keyStats) {
7840
- if (stat.indices.size < threshold) continue;
7841
- if (stat.pathNodes.length >= threshold) {
8020
+ for (const [fieldPath, stat2] of keyStats) {
8021
+ if (stat2.indices.size < threshold) continue;
8022
+ if (stat2.pathNodes.length >= threshold) {
7842
8023
  let mergedFieldPath = null;
7843
- if (stat.pathNodes.length === 1) {
7844
- mergedFieldPath = sanitizeElementPath(stat.pathNodes[0]);
8024
+ if (stat2.pathNodes.length === 1) {
8025
+ mergedFieldPath = sanitizeElementPath(stat2.pathNodes[0]);
7845
8026
  } else {
7846
- mergedFieldPath = mergeElementPathsByMajority(stat.pathNodes);
8027
+ mergedFieldPath = mergeElementPathsByMajority(stat2.pathNodes);
7847
8028
  }
7848
8029
  if (!mergedFieldPath) continue;
7849
8030
  if (clusterSize === 1) {
@@ -7856,17 +8037,17 @@ function buildVariantDescriptorFromCluster(descriptors) {
7856
8037
  mergedFieldPath,
7857
8038
  "field"
7858
8039
  );
7859
- const attrThreshold = stat.pathNodes.length === 1 ? 1 : majorityThreshold(stat.pathNodes.length);
8040
+ const attrThreshold = stat2.pathNodes.length === 1 ? 1 : majorityThreshold(stat2.pathNodes.length);
7860
8041
  mergedFields.push({
7861
8042
  path: fieldPath,
7862
8043
  node: createValueNode({
7863
8044
  elementPath: mergedFieldPath,
7864
- attribute: pickModeString(stat.attributes, attrThreshold)
8045
+ attribute: pickModeString(stat2.attributes, attrThreshold)
7865
8046
  })
7866
8047
  });
7867
8048
  continue;
7868
8049
  }
7869
- const dominantSource = pickModeString(stat.sources, threshold);
8050
+ const dominantSource = pickModeString(stat2.sources, threshold);
7870
8051
  if (dominantSource === "current_url") {
7871
8052
  mergedFields.push({
7872
8053
  path: fieldPath,
@@ -9067,7 +9248,7 @@ function selectPreferredContextPage(browser, contexts) {
9067
9248
  let aboutBlankCandidate = null;
9068
9249
  for (const context of contexts) {
9069
9250
  for (const page of context.pages()) {
9070
- const url = safePageUrl(page);
9251
+ const url = safePageUrl2(page);
9071
9252
  if (!isInternalOrEmptyUrl(url)) {
9072
9253
  return { browser, context, page };
9073
9254
  }
@@ -9078,7 +9259,7 @@ function selectPreferredContextPage(browser, contexts) {
9078
9259
  }
9079
9260
  return aboutBlankCandidate;
9080
9261
  }
9081
- function safePageUrl(page) {
9262
+ function safePageUrl2(page) {
9082
9263
  try {
9083
9264
  return page.url();
9084
9265
  } catch {
@@ -14498,8 +14679,8 @@ function buildLocalRunId(namespace) {
14498
14679
  // src/browser/chromium-profile.ts
14499
14680
  var import_node_util = require("util");
14500
14681
  var import_node_child_process3 = require("child_process");
14501
- var import_node_crypto = require("crypto");
14502
- var import_promises3 = require("fs/promises");
14682
+ var import_node_crypto2 = require("crypto");
14683
+ var import_promises4 = require("fs/promises");
14503
14684
  var import_node_fs2 = require("fs");
14504
14685
  var import_node_path2 = require("path");
14505
14686
  var import_node_os2 = require("os");
@@ -14724,7 +14905,7 @@ function resolveCookieDbPath(profileDir) {
14724
14905
  return null;
14725
14906
  }
14726
14907
  async function selectProfileDirFromUserDataDir(userDataDir) {
14727
- const entries = await (0, import_promises3.readdir)(userDataDir, {
14908
+ const entries = await (0, import_promises4.readdir)(userDataDir, {
14728
14909
  withFileTypes: true
14729
14910
  }).catch(() => []);
14730
14911
  const candidates = entries.filter((entry) => entry.isDirectory()).map((entry) => (0, import_node_path2.join)(userDataDir, entry.name)).filter((entryPath) => resolveCookieDbPath(entryPath));
@@ -14811,20 +14992,20 @@ function detectChromiumBrand(location) {
14811
14992
  return DEFAULT_CHROMIUM_BRAND;
14812
14993
  }
14813
14994
  async function createSqliteSnapshot(dbPath) {
14814
- const snapshotDir = await (0, import_promises3.mkdtemp)((0, import_node_path2.join)((0, import_node_os2.tmpdir)(), "opensteer-cookie-db-"));
14995
+ const snapshotDir = await (0, import_promises4.mkdtemp)((0, import_node_path2.join)((0, import_node_os2.tmpdir)(), "opensteer-cookie-db-"));
14815
14996
  const snapshotPath = (0, import_node_path2.join)(snapshotDir, "Cookies");
14816
- await (0, import_promises3.copyFile)(dbPath, snapshotPath);
14997
+ await (0, import_promises4.copyFile)(dbPath, snapshotPath);
14817
14998
  for (const suffix of ["-wal", "-shm", "-journal"]) {
14818
14999
  const source = `${dbPath}${suffix}`;
14819
15000
  if (!(0, import_node_fs2.existsSync)(source)) {
14820
15001
  continue;
14821
15002
  }
14822
- await (0, import_promises3.copyFile)(source, `${snapshotPath}${suffix}`);
15003
+ await (0, import_promises4.copyFile)(source, `${snapshotPath}${suffix}`);
14823
15004
  }
14824
15005
  return {
14825
15006
  snapshotPath,
14826
15007
  cleanup: async () => {
14827
- await (0, import_promises3.rm)(snapshotDir, { recursive: true, force: true });
15008
+ await (0, import_promises4.rm)(snapshotDir, { recursive: true, force: true });
14828
15009
  }
14829
15010
  };
14830
15011
  }
@@ -14870,7 +15051,7 @@ function stripDomainHashPrefix(buffer, hostKey) {
14870
15051
  if (buffer.length < 32) {
14871
15052
  return buffer;
14872
15053
  }
14873
- const domainHash = (0, import_node_crypto.createHash)("sha256").update(hostKey, "utf8").digest();
15054
+ const domainHash = (0, import_node_crypto2.createHash)("sha256").update(hostKey, "utf8").digest();
14874
15055
  if (buffer.subarray(0, 32).equals(domainHash)) {
14875
15056
  return buffer.subarray(32);
14876
15057
  }
@@ -14879,7 +15060,7 @@ function stripDomainHashPrefix(buffer, hostKey) {
14879
15060
  function decryptChromiumAes128CbcValue(encryptedValue, key, hostKey) {
14880
15061
  const ciphertext = encryptedValue.length > 3 && encryptedValue[0] === 118 && encryptedValue[1] === 49 && (encryptedValue[2] === 48 || encryptedValue[2] === 49) ? encryptedValue.subarray(3) : encryptedValue;
14881
15062
  const iv = Buffer.alloc(AES_BLOCK_BYTES, " ");
14882
- const decipher = (0, import_node_crypto.createDecipheriv)("aes-128-cbc", key, iv);
15063
+ const decipher = (0, import_node_crypto2.createDecipheriv)("aes-128-cbc", key, iv);
14883
15064
  const plaintext = Buffer.concat([
14884
15065
  decipher.update(ciphertext),
14885
15066
  decipher.final()
@@ -14892,7 +15073,7 @@ function decryptChromiumAes256GcmValue(encryptedValue, key) {
14892
15073
  const nonce = encryptedValue.subarray(3, 15);
14893
15074
  const ciphertext = encryptedValue.subarray(15, encryptedValue.length - 16);
14894
15075
  const authTag = encryptedValue.subarray(encryptedValue.length - 16);
14895
- const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-gcm", key, nonce);
15076
+ const decipher = (0, import_node_crypto2.createDecipheriv)("aes-256-gcm", key, nonce);
14896
15077
  decipher.setAuthTag(authTag);
14897
15078
  return Buffer.concat([
14898
15079
  decipher.update(ciphertext),
@@ -14929,7 +15110,7 @@ async function buildChromiumDecryptor(location) {
14929
15110
  `Unable to read ${brand.macService} from macOS Keychain.`
14930
15111
  );
14931
15112
  }
14932
- const key = (0, import_node_crypto.pbkdf2Sync)(password, KEY_SALT, MAC_KEY_ITERATIONS, KEY_LENGTH, "sha1");
15113
+ const key = (0, import_node_crypto2.pbkdf2Sync)(password, KEY_SALT, MAC_KEY_ITERATIONS, KEY_LENGTH, "sha1");
14933
15114
  return async (row) => decryptChromiumAes128CbcValue(
14934
15115
  Buffer.from(row.encrypted_value || "", "hex"),
14935
15116
  key,
@@ -14940,7 +15121,7 @@ async function buildChromiumDecryptor(location) {
14940
15121
  const brand = detectChromiumBrand(location);
14941
15122
  const keychainStore = createKeychainStore();
14942
15123
  const password = keychainStore?.get(brand.macService, brand.macAccount) ?? brand.linuxApplications.map((application) => keychainStore?.get(application, application) ?? null).find(Boolean) ?? null;
14943
- const key = (0, import_node_crypto.pbkdf2Sync)(
15124
+ const key = (0, import_node_crypto2.pbkdf2Sync)(
14944
15125
  password || "peanuts",
14945
15126
  KEY_SALT,
14946
15127
  LINUX_KEY_ITERATIONS,
@@ -14960,7 +15141,7 @@ async function buildChromiumDecryptor(location) {
14960
15141
  );
14961
15142
  }
14962
15143
  const localState = JSON.parse(
14963
- await (0, import_promises3.readFile)(location.localStatePath, "utf8")
15144
+ await (0, import_promises4.readFile)(location.localStatePath, "utf8")
14964
15145
  );
14965
15146
  const encryptedKeyBase64 = localState.os_crypt?.encrypted_key;
14966
15147
  if (!encryptedKeyBase64) {
@@ -15054,18 +15235,18 @@ async function loadCookiesFromSqlite(location) {
15054
15235
  }
15055
15236
  }
15056
15237
  async function loadCookiesFromBrowserSnapshot(location, options) {
15057
- const snapshotRootDir = await (0, import_promises3.mkdtemp)((0, import_node_path2.join)((0, import_node_os2.tmpdir)(), "opensteer-profile-"));
15238
+ const snapshotRootDir = await (0, import_promises4.mkdtemp)((0, import_node_path2.join)((0, import_node_os2.tmpdir)(), "opensteer-profile-"));
15058
15239
  const snapshotProfileDir = (0, import_node_path2.join)(
15059
15240
  snapshotRootDir,
15060
15241
  (0, import_node_path2.basename)(location.profileDir)
15061
15242
  );
15062
15243
  let context = null;
15063
15244
  try {
15064
- await (0, import_promises3.cp)(location.profileDir, snapshotProfileDir, {
15245
+ await (0, import_promises4.cp)(location.profileDir, snapshotProfileDir, {
15065
15246
  recursive: true
15066
15247
  });
15067
15248
  if (location.localStatePath) {
15068
- await (0, import_promises3.copyFile)(location.localStatePath, (0, import_node_path2.join)(snapshotRootDir, "Local State"));
15249
+ await (0, import_promises4.copyFile)(location.localStatePath, (0, import_node_path2.join)(snapshotRootDir, "Local State"));
15069
15250
  }
15070
15251
  const brand = detectChromiumBrand(location);
15071
15252
  const args = [`--profile-directory=${(0, import_node_path2.basename)(snapshotProfileDir)}`];
@@ -15078,7 +15259,7 @@ async function loadCookiesFromBrowserSnapshot(location, options) {
15078
15259
  return await context.cookies();
15079
15260
  } finally {
15080
15261
  await context?.close().catch(() => void 0);
15081
- await (0, import_promises3.rm)(snapshotRootDir, { recursive: true, force: true });
15262
+ await (0, import_promises4.rm)(snapshotRootDir, { recursive: true, force: true });
15082
15263
  }
15083
15264
  }
15084
15265
  function isMissingSqliteBinary(error) {
@@ -15321,7 +15502,7 @@ function toResolvedCloudCredential(source, credential) {
15321
15502
  }
15322
15503
 
15323
15504
  // src/auth/machine-credential-store.ts
15324
- var import_node_crypto2 = require("crypto");
15505
+ var import_node_crypto3 = require("crypto");
15325
15506
  var import_node_fs3 = __toESM(require("fs"), 1);
15326
15507
  var import_node_os3 = __toESM(require("os"), 1);
15327
15508
  var import_node_path3 = __toESM(require("path"), 1);
@@ -15469,7 +15650,7 @@ function createMachineCredentialStore(options = {}) {
15469
15650
  }
15470
15651
  function resolveCredentialSlot(authDir, target) {
15471
15652
  const normalizedBaseUrl = normalizeCredentialUrl(target.baseUrl);
15472
- const storageKey = (0, import_node_crypto2.createHash)("sha256").update(normalizedBaseUrl).digest("hex").slice(0, 24);
15653
+ const storageKey = (0, import_node_crypto3.createHash)("sha256").update(normalizedBaseUrl).digest("hex").slice(0, 24);
15473
15654
  return {
15474
15655
  keychainAccount: `${KEYCHAIN_ACCOUNT_PREFIX}${storageKey}`,
15475
15656
  metadataPath: import_node_path3.default.join(authDir, `cli-login.${storageKey}.json`),
@@ -16394,7 +16575,7 @@ function createDefaultDeps() {
16394
16575
  loadLocalProfileCookies: (profileDir, options) => loadCookiesFromLocalProfileDir(profileDir, options),
16395
16576
  isInteractive: () => Boolean(process.stdin.isTTY && process.stdout.isTTY),
16396
16577
  confirm: async (message) => {
16397
- const rl = (0, import_promises4.createInterface)({
16578
+ const rl = (0, import_promises5.createInterface)({
16398
16579
  input: process.stdin,
16399
16580
  output: process.stderr
16400
16581
  });