@8bitbish/screenshot-service 1.0.0 → 1.0.1

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.
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pairAndroidWireless = pairAndroidWireless;
4
+ exports.getLastAndroidEndpoint = getLastAndroidEndpoint;
5
+ const child_process_1 = require("child_process");
6
+ const fs_1 = require("fs");
7
+ const os_1 = require("os");
8
+ const path_1 = require("path");
9
+ const detect_1 = require("./detect");
10
+ const ANDROID_IP_FILE = (0, path_1.join)((0, os_1.homedir)(), ".screenshotAssistent-android-ip");
11
+ function emitStep(cb, patch) {
12
+ const p = { ...patch };
13
+ cb?.(p);
14
+ return p;
15
+ }
16
+ /**
17
+ * Pair and connect an Android device over wireless ADB.
18
+ *
19
+ * Phone setup (user-driven, before calling this):
20
+ * Settings → Developer Options → Wireless Debugging ON → Pair device with pairing code
21
+ * The phone shows: IP, pairing port, 6-digit code.
22
+ *
23
+ * What this does:
24
+ * 1. `adb pair ip:port` and feeds the code via stdin (no terminal prompt).
25
+ * 2. `adb connect ip:5555` (or custom connectPort) to establish the data connection.
26
+ * 3. Saves the IP for auto-reconnect on subsequent screenshots.
27
+ */
28
+ async function pairAndroidWireless(opts, onProgress) {
29
+ const { ip, port, code, connectPort = "5555" } = opts;
30
+ if (!/^\d+\.\d+\.\d+\.\d+$/.test(ip))
31
+ throw new Error(`Invalid IP address: ${ip}`);
32
+ if (!/^\d+$/.test(port))
33
+ throw new Error(`Invalid pairing port: ${port}`);
34
+ if (!/^\d{6}$/.test(code))
35
+ throw new Error(`Pairing code must be 6 digits: got "${code}"`);
36
+ const { adb } = await (0, detect_1.checkAndroidSetup)();
37
+ if (!adb) {
38
+ throw new Error("adb not found. Run installAndroidDeps() first.");
39
+ }
40
+ const TOTAL = 2;
41
+ // ── Step 1: pair ───────────────────────────────────────────────────────────
42
+ let pair = emitStep(onProgress, {
43
+ id: "pair", label: `Pairing with ${ip}:${port}`, status: "running", index: 1, total: TOTAL,
44
+ });
45
+ await new Promise((resolve, reject) => {
46
+ const child = (0, child_process_1.spawn)(adb, ["pair", `${ip}:${port}`], {
47
+ stdio: ["pipe", "pipe", "pipe"],
48
+ });
49
+ const lines = [];
50
+ const onOut = (chunk) => {
51
+ const text = chunk.toString("utf8");
52
+ lines.push(text);
53
+ for (const line of text.split("\n")) {
54
+ if (!line.trim())
55
+ continue;
56
+ onProgress?.({ ...pair, log: line });
57
+ }
58
+ };
59
+ child.stdout.on("data", onOut);
60
+ child.stderr.on("data", onOut);
61
+ // adb pair prompts "Enter pairing code: " on stdin — feed the code immediately
62
+ child.stdin.write(`${code}\n`);
63
+ child.stdin.end();
64
+ child.on("error", reject);
65
+ child.on("close", (exitCode) => {
66
+ const combined = lines.join("");
67
+ if (exitCode === 0 && /successfully paired/i.test(combined)) {
68
+ resolve();
69
+ }
70
+ else {
71
+ reject(new Error("Pairing failed. Check the IP/port/code are correct and that the phone's pairing screen is still open.\n" +
72
+ combined.trim()));
73
+ }
74
+ });
75
+ });
76
+ emitStep(onProgress, { ...pair, status: "done" });
77
+ // ── Step 2: connect ───────────────────────────────────────────────────────
78
+ let connect = emitStep(onProgress, {
79
+ id: "connect", label: `Connecting to ${ip}:${connectPort}`, status: "running", index: 2, total: TOTAL,
80
+ });
81
+ await new Promise((resolve, reject) => {
82
+ const child = (0, child_process_1.spawn)(adb, ["connect", `${ip}:${connectPort}`], { stdio: ["ignore", "pipe", "pipe"] });
83
+ const lines = [];
84
+ const onOut = (chunk) => {
85
+ const text = chunk.toString("utf8");
86
+ lines.push(text);
87
+ for (const line of text.split("\n")) {
88
+ if (!line.trim())
89
+ continue;
90
+ onProgress?.({ ...connect, log: line });
91
+ }
92
+ };
93
+ child.stdout.on("data", onOut);
94
+ child.stderr.on("data", onOut);
95
+ child.on("error", reject);
96
+ child.on("close", (exitCode) => {
97
+ const combined = lines.join("");
98
+ // adb connect returns 0 even on failure — check the output text
99
+ if (exitCode === 0 && /connected to/i.test(combined) && !/failed to connect/i.test(combined)) {
100
+ resolve();
101
+ }
102
+ else {
103
+ reject(new Error("Connect failed. Make sure your Mac and phone are on the same WiFi and Wireless Debugging is on.\n" +
104
+ combined.trim()));
105
+ }
106
+ });
107
+ });
108
+ // Save IP for auto-reconnect
109
+ try {
110
+ (0, fs_1.writeFileSync)(ANDROID_IP_FILE, `${ip}:${connectPort}`, "utf8");
111
+ }
112
+ catch { }
113
+ emitStep(onProgress, { ...connect, status: "done" });
114
+ }
115
+ /** Returns the last saved wireless ADB endpoint (e.g. "192.168.1.50:5555"), or null. */
116
+ function getLastAndroidEndpoint() {
117
+ try {
118
+ const v = (0, fs_1.readFileSync)(ANDROID_IP_FILE, "utf8").trim();
119
+ return v || null;
120
+ }
121
+ catch {
122
+ return null;
123
+ }
124
+ }
125
+ //# sourceMappingURL=pair.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pair.js","sourceRoot":"","sources":["../../setup/pair.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Returns the path to the first python binary that has pymobiledevice3 installed,
3
+ * or null if none found. Never throws — use {@link findPythonOrThrow} when an
4
+ * exception is the right behaviour.
5
+ */
6
+ export declare function findPython(): string | null;
7
+ export declare function findPythonOrThrow(): string;
8
+ /** Returns the path to a python3 binary (with or without pymobiledevice3), or null. */
9
+ export declare function findAnyPython3(): string | null;
10
+ //# sourceMappingURL=python.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"python.d.ts","sourceRoot":"","sources":["../../setup/python.ts"],"names":[],"mappings":"AAsBA;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,MAAM,GAAG,IAAI,CAQ1C;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAO1C;AAED,uFAAuF;AACvF,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAQ9C"}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findPython = findPython;
4
+ exports.findPythonOrThrow = findPythonOrThrow;
5
+ exports.findAnyPython3 = findAnyPython3;
6
+ const child_process_1 = require("child_process");
7
+ // Candidate python binaries to try, in preference order. Newer versions first so
8
+ // users on 3.14+ get picked up automatically when those land.
9
+ const PYTHON_CANDIDATES = [
10
+ "/opt/homebrew/bin/python3.14",
11
+ "/usr/local/bin/python3.14",
12
+ "python3.14",
13
+ "/opt/homebrew/bin/python3.13",
14
+ "/usr/local/bin/python3.13",
15
+ "python3.13",
16
+ "/opt/homebrew/bin/python3.12",
17
+ "/usr/local/bin/python3.12",
18
+ "python3.12",
19
+ "/opt/homebrew/bin/python3.11",
20
+ "/usr/local/bin/python3.11",
21
+ "python3.11",
22
+ "/opt/homebrew/bin/python3",
23
+ "/usr/local/bin/python3",
24
+ "python3",
25
+ ];
26
+ /**
27
+ * Returns the path to the first python binary that has pymobiledevice3 installed,
28
+ * or null if none found. Never throws — use {@link findPythonOrThrow} when an
29
+ * exception is the right behaviour.
30
+ */
31
+ function findPython() {
32
+ for (const p of PYTHON_CANDIDATES) {
33
+ try {
34
+ (0, child_process_1.execFileSync)(p, ["-c", "import pymobiledevice3"], { stdio: "ignore" });
35
+ return p;
36
+ }
37
+ catch { }
38
+ }
39
+ return null;
40
+ }
41
+ function findPythonOrThrow() {
42
+ const p = findPython();
43
+ if (p)
44
+ return p;
45
+ throw new Error("Python with pymobiledevice3 not found. Run the iOS setup wizard or:\n" +
46
+ " npx @8bitbish/screenshot-service setup-ios");
47
+ }
48
+ /** Returns the path to a python3 binary (with or without pymobiledevice3), or null. */
49
+ function findAnyPython3() {
50
+ for (const p of PYTHON_CANDIDATES) {
51
+ try {
52
+ (0, child_process_1.execFileSync)(p, ["--version"], { stdio: "ignore" });
53
+ return p;
54
+ }
55
+ catch { }
56
+ }
57
+ return null;
58
+ }
59
+ //# sourceMappingURL=python.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"python.js","sourceRoot":"","sources":["../../setup/python.ts"],"names":[],"mappings":";;AA2BA,gCAQC;AAED,8CAOC;AAGD,wCAQC;AAvDD,iDAA6C;AAE7C,iFAAiF;AACjF,8DAA8D;AAC9D,MAAM,iBAAiB,GAAG;IACxB,8BAA8B;IAC9B,2BAA2B;IAC3B,YAAY;IACZ,8BAA8B;IAC9B,2BAA2B;IAC3B,YAAY;IACZ,8BAA8B;IAC9B,2BAA2B;IAC3B,YAAY;IACZ,8BAA8B;IAC9B,2BAA2B;IAC3B,YAAY;IACZ,2BAA2B;IAC3B,wBAAwB;IACxB,SAAS;CACV,CAAC;AAEF;;;;GAIG;AACH,SAAgB,UAAU;IACxB,KAAK,MAAM,CAAC,IAAI,iBAAiB,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,IAAA,4BAAY,EAAC,CAAC,EAAE,CAAC,IAAI,EAAE,wBAAwB,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;YACvE,OAAO,CAAC,CAAC;QACX,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACZ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAgB,iBAAiB;IAC/B,MAAM,CAAC,GAAG,UAAU,EAAE,CAAC;IACvB,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IAChB,MAAM,IAAI,KAAK,CACb,uEAAuE;QACvE,8CAA8C,CAC/C,CAAC;AACJ,CAAC;AAED,uFAAuF;AACvF,SAAgB,cAAc;IAC5B,KAAK,MAAM,CAAC,IAAI,iBAAiB,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,IAAA,4BAAY,EAAC,CAAC,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;YACpD,OAAO,CAAC,CAAC;QACX,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACZ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,37 @@
1
+ export interface IosSetupStatus {
2
+ brew: boolean;
3
+ python: string | null;
4
+ pymobiledevice3: boolean;
5
+ sudoers: boolean;
6
+ deviceVisible: boolean;
7
+ ok: boolean;
8
+ }
9
+ export interface AndroidSetupStatus {
10
+ adb: string | null;
11
+ deviceConnected: boolean;
12
+ ok: boolean;
13
+ }
14
+ export type SetupStepStatus = "running" | "done" | "skipped" | "error";
15
+ export interface SetupProgress {
16
+ /** Stable id for the step — e.g. "brew", "python", "pymobiledevice3", "sudoers". */
17
+ id: string;
18
+ /** Human-readable label — e.g. "Installing Python 3.13". */
19
+ label: string;
20
+ /** Optional sub-detail — e.g. the package name being installed. */
21
+ detail?: string;
22
+ /** Raw log line from the underlying subprocess, when available. */
23
+ log?: string;
24
+ status: SetupStepStatus;
25
+ /** Step index / total, for UI progress bars. */
26
+ index: number;
27
+ total: number;
28
+ }
29
+ export type SetupProgressCallback = (p: SetupProgress) => void;
30
+ export interface AndroidPairOptions {
31
+ ip: string;
32
+ port: string;
33
+ code: string;
34
+ /** Optional override for the connect port. Defaults to 5555. */
35
+ connectPort?: string;
36
+ }
37
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../setup/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,OAAO,CAAC;IACvB,EAAE,EAAE,OAAO,CAAC;CACb;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;IACzB,EAAE,EAAE,OAAO,CAAC;CACb;AAED,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAEvE,MAAM,WAAW,aAAa;IAC5B,oFAAoF;IACpF,EAAE,EAAE,MAAM,CAAC;IACX,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,eAAe,CAAC;IACxB,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,EAAE,aAAa,KAAK,IAAI,CAAC;AAE/D,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB"}
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ // Shared types for the setup/wizard subsystem.
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../setup/types.ts"],"names":[],"mappings":";AAAA,+CAA+C"}
@@ -0,0 +1,18 @@
1
+ export interface SetupWindowOptions {
2
+ /** Which platform's wizard to show. */
3
+ platform: "ios" | "android";
4
+ /** Optional parent BrowserWindow — wizard becomes modal to it. */
5
+ parent?: import("electron").BrowserWindow;
6
+ /** Override the window title. Default: "Setup". */
7
+ title?: string;
8
+ }
9
+ /**
10
+ * Open the setup wizard as a native window. Resolves when the user closes the
11
+ * window (whether they finished setup or bailed out).
12
+ *
13
+ * The window contains an HTML wizard that calls back into the main process
14
+ * over IPC to drive {@link installIosDeps}, {@link installAndroidDeps}, and
15
+ * {@link pairAndroidWireless}. The user never sees a terminal.
16
+ */
17
+ export declare function createSetupWindow(opts: SetupWindowOptions): Promise<void>;
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../wizard/index.ts"],"names":[],"mappings":"AA8BA,MAAM,WAAW,kBAAkB;IACjC,uCAAuC;IACvC,QAAQ,EAAE,KAAK,GAAG,SAAS,CAAC;IAC5B,kEAAkE;IAClE,MAAM,CAAC,EAAE,OAAO,UAAU,EAAE,aAAa,CAAC;IAC1C,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiF/E"}
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSetupWindow = createSetupWindow;
4
+ const path_1 = require("path");
5
+ const setup_1 = require("../setup");
6
+ let cachedElectron = null;
7
+ function loadElectron() {
8
+ if (cachedElectron)
9
+ return cachedElectron;
10
+ try {
11
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
12
+ cachedElectron = require("electron");
13
+ return cachedElectron;
14
+ }
15
+ catch (err) {
16
+ throw new Error("electron is required to open the setup wizard window. Install it in your host app:\n" +
17
+ " npm install electron");
18
+ }
19
+ }
20
+ /**
21
+ * Open the setup wizard as a native window. Resolves when the user closes the
22
+ * window (whether they finished setup or bailed out).
23
+ *
24
+ * The window contains an HTML wizard that calls back into the main process
25
+ * over IPC to drive {@link installIosDeps}, {@link installAndroidDeps}, and
26
+ * {@link pairAndroidWireless}. The user never sees a terminal.
27
+ */
28
+ async function createSetupWindow(opts) {
29
+ const electron = loadElectron();
30
+ const { BrowserWindow, ipcMain } = electron;
31
+ const win = new BrowserWindow({
32
+ width: 640,
33
+ height: 720,
34
+ minWidth: 560,
35
+ minHeight: 560,
36
+ title: opts.title ?? (opts.platform === "ios" ? "Setup iPhone capture" : "Setup Android capture"),
37
+ parent: opts.parent,
38
+ modal: !!opts.parent,
39
+ resizable: true,
40
+ minimizable: false,
41
+ maximizable: false,
42
+ fullscreenable: false,
43
+ autoHideMenuBar: true,
44
+ backgroundColor: "#0e0f12",
45
+ show: false,
46
+ webPreferences: {
47
+ preload: (0, path_1.join)(__dirname, "preload.js"),
48
+ contextIsolation: true,
49
+ nodeIntegration: false,
50
+ sandbox: false,
51
+ },
52
+ });
53
+ win.once("ready-to-show", () => win.show());
54
+ win.loadFile((0, path_1.join)(__dirname, "renderer", "index.html"), {
55
+ query: { platform: opts.platform },
56
+ });
57
+ // ── IPC handlers (scoped to this window) ────────────────────────────────────
58
+ // We register handlers per-window to keep them isolated and ensure they are
59
+ // cleaned up when the window closes.
60
+ const channels = {
61
+ checkIos: `wizard:${win.id}:check-ios`,
62
+ installIos: `wizard:${win.id}:install-ios`,
63
+ checkAndroid: `wizard:${win.id}:check-android`,
64
+ installAndroid: `wizard:${win.id}:install-android`,
65
+ pairAndroid: `wizard:${win.id}:pair-android`,
66
+ close: `wizard:${win.id}:close`,
67
+ };
68
+ // Expose channel names to renderer via initial-load query (read by preload).
69
+ win.webContents.on("did-finish-load", () => {
70
+ win.webContents.send("wizard:init", { platform: opts.platform, channels });
71
+ });
72
+ ipcMain.handle(channels.checkIos, async () => (0, setup_1.checkIosSetup)());
73
+ ipcMain.handle(channels.checkAndroid, async () => (0, setup_1.checkAndroidSetup)());
74
+ ipcMain.handle(channels.installIos, async () => {
75
+ await (0, setup_1.installIosDeps)((p) => {
76
+ if (!win.isDestroyed())
77
+ win.webContents.send(`${channels.installIos}:progress`, p);
78
+ });
79
+ });
80
+ ipcMain.handle(channels.installAndroid, async () => {
81
+ await (0, setup_1.installAndroidDeps)((p) => {
82
+ if (!win.isDestroyed())
83
+ win.webContents.send(`${channels.installAndroid}:progress`, p);
84
+ });
85
+ });
86
+ ipcMain.handle(channels.pairAndroid, async (_e, opts) => {
87
+ await (0, setup_1.pairAndroidWireless)(opts, (p) => {
88
+ if (!win.isDestroyed())
89
+ win.webContents.send(`${channels.pairAndroid}:progress`, p);
90
+ });
91
+ });
92
+ ipcMain.handle(channels.close, async () => {
93
+ if (!win.isDestroyed())
94
+ win.close();
95
+ });
96
+ // Resolve when the window closes
97
+ return new Promise((resolve) => {
98
+ win.on("closed", () => {
99
+ for (const ch of Object.values(channels)) {
100
+ try {
101
+ ipcMain.removeHandler(ch);
102
+ }
103
+ catch { }
104
+ }
105
+ resolve();
106
+ });
107
+ });
108
+ }
109
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../wizard/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ 'use strict';
2
+
3
+ // Preload script for the wizard BrowserWindow. Bridges renderer-side JS to the
4
+ // main process's setup IPC handlers without exposing Node APIs.
5
+ //
6
+ // Plain JavaScript (rather than TypeScript) so the package can build without
7
+ // electron's type definitions installed — electron is an optional peer dep.
8
+
9
+ const { contextBridge, ipcRenderer } = require('electron');
10
+
11
+ let initPayload = null;
12
+ const initListeners = [];
13
+
14
+ ipcRenderer.on('wizard:init', (_e, payload) => {
15
+ initPayload = payload;
16
+ while (initListeners.length) initListeners.shift()(payload);
17
+ });
18
+
19
+ function waitForInit() {
20
+ if (initPayload) return Promise.resolve(initPayload);
21
+ return new Promise((resolve) => initListeners.push(resolve));
22
+ }
23
+
24
+ async function invoke(channelKey, ...args) {
25
+ const { channels } = await waitForInit();
26
+ const channel = channels[channelKey];
27
+ if (!channel) throw new Error(`Unknown wizard channel: ${channelKey}`);
28
+ return ipcRenderer.invoke(channel, ...args);
29
+ }
30
+
31
+ function onProgress(channelKey, cb) {
32
+ let cleanup = () => {};
33
+ waitForInit().then(({ channels }) => {
34
+ const channel = channels[channelKey] + ':progress';
35
+ const handler = (_e, p) => cb(p);
36
+ ipcRenderer.on(channel, handler);
37
+ cleanup = () => ipcRenderer.removeListener(channel, handler);
38
+ });
39
+ return () => cleanup();
40
+ }
41
+
42
+ contextBridge.exposeInMainWorld('wizard', {
43
+ init: waitForInit,
44
+ checkIos: () => invoke('checkIos'),
45
+ installIos: () => invoke('installIos'),
46
+ onInstallIosProgress: (cb) => onProgress('installIos', cb),
47
+ checkAndroid: () => invoke('checkAndroid'),
48
+ installAndroid: () => invoke('installAndroid'),
49
+ onInstallAndroidProgress: (cb) => onProgress('installAndroid', cb),
50
+ pairAndroid: (opts) => invoke('pairAndroid', opts),
51
+ onPairAndroidProgress: (cb) => onProgress('pairAndroid', cb),
52
+ close: () => invoke('close'),
53
+ });