@ulpi/browse 2.3.2 → 2.3.3

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.
Files changed (2) hide show
  1. package/dist/browse.cjs +335 -315
  2. package/package.json +1 -1
package/dist/browse.cjs CHANGED
@@ -864,7 +864,7 @@ var require_package = __commonJS({
864
864
  "package.json"(exports2, module2) {
865
865
  module2.exports = {
866
866
  name: "@ulpi/browse",
867
- version: "2.3.2",
867
+ version: "2.3.3",
868
868
  homepage: "https://browse.ulpi.io",
869
869
  repository: {
870
870
  type: "git",
@@ -2684,158 +2684,282 @@ var init_sim_cli = __esm({
2684
2684
  }
2685
2685
  });
2686
2686
 
2687
+ // src/app/macos/bridge.ts
2688
+ var bridge_exports3 = {};
2689
+ __export(bridge_exports3, {
2690
+ createMacOSBridge: () => createMacOSBridge,
2691
+ ensureMacOSBridge: () => ensureMacOSBridge,
2692
+ resolveBridgePath: () => resolveBridgePath
2693
+ });
2694
+ function resolveBridgePath() {
2695
+ const candidates = [
2696
+ // 1. Local dev build
2697
+ path11.resolve(__dirname_bridge, "../../../browse-ax/.build/release/browse-ax"),
2698
+ path11.resolve(__dirname_bridge, "../../../browse-ax/.build/debug/browse-ax"),
2699
+ // 2. Installed alongside source (bin/ at project root)
2700
+ path11.resolve(__dirname_bridge, "../../bin/browse-ax"),
2701
+ // 3. Bundled build (dist/browse.cjs → ../bin/)
2702
+ path11.resolve(__dirname_bridge, "../bin/browse-ax")
2703
+ ];
2704
+ for (const p of candidates) {
2705
+ if (fs11.existsSync(p)) return p;
2706
+ }
2707
+ const lazyPath = path11.join(
2708
+ process.env.BROWSE_LOCAL_DIR || path11.join(process.cwd(), ".browse"),
2709
+ "bin",
2710
+ "browse-ax"
2711
+ );
2712
+ if (fs11.existsSync(lazyPath)) return lazyPath;
2713
+ throw new Error(
2714
+ "browse-ax binary not found. Run: browse enable macos\nOr build manually: cd browse-ax && swift build -c release"
2715
+ );
2716
+ }
2717
+ async function ensureMacOSBridge() {
2718
+ if (process.platform !== "darwin") {
2719
+ throw new Error("App automation requires macOS (uses Accessibility API)");
2720
+ }
2721
+ return resolveBridgePath();
2722
+ }
2723
+ async function execBridge(bridgePath, args) {
2724
+ return new Promise((resolve9, reject) => {
2725
+ const proc = (0, import_child_process8.spawn)(bridgePath, args, {
2726
+ stdio: ["ignore", "pipe", "pipe"]
2727
+ });
2728
+ const chunks = [];
2729
+ const errChunks = [];
2730
+ proc.stdout.on("data", (c) => chunks.push(c));
2731
+ proc.stderr.on("data", (c) => errChunks.push(c));
2732
+ proc.on("close", (code) => {
2733
+ const stdout = Buffer.concat(chunks).toString("utf-8").trim();
2734
+ const stderr = Buffer.concat(errChunks).toString("utf-8").trim();
2735
+ if (code !== 0) {
2736
+ try {
2737
+ const err = JSON.parse(stderr || stdout);
2738
+ reject(new Error(err.error || `Bridge exited with code ${code}`));
2739
+ } catch {
2740
+ reject(new Error(stderr || `Bridge exited with code ${code}`));
2741
+ }
2742
+ return;
2743
+ }
2744
+ try {
2745
+ resolve9(JSON.parse(stdout));
2746
+ } catch {
2747
+ reject(new Error(`Invalid bridge output: ${stdout.slice(0, 200)}`));
2748
+ }
2749
+ });
2750
+ });
2751
+ }
2752
+ function createMacOSBridge(bridgePath, pid) {
2753
+ const base = ["--pid", String(pid)];
2754
+ return {
2755
+ async tree() {
2756
+ return execBridge(bridgePath, [...base, "tree"]);
2757
+ },
2758
+ async action(nodePath, actionName) {
2759
+ return execBridge(bridgePath, [...base, "action", JSON.stringify(nodePath), actionName]);
2760
+ },
2761
+ async setValue(nodePath, value) {
2762
+ return execBridge(bridgePath, [...base, "set-value", JSON.stringify(nodePath), value]);
2763
+ },
2764
+ async type(text) {
2765
+ return execBridge(bridgePath, [...base, "type", text]);
2766
+ },
2767
+ async press(key) {
2768
+ return execBridge(bridgePath, [...base, "press", key]);
2769
+ },
2770
+ async screenshot(outputPath) {
2771
+ return execBridge(bridgePath, [...base, "screenshot", outputPath]);
2772
+ },
2773
+ async state() {
2774
+ return execBridge(bridgePath, [...base, "state"]);
2775
+ }
2776
+ };
2777
+ }
2778
+ var import_child_process8, path11, fs11, import_url5, __filename_bridge2, __dirname_bridge;
2779
+ var init_bridge3 = __esm({
2780
+ "src/app/macos/bridge.ts"() {
2781
+ "use strict";
2782
+ import_child_process8 = require("child_process");
2783
+ path11 = __toESM(require("path"), 1);
2784
+ fs11 = __toESM(require("fs"), 1);
2785
+ import_url5 = require("url");
2786
+ __filename_bridge2 = (0, import_url5.fileURLToPath)(__import_meta_url);
2787
+ __dirname_bridge = path11.dirname(__filename_bridge2);
2788
+ }
2789
+ });
2790
+
2687
2791
  // src/enable.ts
2688
2792
  var enable_exports = {};
2689
2793
  __export(enable_exports, {
2690
2794
  handleEnable: () => handleEnable
2691
2795
  });
2796
+ function findPath(candidates) {
2797
+ return candidates.find((p) => fs12.existsSync(p)) || null;
2798
+ }
2692
2799
  async function enableAndroid() {
2693
2800
  log("Enabling Android...");
2694
- const { ensureAndroidBridge: ensureAndroidBridge2, AdbNotFoundError: AdbNotFoundError2, installAdb: installAdb2 } = await Promise.resolve().then(() => (init_bridge(), bridge_exports));
2695
- try {
2696
- await ensureAndroidBridge2();
2697
- log("adb: already available");
2698
- } catch (err) {
2699
- if (err instanceof AdbNotFoundError2) {
2700
- const ok = await installAdb2(log);
2701
- if (!ok) throw new Error("Failed to install adb");
2702
- }
2801
+ const prebuiltApk = findPath([
2802
+ path12.resolve(__dirname_enable, "../bin/browse-android.apk"),
2803
+ path12.resolve(__dirname_enable, "../../bin/browse-android.apk"),
2804
+ path12.resolve(__dirname_enable, "browse-android.apk")
2805
+ ]);
2806
+ if (prebuiltApk) {
2807
+ log(`Driver APK: ${prebuiltApk}`);
2808
+ log("Android ready (pre-built).");
2809
+ return;
2703
2810
  }
2704
- const { ensureJavaHome: ensureJavaHome2, findSdkRoot: findSdkRoot2, installSdk: installSdk2, installSystemImage: installSystemImage2, createAvd: createAvd2 } = await Promise.resolve().then(() => (init_emulator(), emulator_exports));
2705
- ensureJavaHome2();
2811
+ log("Pre-built APK not found, building from source...");
2812
+ const { ensureJavaHome: ensureJavaHome2, findSdkRoot: findSdkRoot2, installSdk: installSdk2, createAvd: createAvd2 } = await Promise.resolve().then(() => (init_emulator(), emulator_exports));
2813
+ const { installAdb: installAdb2 } = await Promise.resolve().then(() => (init_bridge(), bridge_exports));
2706
2814
  try {
2707
- (0, import_child_process8.execSync)("java -version", { stdio: "ignore", timeout: 5e3 });
2708
- log("Java: available");
2815
+ (0, import_child_process9.execSync)("adb version", { stdio: "ignore", timeout: 5e3 });
2816
+ log("adb: available");
2709
2817
  } catch {
2710
- log("Java not found. Run: brew install openjdk@21");
2818
+ log("Installing adb...");
2819
+ await installAdb2(log);
2820
+ }
2821
+ ensureJavaHome2();
2822
+ const hasJava = () => {
2823
+ try {
2824
+ (0, import_child_process9.execSync)("java -version", { stdio: "ignore", timeout: 5e3 });
2825
+ return true;
2826
+ } catch {
2827
+ return false;
2828
+ }
2829
+ };
2830
+ if (!hasJava() && process.platform === "darwin") {
2831
+ log("Installing Java (JDK 21)...");
2832
+ try {
2833
+ (0, import_child_process9.execSync)("brew install openjdk@21", { stdio: ["ignore", "pipe", "pipe"], timeout: 3e5 });
2834
+ } catch {
2835
+ }
2836
+ ensureJavaHome2();
2711
2837
  }
2838
+ if (!hasJava()) throw new Error("Java not found. Install: brew install openjdk@21");
2839
+ log("Java: available");
2712
2840
  let sdkRoot = findSdkRoot2();
2713
- if (!sdkRoot) {
2714
- sdkRoot = await installSdk2(log);
2715
- }
2716
- if (sdkRoot) {
2717
- log(`SDK: ${sdkRoot}`);
2718
- const sdkMgr = [
2719
- path11.join(sdkRoot, "cmdline-tools/latest/bin/sdkmanager"),
2720
- path11.join(sdkRoot, "bin/sdkmanager")
2721
- ].find((p) => fs11.existsSync(p));
2722
- if (sdkMgr) {
2841
+ if (!sdkRoot) sdkRoot = await installSdk2(log);
2842
+ if (!sdkRoot) throw new Error("Android SDK not found.");
2843
+ log(`SDK: ${sdkRoot}`);
2844
+ const sdkMgr = findPath([
2845
+ path12.join(sdkRoot, "cmdline-tools/latest/bin/sdkmanager"),
2846
+ path12.join(sdkRoot, "bin/sdkmanager")
2847
+ ]);
2848
+ if (sdkMgr) {
2849
+ try {
2850
+ (0, import_child_process9.execSync)(`yes | "${sdkMgr}" --licenses`, { stdio: "ignore", timeout: 6e4, shell: "/bin/bash" });
2851
+ } catch {
2852
+ }
2853
+ for (const comp of ["emulator", "platform-tools", "platforms;android-35", "build-tools;35.0.0", "system-images;android-35;google_apis;arm64-v8a"]) {
2723
2854
  try {
2724
- (0, import_child_process8.execSync)(`yes | "${sdkMgr}" --licenses`, { stdio: "ignore", timeout: 6e4, shell: "/bin/bash" });
2855
+ (0, import_child_process9.execSync)(`"${sdkMgr}" --install "${comp}"`, { stdio: "ignore", timeout: 3e5 });
2725
2856
  } catch {
2726
2857
  }
2727
- const components = ["emulator", "platform-tools", "platforms;android-35", "build-tools;35.0.0", "system-images;android-35;google_apis;arm64-v8a"];
2728
- for (const comp of components) {
2729
- try {
2730
- (0, import_child_process8.execSync)(`"${sdkMgr}" --install "${comp}"`, { stdio: "ignore", timeout: 3e5 });
2731
- } catch {
2732
- }
2733
- }
2734
- log("SDK components: installed");
2735
- const avdMgr = [
2736
- path11.join(sdkRoot, "cmdline-tools/latest/bin/avdmanager"),
2737
- path11.join(sdkRoot, "bin/avdmanager")
2738
- ].find((p) => fs11.existsSync(p));
2739
- if (avdMgr) {
2740
- try {
2741
- const avds = (0, import_child_process8.execSync)(`"${avdMgr}" list avd -c`, { encoding: "utf-8", timeout: 1e4, stdio: ["ignore", "pipe", "pipe"] }).trim();
2742
- if (!avds.includes("browse_default")) {
2743
- createAvd2(sdkRoot, avdMgr, log);
2744
- } else {
2745
- log("AVD browse_default: exists");
2746
- }
2747
- } catch {
2748
- }
2858
+ }
2859
+ log("SDK components: installed");
2860
+ }
2861
+ const avdMgr = findPath([
2862
+ path12.join(sdkRoot, "cmdline-tools/latest/bin/avdmanager"),
2863
+ path12.join(sdkRoot, "bin/avdmanager")
2864
+ ]);
2865
+ if (avdMgr) {
2866
+ try {
2867
+ const avds = (0, import_child_process9.execSync)(`"${avdMgr}" list avd -c`, { encoding: "utf-8", timeout: 1e4, stdio: ["ignore", "pipe", "pipe"] }).trim();
2868
+ if (!avds.includes("browse_default")) {
2869
+ createAvd2(sdkRoot, avdMgr, log);
2870
+ } else {
2871
+ log("AVD: browse_default exists");
2749
2872
  }
2873
+ } catch {
2750
2874
  }
2751
2875
  }
2752
- const driverDir = path11.resolve(__dirname_enable, "../browse-android");
2753
- const apkPath = path11.join(driverDir, "app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk");
2754
- if (!fs11.existsSync(apkPath) && fs11.existsSync(path11.join(driverDir, "gradlew"))) {
2755
- const { findSdkRoot: findSdkRoot3, ensureJavaHome: ensureJavaHome3 } = await Promise.resolve().then(() => (init_emulator(), emulator_exports));
2756
- ensureJavaHome3();
2757
- const sdkRoot2 = findSdkRoot3();
2758
- if (sdkRoot2) process.env.ANDROID_HOME = sdkRoot2;
2876
+ const driverDir = findPath([
2877
+ path12.resolve(__dirname_enable, "../browse-android"),
2878
+ path12.resolve(__dirname_enable, "../../browse-android")
2879
+ ].filter((d) => fs12.existsSync(path12.join(d, "gradlew"))));
2880
+ if (driverDir) {
2881
+ if (!process.env.ANDROID_HOME) process.env.ANDROID_HOME = sdkRoot;
2759
2882
  log("Building Android driver APK...");
2760
2883
  try {
2761
- (0, import_child_process8.execSync)("./gradlew :app:assembleDebug :app:assembleDebugAndroidTest --no-daemon", {
2884
+ (0, import_child_process9.execSync)("./gradlew :app:assembleDebug :app:assembleDebugAndroidTest --no-daemon", {
2762
2885
  cwd: driverDir,
2763
2886
  stdio: ["ignore", "pipe", "pipe"],
2764
2887
  timeout: 3e5
2765
2888
  });
2766
- log("Android driver APK built.");
2889
+ log("APK built.");
2767
2890
  } catch (err) {
2768
2891
  log(`APK build failed: ${err.message?.split("\n")[0]}`);
2769
2892
  }
2770
- } else if (fs11.existsSync(apkPath)) {
2771
- log("Android driver APK: already built");
2772
2893
  }
2773
2894
  log("Android enabled.");
2774
2895
  }
2775
2896
  async function enableIOS() {
2776
2897
  log("Enabling iOS...");
2777
- if (process.platform !== "darwin") {
2778
- throw new Error("iOS requires macOS with Xcode installed.");
2779
- }
2898
+ if (process.platform !== "darwin") throw new Error("iOS requires macOS with Xcode.");
2780
2899
  try {
2781
- (0, import_child_process8.execSync)("xcodebuild -version", { stdio: "pipe", timeout: 1e4 });
2900
+ (0, import_child_process9.execSync)("xcodebuild -version", { stdio: "pipe", timeout: 1e4 });
2782
2901
  log("Xcode: available");
2783
2902
  } catch {
2784
- throw new Error("Xcode not found. Install from the App Store or: xcode-select --install");
2903
+ throw new Error("Xcode not found. Install from the App Store.");
2785
2904
  }
2786
2905
  try {
2787
- (0, import_child_process8.execSync)("which xcodegen", { stdio: "pipe", timeout: 5e3 });
2788
- log("xcodegen: available");
2906
+ (0, import_child_process9.execSync)("which xcodegen", { stdio: "pipe", timeout: 5e3 });
2789
2907
  } catch {
2790
2908
  log("Installing xcodegen...");
2791
2909
  try {
2792
- (0, import_child_process8.execSync)("brew install xcodegen", { stdio: ["ignore", "pipe", "pipe"], timeout: 12e4 });
2910
+ (0, import_child_process9.execSync)("brew install xcodegen", { stdio: ["ignore", "pipe", "pipe"], timeout: 12e4 });
2793
2911
  } catch {
2794
2912
  throw new Error("xcodegen not found. Install: brew install xcodegen");
2795
2913
  }
2796
2914
  }
2797
- const runnerDir = path11.resolve(__dirname_enable, "../browse-ios-runner");
2798
- if (fs11.existsSync(path11.join(runnerDir, "project.yml"))) {
2799
- if (!fs11.existsSync(path11.join(runnerDir, "BrowseRunner.xcodeproj", "project.pbxproj"))) {
2800
- log("Generating Xcode project...");
2801
- (0, import_child_process8.execSync)("xcodegen generate --spec project.yml", { cwd: runnerDir, stdio: "pipe" });
2802
- }
2803
- const { resolveSimulator: resolveSimulator2 } = await Promise.resolve().then(() => (init_controller(), controller_exports));
2804
- let udid;
2805
- try {
2806
- const sim = await resolveSimulator2();
2807
- udid = sim.udid;
2808
- } catch {
2809
- log("No iOS Simulator found. Skipping pre-build (will build on first sim start).");
2810
- log("iOS enabled (source ready).");
2811
- return;
2812
- }
2915
+ log("xcodegen: available");
2916
+ const runnerDir = findPath([
2917
+ path12.resolve(__dirname_enable, "../browse-ios-runner"),
2918
+ path12.resolve(__dirname_enable, "../../browse-ios-runner"),
2919
+ path12.resolve(__dirname_enable, "../bin/browse-ios-runner")
2920
+ ].filter((d) => fs12.existsSync(path12.join(d, "project.yml"))));
2921
+ if (!runnerDir) {
2922
+ throw new Error("iOS runner source not found. Reinstall: npm install -g @ulpi/browse");
2923
+ }
2924
+ if (!fs12.existsSync(path12.join(runnerDir, "BrowseRunner.xcodeproj", "project.pbxproj"))) {
2925
+ log("Generating Xcode project...");
2926
+ (0, import_child_process9.execSync)("xcodegen generate --spec project.yml", { cwd: runnerDir, stdio: "pipe" });
2927
+ }
2928
+ const { resolveSimulator: resolveSimulator2 } = await Promise.resolve().then(() => (init_controller(), controller_exports));
2929
+ try {
2930
+ const sim = await resolveSimulator2();
2813
2931
  log("Building iOS runner...");
2814
- (0, import_child_process8.execSync)(
2815
- `xcodebuild build-for-testing -project BrowseRunner.xcodeproj -scheme BrowseRunnerApp -sdk iphonesimulator -destination "id=${udid}" -derivedDataPath .build CODE_SIGN_IDENTITY="" CODE_SIGNING_ALLOWED=NO -quiet`,
2932
+ (0, import_child_process9.execSync)(
2933
+ `xcodebuild build-for-testing -project BrowseRunner.xcodeproj -scheme BrowseRunnerApp -sdk iphonesimulator -destination "id=${sim.udid}" -derivedDataPath .build CODE_SIGN_IDENTITY="" CODE_SIGNING_ALLOWED=NO -quiet`,
2816
2934
  { cwd: runnerDir, stdio: "pipe", timeout: 12e4 }
2817
2935
  );
2818
2936
  log("iOS runner built.");
2937
+ } catch {
2938
+ log("No simulator found. Runner will build on first sim start.");
2819
2939
  }
2820
2940
  log("iOS enabled.");
2821
2941
  }
2822
2942
  async function enableMacOS() {
2823
2943
  log("Enabling macOS...");
2824
- if (process.platform !== "darwin") {
2825
- throw new Error("macOS app automation requires macOS.");
2944
+ if (process.platform !== "darwin") throw new Error("macOS app automation requires macOS.");
2945
+ const { resolveBridgePath: resolveBridgePath2 } = await Promise.resolve().then(() => (init_bridge3(), bridge_exports3));
2946
+ try {
2947
+ const existing = resolveBridgePath2();
2948
+ log(`browse-ax: ${existing}`);
2949
+ log("macOS enabled.");
2950
+ return;
2951
+ } catch {
2826
2952
  }
2827
- const axDir = path11.resolve(__dirname_enable, "../browse-ax");
2828
- const axBin = path11.join(axDir, ".build/release/browse-ax");
2829
- if (fs11.existsSync(path11.join(axDir, "Package.swift"))) {
2830
- if (fs11.existsSync(axBin)) {
2831
- log("browse-ax: already built");
2832
- } else {
2833
- log("Building browse-ax...");
2834
- (0, import_child_process8.execSync)("swift build -c release", { cwd: axDir, stdio: ["ignore", "pipe", "pipe"], timeout: 12e4 });
2835
- log("browse-ax built.");
2836
- }
2953
+ const axDir = findPath([
2954
+ path12.resolve(__dirname_enable, "../browse-ax"),
2955
+ path12.resolve(__dirname_enable, "../../browse-ax")
2956
+ ].filter((d) => fs12.existsSync(path12.join(d, "Package.swift"))));
2957
+ if (axDir) {
2958
+ log("Building browse-ax...");
2959
+ (0, import_child_process9.execSync)("swift build -c release", { cwd: axDir, stdio: ["ignore", "pipe", "pipe"], timeout: 12e4 });
2960
+ log("browse-ax built.");
2837
2961
  } else {
2838
- throw new Error("browse-ax source not found.");
2962
+ throw new Error("browse-ax not found. Reinstall: npm install -g @ulpi/browse");
2839
2963
  }
2840
2964
  log("macOS enabled.");
2841
2965
  }
@@ -2844,8 +2968,8 @@ async function handleEnable(args) {
2844
2968
  if (!platform || platform === "--help") {
2845
2969
  console.log("Usage: browse enable android|ios|macos|all");
2846
2970
  console.log("");
2847
- console.log("Downloads dependencies and builds native drivers for each platform.");
2848
- console.log("Run once \u2014 everything is cached for future use.");
2971
+ console.log("Verifies native drivers are ready. Pre-built binaries ship with npm install.");
2972
+ console.log("If missing (building from source), installs dependencies and builds them.");
2849
2973
  return;
2850
2974
  }
2851
2975
  const platforms = platform === "all" ? ["android", "ios", "macos"] : [platform];
@@ -2872,15 +2996,15 @@ async function handleEnable(args) {
2872
2996
  console.log("");
2873
2997
  }
2874
2998
  }
2875
- var import_child_process8, fs11, path11, import_url5, __dirname_enable, log;
2999
+ var import_child_process9, fs12, path12, import_url6, __dirname_enable, log;
2876
3000
  var init_enable = __esm({
2877
3001
  "src/enable.ts"() {
2878
3002
  "use strict";
2879
- import_child_process8 = require("child_process");
2880
- fs11 = __toESM(require("fs"), 1);
2881
- path11 = __toESM(require("path"), 1);
2882
- import_url5 = require("url");
2883
- __dirname_enable = path11.dirname((0, import_url5.fileURLToPath)(__import_meta_url));
3003
+ import_child_process9 = require("child_process");
3004
+ fs12 = __toESM(require("fs"), 1);
3005
+ path12 = __toESM(require("path"), 1);
3006
+ import_url6 = require("url");
3007
+ __dirname_enable = path12.dirname((0, import_url6.fileURLToPath)(__import_meta_url));
2884
3008
  log = (msg) => process.stderr.write(`[browse] ${msg}
2885
3009
  `);
2886
3010
  }
@@ -3174,8 +3298,8 @@ async function handleReadCommand(command, args, bm, buffers) {
3174
3298
  case "eval": {
3175
3299
  const filePath = args[0];
3176
3300
  if (!filePath) throw new Error("Usage: browse eval <js-file>");
3177
- if (!fs12.existsSync(filePath)) throw new Error(`File not found: ${filePath}`);
3178
- const code = fs12.readFileSync(filePath, "utf-8");
3301
+ if (!fs13.existsSync(filePath)) throw new Error(`File not found: ${filePath}`);
3302
+ const code = fs13.readFileSync(filePath, "utf-8");
3179
3303
  const result = await evalCtx.evaluate(code);
3180
3304
  return typeof result === "object" ? JSON.stringify(result, null, 2) : String(result ?? "");
3181
3305
  }
@@ -3515,13 +3639,13 @@ function registerReadDefinitions(registry4) {
3515
3639
  });
3516
3640
  }
3517
3641
  }
3518
- var fs12;
3642
+ var fs13;
3519
3643
  var init_read = __esm({
3520
3644
  "src/commands/read.ts"() {
3521
3645
  "use strict";
3522
3646
  init_emulation();
3523
3647
  init_constants();
3524
- fs12 = __toESM(require("fs"), 1);
3648
+ fs13 = __toESM(require("fs"), 1);
3525
3649
  }
3526
3650
  });
3527
3651
 
@@ -4213,14 +4337,14 @@ async function handleWriteCommand(command, args, bm, domainFilter) {
4213
4337
  const file = args[1];
4214
4338
  if (!file) throw new Error("Usage: browse cookie export <file>");
4215
4339
  const cookies = await page.context().cookies();
4216
- fs13.writeFileSync(file, JSON.stringify(cookies, null, 2));
4340
+ fs14.writeFileSync(file, JSON.stringify(cookies, null, 2));
4217
4341
  return `Exported ${cookies.length} cookie(s) to ${file}`;
4218
4342
  }
4219
4343
  if (cookieStr === "import") {
4220
4344
  const file = args[1];
4221
4345
  if (!file) throw new Error("Usage: browse cookie import <file>");
4222
- if (!fs13.existsSync(file)) throw new Error(`File not found: ${file}`);
4223
- const cookies = JSON.parse(fs13.readFileSync(file, "utf-8"));
4346
+ if (!fs14.existsSync(file)) throw new Error(`File not found: ${file}`);
4347
+ const cookies = JSON.parse(fs14.readFileSync(file, "utf-8"));
4224
4348
  if (!Array.isArray(cookies)) throw new Error("Cookie file must contain a JSON array of cookie objects");
4225
4349
  await page.context().addCookies(cookies);
4226
4350
  return `Imported ${cookies.length} cookie(s) from ${file}`;
@@ -4287,7 +4411,7 @@ Note: Cookies and tab URLs preserved. localStorage/sessionStorage were reset (Pl
4287
4411
  const [selector, ...filePaths] = args;
4288
4412
  if (!selector || filePaths.length === 0) throw new Error("Usage: browse upload <selector> <file1> [file2] ...");
4289
4413
  for (const fp of filePaths) {
4290
- if (!fs13.existsSync(fp)) throw new Error(`File not found: ${fp}`);
4414
+ if (!fs14.existsSync(fp)) throw new Error(`File not found: ${fp}`);
4291
4415
  }
4292
4416
  const resolved = bm.resolveRef(selector);
4293
4417
  if ("locator" in resolved) {
@@ -4747,13 +4871,13 @@ function registerWriteDefinitions(registry4) {
4747
4871
  });
4748
4872
  }
4749
4873
  }
4750
- var fs13;
4874
+ var fs14;
4751
4875
  var init_write = __esm({
4752
4876
  "src/commands/write.ts"() {
4753
4877
  "use strict";
4754
4878
  init_emulation();
4755
4879
  init_constants();
4756
- fs13 = __toESM(require("fs"), 1);
4880
+ fs14 = __toESM(require("fs"), 1);
4757
4881
  }
4758
4882
  });
4759
4883
 
@@ -5127,13 +5251,13 @@ ${legend.join("\n")}`;
5127
5251
  const diffArgs = args.filter((a) => a !== "--full");
5128
5252
  const baseline = diffArgs[0];
5129
5253
  if (!baseline) throw new Error("Usage: browse screenshot-diff <baseline> [current] [--threshold 0.1] [--full]");
5130
- if (!fs14.existsSync(baseline)) throw new Error(`Baseline file not found: ${baseline}`);
5254
+ if (!fs15.existsSync(baseline)) throw new Error(`Baseline file not found: ${baseline}`);
5131
5255
  let thresholdPct = 0.1;
5132
5256
  const threshIdx = diffArgs.indexOf("--threshold");
5133
5257
  if (threshIdx !== -1 && diffArgs[threshIdx + 1]) {
5134
5258
  thresholdPct = parseFloat(diffArgs[threshIdx + 1]);
5135
5259
  }
5136
- const baselineBuffer = fs14.readFileSync(baseline);
5260
+ const baselineBuffer = fs15.readFileSync(baseline);
5137
5261
  let currentBuffer;
5138
5262
  let currentPath;
5139
5263
  for (let i = 1; i < diffArgs.length; i++) {
@@ -5147,8 +5271,8 @@ ${legend.join("\n")}`;
5147
5271
  }
5148
5272
  }
5149
5273
  if (currentPath) {
5150
- if (!fs14.existsSync(currentPath)) throw new Error(`Current screenshot not found: ${currentPath}`);
5151
- currentBuffer = fs14.readFileSync(currentPath);
5274
+ if (!fs15.existsSync(currentPath)) throw new Error(`Current screenshot not found: ${currentPath}`);
5275
+ currentBuffer = fs15.readFileSync(currentPath);
5152
5276
  } else {
5153
5277
  const page = bm.getPage();
5154
5278
  currentBuffer = await page.screenshot({ fullPage: isFullPageDiff });
@@ -5158,7 +5282,7 @@ ${legend.join("\n")}`;
5158
5282
  const extIdx = baseline.lastIndexOf(".");
5159
5283
  const diffPath = extIdx > 0 ? baseline.slice(0, extIdx) + "-diff" + baseline.slice(extIdx) : baseline + "-diff.png";
5160
5284
  if (!result.passed && result.diffImage) {
5161
- fs14.writeFileSync(diffPath, result.diffImage);
5285
+ fs15.writeFileSync(diffPath, result.diffImage);
5162
5286
  }
5163
5287
  return [
5164
5288
  `Pixels: ${result.totalPixels}`,
@@ -5173,11 +5297,11 @@ ${legend.join("\n")}`;
5173
5297
  throw new Error(`Unknown screenshots command: ${command}`);
5174
5298
  }
5175
5299
  }
5176
- var fs14, LOCAL_DIR;
5300
+ var fs15, LOCAL_DIR;
5177
5301
  var init_screenshots = __esm({
5178
5302
  "src/commands/meta/screenshots.ts"() {
5179
5303
  "use strict";
5180
- fs14 = __toESM(require("fs"), 1);
5304
+ fs15 = __toESM(require("fs"), 1);
5181
5305
  LOCAL_DIR = process.env.BROWSE_LOCAL_DIR || "/tmp";
5182
5306
  }
5183
5307
  });
@@ -12943,7 +13067,7 @@ async function handleRecordingCommand(command, args, target, currentSession) {
12943
13067
  throw new Error(`Unknown format: ${format}. Use "browse" (chain JSON), "replay" (Puppeteer), "playwright" (Playwright Test), or "flow" (YAML).`);
12944
13068
  }
12945
13069
  if (filePath) {
12946
- fs15.writeFileSync(filePath, output);
13070
+ fs16.writeFileSync(filePath, output);
12947
13071
  return `Exported ${steps.length} steps as ${format}: ${filePath}`;
12948
13072
  }
12949
13073
  return output;
@@ -12968,7 +13092,7 @@ async function handleRecordingCommand(command, args, target, currentSession) {
12968
13092
  const { formatAsHar: formatAsHar2 } = await Promise.resolve().then(() => (init_har(), har_exports));
12969
13093
  const har = formatAsHar2(sessionBuffers.networkBuffer, recording.startTime);
12970
13094
  const harPath = args[1] || (currentSession ? `${currentSession.outputDir}/recording.har` : `${LOCAL_DIR2}/browse-recording.har`);
12971
- fs15.writeFileSync(harPath, JSON.stringify(har, null, 2));
13095
+ fs16.writeFileSync(harPath, JSON.stringify(har, null, 2));
12972
13096
  const entryCount = har.log.entries.length;
12973
13097
  return `HAR saved: ${harPath} (${entryCount} entries)`;
12974
13098
  }
@@ -13004,11 +13128,11 @@ async function handleRecordingCommand(command, args, target, currentSession) {
13004
13128
  throw new Error(`Unknown recording command: ${command}`);
13005
13129
  }
13006
13130
  }
13007
- var fs15, LOCAL_DIR2;
13131
+ var fs16, LOCAL_DIR2;
13008
13132
  var init_recording = __esm({
13009
13133
  "src/commands/meta/recording.ts"() {
13010
13134
  "use strict";
13011
- fs15 = __toESM(require("fs"), 1);
13135
+ fs16 = __toESM(require("fs"), 1);
13012
13136
  LOCAL_DIR2 = process.env.BROWSE_LOCAL_DIR || "/tmp";
13013
13137
  }
13014
13138
  });
@@ -13032,20 +13156,20 @@ async function saveSessionState(sessionDir, context, encryptionKey) {
13032
13156
  } else {
13033
13157
  content = json;
13034
13158
  }
13035
- fs16.mkdirSync(sessionDir, { recursive: true });
13036
- fs16.writeFileSync(path12.join(sessionDir, STATE_FILENAME), content, { mode: 384 });
13159
+ fs17.mkdirSync(sessionDir, { recursive: true });
13160
+ fs17.writeFileSync(path13.join(sessionDir, STATE_FILENAME), content, { mode: 384 });
13037
13161
  } catch (err) {
13038
13162
  console.log(`[session-persist] Warning: failed to save state: ${err.message}`);
13039
13163
  }
13040
13164
  }
13041
13165
  async function loadSessionState(sessionDir, context, encryptionKey) {
13042
- const statePath = path12.join(sessionDir, STATE_FILENAME);
13043
- if (!fs16.existsSync(statePath)) {
13166
+ const statePath = path13.join(sessionDir, STATE_FILENAME);
13167
+ if (!fs17.existsSync(statePath)) {
13044
13168
  return false;
13045
13169
  }
13046
13170
  let stateData;
13047
13171
  try {
13048
- const raw = fs16.readFileSync(statePath, "utf-8");
13172
+ const raw = fs17.readFileSync(statePath, "utf-8");
13049
13173
  const parsed = JSON.parse(raw);
13050
13174
  if (parsed.encrypted) {
13051
13175
  if (!encryptionKey) {
@@ -13101,24 +13225,24 @@ async function loadSessionState(sessionDir, context, encryptionKey) {
13101
13225
  }
13102
13226
  }
13103
13227
  function hasPersistedState(sessionDir) {
13104
- return fs16.existsSync(path12.join(sessionDir, STATE_FILENAME));
13228
+ return fs17.existsSync(path13.join(sessionDir, STATE_FILENAME));
13105
13229
  }
13106
13230
  function cleanOldStates(localDir, maxAgeDays) {
13107
13231
  const maxAgeMs = maxAgeDays * 24 * 60 * 60 * 1e3;
13108
13232
  const now = Date.now();
13109
13233
  let deleted = 0;
13110
- const statesDir = path12.join(localDir, "states");
13111
- if (fs16.existsSync(statesDir)) {
13234
+ const statesDir = path13.join(localDir, "states");
13235
+ if (fs17.existsSync(statesDir)) {
13112
13236
  try {
13113
- const entries = fs16.readdirSync(statesDir);
13237
+ const entries = fs17.readdirSync(statesDir);
13114
13238
  for (const entry of entries) {
13115
13239
  if (!entry.endsWith(".json")) continue;
13116
- const filePath = path12.join(statesDir, entry);
13240
+ const filePath = path13.join(statesDir, entry);
13117
13241
  try {
13118
- const stat = fs16.statSync(filePath);
13242
+ const stat = fs17.statSync(filePath);
13119
13243
  if (!stat.isFile()) continue;
13120
13244
  if (now - stat.mtimeMs > maxAgeMs) {
13121
- fs16.unlinkSync(filePath);
13245
+ fs17.unlinkSync(filePath);
13122
13246
  deleted++;
13123
13247
  }
13124
13248
  } catch (_) {
@@ -13127,23 +13251,23 @@ function cleanOldStates(localDir, maxAgeDays) {
13127
13251
  } catch (_) {
13128
13252
  }
13129
13253
  }
13130
- const sessionsDir = path12.join(localDir, "sessions");
13131
- if (fs16.existsSync(sessionsDir)) {
13254
+ const sessionsDir = path13.join(localDir, "sessions");
13255
+ if (fs17.existsSync(sessionsDir)) {
13132
13256
  try {
13133
- const sessionDirs = fs16.readdirSync(sessionsDir);
13257
+ const sessionDirs = fs17.readdirSync(sessionsDir);
13134
13258
  for (const dir of sessionDirs) {
13135
- const dirPath = path12.join(sessionsDir, dir);
13259
+ const dirPath = path13.join(sessionsDir, dir);
13136
13260
  try {
13137
- const dirStat = fs16.statSync(dirPath);
13261
+ const dirStat = fs17.statSync(dirPath);
13138
13262
  if (!dirStat.isDirectory()) continue;
13139
13263
  } catch (_) {
13140
13264
  continue;
13141
13265
  }
13142
- const statePath = path12.join(dirPath, STATE_FILENAME);
13266
+ const statePath = path13.join(dirPath, STATE_FILENAME);
13143
13267
  try {
13144
- const stat = fs16.statSync(statePath);
13268
+ const stat = fs17.statSync(statePath);
13145
13269
  if (now - stat.mtimeMs > maxAgeMs) {
13146
- fs16.unlinkSync(statePath);
13270
+ fs17.unlinkSync(statePath);
13147
13271
  deleted++;
13148
13272
  }
13149
13273
  } catch (_) {
@@ -13154,12 +13278,12 @@ function cleanOldStates(localDir, maxAgeDays) {
13154
13278
  }
13155
13279
  return { deleted };
13156
13280
  }
13157
- var fs16, path12, STATE_FILENAME;
13281
+ var fs17, path13, STATE_FILENAME;
13158
13282
  var init_persist = __esm({
13159
13283
  "src/session/persist.ts"() {
13160
13284
  "use strict";
13161
- fs16 = __toESM(require("fs"), 1);
13162
- path12 = __toESM(require("path"), 1);
13285
+ fs17 = __toESM(require("fs"), 1);
13286
+ path13 = __toESM(require("path"), 1);
13163
13287
  init_encryption();
13164
13288
  STATE_FILENAME = "state.json";
13165
13289
  }
@@ -13594,7 +13718,7 @@ async function handleSessionsCommand(command, args, bm, sessionManager2, current
13594
13718
  const lines = entries.map(
13595
13719
  (e) => `[${new Date(e.timestamp).toISOString()}] [${e.level}] ${e.text}`
13596
13720
  ).join("\n") + "\n";
13597
- fs17.appendFileSync(consolePath, lines);
13721
+ fs18.appendFileSync(consolePath, lines);
13598
13722
  buffers.lastConsoleFlushed = buffers.consoleTotalAdded;
13599
13723
  }
13600
13724
  const newNetworkCount = buffers.networkTotalAdded - buffers.lastNetworkFlushed;
@@ -13604,7 +13728,7 @@ async function handleSessionsCommand(command, args, bm, sessionManager2, current
13604
13728
  const lines = entries.map(
13605
13729
  (e) => `[${new Date(e.timestamp).toISOString()}] ${e.method} ${e.url} \u2192 ${e.status || "pending"} (${e.duration || "?"}ms, ${e.size || "?"}B)`
13606
13730
  ).join("\n") + "\n";
13607
- fs17.appendFileSync(networkPath, lines);
13731
+ fs18.appendFileSync(networkPath, lines);
13608
13732
  buffers.lastNetworkFlushed = buffers.networkTotalAdded;
13609
13733
  }
13610
13734
  }
@@ -13620,22 +13744,22 @@ async function handleSessionsCommand(command, args, bm, sessionManager2, current
13620
13744
  const statesDir = `${LOCAL_DIR3}/states`;
13621
13745
  const statePath = `${statesDir}/${name}.json`;
13622
13746
  if (subcommand === "list") {
13623
- if (!fs17.existsSync(statesDir)) return "(no saved states)";
13624
- const files = fs17.readdirSync(statesDir).filter((f) => f.endsWith(".json"));
13747
+ if (!fs18.existsSync(statesDir)) return "(no saved states)";
13748
+ const files = fs18.readdirSync(statesDir).filter((f) => f.endsWith(".json"));
13625
13749
  if (files.length === 0) return "(no saved states)";
13626
13750
  const lines = [];
13627
13751
  for (const file of files) {
13628
13752
  const fp = `${statesDir}/${file}`;
13629
- const stat = fs17.statSync(fp);
13753
+ const stat = fs18.statSync(fp);
13630
13754
  lines.push(` ${file.replace(".json", "")} ${stat.size}B ${new Date(stat.mtimeMs).toISOString()}`);
13631
13755
  }
13632
13756
  return lines.join("\n");
13633
13757
  }
13634
13758
  if (subcommand === "show") {
13635
- if (!fs17.existsSync(statePath)) {
13759
+ if (!fs18.existsSync(statePath)) {
13636
13760
  throw new Error(`State file not found: ${statePath}`);
13637
13761
  }
13638
- const data = JSON.parse(fs17.readFileSync(statePath, "utf-8"));
13762
+ const data = JSON.parse(fs18.readFileSync(statePath, "utf-8"));
13639
13763
  const cookieCount = data.cookies?.length || 0;
13640
13764
  const originCount = data.origins?.length || 0;
13641
13765
  const storageItems = (data.origins || []).reduce((sum, o) => sum + (o.localStorage?.length || 0), 0);
@@ -13660,15 +13784,15 @@ async function handleSessionsCommand(command, args, bm, sessionManager2, current
13660
13784
  const context = bm.getContext();
13661
13785
  if (!context) throw new Error("No browser context");
13662
13786
  const state = await context.storageState();
13663
- fs17.mkdirSync(statesDir, { recursive: true });
13664
- fs17.writeFileSync(statePath, JSON.stringify(state, null, 2), { mode: 384 });
13787
+ fs18.mkdirSync(statesDir, { recursive: true });
13788
+ fs18.writeFileSync(statePath, JSON.stringify(state, null, 2), { mode: 384 });
13665
13789
  return `State saved: ${statePath}`;
13666
13790
  }
13667
13791
  if (subcommand === "load") {
13668
- if (!fs17.existsSync(statePath)) {
13792
+ if (!fs18.existsSync(statePath)) {
13669
13793
  throw new Error(`State file not found: ${statePath}. Run "browse state save ${name}" first.`);
13670
13794
  }
13671
- const stateData = JSON.parse(fs17.readFileSync(statePath, "utf-8"));
13795
+ const stateData = JSON.parse(fs18.readFileSync(statePath, "utf-8"));
13672
13796
  const context = bm.getContext();
13673
13797
  if (!context) throw new Error("No browser context");
13674
13798
  const warnings = [];
@@ -13721,12 +13845,12 @@ ${snapshot}`;
13721
13845
  throw new Error(`Unknown sessions command: ${command}`);
13722
13846
  }
13723
13847
  }
13724
- var fs17, LOCAL_DIR3;
13848
+ var fs18, LOCAL_DIR3;
13725
13849
  var init_sessions = __esm({
13726
13850
  "src/commands/meta/sessions.ts"() {
13727
13851
  "use strict";
13728
13852
  init_sanitize();
13729
- fs17 = __toESM(require("fs"), 1);
13853
+ fs18 = __toESM(require("fs"), 1);
13730
13854
  LOCAL_DIR3 = process.env.BROWSE_LOCAL_DIR || "/tmp";
13731
13855
  }
13732
13856
  });
@@ -14129,7 +14253,7 @@ var init_lib = __esm({
14129
14253
  tokenize: function tokenize(value) {
14130
14254
  return Array.from(value);
14131
14255
  },
14132
- join: function join13(chars) {
14256
+ join: function join14(chars) {
14133
14257
  return chars.join("");
14134
14258
  },
14135
14259
  postProcess: function postProcess(changeObjects) {
@@ -19690,19 +19814,19 @@ __export(detection_exports, {
19690
19814
  detectStack: () => detectStack
19691
19815
  });
19692
19816
  function loadCustomSignatures(localDir) {
19693
- const detectionDir = path13.join(localDir, "detections");
19694
- if (!fs18.existsSync(detectionDir)) return [];
19817
+ const detectionDir = path14.join(localDir, "detections");
19818
+ if (!fs19.existsSync(detectionDir)) return [];
19695
19819
  const sigs = [];
19696
19820
  let entries;
19697
19821
  try {
19698
- entries = fs18.readdirSync(detectionDir).filter((f) => f.endsWith(".json"));
19822
+ entries = fs19.readdirSync(detectionDir).filter((f) => f.endsWith(".json"));
19699
19823
  } catch {
19700
19824
  return [];
19701
19825
  }
19702
19826
  for (const entry of entries) {
19703
- const filePath = path13.join(detectionDir, entry);
19827
+ const filePath = path14.join(detectionDir, entry);
19704
19828
  try {
19705
- const raw = fs18.readFileSync(filePath, "utf-8");
19829
+ const raw = fs19.readFileSync(filePath, "utf-8");
19706
19830
  const parsed = JSON.parse(raw);
19707
19831
  if (parsed == null || typeof parsed !== "object" || parsed.version !== 1 || typeof parsed.name !== "string" || typeof parsed.detect !== "string") {
19708
19832
  process.stderr.write(
@@ -19828,15 +19952,15 @@ async function detectStack(page, networkEntries, localDir) {
19828
19952
  const thirdParty = buildThirdPartyInventory(page, networkEntries ?? []);
19829
19953
  return { frameworks, saas, infrastructure, thirdParty, custom };
19830
19954
  }
19831
- var fs18, path13, THIRD_PARTY_DOMAINS;
19955
+ var fs19, path14, THIRD_PARTY_DOMAINS;
19832
19956
  var init_detection = __esm({
19833
19957
  "src/detection/index.ts"() {
19834
19958
  "use strict";
19835
19959
  init_frameworks();
19836
19960
  init_saas();
19837
19961
  init_infrastructure();
19838
- fs18 = __toESM(require("fs"), 1);
19839
- path13 = __toESM(require("path"), 1);
19962
+ fs19 = __toESM(require("fs"), 1);
19963
+ path14 = __toESM(require("path"), 1);
19840
19964
  THIRD_PARTY_DOMAINS = {
19841
19965
  // Analytics
19842
19966
  "google-analytics.com": "analytics",
@@ -21918,35 +22042,35 @@ __export(persist_exports2, {
21918
22042
  saveAudit: () => saveAudit
21919
22043
  });
21920
22044
  function auditsDir(localDir) {
21921
- return path14.join(localDir, "audits");
22045
+ return path15.join(localDir, "audits");
21922
22046
  }
21923
22047
  function auditPath(localDir, name) {
21924
- return path14.join(auditsDir(localDir), `${sanitizeName(name)}.json`);
22048
+ return path15.join(auditsDir(localDir), `${sanitizeName(name)}.json`);
21925
22049
  }
21926
22050
  function saveAudit(localDir, name, report) {
21927
22051
  const dir = auditsDir(localDir);
21928
- fs19.mkdirSync(dir, { recursive: true });
22052
+ fs20.mkdirSync(dir, { recursive: true });
21929
22053
  const filePath = auditPath(localDir, name);
21930
- fs19.writeFileSync(filePath, JSON.stringify(report, null, 2), { mode: 384 });
22054
+ fs20.writeFileSync(filePath, JSON.stringify(report, null, 2), { mode: 384 });
21931
22055
  return filePath;
21932
22056
  }
21933
22057
  function loadAudit(localDir, name) {
21934
22058
  const filePath = auditPath(localDir, name);
21935
- if (!fs19.existsSync(filePath)) {
22059
+ if (!fs20.existsSync(filePath)) {
21936
22060
  throw new Error(
21937
22061
  `Audit not found: ${filePath}. Run "browse perf-audit save ${name}" first.`
21938
22062
  );
21939
22063
  }
21940
- return JSON.parse(fs19.readFileSync(filePath, "utf-8"));
22064
+ return JSON.parse(fs20.readFileSync(filePath, "utf-8"));
21941
22065
  }
21942
22066
  function listAudits(localDir) {
21943
22067
  const dir = auditsDir(localDir);
21944
- if (!fs19.existsSync(dir)) return [];
21945
- const files = fs19.readdirSync(dir).filter((f) => f.endsWith(".json"));
22068
+ if (!fs20.existsSync(dir)) return [];
22069
+ const files = fs20.readdirSync(dir).filter((f) => f.endsWith(".json"));
21946
22070
  if (files.length === 0) return [];
21947
22071
  const entries = files.map((file) => {
21948
- const fp = path14.join(dir, file);
21949
- const stat = fs19.statSync(fp);
22072
+ const fp = path15.join(dir, file);
22073
+ const stat = fs20.statSync(fp);
21950
22074
  return {
21951
22075
  name: file.replace(".json", ""),
21952
22076
  sizeBytes: stat.size,
@@ -21958,19 +22082,19 @@ function listAudits(localDir) {
21958
22082
  }
21959
22083
  function deleteAudit(localDir, name) {
21960
22084
  const filePath = auditPath(localDir, name);
21961
- if (!fs19.existsSync(filePath)) {
22085
+ if (!fs20.existsSync(filePath)) {
21962
22086
  throw new Error(
21963
22087
  `Audit not found: ${filePath}. Nothing to delete.`
21964
22088
  );
21965
22089
  }
21966
- fs19.unlinkSync(filePath);
22090
+ fs20.unlinkSync(filePath);
21967
22091
  }
21968
- var fs19, path14;
22092
+ var fs20, path15;
21969
22093
  var init_persist2 = __esm({
21970
22094
  "src/perf-audit/persist.ts"() {
21971
22095
  "use strict";
21972
- fs19 = __toESM(require("fs"), 1);
21973
- path14 = __toESM(require("path"), 1);
22096
+ fs20 = __toESM(require("fs"), 1);
22097
+ path15 = __toESM(require("path"), 1);
21974
22098
  init_sanitize();
21975
22099
  }
21976
22100
  });
@@ -23417,12 +23541,12 @@ async function autoDetectSelector(page, field) {
23417
23541
  }
23418
23542
  throw new Error("Could not auto-detect submit button.");
23419
23543
  }
23420
- var fs20, path15, AuthVault;
23544
+ var fs21, path16, AuthVault;
23421
23545
  var init_auth_vault = __esm({
23422
23546
  "src/security/auth-vault.ts"() {
23423
23547
  "use strict";
23424
- fs20 = __toESM(require("fs"), 1);
23425
- path15 = __toESM(require("path"), 1);
23548
+ fs21 = __toESM(require("fs"), 1);
23549
+ path16 = __toESM(require("path"), 1);
23426
23550
  init_constants();
23427
23551
  init_encryption();
23428
23552
  init_sanitize();
@@ -23430,11 +23554,11 @@ var init_auth_vault = __esm({
23430
23554
  authDir;
23431
23555
  encryptionKey;
23432
23556
  constructor(localDir) {
23433
- this.authDir = path15.join(localDir, "auth");
23557
+ this.authDir = path16.join(localDir, "auth");
23434
23558
  this.encryptionKey = resolveEncryptionKey(localDir);
23435
23559
  }
23436
23560
  save(name, url, username, password, selectors) {
23437
- fs20.mkdirSync(this.authDir, { recursive: true });
23561
+ fs21.mkdirSync(this.authDir, { recursive: true });
23438
23562
  const { ciphertext, iv, authTag } = encrypt(password, this.encryptionKey);
23439
23563
  const now = (/* @__PURE__ */ new Date()).toISOString();
23440
23564
  const credential = {
@@ -23451,15 +23575,15 @@ var init_auth_vault = __esm({
23451
23575
  createdAt: now,
23452
23576
  updatedAt: now
23453
23577
  };
23454
- const filePath = path15.join(this.authDir, `${sanitizeName(name)}.json`);
23455
- fs20.writeFileSync(filePath, JSON.stringify(credential, null, 2), { mode: 384 });
23578
+ const filePath = path16.join(this.authDir, `${sanitizeName(name)}.json`);
23579
+ fs21.writeFileSync(filePath, JSON.stringify(credential, null, 2), { mode: 384 });
23456
23580
  }
23457
23581
  load(name) {
23458
- const filePath = path15.join(this.authDir, `${sanitizeName(name)}.json`);
23459
- if (!fs20.existsSync(filePath)) {
23582
+ const filePath = path16.join(this.authDir, `${sanitizeName(name)}.json`);
23583
+ if (!fs21.existsSync(filePath)) {
23460
23584
  throw new Error(`Credential "${name}" not found. Run "browse auth list" to see saved credentials.`);
23461
23585
  }
23462
- return JSON.parse(fs20.readFileSync(filePath, "utf-8"));
23586
+ return JSON.parse(fs21.readFileSync(filePath, "utf-8"));
23463
23587
  }
23464
23588
  async login(name, bm) {
23465
23589
  const cred = this.load(name);
@@ -23480,11 +23604,11 @@ var init_auth_vault = __esm({
23480
23604
  return `Logged in as ${cred.username} at ${page.url()}`;
23481
23605
  }
23482
23606
  list() {
23483
- if (!fs20.existsSync(this.authDir)) return [];
23484
- const files = fs20.readdirSync(this.authDir).filter((f) => f.endsWith(".json"));
23607
+ if (!fs21.existsSync(this.authDir)) return [];
23608
+ const files = fs21.readdirSync(this.authDir).filter((f) => f.endsWith(".json"));
23485
23609
  return files.map((f) => {
23486
23610
  try {
23487
- const data = JSON.parse(fs20.readFileSync(path15.join(this.authDir, f), "utf-8"));
23611
+ const data = JSON.parse(fs21.readFileSync(path16.join(this.authDir, f), "utf-8"));
23488
23612
  return {
23489
23613
  name: data.name,
23490
23614
  url: data.url,
@@ -23498,11 +23622,11 @@ var init_auth_vault = __esm({
23498
23622
  }).filter(Boolean);
23499
23623
  }
23500
23624
  delete(name) {
23501
- const filePath = path15.join(this.authDir, `${sanitizeName(name)}.json`);
23502
- if (!fs20.existsSync(filePath)) {
23625
+ const filePath = path16.join(this.authDir, `${sanitizeName(name)}.json`);
23626
+ if (!fs21.existsSync(filePath)) {
23503
23627
  throw new Error(`Credential "${name}" not found.`);
23504
23628
  }
23505
- fs20.unlinkSync(filePath);
23629
+ fs21.unlinkSync(filePath);
23506
23630
  }
23507
23631
  };
23508
23632
  }
@@ -23639,20 +23763,20 @@ __export(policy_exports, {
23639
23763
  function findFileUpward(filename) {
23640
23764
  let dir = process.cwd();
23641
23765
  for (let i = 0; i < 20; i++) {
23642
- const candidate = path16.join(dir, filename);
23643
- if (fs21.existsSync(candidate)) return candidate;
23644
- const parent = path16.dirname(dir);
23766
+ const candidate = path17.join(dir, filename);
23767
+ if (fs22.existsSync(candidate)) return candidate;
23768
+ const parent = path17.dirname(dir);
23645
23769
  if (parent === dir) break;
23646
23770
  dir = parent;
23647
23771
  }
23648
23772
  return null;
23649
23773
  }
23650
- var fs21, path16, PolicyChecker;
23774
+ var fs22, path17, PolicyChecker;
23651
23775
  var init_policy = __esm({
23652
23776
  "src/security/policy.ts"() {
23653
23777
  "use strict";
23654
- fs21 = __toESM(require("fs"), 1);
23655
- path16 = __toESM(require("path"), 1);
23778
+ fs22 = __toESM(require("fs"), 1);
23779
+ path17 = __toESM(require("path"), 1);
23656
23780
  PolicyChecker = class {
23657
23781
  filePath = null;
23658
23782
  lastMtime = 0;
@@ -23671,10 +23795,10 @@ var init_policy = __esm({
23671
23795
  reload() {
23672
23796
  if (!this.filePath) return;
23673
23797
  try {
23674
- const stat = fs21.statSync(this.filePath);
23798
+ const stat = fs22.statSync(this.filePath);
23675
23799
  if (stat.mtimeMs === this.lastMtime) return;
23676
23800
  this.lastMtime = stat.mtimeMs;
23677
- const raw = fs21.readFileSync(this.filePath, "utf-8");
23801
+ const raw = fs22.readFileSync(this.filePath, "utf-8");
23678
23802
  this.policy = JSON.parse(raw);
23679
23803
  } catch {
23680
23804
  }
@@ -23851,110 +23975,6 @@ var init_buffers = __esm({
23851
23975
  }
23852
23976
  });
23853
23977
 
23854
- // src/app/macos/bridge.ts
23855
- var bridge_exports3 = {};
23856
- __export(bridge_exports3, {
23857
- createMacOSBridge: () => createMacOSBridge,
23858
- ensureMacOSBridge: () => ensureMacOSBridge,
23859
- resolveBridgePath: () => resolveBridgePath
23860
- });
23861
- function resolveBridgePath() {
23862
- const candidates = [
23863
- // 1. Local dev build
23864
- path17.resolve(__dirname_bridge, "../../../browse-ax/.build/release/browse-ax"),
23865
- path17.resolve(__dirname_bridge, "../../../browse-ax/.build/debug/browse-ax"),
23866
- // 2. Installed alongside source (bin/ at project root)
23867
- path17.resolve(__dirname_bridge, "../../bin/browse-ax"),
23868
- // 3. Bundled build (dist/browse.cjs → ../bin/)
23869
- path17.resolve(__dirname_bridge, "../bin/browse-ax")
23870
- ];
23871
- for (const p of candidates) {
23872
- if (fs22.existsSync(p)) return p;
23873
- }
23874
- const lazyPath = path17.join(
23875
- process.env.BROWSE_LOCAL_DIR || path17.join(process.cwd(), ".browse"),
23876
- "bin",
23877
- "browse-ax"
23878
- );
23879
- if (fs22.existsSync(lazyPath)) return lazyPath;
23880
- throw new Error(
23881
- "browse-ax binary not found. Run: browse enable macos\nOr build manually: cd browse-ax && swift build -c release"
23882
- );
23883
- }
23884
- async function ensureMacOSBridge() {
23885
- if (process.platform !== "darwin") {
23886
- throw new Error("App automation requires macOS (uses Accessibility API)");
23887
- }
23888
- return resolveBridgePath();
23889
- }
23890
- async function execBridge(bridgePath, args) {
23891
- return new Promise((resolve9, reject) => {
23892
- const proc = (0, import_child_process9.spawn)(bridgePath, args, {
23893
- stdio: ["ignore", "pipe", "pipe"]
23894
- });
23895
- const chunks = [];
23896
- const errChunks = [];
23897
- proc.stdout.on("data", (c) => chunks.push(c));
23898
- proc.stderr.on("data", (c) => errChunks.push(c));
23899
- proc.on("close", (code) => {
23900
- const stdout = Buffer.concat(chunks).toString("utf-8").trim();
23901
- const stderr = Buffer.concat(errChunks).toString("utf-8").trim();
23902
- if (code !== 0) {
23903
- try {
23904
- const err = JSON.parse(stderr || stdout);
23905
- reject(new Error(err.error || `Bridge exited with code ${code}`));
23906
- } catch {
23907
- reject(new Error(stderr || `Bridge exited with code ${code}`));
23908
- }
23909
- return;
23910
- }
23911
- try {
23912
- resolve9(JSON.parse(stdout));
23913
- } catch {
23914
- reject(new Error(`Invalid bridge output: ${stdout.slice(0, 200)}`));
23915
- }
23916
- });
23917
- });
23918
- }
23919
- function createMacOSBridge(bridgePath, pid) {
23920
- const base = ["--pid", String(pid)];
23921
- return {
23922
- async tree() {
23923
- return execBridge(bridgePath, [...base, "tree"]);
23924
- },
23925
- async action(nodePath, actionName) {
23926
- return execBridge(bridgePath, [...base, "action", JSON.stringify(nodePath), actionName]);
23927
- },
23928
- async setValue(nodePath, value) {
23929
- return execBridge(bridgePath, [...base, "set-value", JSON.stringify(nodePath), value]);
23930
- },
23931
- async type(text) {
23932
- return execBridge(bridgePath, [...base, "type", text]);
23933
- },
23934
- async press(key) {
23935
- return execBridge(bridgePath, [...base, "press", key]);
23936
- },
23937
- async screenshot(outputPath) {
23938
- return execBridge(bridgePath, [...base, "screenshot", outputPath]);
23939
- },
23940
- async state() {
23941
- return execBridge(bridgePath, [...base, "state"]);
23942
- }
23943
- };
23944
- }
23945
- var import_child_process9, path17, fs22, import_url6, __filename_bridge2, __dirname_bridge;
23946
- var init_bridge3 = __esm({
23947
- "src/app/macos/bridge.ts"() {
23948
- "use strict";
23949
- import_child_process9 = require("child_process");
23950
- path17 = __toESM(require("path"), 1);
23951
- fs22 = __toESM(require("fs"), 1);
23952
- import_url6 = require("url");
23953
- __filename_bridge2 = (0, import_url6.fileURLToPath)(__import_meta_url);
23954
- __dirname_bridge = path17.dirname(__filename_bridge2);
23955
- }
23956
- });
23957
-
23958
23978
  // src/commands/meta/system.ts
23959
23979
  async function handleSystemCommand(command, args, target, shutdown2, sessionManager2, currentSession, lifecycle) {
23960
23980
  switch (command) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ulpi/browse",
3
- "version": "2.3.2",
3
+ "version": "2.3.3",
4
4
  "homepage": "https://browse.ulpi.io",
5
5
  "repository": {
6
6
  "type": "git",