mirror of
https://github.com/xenos1337/httptoolkit-patcher.git
synced 2026-07-15 16:20:02 +02:00
Initial Commit
Signed-off-by: xenos <66328734+xenos1337@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,120 @@
|
|||||||
|
name: Release Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build ${{ matrix.os }}
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: linux
|
||||||
|
runner: ubuntu-latest
|
||||||
|
targets: node18-linux-x64,node18-linux-arm64
|
||||||
|
- os: macos
|
||||||
|
runner: macos-latest
|
||||||
|
targets: node18-macos-x64,node18-macos-arm64
|
||||||
|
- os: windows
|
||||||
|
runner: windows-latest
|
||||||
|
targets: node18-win-x64
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '18'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Create dist directory
|
||||||
|
run: mkdir -p dist
|
||||||
|
|
||||||
|
- name: Build for ${{ matrix.os }}
|
||||||
|
run: |
|
||||||
|
npm run build
|
||||||
|
npx pkg dist/bundle.cjs --targets ${{ matrix.targets }} --compress GZip --out-path dist/
|
||||||
|
|
||||||
|
- name: List built files (debug - Unix)
|
||||||
|
if: matrix.os != 'windows'
|
||||||
|
run: ls -lah dist/
|
||||||
|
|
||||||
|
- name: List built files (debug - Windows)
|
||||||
|
if: matrix.os == 'windows'
|
||||||
|
shell: pwsh
|
||||||
|
run: Get-ChildItem -Path dist/ | Format-Table -AutoSize
|
||||||
|
|
||||||
|
- name: Rename executables (Linux)
|
||||||
|
if: matrix.os == 'linux'
|
||||||
|
run: |
|
||||||
|
[ -f dist/bundle-x64 ] && mv dist/bundle-x64 dist/httptoolkit-patcher-linux-x64
|
||||||
|
[ -f dist/bundle-arm64 ] && mv dist/bundle-arm64 dist/httptoolkit-patcher-linux-arm64
|
||||||
|
rm -f dist/bundle dist/bundle.cjs 2>/dev/null || true
|
||||||
|
|
||||||
|
- name: Rename executables (macOS)
|
||||||
|
if: matrix.os == 'macos'
|
||||||
|
run: |
|
||||||
|
[ -f dist/bundle-x64 ] && mv dist/bundle-x64 dist/httptoolkit-patcher-macos-x64
|
||||||
|
[ -f dist/bundle-arm64 ] && mv dist/bundle-arm64 dist/httptoolkit-patcher-macos-arm64
|
||||||
|
rm -f dist/bundle dist/bundle.cjs 2>/dev/null || true
|
||||||
|
|
||||||
|
- name: Rename executables (Windows)
|
||||||
|
if: matrix.os == 'windows'
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
if (Test-Path dist/bundle.exe) { Move-Item -Force dist/bundle.exe dist/httptoolkit-patcher.exe }
|
||||||
|
if (Test-Path dist/bundle-x64.exe) { Move-Item -Force dist/bundle-x64.exe dist/httptoolkit-patcher-win-x64.exe }
|
||||||
|
Remove-Item -Force dist/bundle.cjs -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: binaries-${{ matrix.os }}
|
||||||
|
path: dist/*
|
||||||
|
|
||||||
|
release:
|
||||||
|
name: Create Release
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Download all artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
pattern: binaries-*
|
||||||
|
path: artifacts
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Prepare release files
|
||||||
|
run: |
|
||||||
|
mkdir -p release
|
||||||
|
cp artifacts/* release/ 2>/dev/null || find artifacts -type f -exec cp {} release/ \;
|
||||||
|
ls -lah release/
|
||||||
|
|
||||||
|
- name: Extract version from tag
|
||||||
|
id: get_version
|
||||||
|
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Create Release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
files: release/*
|
||||||
|
draft: false
|
||||||
|
prerelease: false
|
||||||
|
generate_release_notes: true
|
||||||
|
name: Release ${{ steps.get_version.outputs.VERSION }}
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
yarn.lock
|
||||||
|
node_modules
|
||||||
|
.yarn
|
||||||
|
.yarnrc.yml
|
||||||
|
dist/
|
||||||
|
release/
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 xenos1337
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
# HTTP Toolkit Patcher
|
||||||
|
|
||||||
|
A minimal, cross-platform patcher for HTTP Toolkit that removes subscription requirements.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Why?
|
||||||
|
|
||||||
|
I don't feel like paying a **monthly subscription** for an HTTP proxy/interceptor. A lifetime license? Sure. But subscription-based for a dev tool? No thanks.
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
The patcher intercepts HTTP Toolkit's authentication functions:
|
||||||
|
- `isPaidUser`
|
||||||
|
- `isLoggedIn`
|
||||||
|
- `userHasSubscription`
|
||||||
|
- `userEmail`
|
||||||
|
- `mightBePaidUser`
|
||||||
|
- `isPastDueUser`
|
||||||
|
|
||||||
|
By hooking these functions, we bypass the subscription checks entirely.
|
||||||
|
|
||||||
|
### Can They Fix It?
|
||||||
|
|
||||||
|
Yes, but they most likely won't. Fixing this would require changing their entire codebase architecture. And if they do? I'll just update the patcher.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Install dependencies:
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Build standalone executables for all platforms and architectures:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates self-contained executables in the `dist/` directory:
|
||||||
|
|
||||||
|
**Windows:**
|
||||||
|
- `httptoolkit-patcher-win-x64.exe` (64-bit)
|
||||||
|
|
||||||
|
**Linux:**
|
||||||
|
- `httptoolkit-patcher-linux-x64` (x86_64/AMD64)
|
||||||
|
- `httptoolkit-patcher-linux-arm64` (ARM64/AArch64)
|
||||||
|
|
||||||
|
**macOS:**
|
||||||
|
- `httptoolkit-patcher-macos-x64` (Intel)
|
||||||
|
- `httptoolkit-patcher-macos-arm64` (Apple Silicon M Chip)
|
||||||
|
|
||||||
|
**Note:** These are standalone executables created with `pkg` that include Node.js runtime and all dependencies. No need to install Node.js separately!
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### From Source
|
||||||
|
|
||||||
|
**Patch:**
|
||||||
|
```bash
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
**Unpatch:**
|
||||||
|
```bash
|
||||||
|
npm run unpatch
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using Prebuilt Executables
|
||||||
|
|
||||||
|
Download the appropriate executable for your platform from [Releases](https://github.com/xenos1337/httptoolkit-patcher/releases), then:
|
||||||
|
|
||||||
|
**Windows:**
|
||||||
|
```cmd
|
||||||
|
httptoolkit-patcher-win-x64.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
**Linux/macOS:**
|
||||||
|
```bash
|
||||||
|
chmod +x httptoolkit-patcher-linux-x64 # or your architecture
|
||||||
|
./httptoolkit-patcher-linux-x64
|
||||||
|
```
|
||||||
|
|
||||||
|
That's it. The patcher handles everything automatically.
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
1. Finds HTTP Toolkit installation
|
||||||
|
2. Kills running processes
|
||||||
|
3. Requests elevation if needed
|
||||||
|
4. Backs up `app.asar`
|
||||||
|
5. Extracts and patches `preload.js`
|
||||||
|
6. Repackages and launches
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**Permission errors?** Run as admin/sudo or let the patcher request elevation.
|
||||||
|
|
||||||
|
**Already patched?** The patcher will ask if you want to repatch.
|
||||||
|
|
||||||
|
**Want to restore?** Run `npm run unpatch` to restore from backup.
|
||||||
|
|
||||||
|
**Anything else?** Open an issue on the [GitHub repository](https://github.com/xenos1337/httptoolkit-patcher/issues).
|
||||||
|
|
||||||
|
## GitHub Release Workflow
|
||||||
|
|
||||||
|
This project includes an automated GitHub Actions workflow that builds and releases the patcher for all platforms.
|
||||||
|
|
||||||
|
### Creating a Release
|
||||||
|
|
||||||
|
1. Create and push a version tag:
|
||||||
|
```bash
|
||||||
|
git tag v2.0.1
|
||||||
|
git push origin v2.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
2. The GitHub Actions workflow will automatically:
|
||||||
|
- Build for all platforms and architectures (Windows x64, Linux x64/ARM64, macOS Intel/Apple Silicon)
|
||||||
|
- Create standalone native executables using pkg
|
||||||
|
- Create a GitHub release with all binaries as downloadable artifacts
|
||||||
|
|
||||||
|
### Manual Workflow Trigger
|
||||||
|
|
||||||
|
You can also manually trigger the release workflow from the GitHub Actions tab.
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
|
||||||
|
This tool is provided as-is. Use at your own risk. For educational purposes only.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT License - see [LICENSE](LICENSE) file.
|
||||||
|
|
||||||
@@ -0,0 +1,454 @@
|
|||||||
|
// @ts-check
|
||||||
|
import { spawn, execSync } from "child_process";
|
||||||
|
import asar from "@electron/asar";
|
||||||
|
import chalk from "chalk";
|
||||||
|
import path from "path";
|
||||||
|
import fs from "fs";
|
||||||
|
import readline from "readline";
|
||||||
|
import https from "https";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
|
const isWin = process.platform === "win32";
|
||||||
|
const isMac = process.platform === "darwin";
|
||||||
|
const isLinux = process.platform === "linux";
|
||||||
|
|
||||||
|
// Handle both ESM and pkg-bundled scenarios
|
||||||
|
const __filename = (() => {
|
||||||
|
// Check if running as pkg bundle
|
||||||
|
if (process.pkg) {
|
||||||
|
return process.execPath;
|
||||||
|
}
|
||||||
|
// Check if import.meta.url exists (ESM)
|
||||||
|
if (typeof import.meta !== 'undefined' && import.meta.url) {
|
||||||
|
return fileURLToPath(import.meta.url);
|
||||||
|
}
|
||||||
|
// Fallback to __filename if in CommonJS
|
||||||
|
return typeof __filename !== 'undefined' ? __filename : process.argv[1];
|
||||||
|
})();
|
||||||
|
|
||||||
|
// Check if running with elevated privileges
|
||||||
|
const isElevated = () => {
|
||||||
|
if (isWin) {
|
||||||
|
try {
|
||||||
|
execSync("net session", { stdio: "ignore" });
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return process.getuid && process.getuid() === 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper function to prompt user for input
|
||||||
|
const prompt = question => {
|
||||||
|
const rl = readline.createInterface({
|
||||||
|
input: process.stdin,
|
||||||
|
output: process.stdout,
|
||||||
|
});
|
||||||
|
return new Promise(resolve => {
|
||||||
|
rl.question(question, answer => {
|
||||||
|
rl.close();
|
||||||
|
resolve(answer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper function to fetch content from URL
|
||||||
|
const fetchUrl = url => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
https
|
||||||
|
.get(url, res => {
|
||||||
|
let data = "";
|
||||||
|
res.on("data", chunk => (data += chunk));
|
||||||
|
res.on("end", () => resolve(data));
|
||||||
|
})
|
||||||
|
.on("error", reject);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper function to remove directory recursively
|
||||||
|
const rm = dirPath => {
|
||||||
|
if (!fs.existsSync(dirPath)) return;
|
||||||
|
if (!fs.lstatSync(dirPath).isDirectory()) return fs.rmSync(dirPath, { force: true });
|
||||||
|
for (const entry of fs.readdirSync(dirPath)) {
|
||||||
|
const entryPath = path.join(dirPath, entry);
|
||||||
|
if (fs.lstatSync(entryPath).isDirectory()) rm(entryPath);
|
||||||
|
else fs.rmSync(entryPath, { force: true });
|
||||||
|
}
|
||||||
|
fs.rmdirSync(dirPath);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Find HTTP Toolkit installation path
|
||||||
|
const findAppPath = async () => {
|
||||||
|
const possiblePaths = isWin ? [path.join("C:", "Program Files", "HTTP Toolkit", "resources"), path.join("C:", "Program Files (x86)", "HTTP Toolkit", "resources")] : isMac ? ["/Applications/HTTP Toolkit.app/Contents/Resources"] : ["/opt/HTTP Toolkit/resources", "/opt/httptoolkit/resources"];
|
||||||
|
|
||||||
|
for (const p of possiblePaths) {
|
||||||
|
if (fs.existsSync(path.join(p, "app.asar"))) {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(chalk.yellowBright`[!] HTTP Toolkit not found in default locations`);
|
||||||
|
const userPath = await prompt("Please enter the path to HTTP Toolkit executable/app: ");
|
||||||
|
|
||||||
|
if (!userPath) {
|
||||||
|
console.error(chalk.redBright`[-] No path provided`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract resources path from user input
|
||||||
|
let resourcesPath = userPath.trim().replace(/['"]/g, "");
|
||||||
|
|
||||||
|
if (resourcesPath.endsWith(".exe") || resourcesPath.endsWith(".app")) resourcesPath = path.dirname(resourcesPath);
|
||||||
|
if (!resourcesPath.endsWith("resources")) resourcesPath = path.join(resourcesPath, "resources");
|
||||||
|
|
||||||
|
if (!fs.existsSync(path.join(resourcesPath, "app.asar"))) {
|
||||||
|
console.error(chalk.redBright`[-] app.asar not found at ${resourcesPath}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourcesPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Request elevated permissions
|
||||||
|
const requestElevation = async () => {
|
||||||
|
console.log(chalk.yellowBright`[!] Requesting elevated permissions...`);
|
||||||
|
|
||||||
|
if (isWin) {
|
||||||
|
// Windows: Use PowerShell to run as administrator
|
||||||
|
const script = `Start-Process -FilePath "node" -ArgumentList '"${__filename}"' -Verb RunAs`;
|
||||||
|
try {
|
||||||
|
spawn("powershell", ["-Command", script], {
|
||||||
|
stdio: "inherit",
|
||||||
|
shell: true,
|
||||||
|
});
|
||||||
|
console.log(chalk.blueBright`[+] Restarting with administrator privileges...`);
|
||||||
|
process.exit(0);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(chalk.redBright`[-] Failed to elevate permissions: ${e.message}`);
|
||||||
|
console.error(chalk.redBright`[-] Please run as administrator manually`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(chalk.blueBright`[+] Restarting with sudo...`);
|
||||||
|
try {
|
||||||
|
const child = spawn("sudo", ["node", __filename], {
|
||||||
|
stdio: "inherit",
|
||||||
|
});
|
||||||
|
child.on("exit", code => process.exit(code || 0));
|
||||||
|
} catch (e) {
|
||||||
|
console.error(chalk.redBright`[-] Failed to elevate permissions: ${e.message}`);
|
||||||
|
console.error(chalk.redBright`[-] Please run with sudo manually`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if we have write permissions
|
||||||
|
const checkPermissions = filePath => {
|
||||||
|
try {
|
||||||
|
fs.accessSync(filePath, fs.constants.W_OK);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Kill HTTP Toolkit processes
|
||||||
|
const killHttpToolkit = async () => {
|
||||||
|
console.log(chalk.yellowBright`[+] Checking for running HTTP Toolkit processes...`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (isWin) {
|
||||||
|
// Windows: Use tasklist to find and taskkill to terminate
|
||||||
|
const output = execSync('tasklist /FI "IMAGENAME eq HTTP Toolkit.exe" /FO CSV /NH', { encoding: "utf-8" });
|
||||||
|
if (output.includes("HTTP Toolkit.exe")) {
|
||||||
|
console.log(chalk.yellowBright`[!] HTTP Toolkit is running, attempting to close it...`);
|
||||||
|
execSync('taskkill /F /IM "HTTP Toolkit.exe" /T', { stdio: "ignore" });
|
||||||
|
console.log(chalk.greenBright`[+] HTTP Toolkit processes terminated`);
|
||||||
|
// Wait a moment for the process to fully close
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||||
|
} else {
|
||||||
|
console.log(chalk.greenBright`[+] HTTP Toolkit is not running`);
|
||||||
|
}
|
||||||
|
} else if (isMac) {
|
||||||
|
// macOS: Use pgrep and pkill
|
||||||
|
try {
|
||||||
|
execSync('pgrep -f "HTTP Toolkit"', { stdio: "ignore" });
|
||||||
|
console.log(chalk.yellowBright`[!] HTTP Toolkit is running, attempting to close it...`);
|
||||||
|
execSync('pkill -f "HTTP Toolkit"', { stdio: "ignore" });
|
||||||
|
console.log(chalk.greenBright`[+] HTTP Toolkit processes terminated`);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||||
|
} catch (e) {
|
||||||
|
console.log(chalk.greenBright`[+] HTTP Toolkit is not running`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Linux: Use pgrep and pkill
|
||||||
|
try {
|
||||||
|
execSync('pgrep -f "HTTP Toolkit"', { stdio: "ignore" });
|
||||||
|
console.log(chalk.yellowBright`[!] HTTP Toolkit is running, attempting to close it...`);
|
||||||
|
execSync('pkill -f "HTTP Toolkit"', { stdio: "ignore" });
|
||||||
|
console.log(chalk.greenBright`[+] HTTP Toolkit processes terminated`);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||||
|
} catch (e) {
|
||||||
|
console.log(chalk.greenBright`[+] HTTP Toolkit is not running`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(chalk.yellowBright`[!] Could not check/kill processes: ${e.message}`);
|
||||||
|
console.log(chalk.yellowBright`[!] If HTTP Toolkit is running, please close it manually`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Unpatch/restore function
|
||||||
|
const unpatchApp = async () => {
|
||||||
|
console.log(chalk.blueBright`[+] HTTP Toolkit Unpatcher Started`);
|
||||||
|
|
||||||
|
// Step 1: Find app path
|
||||||
|
const appPath = await findAppPath();
|
||||||
|
console.log(chalk.greenBright`[+] HTTP Toolkit found at ${appPath}`);
|
||||||
|
|
||||||
|
// Step 2: Kill HTTP Toolkit if running
|
||||||
|
await killHttpToolkit();
|
||||||
|
|
||||||
|
// Step 3: Check permissions
|
||||||
|
const asarPath = path.join(appPath, "app.asar");
|
||||||
|
const backupPath = path.join(appPath, "app.asar.bak");
|
||||||
|
const extractPath = path.join(appPath, "app.asar_extracted");
|
||||||
|
|
||||||
|
// Check if we have write permissions on both the directory and the file
|
||||||
|
const hasPermissions = checkPermissions(appPath) && checkPermissions(asarPath);
|
||||||
|
|
||||||
|
if (!hasPermissions) {
|
||||||
|
console.log(chalk.yellowBright`[!] No write permissions for ${appPath}`);
|
||||||
|
|
||||||
|
if (isElevated()) {
|
||||||
|
console.error(chalk.redBright`[-] Still no permissions even with elevated privileges`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(chalk.yellowBright`[!] Administrator/sudo privileges required for unpatching`);
|
||||||
|
const answer = await prompt("Do you want to request elevated permissions? [Y/n]: ");
|
||||||
|
if (!answer || answer.toLowerCase() === "y" || answer.toLowerCase() === "yes") {
|
||||||
|
await requestElevation();
|
||||||
|
} else {
|
||||||
|
console.log(chalk.redBright`[-] Cannot proceed without write permissions`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: Check if backup exists
|
||||||
|
if (!fs.existsSync(backupPath)) {
|
||||||
|
console.error(chalk.redBright`[-] Backup file not found at ${backupPath}`);
|
||||||
|
console.error(chalk.redBright`[-] Cannot unpatch without backup file`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 5: Restore from backup
|
||||||
|
console.log(chalk.yellowBright`[+] Restoring from backup...`);
|
||||||
|
try {
|
||||||
|
fs.copyFileSync(backupPath, asarPath);
|
||||||
|
console.log(chalk.greenBright`[+] Restored app.asar from backup`);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(chalk.redBright`[-] Failed to restore backup: ${e.message}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 6: Clean up extracted files if they exist
|
||||||
|
if (fs.existsSync(extractPath)) {
|
||||||
|
console.log(chalk.yellowBright`[+] Removing extracted files...`);
|
||||||
|
rm(extractPath);
|
||||||
|
console.log(chalk.greenBright`[+] Cleaned up extracted files`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 7: Optionally remove backup
|
||||||
|
const removeBackup = await prompt("Do you want to remove the backup file? (y/n): ");
|
||||||
|
if (removeBackup.toLowerCase() === "y" || removeBackup.toLowerCase() === "yes") {
|
||||||
|
fs.rmSync(backupPath, { force: true });
|
||||||
|
console.log(chalk.greenBright`[+] Backup file removed`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(chalk.greenBright`[+] Successfully unpatched!`);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Main patching function
|
||||||
|
const patchApp = async () => {
|
||||||
|
console.log(chalk.blueBright`[+] HTTP Toolkit Patcher Started`);
|
||||||
|
|
||||||
|
// Step 1: Find app path
|
||||||
|
const appPath = await findAppPath();
|
||||||
|
console.log(chalk.greenBright`[+] HTTP Toolkit found at ${appPath}`);
|
||||||
|
|
||||||
|
// Step 2: Kill HTTP Toolkit if running
|
||||||
|
await killHttpToolkit();
|
||||||
|
|
||||||
|
// Step 3: Check permissions
|
||||||
|
const asarPath = path.join(appPath, "app.asar");
|
||||||
|
|
||||||
|
// Check if we have write permissions on both the directory and the file
|
||||||
|
const hasPermissions = checkPermissions(appPath) && checkPermissions(asarPath);
|
||||||
|
|
||||||
|
if (!hasPermissions) {
|
||||||
|
console.log(chalk.yellowBright`[!] No write permissions for ${appPath}`);
|
||||||
|
|
||||||
|
if (isElevated()) {
|
||||||
|
console.error(chalk.redBright`[-] Still no permissions even with elevated privileges`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(chalk.yellowBright`[!] Administrator/sudo privileges required for patching`);
|
||||||
|
const answer = await prompt("Do you want to request elevated permissions? [Y/n]: ");
|
||||||
|
if (!answer || answer.toLowerCase() === "y" || answer.toLowerCase() === "yes") {
|
||||||
|
await requestElevation();
|
||||||
|
} else {
|
||||||
|
console.log(chalk.redBright`[-] Cannot proceed without write permissions`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: Backup app.asar
|
||||||
|
const backupPath = path.join(appPath, "app.asar.bak");
|
||||||
|
if (!fs.existsSync(backupPath)) {
|
||||||
|
console.log(chalk.yellowBright`[+] Creating backup...`);
|
||||||
|
fs.copyFileSync(asarPath, backupPath);
|
||||||
|
console.log(chalk.greenBright`[+] Backup created at ${backupPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 5: Extract app.asar
|
||||||
|
const extractPath = path.join(appPath, "app.asar_extracted");
|
||||||
|
console.log(chalk.yellowBright`[+] Extracting app.asar...`);
|
||||||
|
rm(extractPath);
|
||||||
|
asar.extractAll(asarPath, extractPath);
|
||||||
|
console.log(chalk.greenBright`[+] Extracted to ${extractPath}`);
|
||||||
|
|
||||||
|
// Step 6: Check if preload.js exists
|
||||||
|
const preloadPath = path.join(extractPath, "build", "preload.js");
|
||||||
|
if (!fs.existsSync(preloadPath)) {
|
||||||
|
console.error(chalk.redBright`[-] preload.js not found at ${preloadPath}`);
|
||||||
|
rm(extractPath);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 7: Fetch inject.js from GitHub
|
||||||
|
console.log(chalk.yellowBright`[+] Fetching inject code from GitHub...`);
|
||||||
|
const injectCode = await fetchUrl("https://raw.githubusercontent.com/xenos1337/httptoolkit-patcher/refs/heads/master/inject.js");
|
||||||
|
if (!injectCode || !injectCode.includes("injectPageContextHooks")) {
|
||||||
|
console.error(chalk.redBright`[-] Failed to fetch inject.js from GitHub`);
|
||||||
|
rm(extractPath);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
console.log(chalk.greenBright`[+] Inject code fetched successfully`);
|
||||||
|
|
||||||
|
// Step 8: Read preload.js and check if already patched
|
||||||
|
let preloadContent = fs.readFileSync(preloadPath, "utf-8");
|
||||||
|
const isPatched = preloadContent.includes("injectPageContextHooks");
|
||||||
|
|
||||||
|
if (isPatched) {
|
||||||
|
console.log(chalk.yellowBright`[!] File already patched`);
|
||||||
|
const answer = await prompt("Do you want to repatch? (y/n): ");
|
||||||
|
|
||||||
|
if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
|
||||||
|
console.log(chalk.blueBright`[+] Patching cancelled`);
|
||||||
|
rm(extractPath);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the existing injectPageContextHooks function
|
||||||
|
console.log(chalk.yellowBright`[+] Replacing existing patch...`);
|
||||||
|
const functionRegex = /\(function injectPageContextHooks\(\) \{[\s\S]*?\}\)\(\);/;
|
||||||
|
preloadContent = preloadContent.replace(functionRegex, injectCode);
|
||||||
|
} else {
|
||||||
|
// Find line with electron_1 and insert inject code below it
|
||||||
|
console.log(chalk.yellowBright`[+] Applying patch...`);
|
||||||
|
const lines = preloadContent.split("\n");
|
||||||
|
let insertIndex = -1;
|
||||||
|
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
if (lines[i].includes("electron_1")) {
|
||||||
|
insertIndex = i + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insertIndex === -1) {
|
||||||
|
console.error(chalk.redBright`[-] Could not find insertion point (electron_1) in preload.js`);
|
||||||
|
rm(extractPath);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.splice(insertIndex, 0, injectCode);
|
||||||
|
preloadContent = lines.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 9: Write patched preload.js
|
||||||
|
fs.writeFileSync(preloadPath, preloadContent, "utf-8");
|
||||||
|
console.log(chalk.greenBright`[+] preload.js patched successfully`);
|
||||||
|
|
||||||
|
// Step 10: Repackage app.asar
|
||||||
|
console.log(chalk.yellowBright`[+] Repackaging app.asar...`);
|
||||||
|
await asar.createPackage(extractPath, asarPath);
|
||||||
|
console.log(chalk.greenBright`[+] app.asar repackaged successfully`);
|
||||||
|
|
||||||
|
// Step 11: Clean up
|
||||||
|
console.log(chalk.yellowBright`[+] Cleaning up temporary files...`);
|
||||||
|
rm(extractPath);
|
||||||
|
console.log(chalk.greenBright`[+] Successfully patched!`);
|
||||||
|
|
||||||
|
// Step 12: Open HTTP Toolkit as detached process
|
||||||
|
console.log(chalk.blueBright`[+] Opening HTTP Toolkit...`);
|
||||||
|
try {
|
||||||
|
const command = isWin ? `"${path.resolve(appPath, "..", "HTTP Toolkit.exe")}"` : isMac ? 'open -a "HTTP Toolkit"' : "httptoolkit";
|
||||||
|
const child = spawn(command, {
|
||||||
|
stdio: "ignore",
|
||||||
|
shell: true,
|
||||||
|
detached: true,
|
||||||
|
});
|
||||||
|
child.unref(); // Completely detach the child process
|
||||||
|
console.log(chalk.greenBright`[+] HTTP Toolkit launched successfully`);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(chalk.yellowBright`[!] Could not auto-start HTTP Toolkit: ${e.message}`);
|
||||||
|
console.log(chalk.blueBright`[+] Please start HTTP Toolkit manually`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse command line arguments
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
const command = args[0];
|
||||||
|
|
||||||
|
// Get the command name for display
|
||||||
|
const commandName = process.pkg ? path.basename(__filename) : 'node index.js';
|
||||||
|
|
||||||
|
// Run the appropriate command
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
if (command === "unpatch" || command === "restore") {
|
||||||
|
await unpatchApp();
|
||||||
|
} else if (command === "help" || command === "-h" || command === "--help") {
|
||||||
|
console.log(chalk.blueBright`HTTP Toolkit Patcher`);
|
||||||
|
console.log(chalk.white`\nUsage:`);
|
||||||
|
console.log(chalk.white` ${commandName} [command]`);
|
||||||
|
console.log(chalk.white`\nCommands:`);
|
||||||
|
console.log(chalk.white` patch ${chalk.gray`- Patch HTTP Toolkit (default)`}`);
|
||||||
|
console.log(chalk.white` unpatch ${chalk.gray`- Restore original HTTP Toolkit from backup`}`);
|
||||||
|
console.log(chalk.white` restore ${chalk.gray`- Alias for unpatch`}`);
|
||||||
|
console.log(chalk.white` help ${chalk.gray`- Show this help message`}`);
|
||||||
|
process.exit(0);
|
||||||
|
} else if (!command || command === "patch") {
|
||||||
|
// Ask for confirmation before patching
|
||||||
|
const answer = await prompt("Do you want to patch HTTP Toolkit? [Y/n]: ");
|
||||||
|
if (answer.toLowerCase() === "n" || answer.toLowerCase() === "no") {
|
||||||
|
console.log(chalk.blueBright`[+] Patching cancelled`);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
await patchApp();
|
||||||
|
} else {
|
||||||
|
console.error(chalk.redBright`[-] Unknown command: ${command}`);
|
||||||
|
console.log(chalk.yellowBright`[!] Use 'help' to see available commands`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(chalk.redBright`[-] Error: ${error.message}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
(function injectPageContextHooks() {
|
||||||
|
const injectionCode = `
|
||||||
|
(function() {
|
||||||
|
console.log("[PAGE-INJECT] Installing hooks in page context");
|
||||||
|
|
||||||
|
const propertyHooks = {
|
||||||
|
isPaidUser: true,
|
||||||
|
isLoggedIn: true,
|
||||||
|
userHasSubscription: true,
|
||||||
|
userEmail: "hi@httptoolkit.com",
|
||||||
|
mightBePaidUser: true,
|
||||||
|
isPastDueUser: false,
|
||||||
|
userSubscription: {
|
||||||
|
status: "active",
|
||||||
|
plan: "pro",
|
||||||
|
sku: "sku",
|
||||||
|
tierCode: "pro",
|
||||||
|
interval: "monthly",
|
||||||
|
quantity: 1,
|
||||||
|
expiry: new Date("2999-01-01"),
|
||||||
|
updateBillingDetailsUrl: "https://httptoolkit.com/",
|
||||||
|
cancelSubscriptionUrl: "https://httptoolkit.com/",
|
||||||
|
lastReceiptUrl: "https://httptoolkit.com/",
|
||||||
|
canManageSubscription: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const hookedObjects = new WeakSet();
|
||||||
|
|
||||||
|
// Override Object.defineProperty to intercept all property definitions
|
||||||
|
const originalDefineProperty = Object.defineProperty;
|
||||||
|
Object.defineProperty = function(target, prop, descriptor) {
|
||||||
|
// Intercept our target properties
|
||||||
|
if (prop in propertyHooks) {
|
||||||
|
console.log("[PAGE-INJECT] Intercepting defineProperty for: " + prop);
|
||||||
|
|
||||||
|
if (descriptor && descriptor.get) {
|
||||||
|
const originalGetter = descriptor.get;
|
||||||
|
descriptor.get = function() {
|
||||||
|
const originalValue = originalGetter.call(this);
|
||||||
|
console.log("[PAGE-INJECT] " + prop + " getter called, original=" + originalValue + ", returning=" + JSON.stringify(propertyHooks[prop]));
|
||||||
|
return propertyHooks[prop];
|
||||||
|
};
|
||||||
|
} else if (descriptor && descriptor.value !== undefined) {
|
||||||
|
console.log("[PAGE-INJECT] " + prop + " value being defined, overriding to " + JSON.stringify(propertyHooks[prop]));
|
||||||
|
descriptor.value = propertyHooks[prop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return originalDefineProperty.call(this, target, prop, descriptor);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hook Object.defineProperties too
|
||||||
|
const originalDefineProperties = Object.defineProperties;
|
||||||
|
Object.defineProperties = function(target, props) {
|
||||||
|
for (let prop in props) {
|
||||||
|
if (prop in propertyHooks) {
|
||||||
|
console.log("[PAGE-INJECT] Intercepting defineProperties for: " + prop);
|
||||||
|
if (props[prop].get) {
|
||||||
|
const originalGetter = props[prop].get;
|
||||||
|
props[prop].get = function() {
|
||||||
|
const originalValue = originalGetter.call(this);
|
||||||
|
console.log("[PAGE-INJECT] " + prop + " getter called, original=" + originalValue + ", returning=" + JSON.stringify(propertyHooks[prop]));
|
||||||
|
return propertyHooks[prop];
|
||||||
|
};
|
||||||
|
} else if (props[prop].value !== undefined) {
|
||||||
|
props[prop].value = propertyHooks[prop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return originalDefineProperties.call(this, target, props);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Periodically scan and patch existing objects
|
||||||
|
function scanAndPatch() {
|
||||||
|
// Search through window and common store locations
|
||||||
|
const searchPaths = [
|
||||||
|
window,
|
||||||
|
window.accountStore,
|
||||||
|
window.stores && window.stores.accountStore,
|
||||||
|
window.appState && window.appState.accountStore,
|
||||||
|
];
|
||||||
|
|
||||||
|
searchPaths.forEach((obj, idx) => {
|
||||||
|
if (!obj || hookedObjects.has(obj)) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Object.keys(propertyHooks).forEach(prop => {
|
||||||
|
try {
|
||||||
|
const desc = Object.getOwnPropertyDescriptor(obj, prop);
|
||||||
|
if (desc && desc.configurable) {
|
||||||
|
console.log("[PAGE-INJECT] Found " + prop + " on object #" + idx + ", patching...");
|
||||||
|
|
||||||
|
if (desc.get) {
|
||||||
|
const originalGetter = desc.get;
|
||||||
|
Object.defineProperty(obj, prop, {
|
||||||
|
get: function() {
|
||||||
|
const originalValue = originalGetter.call(this);
|
||||||
|
console.log("[PAGE-INJECT] " + prop + " getter intercepted, original=" + originalValue + ", returning=" + JSON.stringify(propertyHooks[prop]));
|
||||||
|
return propertyHooks[prop];
|
||||||
|
},
|
||||||
|
set: desc.set,
|
||||||
|
configurable: true,
|
||||||
|
enumerable: desc.enumerable
|
||||||
|
});
|
||||||
|
} else if (desc.writable) {
|
||||||
|
obj[prop] = propertyHooks[prop];
|
||||||
|
console.log("[PAGE-INJECT] " + prop + " value set to " + JSON.stringify(propertyHooks[prop]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Ignore individual property errors
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
hookedObjects.add(obj);
|
||||||
|
} catch (e) {
|
||||||
|
// Ignore object access errors
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Also try to find accountStore by scanning window properties
|
||||||
|
try {
|
||||||
|
for (let key in window) {
|
||||||
|
try {
|
||||||
|
const obj = window[key];
|
||||||
|
if (obj && typeof obj === 'object' && 'accountStore' in obj) {
|
||||||
|
console.log("[PAGE-INJECT] Found accountStore in window." + key);
|
||||||
|
const store = obj.accountStore;
|
||||||
|
if (store && !hookedObjects.has(store)) {
|
||||||
|
Object.keys(propertyHooks).forEach(prop => {
|
||||||
|
try {
|
||||||
|
const desc = Object.getOwnPropertyDescriptor(store, prop);
|
||||||
|
if (desc && desc.configurable && desc.get) {
|
||||||
|
const originalGetter = desc.get;
|
||||||
|
Object.defineProperty(store, prop, {
|
||||||
|
get: function() {
|
||||||
|
const originalValue = originalGetter.call(this);
|
||||||
|
console.log("[PAGE-INJECT] accountStore." + prop + " intercepted, original=" + originalValue + ", returning=" + JSON.stringify(propertyHooks[prop]));
|
||||||
|
return propertyHooks[prop];
|
||||||
|
},
|
||||||
|
set: desc.set,
|
||||||
|
configurable: true,
|
||||||
|
enumerable: desc.enumerable
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
});
|
||||||
|
hookedObjects.add(store);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run initial scan
|
||||||
|
scanAndPatch();
|
||||||
|
|
||||||
|
// Scan periodically for late-initialized stores
|
||||||
|
let scanCount = 0;
|
||||||
|
const scanInterval = setInterval(() => {
|
||||||
|
scanCount++;
|
||||||
|
scanAndPatch();
|
||||||
|
|
||||||
|
if (scanCount >= 10) {
|
||||||
|
clearInterval(scanInterval);
|
||||||
|
console.log("[PAGE-INJECT] Stopped periodic scanning after 10 attempts");
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
console.log("[PAGE-INJECT] Hooks installed successfully");
|
||||||
|
})();
|
||||||
|
`;
|
||||||
|
|
||||||
|
function injectScript() {
|
||||||
|
try {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.textContent = injectionCode;
|
||||||
|
(document.head || document.documentElement).appendChild(script);
|
||||||
|
script.remove(); // Clean up the script tag
|
||||||
|
console.log("[PRELOAD] Injected page context hooks");
|
||||||
|
} catch (e) {
|
||||||
|
console.error("[PRELOAD] Failed to inject hooks:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to inject immediately if DOM is ready, otherwise wait
|
||||||
|
if (document.documentElement) {
|
||||||
|
injectScript();
|
||||||
|
} else {
|
||||||
|
// Wait for document to be created
|
||||||
|
const observer = new MutationObserver(() => {
|
||||||
|
if (document.documentElement) {
|
||||||
|
observer.disconnect();
|
||||||
|
injectScript();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
observer.observe(document, { childList: true, subtree: true });
|
||||||
|
|
||||||
|
// Also try on DOMContentLoaded as backup
|
||||||
|
if (document.addEventListener) {
|
||||||
|
document.addEventListener('DOMContentLoaded', injectScript, { once: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
Generated
+2200
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"name": "httptoolkit-patcher",
|
||||||
|
"version": "2.0.5",
|
||||||
|
"description": "HTTP Toolkit Pro Patcher",
|
||||||
|
"main": "index.js",
|
||||||
|
"type": "module",
|
||||||
|
"bin": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node index.js",
|
||||||
|
"patch": "node index.js patch",
|
||||||
|
"unpatch": "node index.js unpatch",
|
||||||
|
"restore": "node index.js restore",
|
||||||
|
"build": "esbuild index.js --bundle --platform=node --format=cjs --target=node18 --outfile=dist/bundle.cjs --external:@electron/asar --external:chalk"
|
||||||
|
},
|
||||||
|
"pkg": {
|
||||||
|
"targets": [
|
||||||
|
"node18-win-x64",
|
||||||
|
"node18-linux-x64",
|
||||||
|
"node18-linux-arm64",
|
||||||
|
"node18-macos-x64",
|
||||||
|
"node18-macos-arm64"
|
||||||
|
],
|
||||||
|
"outputPath": "dist"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@electron/asar": "^3.2.9",
|
||||||
|
"chalk": "4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.14.9",
|
||||||
|
"esbuild": "^0.25.11",
|
||||||
|
"pkg": "^5.8.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user