import { existsSync } from "node:fs"; import { spawnSync } from "node:child_process"; import path from "node:path"; const root = path.resolve(import.meta.dir, ".."); const args = process.argv.slice(2); const app = value("--app") ?? "MoanNote"; const profiles: Record< string, { patchScript: string; legacyPackage: string; patchedPackage: string; removeLegacy: boolean; legacySystemImage: boolean; } > = { MoanNote: { patchScript: "patch:moannotes", legacyPackage: "com.moan.notes", patchedPackage: "rip.crit.ms.com.moan.notes", removeLegacy: false, legacySystemImage: true, }, MoanLauncher: { patchScript: "patch:moanlauncher", legacyPackage: "com.note.launcher", patchedPackage: "rip.crit.ms.com.note.launcher", removeLegacy: false, legacySystemImage: true, }, }; const profile = profiles[app]; if (!profile) throw new Error(`Unsupported app ${app}`); function value(flag: string): string | undefined { const index = args.indexOf(flag); return index >= 0 ? args[index + 1] : undefined; } function run(command: string, commandArgs: string[]): void { const result = spawnSync(command, commandArgs, { cwd: root, stdio: "inherit", }); if (result.error) throw result.error; if (result.status !== 0) throw new Error(`${command} ${commandArgs.join(" ")} failed`); } function uninstallIfPresent(serial: string, packageName: string): void { const result = spawnSync("adb", ["-s", serial, "uninstall", packageName], { cwd: root, encoding: "utf8", }); if (result.error) throw result.error; if (result.status === 0) { if (result.stdout) process.stdout.write(result.stdout); return; } const output = `${result.stdout ?? ""}${result.stderr ?? ""}`; if (/not installed/i.test(output)) { console.log(`[info] ${packageName} was not installed`); return; } // OEM apps in /system cannot be uninstalled from the system partition. For a // clean, per-user replacement, Package Manager can remove that system app // for user 0. This also clears its user data. if (/DELETE_FAILED_INTERNAL_ERROR|unknown system package/i.test(output)) { const perUserResult = spawnSync( "adb", ["-s", serial, "shell", "pm", "uninstall", "--user", "0", packageName], { cwd: root, encoding: "utf8" }, ); if (perUserResult.error) throw perUserResult.error; if (perUserResult.status === 0) { if (perUserResult.stdout) process.stdout.write(perUserResult.stdout); console.log(`[info] Removed system app ${packageName} for user 0`); return; } const perUserOutput = `${perUserResult.stdout ?? ""}${perUserResult.stderr ?? ""}`; if (/not installed/i.test(perUserOutput)) { console.log(`[info] ${packageName} was not installed for user 0`); return; } if (perUserOutput) process.stderr.write(perUserOutput); } if (output) process.stderr.write(output); throw new Error(`Unable to remove ${packageName}`); } function requireConnectedDevice(serial: string): void { const result = spawnSync("adb", ["-s", serial, "get-state"], { cwd: root, encoding: "utf8", }); if (result.error) throw result.error; if (result.status !== 0 || result.stdout.trim() !== "device") throw new Error(`ADB device ${serial} is not connected and authorized`); } const serial = value("--serial"); if (!serial) throw new Error("Specify the Xiaomi target with --serial "); if (!args.includes("--confirm-reinstall")) throw new Error("Refusing to replace an app without --confirm-reinstall"); requireConnectedDevice(serial); run("bun", ["run", profile.patchScript]); const apk = value("--apk") ?? path.join(root, "outputs", `${app}_patched-signed.apk`); if (!existsSync(apk)) { throw new Error( `Signed APK is missing at ${path.join("outputs", `${app}_patched-signed.apk`)}`, ); } // Android can reject an uninstall of an already-patched package with // DELETE_FAILED_INTERNAL_ERROR, even though `adb install -r` updates the same // package normally. Keep the patched app in place and only remove the original // package when its unchanged provider authority would conflict with the patch. if (profile.removeLegacy) { uninstallIfPresent(serial, profile.legacyPackage); } else { console.log(`[info] Keeping protected OEM package ${profile.legacyPackage}`); } run("adb", ["-s", serial, "install", "-r", "--no-incremental", apk]); console.log( `[ok] Installed ${path.basename(apk)}${profile.legacySystemImage ? ` alongside protected OEM system image ${profile.legacyPackage}` : profile.removeLegacy ? ` after replacing ${profile.legacyPackage}` : " as a side-by-side update"}.`, );