pal-explorer-cli 0.4.6 → 0.4.8
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/bin/pal.js +231 -231
- package/extensions/@palexplorer/analytics/extension.json +1 -1
- package/extensions/@palexplorer/discovery/extension.json +1 -0
- package/extensions/@palexplorer/explorer-integration/extension.json +1 -1
- package/extensions/@palexplorer/networks/extension.json +1 -1
- package/extensions/@palexplorer/vfs/extension.json +1 -1
- package/lib/commands/sync.js +541 -389
- package/lib/core/extensions.js +11 -22
- package/lib/core/syncEngine.js +276 -3
- package/lib/core/syncOptions.js +80 -0
- package/lib/core/syncProtocolHandlers.js +87 -0
- package/lib/core/syncTransport.js +203 -0
- package/package.json +3 -2
- package/extensions/@palexplorer/analytics/README.md +0 -45
- package/extensions/@palexplorer/analytics/docs/MONETIZATION.md +0 -14
- package/extensions/@palexplorer/analytics/docs/PLAN.md +0 -23
- package/extensions/@palexplorer/analytics/docs/PRIVACY.md +0 -38
- package/extensions/@palexplorer/analytics/test/analytics.test.js +0 -82
package/bin/pal.js
CHANGED
|
@@ -1,231 +1,231 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// Enable V8 code caching for faster subsequent boots
|
|
4
|
-
import 'v8-compile-cache-lib';
|
|
5
|
-
|
|
6
|
-
import { program } from 'commander';
|
|
7
|
-
import chalk from 'chalk';
|
|
8
|
-
|
|
9
|
-
// Utility for lazy loading commands
|
|
10
|
-
const lazy = (path) => async (program) => {
|
|
11
|
-
const mod = await import(path);
|
|
12
|
-
if (mod.default) mod.default(program);
|
|
13
|
-
else if (typeof mod === 'function') mod(program);
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
// --- Eagerly load ONLY what is needed for initial setup/parsing ---
|
|
17
|
-
import logger from '../lib/utils/logger.js';
|
|
18
|
-
import { installGlobalErrorHandler } from '../lib/core/analytics.js';
|
|
19
|
-
installGlobalErrorHandler();
|
|
20
|
-
|
|
21
|
-
globalThis.__palMode = 'cli';
|
|
22
|
-
|
|
23
|
-
// --- Lazy load the big migration/core logic only if needed ---
|
|
24
|
-
const runMigrations = async () => {
|
|
25
|
-
const { migrateShareKeys } = await import('../lib/core/shares.js');
|
|
26
|
-
const { migrateToMultiUser } = await import('../lib/core/users.js');
|
|
27
|
-
await migrateShareKeys();
|
|
28
|
-
migrateToMultiUser();
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
// Apply persisted log level from config before anything else
|
|
32
|
-
import config from '../lib/utils/config.js';
|
|
33
|
-
const settings = config.get('settings') || {};
|
|
34
|
-
const configuredLevel = settings.logLevel;
|
|
35
|
-
if (configuredLevel) logger.setLevel(configuredLevel);
|
|
36
|
-
|
|
37
|
-
// Parse --log-level early (before Commander) so it works before subcommands
|
|
38
|
-
const logLevelIdx = process.argv.indexOf('--log-level');
|
|
39
|
-
if (logLevelIdx !== -1 && process.argv[logLevelIdx + 1]) {
|
|
40
|
-
const cliLevel = process.argv[logLevelIdx + 1];
|
|
41
|
-
if (logger.LEVELS.includes(cliLevel)) {
|
|
42
|
-
logger.setLevel(cliLevel);
|
|
43
|
-
process.argv.splice(logLevelIdx, 2);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
logger.applyGlobalOverride();
|
|
48
|
-
|
|
49
|
-
program
|
|
50
|
-
.name('pe')
|
|
51
|
-
.description('p2p file sharing for friends')
|
|
52
|
-
.version('0.4.0')
|
|
53
|
-
.option('--json', 'output in JSON format')
|
|
54
|
-
.option('--log-level <level>', 'set log level (debug, info, warn, error, silent)');
|
|
55
|
-
|
|
56
|
-
// Register Commands LAZILY
|
|
57
|
-
// Note: Commander requires commands to be defined upfront for help/autocompletion,
|
|
58
|
-
// but the actual .js files containing the complex registration logic (with their own imports)
|
|
59
|
-
// are what we are lazy-loading here.
|
|
60
|
-
const commands = [
|
|
61
|
-
['init', '../lib/commands/init.js'],
|
|
62
|
-
['whoami', '../lib/commands/whoami.js'],
|
|
63
|
-
['register', '../lib/commands/register.js'],
|
|
64
|
-
['recover', '../lib/commands/recover.js'],
|
|
65
|
-
['share', '../lib/commands/share.js'],
|
|
66
|
-
['download', '../lib/commands/download.js'],
|
|
67
|
-
['list', '../lib/commands/list.js'],
|
|
68
|
-
['revoke', '../lib/commands/revoke.js'],
|
|
69
|
-
['serve', '../lib/commands/serve.js'],
|
|
70
|
-
['pal', '../lib/commands/pal.js'],
|
|
71
|
-
['invite', '../lib/commands/invite.js'],
|
|
72
|
-
['nearby', '../lib/commands/nearby.js'],
|
|
73
|
-
['group', '../lib/commands/group.js'],
|
|
74
|
-
['sync', '../lib/commands/sync.js'],
|
|
75
|
-
['transfers', '../lib/commands/transfers.js'],
|
|
76
|
-
['config', '../lib/commands/config.js'],
|
|
77
|
-
['device', '../lib/commands/device.js'],
|
|
78
|
-
['server', '../lib/commands/server.js'],
|
|
79
|
-
['explorer', '../lib/commands/explorer.js'],
|
|
80
|
-
['status', '../lib/commands/status.js'],
|
|
81
|
-
['log', '../lib/commands/log.js'],
|
|
82
|
-
['completion', '../lib/commands/completion.js'],
|
|
83
|
-
['web', '../lib/commands/web.js'],
|
|
84
|
-
['web-login', '../lib/commands/web-login.js'],
|
|
85
|
-
['gui-share', '../lib/commands/gui-share.js'],
|
|
86
|
-
['vfs', '../lib/commands/vfs.js'],
|
|
87
|
-
['file', '../lib/commands/file.js'],
|
|
88
|
-
['remote', '../lib/commands/remote.js'],
|
|
89
|
-
['user', '../lib/commands/user.js'],
|
|
90
|
-
['schedule', '../lib/commands/schedule.js'],
|
|
91
|
-
['chat', '../lib/commands/chat.js'],
|
|
92
|
-
['backup', '../lib/commands/backup.js'],
|
|
93
|
-
['api-keys', '../lib/commands/api-keys.js'],
|
|
94
|
-
['share-link', '../lib/commands/share-link.js'],
|
|
95
|
-
['comment', '../lib/commands/comment.js'],
|
|
96
|
-
['update', '../lib/commands/update.js'],
|
|
97
|
-
['auth', '../lib/commands/auth.js'],
|
|
98
|
-
['network', '../lib/commands/network.js'],
|
|
99
|
-
['search', '../lib/commands/search.js'],
|
|
100
|
-
['connect', '../lib/commands/connect.js'],
|
|
101
|
-
['protocol', '../lib/commands/protocol.js'],
|
|
102
|
-
['workspace', '../lib/commands/workspace.js'],
|
|
103
|
-
['favorite', '../lib/commands/favorite.js'],
|
|
104
|
-
['pin', '../lib/commands/pin.js'],
|
|
105
|
-
['stream', '../lib/commands/stream.js'],
|
|
106
|
-
['uninstall', '../lib/commands/uninstall.js'],
|
|
107
|
-
['extension', '../lib/commands/extension.js'],
|
|
108
|
-
['ext', '../lib/commands/extension.js'],
|
|
109
|
-
['billing', '../lib/commands/billing.js'],
|
|
110
|
-
['permissions', '../lib/commands/permissions.js'],
|
|
111
|
-
['org', '../lib/commands/org.js'],
|
|
112
|
-
['su', '../lib/commands/su.js'],
|
|
113
|
-
['federation', '../lib/commands/federation.js'],
|
|
114
|
-
['analytics', '../lib/commands/analytics.js'],
|
|
115
|
-
['webhook', '../lib/commands/webhook.js'],
|
|
116
|
-
['relay', '../lib/commands/relay.js'],
|
|
117
|
-
['compliance', '../lib/commands/compliance.js'],
|
|
118
|
-
['sso', '../lib/commands/sso.js'],
|
|
119
|
-
['rbac', '../lib/commands/rbac.js'],
|
|
120
|
-
['scim', '../lib/commands/scim.js'],
|
|
121
|
-
['audit', '../lib/commands/audit.js'],
|
|
122
|
-
['scanner', '../lib/commands/scanner.js'],
|
|
123
|
-
['notify', '../lib/commands/notify.js'],
|
|
124
|
-
['cloud-backup', '../lib/commands/cloud-backup.js'],
|
|
125
|
-
['dept', '../lib/commands/dept.js'],
|
|
126
|
-
];
|
|
127
|
-
|
|
128
|
-
// To keep it truly fast, we should only register the specific command being run.
|
|
129
|
-
// Commander's parseAsync will handle the matching.
|
|
130
|
-
const currentCmd = process.argv[2];
|
|
131
|
-
const cmdMatch = commands.find(c => c[0] === currentCmd);
|
|
132
|
-
|
|
133
|
-
// Commands that don't modify data — skip migrations for speed
|
|
134
|
-
const readOnlyCommands = new Set([
|
|
135
|
-
'whoami', 'status', 'list', 'log', 'completion', 'nearby',
|
|
136
|
-
'protocol', 'pin', 'favorite', 'search', 'workspace',
|
|
137
|
-
]);
|
|
138
|
-
const isVersionOrHelp = !currentCmd || currentCmd === '--help' || currentCmd === '-h'
|
|
139
|
-
|| currentCmd === '--version' || currentCmd === '-V';
|
|
140
|
-
const needsMigrations = cmdMatch && !readOnlyCommands.has(cmdMatch[0]) && !isVersionOrHelp;
|
|
141
|
-
|
|
142
|
-
if (cmdMatch) {
|
|
143
|
-
if (needsMigrations) await runMigrations();
|
|
144
|
-
const register = await lazy(cmdMatch[1]);
|
|
145
|
-
await register(program);
|
|
146
|
-
} else if (isVersionOrHelp) {
|
|
147
|
-
// Help/version — load all commands for full help text, skip migrations
|
|
148
|
-
await Promise.all(commands.map(c => lazy(c[1])(program)));
|
|
149
|
-
} else {
|
|
150
|
-
// Unknown command — load all for Commander to handle
|
|
151
|
-
await runMigrations();
|
|
152
|
-
await Promise.all(commands.map(c => lazy(c[1])(program)));
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Grouped help text
|
|
156
|
-
program.addHelpText('after', `
|
|
157
|
-
${chalk.cyan.bold('Command Groups:')}
|
|
158
|
-
|
|
159
|
-
${chalk.yellow('Identity:')} init, whoami, register, verify, recover
|
|
160
|
-
${chalk.yellow('Users:')} user list/add/remove/promote, login
|
|
161
|
-
${chalk.yellow('Sharing:')} share, list, revoke, serve, download, share-link, share-rename, permissions
|
|
162
|
-
${chalk.yellow('Sync:')} sync push/pull/status/watch/list/remove/diff
|
|
163
|
-
${chalk.yellow('Files:')} file ls/tree/info/copy/move/rename/mkdir/delete/search/open/reveal/audit
|
|
164
|
-
${chalk.yellow('Remote:')} remote browse/files/download
|
|
165
|
-
${chalk.yellow('Social:')} pal, invite, nearby, group, chat, comment
|
|
166
|
-
${chalk.yellow('Search:')} search (files, pals, groups)
|
|
167
|
-
${chalk.yellow('Network:')} network create/list/info/invite/join/members/groups/connect/disconnect
|
|
168
|
-
${chalk.yellow('Workspace:')} workspace list/create/delete/add/remove
|
|
169
|
-
${chalk.yellow('Management:')} transfers, config, device, server, explorer, vfs, web, schedule, favorite
|
|
170
|
-
${chalk.yellow('Protocol:')} protocol info/policy/route/envelope/keys
|
|
171
|
-
${chalk.yellow('PIN:')} pin set/remove/status
|
|
172
|
-
${chalk.yellow('Billing:')} billing status/plans/activate/deactivate/checkout
|
|
173
|
-
${chalk.yellow('Pro:')} backup, api-keys
|
|
174
|
-
${chalk.yellow('Streaming:')} stream local/remote/stop/status/broadcast/join/transport
|
|
175
|
-
${chalk.yellow('Extensions:')} ext list/install/remove/enable/disable/info/config/create
|
|
176
|
-
${chalk.yellow('Orgs:')} org create/list/info/invite/remove/subscribe/unsubscribe/billing
|
|
177
|
-
${chalk.yellow('Utilities:')} status, log, completion, gui-share, uninstall
|
|
178
|
-
|
|
179
|
-
${chalk.gray('Aliases: pe connect → pe network connect, pe disconnect → pe network disconnect')}
|
|
180
|
-
`);
|
|
181
|
-
|
|
182
|
-
// Load extensions and emit lifecycle hooks
|
|
183
|
-
let extensionsLoaded = false;
|
|
184
|
-
if (!isVersionOrHelp) {
|
|
185
|
-
try {
|
|
186
|
-
const { loadAllExtensions, hooks, ensureDefaultExtensions } = await import('../lib/core/extensions.js');
|
|
187
|
-
ensureDefaultExtensions();
|
|
188
|
-
await loadAllExtensions();
|
|
189
|
-
extensionsLoaded = true;
|
|
190
|
-
|
|
191
|
-
// Clean stale transfers (0% for >24h)
|
|
192
|
-
const { cleanStaleTransfers } = await import('../lib/core/transfers.js');
|
|
193
|
-
const cleaned = cleanStaleTransfers(24);
|
|
194
|
-
if (cleaned > 0) console.log(`[transfers] Cleaned ${cleaned} stale transfer(s)`);
|
|
195
|
-
|
|
196
|
-
const identity = (await import('../lib/utils/config.js')).default.get('identity');
|
|
197
|
-
await hooks.emit('on:app:ready', {
|
|
198
|
-
version: program.version(),
|
|
199
|
-
identity: identity ? { handle: identity.handle, publicKey: identity.publicKey } : null,
|
|
200
|
-
mode: 'cli',
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
const shutdown = async () => {
|
|
204
|
-
await hooks.emit('on:app:shutdown', {});
|
|
205
|
-
process.exit(process.exitCode || 0);
|
|
206
|
-
};
|
|
207
|
-
process.on('SIGINT', shutdown);
|
|
208
|
-
process.on('SIGTERM', shutdown);
|
|
209
|
-
} catch (err) {
|
|
210
|
-
// Extension loading is non-fatal
|
|
211
|
-
logger.warn?.(`Extension loading failed: ${err.message}`) || console.warn(`[extensions] ${err.message}`);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (!process.argv.slice(2).length) {
|
|
216
|
-
program.outputHelp();
|
|
217
|
-
} else {
|
|
218
|
-
try {
|
|
219
|
-
await program.parseAsync(process.argv);
|
|
220
|
-
} catch (err) {
|
|
221
|
-
if (err.code !== 'commander.helpDisplayed' && err.code !== 'commander.unknownCommand') {
|
|
222
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// keytar's native D-Bus handles block the event loop on headless Linux
|
|
228
|
-
const longRunning = new Set(['serve', 'nearby', 'stream']);
|
|
229
|
-
if (!longRunning.has(currentCmd)) {
|
|
230
|
-
process.exit(process.exitCode || 0);
|
|
231
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Enable V8 code caching for faster subsequent boots
|
|
4
|
+
import 'v8-compile-cache-lib';
|
|
5
|
+
|
|
6
|
+
import { program } from 'commander';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
|
|
9
|
+
// Utility for lazy loading commands
|
|
10
|
+
const lazy = (path) => async (program) => {
|
|
11
|
+
const mod = await import(path);
|
|
12
|
+
if (mod.default) mod.default(program);
|
|
13
|
+
else if (typeof mod === 'function') mod(program);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// --- Eagerly load ONLY what is needed for initial setup/parsing ---
|
|
17
|
+
import logger from '../lib/utils/logger.js';
|
|
18
|
+
import { installGlobalErrorHandler } from '../lib/core/analytics.js';
|
|
19
|
+
installGlobalErrorHandler();
|
|
20
|
+
|
|
21
|
+
globalThis.__palMode = 'cli';
|
|
22
|
+
|
|
23
|
+
// --- Lazy load the big migration/core logic only if needed ---
|
|
24
|
+
const runMigrations = async () => {
|
|
25
|
+
const { migrateShareKeys } = await import('../lib/core/shares.js');
|
|
26
|
+
const { migrateToMultiUser } = await import('../lib/core/users.js');
|
|
27
|
+
await migrateShareKeys();
|
|
28
|
+
migrateToMultiUser();
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Apply persisted log level from config before anything else
|
|
32
|
+
import config from '../lib/utils/config.js';
|
|
33
|
+
const settings = config.get('settings') || {};
|
|
34
|
+
const configuredLevel = settings.logLevel;
|
|
35
|
+
if (configuredLevel) logger.setLevel(configuredLevel);
|
|
36
|
+
|
|
37
|
+
// Parse --log-level early (before Commander) so it works before subcommands
|
|
38
|
+
const logLevelIdx = process.argv.indexOf('--log-level');
|
|
39
|
+
if (logLevelIdx !== -1 && process.argv[logLevelIdx + 1]) {
|
|
40
|
+
const cliLevel = process.argv[logLevelIdx + 1];
|
|
41
|
+
if (logger.LEVELS.includes(cliLevel)) {
|
|
42
|
+
logger.setLevel(cliLevel);
|
|
43
|
+
process.argv.splice(logLevelIdx, 2);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
logger.applyGlobalOverride();
|
|
48
|
+
|
|
49
|
+
program
|
|
50
|
+
.name('pe')
|
|
51
|
+
.description('p2p file sharing for friends')
|
|
52
|
+
.version('0.4.0')
|
|
53
|
+
.option('--json', 'output in JSON format')
|
|
54
|
+
.option('--log-level <level>', 'set log level (debug, info, warn, error, silent)');
|
|
55
|
+
|
|
56
|
+
// Register Commands LAZILY
|
|
57
|
+
// Note: Commander requires commands to be defined upfront for help/autocompletion,
|
|
58
|
+
// but the actual .js files containing the complex registration logic (with their own imports)
|
|
59
|
+
// are what we are lazy-loading here.
|
|
60
|
+
const commands = [
|
|
61
|
+
['init', '../lib/commands/init.js'],
|
|
62
|
+
['whoami', '../lib/commands/whoami.js'],
|
|
63
|
+
['register', '../lib/commands/register.js'],
|
|
64
|
+
['recover', '../lib/commands/recover.js'],
|
|
65
|
+
['share', '../lib/commands/share.js'],
|
|
66
|
+
['download', '../lib/commands/download.js'],
|
|
67
|
+
['list', '../lib/commands/list.js'],
|
|
68
|
+
['revoke', '../lib/commands/revoke.js'],
|
|
69
|
+
['serve', '../lib/commands/serve.js'],
|
|
70
|
+
['pal', '../lib/commands/pal.js'],
|
|
71
|
+
['invite', '../lib/commands/invite.js'],
|
|
72
|
+
['nearby', '../lib/commands/nearby.js'],
|
|
73
|
+
['group', '../lib/commands/group.js'],
|
|
74
|
+
['sync', '../lib/commands/sync.js'],
|
|
75
|
+
['transfers', '../lib/commands/transfers.js'],
|
|
76
|
+
['config', '../lib/commands/config.js'],
|
|
77
|
+
['device', '../lib/commands/device.js'],
|
|
78
|
+
['server', '../lib/commands/server.js'],
|
|
79
|
+
['explorer', '../lib/commands/explorer.js'],
|
|
80
|
+
['status', '../lib/commands/status.js'],
|
|
81
|
+
['log', '../lib/commands/log.js'],
|
|
82
|
+
['completion', '../lib/commands/completion.js'],
|
|
83
|
+
['web', '../lib/commands/web.js'],
|
|
84
|
+
['web-login', '../lib/commands/web-login.js'],
|
|
85
|
+
['gui-share', '../lib/commands/gui-share.js'],
|
|
86
|
+
['vfs', '../lib/commands/vfs.js'],
|
|
87
|
+
['file', '../lib/commands/file.js'],
|
|
88
|
+
['remote', '../lib/commands/remote.js'],
|
|
89
|
+
['user', '../lib/commands/user.js'],
|
|
90
|
+
['schedule', '../lib/commands/schedule.js'],
|
|
91
|
+
['chat', '../lib/commands/chat.js'],
|
|
92
|
+
['backup', '../lib/commands/backup.js'],
|
|
93
|
+
['api-keys', '../lib/commands/api-keys.js'],
|
|
94
|
+
['share-link', '../lib/commands/share-link.js'],
|
|
95
|
+
['comment', '../lib/commands/comment.js'],
|
|
96
|
+
['update', '../lib/commands/update.js'],
|
|
97
|
+
['auth', '../lib/commands/auth.js'],
|
|
98
|
+
['network', '../lib/commands/network.js'],
|
|
99
|
+
['search', '../lib/commands/search.js'],
|
|
100
|
+
['connect', '../lib/commands/connect.js'],
|
|
101
|
+
['protocol', '../lib/commands/protocol.js'],
|
|
102
|
+
['workspace', '../lib/commands/workspace.js'],
|
|
103
|
+
['favorite', '../lib/commands/favorite.js'],
|
|
104
|
+
['pin', '../lib/commands/pin.js'],
|
|
105
|
+
['stream', '../lib/commands/stream.js'],
|
|
106
|
+
['uninstall', '../lib/commands/uninstall.js'],
|
|
107
|
+
['extension', '../lib/commands/extension.js'],
|
|
108
|
+
['ext', '../lib/commands/extension.js'],
|
|
109
|
+
['billing', '../lib/commands/billing.js'],
|
|
110
|
+
['permissions', '../lib/commands/permissions.js'],
|
|
111
|
+
['org', '../lib/commands/org.js'],
|
|
112
|
+
['su', '../lib/commands/su.js'],
|
|
113
|
+
['federation', '../lib/commands/federation.js'],
|
|
114
|
+
['analytics', '../lib/commands/analytics.js'],
|
|
115
|
+
['webhook', '../lib/commands/webhook.js'],
|
|
116
|
+
['relay', '../lib/commands/relay.js'],
|
|
117
|
+
['compliance', '../lib/commands/compliance.js'],
|
|
118
|
+
['sso', '../lib/commands/sso.js'],
|
|
119
|
+
['rbac', '../lib/commands/rbac.js'],
|
|
120
|
+
['scim', '../lib/commands/scim.js'],
|
|
121
|
+
['audit', '../lib/commands/audit.js'],
|
|
122
|
+
['scanner', '../lib/commands/scanner.js'],
|
|
123
|
+
['notify', '../lib/commands/notify.js'],
|
|
124
|
+
['cloud-backup', '../lib/commands/cloud-backup.js'],
|
|
125
|
+
['dept', '../lib/commands/dept.js'],
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
// To keep it truly fast, we should only register the specific command being run.
|
|
129
|
+
// Commander's parseAsync will handle the matching.
|
|
130
|
+
const currentCmd = process.argv[2];
|
|
131
|
+
const cmdMatch = commands.find(c => c[0] === currentCmd);
|
|
132
|
+
|
|
133
|
+
// Commands that don't modify data — skip migrations for speed
|
|
134
|
+
const readOnlyCommands = new Set([
|
|
135
|
+
'whoami', 'status', 'list', 'log', 'completion', 'nearby',
|
|
136
|
+
'protocol', 'pin', 'favorite', 'search', 'workspace',
|
|
137
|
+
]);
|
|
138
|
+
const isVersionOrHelp = !currentCmd || currentCmd === '--help' || currentCmd === '-h'
|
|
139
|
+
|| currentCmd === '--version' || currentCmd === '-V';
|
|
140
|
+
const needsMigrations = cmdMatch && !readOnlyCommands.has(cmdMatch[0]) && !isVersionOrHelp;
|
|
141
|
+
|
|
142
|
+
if (cmdMatch) {
|
|
143
|
+
if (needsMigrations) await runMigrations();
|
|
144
|
+
const register = await lazy(cmdMatch[1]);
|
|
145
|
+
await register(program);
|
|
146
|
+
} else if (isVersionOrHelp) {
|
|
147
|
+
// Help/version — load all commands for full help text, skip migrations
|
|
148
|
+
await Promise.all(commands.map(c => lazy(c[1])(program)));
|
|
149
|
+
} else {
|
|
150
|
+
// Unknown command — load all for Commander to handle
|
|
151
|
+
await runMigrations();
|
|
152
|
+
await Promise.all(commands.map(c => lazy(c[1])(program)));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Grouped help text
|
|
156
|
+
program.addHelpText('after', `
|
|
157
|
+
${chalk.cyan.bold('Command Groups:')}
|
|
158
|
+
|
|
159
|
+
${chalk.yellow('Identity:')} init, whoami, register, verify, recover
|
|
160
|
+
${chalk.yellow('Users:')} user list/add/remove/promote, login
|
|
161
|
+
${chalk.yellow('Sharing:')} share, list, revoke, serve, download, share-link, share-rename, permissions
|
|
162
|
+
${chalk.yellow('Sync:')} sync push/pull/status/watch/list/remove/diff
|
|
163
|
+
${chalk.yellow('Files:')} file ls/tree/info/copy/move/rename/mkdir/delete/search/open/reveal/audit
|
|
164
|
+
${chalk.yellow('Remote:')} remote browse/files/download
|
|
165
|
+
${chalk.yellow('Social:')} pal, invite, nearby, group, chat, comment
|
|
166
|
+
${chalk.yellow('Search:')} search (files, pals, groups)
|
|
167
|
+
${chalk.yellow('Network:')} network create/list/info/invite/join/members/groups/connect/disconnect
|
|
168
|
+
${chalk.yellow('Workspace:')} workspace list/create/delete/add/remove
|
|
169
|
+
${chalk.yellow('Management:')} transfers, config, device, server, explorer, vfs, web, schedule, favorite
|
|
170
|
+
${chalk.yellow('Protocol:')} protocol info/policy/route/envelope/keys
|
|
171
|
+
${chalk.yellow('PIN:')} pin set/remove/status
|
|
172
|
+
${chalk.yellow('Billing:')} billing status/plans/activate/deactivate/checkout
|
|
173
|
+
${chalk.yellow('Pro:')} backup, api-keys
|
|
174
|
+
${chalk.yellow('Streaming:')} stream local/remote/stop/status/broadcast/join/transport
|
|
175
|
+
${chalk.yellow('Extensions:')} ext list/install/remove/enable/disable/info/config/create
|
|
176
|
+
${chalk.yellow('Orgs:')} org create/list/info/invite/remove/subscribe/unsubscribe/billing
|
|
177
|
+
${chalk.yellow('Utilities:')} status, log, completion, gui-share, uninstall
|
|
178
|
+
|
|
179
|
+
${chalk.gray('Aliases: pe connect → pe network connect, pe disconnect → pe network disconnect')}
|
|
180
|
+
`);
|
|
181
|
+
|
|
182
|
+
// Load extensions and emit lifecycle hooks
|
|
183
|
+
let extensionsLoaded = false;
|
|
184
|
+
if (!isVersionOrHelp) {
|
|
185
|
+
try {
|
|
186
|
+
const { loadAllExtensions, hooks, ensureDefaultExtensions } = await import('../lib/core/extensions.js');
|
|
187
|
+
ensureDefaultExtensions();
|
|
188
|
+
await loadAllExtensions();
|
|
189
|
+
extensionsLoaded = true;
|
|
190
|
+
|
|
191
|
+
// Clean stale transfers (0% for >24h)
|
|
192
|
+
const { cleanStaleTransfers } = await import('../lib/core/transfers.js');
|
|
193
|
+
const cleaned = cleanStaleTransfers(24);
|
|
194
|
+
if (cleaned > 0) console.log(`[transfers] Cleaned ${cleaned} stale transfer(s)`);
|
|
195
|
+
|
|
196
|
+
const identity = (await import('../lib/utils/config.js')).default.get('identity');
|
|
197
|
+
await hooks.emit('on:app:ready', {
|
|
198
|
+
version: program.version(),
|
|
199
|
+
identity: identity ? { handle: identity.handle, publicKey: identity.publicKey } : null,
|
|
200
|
+
mode: 'cli',
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const shutdown = async () => {
|
|
204
|
+
await hooks.emit('on:app:shutdown', {});
|
|
205
|
+
process.exit(process.exitCode || 0);
|
|
206
|
+
};
|
|
207
|
+
process.on('SIGINT', shutdown);
|
|
208
|
+
process.on('SIGTERM', shutdown);
|
|
209
|
+
} catch (err) {
|
|
210
|
+
// Extension loading is non-fatal
|
|
211
|
+
logger.warn?.(`Extension loading failed: ${err.message}`) || console.warn(`[extensions] ${err.message}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (!process.argv.slice(2).length) {
|
|
216
|
+
program.outputHelp();
|
|
217
|
+
} else {
|
|
218
|
+
try {
|
|
219
|
+
await program.parseAsync(process.argv);
|
|
220
|
+
} catch (err) {
|
|
221
|
+
if (err.code !== 'commander.helpDisplayed' && err.code !== 'commander.unknownCommand') {
|
|
222
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// keytar's native D-Bus handles block the event loop on headless Linux
|
|
228
|
+
const longRunning = new Set(['serve', 'nearby', 'stream']);
|
|
229
|
+
if (!longRunning.has(currentCmd)) {
|
|
230
|
+
process.exit(process.exitCode || 0);
|
|
231
|
+
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
],
|
|
18
18
|
"permissions": ["config:read", "config:write", "net:http"],
|
|
19
19
|
"config": {
|
|
20
|
-
"enabled": { "type": "boolean", "default": true, "description": "Enable anonymous usage analytics" },
|
|
20
|
+
"enabled": { "type": "boolean", "default": true, "description": "Enable anonymous usage analytics (PostHog)" },
|
|
21
21
|
"posthogKey": { "type": "string", "default": "phc_PSslPgpRuFzQf6s5qbN9atXFGUDHzCvMlmpBTtdTkte", "description": "PostHog project API key (leave empty for default)" },
|
|
22
22
|
"posthogHost": { "type": "string", "default": "https://us.i.posthog.com", "description": "PostHog ingestion host" },
|
|
23
23
|
"sessionTracking": { "type": "boolean", "default": true, "description": "Track session duration" }
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"hooks": ["on:app:ready"],
|
|
9
9
|
"permissions": ["config:read", "config:write", "identity:read"],
|
|
10
10
|
"config": {
|
|
11
|
+
"enabled": { "type": "boolean", "default": true, "description": "Enable discovery server integration" },
|
|
11
12
|
"bootstrapServers": { "type": "array", "default": ["https://discovery-1.palexplorer.com", "https://discovery-2.palexplorer.com"], "description": "Bootstrap discovery server URLs" },
|
|
12
13
|
"refreshInterval": { "type": "number", "default": 1800000, "description": "Server list refresh interval in ms (default: 30 min)" },
|
|
13
14
|
"enableGossip": { "type": "boolean", "default": true, "description": "Exchange server lists with connected peers" }
|
|
@@ -12,6 +12,6 @@
|
|
|
12
12
|
"driveLetter": { "type": "string", "default": "P", "description": "Windows drive letter" },
|
|
13
13
|
"autoMount": { "type": "boolean", "default": true, "description": "Auto-mount on startup" }
|
|
14
14
|
},
|
|
15
|
-
"tier": "
|
|
15
|
+
"tier": "pro",
|
|
16
16
|
"minAppVersion": "0.5.0"
|
|
17
17
|
}
|