pal-explorer-cli 0.4.12 → 0.4.14
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 +149 -149
- package/bin/pal.js +77 -4
- package/extensions/@palexplorer/analytics/extension.json +20 -1
- package/extensions/@palexplorer/analytics/index.js +19 -9
- package/extensions/@palexplorer/audit/extension.json +14 -0
- package/extensions/@palexplorer/auth-email/extension.json +15 -0
- package/extensions/@palexplorer/auth-oauth/extension.json +15 -0
- package/extensions/@palexplorer/chat/extension.json +14 -0
- package/extensions/@palexplorer/discovery/extension.json +17 -0
- package/extensions/@palexplorer/discovery/index.js +1 -1
- package/extensions/@palexplorer/email-notifications/extension.json +23 -0
- package/extensions/@palexplorer/groups/extension.json +15 -0
- package/extensions/@palexplorer/share-links/extension.json +15 -0
- package/extensions/@palexplorer/sync/extension.json +16 -0
- package/extensions/@palexplorer/user-mgmt/extension.json +15 -0
- package/lib/capabilities.js +24 -24
- package/lib/commands/analytics.js +175 -175
- package/lib/commands/api-keys.js +131 -131
- package/lib/commands/audit.js +235 -235
- package/lib/commands/auth.js +137 -137
- package/lib/commands/backup.js +76 -76
- package/lib/commands/billing.js +148 -148
- package/lib/commands/chat.js +217 -217
- package/lib/commands/cloud-backup.js +231 -231
- package/lib/commands/comment.js +99 -99
- package/lib/commands/completion.js +265 -203
- package/lib/commands/compliance.js +218 -218
- package/lib/commands/config.js +136 -136
- package/lib/commands/connect.js +44 -44
- package/lib/commands/dept.js +294 -294
- package/lib/commands/device.js +146 -146
- package/lib/commands/download.js +240 -226
- package/lib/commands/explorer.js +178 -178
- package/lib/commands/extension.js +1060 -970
- package/lib/commands/favorite.js +90 -90
- package/lib/commands/federation.js +270 -270
- package/lib/commands/file.js +533 -533
- package/lib/commands/group.js +271 -271
- package/lib/commands/gui-share.js +29 -29
- package/lib/commands/init.js +61 -61
- package/lib/commands/invite.js +59 -59
- package/lib/commands/list.js +58 -58
- package/lib/commands/log.js +116 -116
- package/lib/commands/nearby.js +108 -108
- package/lib/commands/network.js +251 -251
- package/lib/commands/notify.js +198 -198
- package/lib/commands/org.js +273 -273
- package/lib/commands/pal.js +403 -180
- package/lib/commands/permissions.js +216 -216
- package/lib/commands/pin.js +97 -97
- package/lib/commands/protocol.js +357 -357
- package/lib/commands/rbac.js +147 -147
- package/lib/commands/recover.js +36 -36
- package/lib/commands/register.js +171 -171
- package/lib/commands/relay.js +131 -131
- package/lib/commands/remote.js +368 -368
- package/lib/commands/revoke.js +50 -50
- package/lib/commands/scanner.js +280 -280
- package/lib/commands/schedule.js +344 -344
- package/lib/commands/scim.js +203 -203
- package/lib/commands/search.js +181 -181
- package/lib/commands/serve.js +438 -438
- package/lib/commands/server.js +350 -350
- package/lib/commands/share-link.js +199 -199
- package/lib/commands/share.js +336 -323
- package/lib/commands/sso.js +200 -200
- package/lib/commands/status.js +145 -145
- package/lib/commands/stream.js +562 -562
- package/lib/commands/su.js +187 -187
- package/lib/commands/sync.js +979 -979
- package/lib/commands/transfers.js +152 -152
- package/lib/commands/uninstall.js +188 -188
- package/lib/commands/update.js +204 -204
- package/lib/commands/user.js +276 -276
- package/lib/commands/vfs.js +84 -84
- package/lib/commands/web-login.js +79 -79
- package/lib/commands/web.js +52 -52
- package/lib/commands/webhook.js +180 -180
- package/lib/commands/whoami.js +59 -59
- package/lib/commands/workspace.js +121 -121
- package/lib/core/billing.js +16 -5
- package/lib/core/dhtDiscovery.js +9 -2
- package/lib/core/discoveryClient.js +13 -7
- package/lib/core/extensions.js +142 -1
- package/lib/core/identity.js +33 -2
- package/lib/core/imageProcessor.js +109 -0
- package/lib/core/imageTorrent.js +167 -0
- package/lib/core/permissions.js +1 -1
- package/lib/core/pro.js +11 -4
- package/lib/core/serverList.js +4 -1
- package/lib/core/shares.js +12 -1
- package/lib/core/signalingServer.js +14 -2
- package/lib/core/su.js +1 -1
- package/lib/core/users.js +1 -1
- package/lib/protocol/messages.js +12 -3
- package/lib/utils/explorer.js +1 -1
- package/lib/utils/fuzzy.js +47 -0
- package/lib/utils/help.js +357 -357
- package/lib/utils/torrent.js +1 -0
- package/package.json +4 -3
package/lib/commands/update.js
CHANGED
|
@@ -1,204 +1,204 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import crypto from 'crypto';
|
|
3
|
-
import { execSync, spawnSync } from 'child_process';
|
|
4
|
-
import fs from 'fs';
|
|
5
|
-
import os from 'os';
|
|
6
|
-
import path from 'path';
|
|
7
|
-
import { pipeline } from 'stream/promises';
|
|
8
|
-
import { createWriteStream } from 'fs';
|
|
9
|
-
import { fileURLToPath } from 'url';
|
|
10
|
-
|
|
11
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
-
const ROOT = path.resolve(__dirname, '../..');
|
|
13
|
-
const pkgPath = path.join(ROOT, 'package.json');
|
|
14
|
-
|
|
15
|
-
import { getPrimaryServer } from '../core/discoveryClient.js';
|
|
16
|
-
const UPDATE_SERVER = getPrimaryServer();
|
|
17
|
-
|
|
18
|
-
function detectPlatform() {
|
|
19
|
-
const p = os.platform();
|
|
20
|
-
if (p === 'win32') return 'win';
|
|
21
|
-
if (p === 'darwin') return 'mac';
|
|
22
|
-
if (p === 'linux') return 'linux';
|
|
23
|
-
return p;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export default function updateCommand(program) {
|
|
27
|
-
const cmd = program
|
|
28
|
-
.command('update')
|
|
29
|
-
.description('check for updates and install them')
|
|
30
|
-
.option('--force', 'Force update even if on latest version')
|
|
31
|
-
.addHelpText('after', `
|
|
32
|
-
Examples:
|
|
33
|
-
$
|
|
34
|
-
$
|
|
35
|
-
$
|
|
36
|
-
`)
|
|
37
|
-
.action(async (opts) => {
|
|
38
|
-
console.log(chalk.cyan('Checking for updates...'));
|
|
39
|
-
const result = await checkForUpdate();
|
|
40
|
-
if (!result) {
|
|
41
|
-
console.log(chalk.gray('Could not check for updates.'));
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
if (!result.hasUpdate && !opts.force) {
|
|
45
|
-
console.log(chalk.green(`You're on the latest version (${result.current}).`));
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (result.hasUpdate) {
|
|
50
|
-
console.log(chalk.yellow(`Update available: ${result.current} → ${result.latest}`));
|
|
51
|
-
if (result.forced) {
|
|
52
|
-
console.log(chalk.red(`This update is required (minimum version: ${result.minVersion}).`));
|
|
53
|
-
}
|
|
54
|
-
if (result.releaseNotes) {
|
|
55
|
-
console.log(chalk.gray(result.releaseNotes));
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (!result.downloadUrl) {
|
|
60
|
-
console.log(chalk.gray('No download URL available for this platform.'));
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
await installUpdate(result);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
cmd
|
|
68
|
-
.command('check')
|
|
69
|
-
.description('check if a new version is available')
|
|
70
|
-
.action(async () => {
|
|
71
|
-
const result = await checkForUpdate();
|
|
72
|
-
if (!result) {
|
|
73
|
-
console.log(chalk.gray('Could not check for updates.'));
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
if (result.hasUpdate) {
|
|
77
|
-
console.log(chalk.yellow(`Update available: ${result.current} → ${result.latest}`));
|
|
78
|
-
if (result.releaseNotes) console.log(chalk.gray(result.releaseNotes));
|
|
79
|
-
} else {
|
|
80
|
-
console.log(chalk.green(`Up to date (${result.current}).`));
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export async function checkForUpdate() {
|
|
86
|
-
try {
|
|
87
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
88
|
-
const current = pkg.version;
|
|
89
|
-
const platform = detectPlatform();
|
|
90
|
-
const url = `${UPDATE_SERVER}/api/v1/updates/check?platform=cli&version=${current}&channel=stable`;
|
|
91
|
-
const res = await fetch(url, {
|
|
92
|
-
signal: AbortSignal.timeout(5000),
|
|
93
|
-
headers: { 'User-Agent': `palexplorer-cli/${current}` },
|
|
94
|
-
});
|
|
95
|
-
if (!res.ok) return null;
|
|
96
|
-
const data = await res.json();
|
|
97
|
-
return {
|
|
98
|
-
current,
|
|
99
|
-
latest: data.latestVersion || current,
|
|
100
|
-
hasUpdate: !!data.available,
|
|
101
|
-
forced: !!data.forced,
|
|
102
|
-
minVersion: data.minVersion,
|
|
103
|
-
downloadUrl: data.downloadUrl,
|
|
104
|
-
sha256: data.sha256,
|
|
105
|
-
releaseNotes: data.releaseNotes,
|
|
106
|
-
};
|
|
107
|
-
} catch {
|
|
108
|
-
return null;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
async function installUpdate(update) {
|
|
113
|
-
const tmpDir = path.join(os.tmpdir(), `palexplorer-update-${Date.now()}`);
|
|
114
|
-
const tarballPath = path.join(tmpDir, 'update.tar.gz');
|
|
115
|
-
|
|
116
|
-
try {
|
|
117
|
-
fs.mkdirSync(tmpDir, { recursive: true });
|
|
118
|
-
|
|
119
|
-
// Download
|
|
120
|
-
console.log(chalk.cyan(`Downloading v${update.latest}...`));
|
|
121
|
-
const res = await fetch(update.downloadUrl, { signal: AbortSignal.timeout(120000) });
|
|
122
|
-
if (!res.ok) throw new Error(`Download failed: HTTP ${res.status}`);
|
|
123
|
-
await pipeline(res.body, createWriteStream(tarballPath));
|
|
124
|
-
|
|
125
|
-
const size = fs.statSync(tarballPath).size;
|
|
126
|
-
console.log(chalk.gray(` Downloaded ${(size / 1024 / 1024).toFixed(1)} MB`));
|
|
127
|
-
|
|
128
|
-
// Verify sha256 if provided
|
|
129
|
-
if (update.sha256) {
|
|
130
|
-
const hash = crypto.createHash('sha256').update(fs.readFileSync(tarballPath)).digest('hex');
|
|
131
|
-
if (hash !== update.sha256) {
|
|
132
|
-
throw new Error(`Integrity check failed. Expected ${update.sha256}, got ${hash}`);
|
|
133
|
-
}
|
|
134
|
-
console.log(chalk.gray(' Integrity verified (sha256)'));
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Extract
|
|
138
|
-
console.log(chalk.cyan('Installing...'));
|
|
139
|
-
const extractDir = path.join(tmpDir, 'extracted');
|
|
140
|
-
fs.mkdirSync(extractDir, { recursive: true });
|
|
141
|
-
const tarResult = spawnSync('tar', ['-xzf', tarballPath, '-C', extractDir], { stdio: 'pipe' });
|
|
142
|
-
if (tarResult.status !== 0) throw new Error('Failed to extract update package');
|
|
143
|
-
|
|
144
|
-
// Find the package root inside the tarball (may be nested in a directory)
|
|
145
|
-
let sourceDir = extractDir;
|
|
146
|
-
const entries = fs.readdirSync(extractDir);
|
|
147
|
-
if (entries.length === 1 && fs.statSync(path.join(extractDir, entries[0])).isDirectory()) {
|
|
148
|
-
sourceDir = path.join(extractDir, entries[0]);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Verify it's a valid palexplorer package
|
|
152
|
-
const newPkgPath = path.join(sourceDir, 'package.json');
|
|
153
|
-
if (!fs.existsSync(newPkgPath)) {
|
|
154
|
-
throw new Error('Invalid update package: no package.json found');
|
|
155
|
-
}
|
|
156
|
-
const newPkg = JSON.parse(fs.readFileSync(newPkgPath, 'utf8'));
|
|
157
|
-
if (newPkg.name !== 'palexplorer') {
|
|
158
|
-
throw new Error(`Invalid update package: expected palexplorer, got ${newPkg.name}`);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Backup current version
|
|
162
|
-
const backupDir = path.join(os.tmpdir(), `palexplorer-backup-${Date.now()}`);
|
|
163
|
-
console.log(chalk.gray(` Backing up current version to ${backupDir}`));
|
|
164
|
-
fs.mkdirSync(backupDir, { recursive: true });
|
|
165
|
-
for (const item of ['lib', 'bin', 'package.json']) {
|
|
166
|
-
const src = path.join(ROOT, item);
|
|
167
|
-
const dest = path.join(backupDir, item);
|
|
168
|
-
if (fs.existsSync(src)) {
|
|
169
|
-
fs.cpSync(src, dest, { recursive: true });
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Copy new files over current installation
|
|
174
|
-
for (const item of ['lib', 'bin', 'package.json']) {
|
|
175
|
-
const src = path.join(sourceDir, item);
|
|
176
|
-
const dest = path.join(ROOT, item);
|
|
177
|
-
if (fs.existsSync(src)) {
|
|
178
|
-
if (fs.existsSync(dest)) fs.rmSync(dest, { recursive: true, force: true });
|
|
179
|
-
fs.cpSync(src, dest, { recursive: true });
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Install dependencies if node_modules not bundled
|
|
184
|
-
const newNodeModules = path.join(sourceDir, 'node_modules');
|
|
185
|
-
if (fs.existsSync(newNodeModules)) {
|
|
186
|
-
console.log(chalk.gray(' Using bundled node_modules'));
|
|
187
|
-
fs.cpSync(newNodeModules, path.join(ROOT, 'node_modules'), { recursive: true });
|
|
188
|
-
} else {
|
|
189
|
-
console.log(chalk.gray(' Installing dependencies...'));
|
|
190
|
-
execSync('npm install --production', { cwd: ROOT, stdio: 'pipe' });
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
console.log(chalk.green(`\nUpdated to v${newPkg.version}!`));
|
|
194
|
-
console.log(chalk.gray(`Backup saved to: ${backupDir}`));
|
|
195
|
-
console.log(chalk.gray('Run `
|
|
196
|
-
|
|
197
|
-
} catch (err) {
|
|
198
|
-
console.error(chalk.red(`Update failed: ${err.message}`));
|
|
199
|
-
console.log(chalk.gray('Your current installation is unchanged.'));
|
|
200
|
-
} finally {
|
|
201
|
-
// Clean up temp dir
|
|
202
|
-
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import crypto from 'crypto';
|
|
3
|
+
import { execSync, spawnSync } from 'child_process';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import os from 'os';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { pipeline } from 'stream/promises';
|
|
8
|
+
import { createWriteStream } from 'fs';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const ROOT = path.resolve(__dirname, '../..');
|
|
13
|
+
const pkgPath = path.join(ROOT, 'package.json');
|
|
14
|
+
|
|
15
|
+
import { getPrimaryServer } from '../core/discoveryClient.js';
|
|
16
|
+
const UPDATE_SERVER = getPrimaryServer();
|
|
17
|
+
|
|
18
|
+
function detectPlatform() {
|
|
19
|
+
const p = os.platform();
|
|
20
|
+
if (p === 'win32') return 'win';
|
|
21
|
+
if (p === 'darwin') return 'mac';
|
|
22
|
+
if (p === 'linux') return 'linux';
|
|
23
|
+
return p;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default function updateCommand(program) {
|
|
27
|
+
const cmd = program
|
|
28
|
+
.command('update')
|
|
29
|
+
.description('check for updates and install them')
|
|
30
|
+
.option('--force', 'Force update even if on latest version')
|
|
31
|
+
.addHelpText('after', `
|
|
32
|
+
Examples:
|
|
33
|
+
$ pal update check Check if a new version is available
|
|
34
|
+
$ pal update Download and install the latest version
|
|
35
|
+
$ pal update --force Force reinstall even if up to date
|
|
36
|
+
`)
|
|
37
|
+
.action(async (opts) => {
|
|
38
|
+
console.log(chalk.cyan('Checking for updates...'));
|
|
39
|
+
const result = await checkForUpdate();
|
|
40
|
+
if (!result) {
|
|
41
|
+
console.log(chalk.gray('Could not check for updates.'));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (!result.hasUpdate && !opts.force) {
|
|
45
|
+
console.log(chalk.green(`You're on the latest version (${result.current}).`));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (result.hasUpdate) {
|
|
50
|
+
console.log(chalk.yellow(`Update available: ${result.current} → ${result.latest}`));
|
|
51
|
+
if (result.forced) {
|
|
52
|
+
console.log(chalk.red(`This update is required (minimum version: ${result.minVersion}).`));
|
|
53
|
+
}
|
|
54
|
+
if (result.releaseNotes) {
|
|
55
|
+
console.log(chalk.gray(result.releaseNotes));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!result.downloadUrl) {
|
|
60
|
+
console.log(chalk.gray('No download URL available for this platform.'));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
await installUpdate(result);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
cmd
|
|
68
|
+
.command('check')
|
|
69
|
+
.description('check if a new version is available')
|
|
70
|
+
.action(async () => {
|
|
71
|
+
const result = await checkForUpdate();
|
|
72
|
+
if (!result) {
|
|
73
|
+
console.log(chalk.gray('Could not check for updates.'));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (result.hasUpdate) {
|
|
77
|
+
console.log(chalk.yellow(`Update available: ${result.current} → ${result.latest}`));
|
|
78
|
+
if (result.releaseNotes) console.log(chalk.gray(result.releaseNotes));
|
|
79
|
+
} else {
|
|
80
|
+
console.log(chalk.green(`Up to date (${result.current}).`));
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function checkForUpdate() {
|
|
86
|
+
try {
|
|
87
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
88
|
+
const current = pkg.version;
|
|
89
|
+
const platform = detectPlatform();
|
|
90
|
+
const url = `${UPDATE_SERVER}/api/v1/updates/check?platform=cli&version=${current}&channel=stable`;
|
|
91
|
+
const res = await fetch(url, {
|
|
92
|
+
signal: AbortSignal.timeout(5000),
|
|
93
|
+
headers: { 'User-Agent': `palexplorer-cli/${current}` },
|
|
94
|
+
});
|
|
95
|
+
if (!res.ok) return null;
|
|
96
|
+
const data = await res.json();
|
|
97
|
+
return {
|
|
98
|
+
current,
|
|
99
|
+
latest: data.latestVersion || current,
|
|
100
|
+
hasUpdate: !!data.available,
|
|
101
|
+
forced: !!data.forced,
|
|
102
|
+
minVersion: data.minVersion,
|
|
103
|
+
downloadUrl: data.downloadUrl,
|
|
104
|
+
sha256: data.sha256,
|
|
105
|
+
releaseNotes: data.releaseNotes,
|
|
106
|
+
};
|
|
107
|
+
} catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function installUpdate(update) {
|
|
113
|
+
const tmpDir = path.join(os.tmpdir(), `palexplorer-update-${Date.now()}`);
|
|
114
|
+
const tarballPath = path.join(tmpDir, 'update.tar.gz');
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
118
|
+
|
|
119
|
+
// Download
|
|
120
|
+
console.log(chalk.cyan(`Downloading v${update.latest}...`));
|
|
121
|
+
const res = await fetch(update.downloadUrl, { signal: AbortSignal.timeout(120000) });
|
|
122
|
+
if (!res.ok) throw new Error(`Download failed: HTTP ${res.status}`);
|
|
123
|
+
await pipeline(res.body, createWriteStream(tarballPath));
|
|
124
|
+
|
|
125
|
+
const size = fs.statSync(tarballPath).size;
|
|
126
|
+
console.log(chalk.gray(` Downloaded ${(size / 1024 / 1024).toFixed(1)} MB`));
|
|
127
|
+
|
|
128
|
+
// Verify sha256 if provided
|
|
129
|
+
if (update.sha256) {
|
|
130
|
+
const hash = crypto.createHash('sha256').update(fs.readFileSync(tarballPath)).digest('hex');
|
|
131
|
+
if (hash !== update.sha256) {
|
|
132
|
+
throw new Error(`Integrity check failed. Expected ${update.sha256}, got ${hash}`);
|
|
133
|
+
}
|
|
134
|
+
console.log(chalk.gray(' Integrity verified (sha256)'));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Extract
|
|
138
|
+
console.log(chalk.cyan('Installing...'));
|
|
139
|
+
const extractDir = path.join(tmpDir, 'extracted');
|
|
140
|
+
fs.mkdirSync(extractDir, { recursive: true });
|
|
141
|
+
const tarResult = spawnSync('tar', ['-xzf', tarballPath, '-C', extractDir], { stdio: 'pipe' });
|
|
142
|
+
if (tarResult.status !== 0) throw new Error('Failed to extract update package');
|
|
143
|
+
|
|
144
|
+
// Find the package root inside the tarball (may be nested in a directory)
|
|
145
|
+
let sourceDir = extractDir;
|
|
146
|
+
const entries = fs.readdirSync(extractDir);
|
|
147
|
+
if (entries.length === 1 && fs.statSync(path.join(extractDir, entries[0])).isDirectory()) {
|
|
148
|
+
sourceDir = path.join(extractDir, entries[0]);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Verify it's a valid palexplorer package
|
|
152
|
+
const newPkgPath = path.join(sourceDir, 'package.json');
|
|
153
|
+
if (!fs.existsSync(newPkgPath)) {
|
|
154
|
+
throw new Error('Invalid update package: no package.json found');
|
|
155
|
+
}
|
|
156
|
+
const newPkg = JSON.parse(fs.readFileSync(newPkgPath, 'utf8'));
|
|
157
|
+
if (newPkg.name !== 'palexplorer') {
|
|
158
|
+
throw new Error(`Invalid update package: expected palexplorer, got ${newPkg.name}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Backup current version
|
|
162
|
+
const backupDir = path.join(os.tmpdir(), `palexplorer-backup-${Date.now()}`);
|
|
163
|
+
console.log(chalk.gray(` Backing up current version to ${backupDir}`));
|
|
164
|
+
fs.mkdirSync(backupDir, { recursive: true });
|
|
165
|
+
for (const item of ['lib', 'bin', 'package.json']) {
|
|
166
|
+
const src = path.join(ROOT, item);
|
|
167
|
+
const dest = path.join(backupDir, item);
|
|
168
|
+
if (fs.existsSync(src)) {
|
|
169
|
+
fs.cpSync(src, dest, { recursive: true });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Copy new files over current installation
|
|
174
|
+
for (const item of ['lib', 'bin', 'package.json']) {
|
|
175
|
+
const src = path.join(sourceDir, item);
|
|
176
|
+
const dest = path.join(ROOT, item);
|
|
177
|
+
if (fs.existsSync(src)) {
|
|
178
|
+
if (fs.existsSync(dest)) fs.rmSync(dest, { recursive: true, force: true });
|
|
179
|
+
fs.cpSync(src, dest, { recursive: true });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Install dependencies if node_modules not bundled
|
|
184
|
+
const newNodeModules = path.join(sourceDir, 'node_modules');
|
|
185
|
+
if (fs.existsSync(newNodeModules)) {
|
|
186
|
+
console.log(chalk.gray(' Using bundled node_modules'));
|
|
187
|
+
fs.cpSync(newNodeModules, path.join(ROOT, 'node_modules'), { recursive: true });
|
|
188
|
+
} else {
|
|
189
|
+
console.log(chalk.gray(' Installing dependencies...'));
|
|
190
|
+
execSync('npm install --production', { cwd: ROOT, stdio: 'pipe' });
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
console.log(chalk.green(`\nUpdated to v${newPkg.version}!`));
|
|
194
|
+
console.log(chalk.gray(`Backup saved to: ${backupDir}`));
|
|
195
|
+
console.log(chalk.gray('Run `pal --version` to verify.'));
|
|
196
|
+
|
|
197
|
+
} catch (err) {
|
|
198
|
+
console.error(chalk.red(`Update failed: ${err.message}`));
|
|
199
|
+
console.log(chalk.gray('Your current installation is unchanged.'));
|
|
200
|
+
} finally {
|
|
201
|
+
// Clean up temp dir
|
|
202
|
+
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
|
|
203
|
+
}
|
|
204
|
+
}
|