@getimole/imole 0.3.6
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/README.md +32 -0
- package/install.js +173 -0
- package/package.json +42 -0
- package/run.js +21 -0
package/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# imole
|
|
2
|
+
|
|
3
|
+
🐹 Back up, clean, and slim down your iPhone from the terminal.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g imole
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires [Node.js](https://nodejs.org) for the install script. The actual binary has no runtime dependencies.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
imole doctor # check device connection
|
|
17
|
+
imole scan --summary # see what's eating space
|
|
18
|
+
imole backup --to ~/iphone-backup --only videos # back up videos
|
|
19
|
+
imole clean --manifest ~/iphone-backup/manifest.json # delete verified files
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Supported Platforms
|
|
23
|
+
|
|
24
|
+
- macOS (darwin) x64 / arm64
|
|
25
|
+
- Linux x64 / arm64
|
|
26
|
+
- Windows x64
|
|
27
|
+
|
|
28
|
+
## Details
|
|
29
|
+
|
|
30
|
+
The npm package downloads the pre-built binary from GitHub Releases during `npm install`. The binary is extracted to `node_modules/.bin/imole` and ready to use immediately.
|
|
31
|
+
|
|
32
|
+
On macOS, the quarantine attribute is automatically removed so the binary can run without Gatekeeper complaints.
|
package/install.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
const { execSync } = require("child_process");
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const https = require("https");
|
|
9
|
+
const http = require("http");
|
|
10
|
+
|
|
11
|
+
const PACKAGE = require("./package.json");
|
|
12
|
+
const VERSION = `v${PACKAGE.version}`;
|
|
13
|
+
const NAME = "imole";
|
|
14
|
+
|
|
15
|
+
const GITHUB_REPO = "chenhg5/imole";
|
|
16
|
+
|
|
17
|
+
const PLATFORM_MAP = {
|
|
18
|
+
darwin: "darwin",
|
|
19
|
+
linux: "linux",
|
|
20
|
+
win32: "windows",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const ARCH_MAP = {
|
|
24
|
+
x64: "amd64",
|
|
25
|
+
arm64: "arm64",
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function getPlatformInfo() {
|
|
29
|
+
const platform = PLATFORM_MAP[process.platform];
|
|
30
|
+
const arch = ARCH_MAP[process.arch];
|
|
31
|
+
if (!platform || !arch) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
`Unsupported platform: ${process.platform}/${process.arch}. ` +
|
|
34
|
+
`Supported: darwin/linux/win32 x64/arm64`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
const ext = platform === "windows" ? ".zip" : ".tar.gz";
|
|
38
|
+
const filename = `${NAME}-${platform}-${arch}${ext}`;
|
|
39
|
+
const binaryName = platform === "windows" ? `${NAME}.exe` : NAME;
|
|
40
|
+
return { platform, arch, ext, filename, binaryName };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function fetch(url, redirects = 5) {
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
if (redirects <= 0) return reject(new Error("Too many redirects"));
|
|
46
|
+
const mod = url.startsWith("https") ? https : http;
|
|
47
|
+
mod
|
|
48
|
+
.get(url, { headers: { "User-Agent": "imole-npm" } }, (res) => {
|
|
49
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
50
|
+
return resolve(fetch(res.headers.location, redirects - 1));
|
|
51
|
+
}
|
|
52
|
+
if (res.statusCode !== 200) {
|
|
53
|
+
res.resume();
|
|
54
|
+
return reject(new Error(`HTTP ${res.statusCode} for ${url}`));
|
|
55
|
+
}
|
|
56
|
+
const chunks = [];
|
|
57
|
+
res.on("data", (c) => chunks.push(c));
|
|
58
|
+
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
59
|
+
res.on("error", reject);
|
|
60
|
+
})
|
|
61
|
+
.on("error", reject);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function download(url) {
|
|
66
|
+
console.log(`[@getimole/imole] Downloading ${NAME} ${VERSION} for ${process.platform}/${process.arch}...`);
|
|
67
|
+
try {
|
|
68
|
+
const data = await fetch(url);
|
|
69
|
+
console.log(`[@chenhg5/imole] Downloaded ${(data.length / 1024 / 1024).toFixed(1)} MB`);
|
|
70
|
+
return data;
|
|
71
|
+
} catch (err) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
`[@getimole/imole] Could not download binary.\n` +
|
|
74
|
+
` ${err.message}\n` +
|
|
75
|
+
` You can download manually from https://github.com/${GITHUB_REPO}/releases/tag/${VERSION}`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function extractTarGz(buffer, destDir, binaryName) {
|
|
81
|
+
const tmpFile = path.join(destDir, "_tmp.tar.gz");
|
|
82
|
+
fs.writeFileSync(tmpFile, buffer);
|
|
83
|
+
try {
|
|
84
|
+
execSync(`tar xzf "${tmpFile}" -C "${destDir}"`, { stdio: "pipe" });
|
|
85
|
+
} finally {
|
|
86
|
+
fs.unlinkSync(tmpFile);
|
|
87
|
+
}
|
|
88
|
+
const entries = fs.readdirSync(destDir);
|
|
89
|
+
const extracted = entries.find(
|
|
90
|
+
(f) => f.startsWith(NAME) && !f.endsWith(".tar.gz") && !f.endsWith(".zip")
|
|
91
|
+
);
|
|
92
|
+
if (extracted && extracted !== binaryName) {
|
|
93
|
+
fs.renameSync(path.join(destDir, extracted), path.join(destDir, binaryName));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function extractZip(buffer, destDir, binaryName) {
|
|
98
|
+
const tmpFile = path.join(destDir, "_tmp.zip");
|
|
99
|
+
fs.writeFileSync(tmpFile, buffer);
|
|
100
|
+
try {
|
|
101
|
+
try {
|
|
102
|
+
execSync(`unzip -o "${tmpFile}" -d "${destDir}"`, { stdio: "pipe" });
|
|
103
|
+
} catch {
|
|
104
|
+
execSync(
|
|
105
|
+
`powershell -Command "Expand-Archive -Force '${tmpFile}' '${destDir}'"`,
|
|
106
|
+
{ stdio: "pipe" }
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
} finally {
|
|
110
|
+
try { fs.unlinkSync(tmpFile); } catch {}
|
|
111
|
+
}
|
|
112
|
+
const entries = fs.readdirSync(destDir);
|
|
113
|
+
const extracted = entries.find((f) => f.startsWith(NAME) && f.endsWith(".exe"));
|
|
114
|
+
if (extracted && extracted !== binaryName) {
|
|
115
|
+
fs.renameSync(path.join(destDir, extracted), path.join(destDir, binaryName));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function main() {
|
|
120
|
+
const { platform, arch, ext, filename, binaryName } = getPlatformInfo();
|
|
121
|
+
const binDir = path.join(__dirname, "bin");
|
|
122
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
123
|
+
|
|
124
|
+
const binaryPath = path.join(binDir, binaryName);
|
|
125
|
+
|
|
126
|
+
if (fs.existsSync(binaryPath)) {
|
|
127
|
+
try {
|
|
128
|
+
const out = execSync(`"${binaryPath}" --version`, {
|
|
129
|
+
encoding: "utf8",
|
|
130
|
+
timeout: 5000,
|
|
131
|
+
});
|
|
132
|
+
const expectedVer = VERSION.slice(1);
|
|
133
|
+
if (out.includes(expectedVer)) {
|
|
134
|
+
console.log(`[@getimole/imole] Binary ${VERSION} already installed, skipping.`);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
console.log(`[@getimole/imole] Existing binary is outdated, upgrading to ${VERSION}...`);
|
|
138
|
+
fs.unlinkSync(binaryPath);
|
|
139
|
+
} catch {
|
|
140
|
+
console.log(`[@getimole/imole] Replacing existing binary with ${VERSION}...`);
|
|
141
|
+
fs.unlinkSync(binaryPath);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const url = `https://github.com/${GITHUB_REPO}/releases/download/${VERSION}/${filename}`;
|
|
146
|
+
const data = await download(url);
|
|
147
|
+
|
|
148
|
+
if (ext === ".tar.gz") {
|
|
149
|
+
extractTarGz(data, binDir, binaryName);
|
|
150
|
+
} else {
|
|
151
|
+
extractZip(data, binDir, binaryName);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (platform !== "windows") {
|
|
155
|
+
fs.chmodSync(binaryPath, 0o755);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (platform === "darwin") {
|
|
159
|
+
try {
|
|
160
|
+
execSync(`xattr -d com.apple.quarantine "${binaryPath}"`, { stdio: "pipe" });
|
|
161
|
+
console.log(`[@getimole/imole] Removed macOS quarantine attribute`);
|
|
162
|
+
} catch {
|
|
163
|
+
// xattr fails if the attribute doesn't exist, which is fine
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
console.log(`[@getimole/imole] Installed to ${binaryPath}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
main().catch((err) => {
|
|
171
|
+
console.error(err.message);
|
|
172
|
+
process.exit(1);
|
|
173
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@getimole/imole",
|
|
3
|
+
"version": "0.3.6",
|
|
4
|
+
"description": "🐹 Back up, clean, and slim down your iPhone from the terminal",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"iphone",
|
|
7
|
+
"ios",
|
|
8
|
+
"backup",
|
|
9
|
+
"photos",
|
|
10
|
+
"videos",
|
|
11
|
+
"cleanup",
|
|
12
|
+
"iphone-storage",
|
|
13
|
+
"imobiledevice"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://github.com/chenhg5/imole",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/chenhg5/imole.git"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"author": "chenhg5",
|
|
22
|
+
"bin": {
|
|
23
|
+
"imole": "run.js"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"postinstall": "node install.js"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"install.js",
|
|
30
|
+
"run.js",
|
|
31
|
+
"README.md"
|
|
32
|
+
],
|
|
33
|
+
"os": [
|
|
34
|
+
"darwin",
|
|
35
|
+
"linux",
|
|
36
|
+
"win32"
|
|
37
|
+
],
|
|
38
|
+
"cpu": [
|
|
39
|
+
"x64",
|
|
40
|
+
"arm64"
|
|
41
|
+
]
|
|
42
|
+
}
|
package/run.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const { spawn } = require("child_process");
|
|
7
|
+
|
|
8
|
+
const binDir = path.join(__dirname, "bin");
|
|
9
|
+
const platform = process.platform === "win32" ? "windows" : process.platform;
|
|
10
|
+
const arch = process.arch === "x64" ? "amd64" : process.arch;
|
|
11
|
+
const binaryName = platform === "windows" ? "imole.exe" : "imole";
|
|
12
|
+
const binaryPath = path.join(binDir, binaryName);
|
|
13
|
+
|
|
14
|
+
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
15
|
+
stdio: "inherit",
|
|
16
|
+
cwd: process.cwd(),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
child.on("exit", (code) => {
|
|
20
|
+
process.exit(code || 0);
|
|
21
|
+
});
|