@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.
- package/dist/browse.cjs +335 -315
- 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.
|
|
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
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
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
|
-
|
|
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,
|
|
2708
|
-
log("
|
|
2815
|
+
(0, import_child_process9.execSync)("adb version", { stdio: "ignore", timeout: 5e3 });
|
|
2816
|
+
log("adb: available");
|
|
2709
2817
|
} catch {
|
|
2710
|
-
log("
|
|
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
|
-
|
|
2715
|
-
}
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
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,
|
|
2855
|
+
(0, import_child_process9.execSync)(`"${sdkMgr}" --install "${comp}"`, { stdio: "ignore", timeout: 3e5 });
|
|
2725
2856
|
} catch {
|
|
2726
2857
|
}
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
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 =
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
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,
|
|
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("
|
|
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,
|
|
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
|
|
2903
|
+
throw new Error("Xcode not found. Install from the App Store.");
|
|
2785
2904
|
}
|
|
2786
2905
|
try {
|
|
2787
|
-
(0,
|
|
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,
|
|
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
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
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,
|
|
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
|
-
|
|
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 =
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
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
|
|
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("
|
|
2848
|
-
console.log("
|
|
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
|
|
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
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
__dirname_enable =
|
|
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 (!
|
|
3178
|
-
const code =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
4223
|
-
const cookies = JSON.parse(
|
|
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 (!
|
|
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
|
|
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
|
-
|
|
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 (!
|
|
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 =
|
|
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 (!
|
|
5151
|
-
currentBuffer =
|
|
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
|
-
|
|
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
|
|
5300
|
+
var fs15, LOCAL_DIR;
|
|
5177
5301
|
var init_screenshots = __esm({
|
|
5178
5302
|
"src/commands/meta/screenshots.ts"() {
|
|
5179
5303
|
"use strict";
|
|
5180
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
13131
|
+
var fs16, LOCAL_DIR2;
|
|
13008
13132
|
var init_recording = __esm({
|
|
13009
13133
|
"src/commands/meta/recording.ts"() {
|
|
13010
13134
|
"use strict";
|
|
13011
|
-
|
|
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
|
-
|
|
13036
|
-
|
|
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 =
|
|
13043
|
-
if (!
|
|
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 =
|
|
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
|
|
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 =
|
|
13111
|
-
if (
|
|
13234
|
+
const statesDir = path13.join(localDir, "states");
|
|
13235
|
+
if (fs17.existsSync(statesDir)) {
|
|
13112
13236
|
try {
|
|
13113
|
-
const entries =
|
|
13237
|
+
const entries = fs17.readdirSync(statesDir);
|
|
13114
13238
|
for (const entry of entries) {
|
|
13115
13239
|
if (!entry.endsWith(".json")) continue;
|
|
13116
|
-
const filePath =
|
|
13240
|
+
const filePath = path13.join(statesDir, entry);
|
|
13117
13241
|
try {
|
|
13118
|
-
const stat =
|
|
13242
|
+
const stat = fs17.statSync(filePath);
|
|
13119
13243
|
if (!stat.isFile()) continue;
|
|
13120
13244
|
if (now - stat.mtimeMs > maxAgeMs) {
|
|
13121
|
-
|
|
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 =
|
|
13131
|
-
if (
|
|
13254
|
+
const sessionsDir = path13.join(localDir, "sessions");
|
|
13255
|
+
if (fs17.existsSync(sessionsDir)) {
|
|
13132
13256
|
try {
|
|
13133
|
-
const sessionDirs =
|
|
13257
|
+
const sessionDirs = fs17.readdirSync(sessionsDir);
|
|
13134
13258
|
for (const dir of sessionDirs) {
|
|
13135
|
-
const dirPath =
|
|
13259
|
+
const dirPath = path13.join(sessionsDir, dir);
|
|
13136
13260
|
try {
|
|
13137
|
-
const dirStat =
|
|
13261
|
+
const dirStat = fs17.statSync(dirPath);
|
|
13138
13262
|
if (!dirStat.isDirectory()) continue;
|
|
13139
13263
|
} catch (_) {
|
|
13140
13264
|
continue;
|
|
13141
13265
|
}
|
|
13142
|
-
const statePath =
|
|
13266
|
+
const statePath = path13.join(dirPath, STATE_FILENAME);
|
|
13143
13267
|
try {
|
|
13144
|
-
const stat =
|
|
13268
|
+
const stat = fs17.statSync(statePath);
|
|
13145
13269
|
if (now - stat.mtimeMs > maxAgeMs) {
|
|
13146
|
-
|
|
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
|
|
13281
|
+
var fs17, path13, STATE_FILENAME;
|
|
13158
13282
|
var init_persist = __esm({
|
|
13159
13283
|
"src/session/persist.ts"() {
|
|
13160
13284
|
"use strict";
|
|
13161
|
-
|
|
13162
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
13624
|
-
const files =
|
|
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 =
|
|
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 (!
|
|
13759
|
+
if (!fs18.existsSync(statePath)) {
|
|
13636
13760
|
throw new Error(`State file not found: ${statePath}`);
|
|
13637
13761
|
}
|
|
13638
|
-
const data = JSON.parse(
|
|
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
|
-
|
|
13664
|
-
|
|
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 (!
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
|
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 =
|
|
19694
|
-
if (!
|
|
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 =
|
|
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 =
|
|
19827
|
+
const filePath = path14.join(detectionDir, entry);
|
|
19704
19828
|
try {
|
|
19705
|
-
const raw =
|
|
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
|
|
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
|
-
|
|
19839
|
-
|
|
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
|
|
22045
|
+
return path15.join(localDir, "audits");
|
|
21922
22046
|
}
|
|
21923
22047
|
function auditPath(localDir, name) {
|
|
21924
|
-
return
|
|
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
|
-
|
|
22052
|
+
fs20.mkdirSync(dir, { recursive: true });
|
|
21929
22053
|
const filePath = auditPath(localDir, name);
|
|
21930
|
-
|
|
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 (!
|
|
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(
|
|
22064
|
+
return JSON.parse(fs20.readFileSync(filePath, "utf-8"));
|
|
21941
22065
|
}
|
|
21942
22066
|
function listAudits(localDir) {
|
|
21943
22067
|
const dir = auditsDir(localDir);
|
|
21944
|
-
if (!
|
|
21945
|
-
const files =
|
|
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 =
|
|
21949
|
-
const stat =
|
|
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 (!
|
|
22085
|
+
if (!fs20.existsSync(filePath)) {
|
|
21962
22086
|
throw new Error(
|
|
21963
22087
|
`Audit not found: ${filePath}. Nothing to delete.`
|
|
21964
22088
|
);
|
|
21965
22089
|
}
|
|
21966
|
-
|
|
22090
|
+
fs20.unlinkSync(filePath);
|
|
21967
22091
|
}
|
|
21968
|
-
var
|
|
22092
|
+
var fs20, path15;
|
|
21969
22093
|
var init_persist2 = __esm({
|
|
21970
22094
|
"src/perf-audit/persist.ts"() {
|
|
21971
22095
|
"use strict";
|
|
21972
|
-
|
|
21973
|
-
|
|
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
|
|
23544
|
+
var fs21, path16, AuthVault;
|
|
23421
23545
|
var init_auth_vault = __esm({
|
|
23422
23546
|
"src/security/auth-vault.ts"() {
|
|
23423
23547
|
"use strict";
|
|
23424
|
-
|
|
23425
|
-
|
|
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 =
|
|
23557
|
+
this.authDir = path16.join(localDir, "auth");
|
|
23434
23558
|
this.encryptionKey = resolveEncryptionKey(localDir);
|
|
23435
23559
|
}
|
|
23436
23560
|
save(name, url, username, password, selectors) {
|
|
23437
|
-
|
|
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 =
|
|
23455
|
-
|
|
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 =
|
|
23459
|
-
if (!
|
|
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(
|
|
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 (!
|
|
23484
|
-
const files =
|
|
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(
|
|
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 =
|
|
23502
|
-
if (!
|
|
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
|
-
|
|
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 =
|
|
23643
|
-
if (
|
|
23644
|
-
const parent =
|
|
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
|
|
23774
|
+
var fs22, path17, PolicyChecker;
|
|
23651
23775
|
var init_policy = __esm({
|
|
23652
23776
|
"src/security/policy.ts"() {
|
|
23653
23777
|
"use strict";
|
|
23654
|
-
|
|
23655
|
-
|
|
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 =
|
|
23798
|
+
const stat = fs22.statSync(this.filePath);
|
|
23675
23799
|
if (stat.mtimeMs === this.lastMtime) return;
|
|
23676
23800
|
this.lastMtime = stat.mtimeMs;
|
|
23677
|
-
const raw =
|
|
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) {
|