pal-explorer-cli 0.4.0

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.
Files changed (156) hide show
  1. package/LICENSE.md +18 -0
  2. package/README.md +314 -0
  3. package/bin/pal.js +230 -0
  4. package/extensions/@palexplorer/analytics/README.md +45 -0
  5. package/extensions/@palexplorer/analytics/docs/MONETIZATION.md +14 -0
  6. package/extensions/@palexplorer/analytics/docs/PLAN.md +23 -0
  7. package/extensions/@palexplorer/analytics/docs/PRIVACY.md +38 -0
  8. package/extensions/@palexplorer/analytics/extension.json +27 -0
  9. package/extensions/@palexplorer/analytics/index.js +186 -0
  10. package/extensions/@palexplorer/analytics/test/analytics.test.js +82 -0
  11. package/extensions/@palexplorer/audit/extension.json +17 -0
  12. package/extensions/@palexplorer/audit/index.js +2 -0
  13. package/extensions/@palexplorer/auth-email/extension.json +17 -0
  14. package/extensions/@palexplorer/auth-email/index.js +102 -0
  15. package/extensions/@palexplorer/auth-oauth/extension.json +16 -0
  16. package/extensions/@palexplorer/auth-oauth/index.js +199 -0
  17. package/extensions/@palexplorer/chat/extension.json +17 -0
  18. package/extensions/@palexplorer/chat/index.js +2 -0
  19. package/extensions/@palexplorer/discovery/extension.json +16 -0
  20. package/extensions/@palexplorer/discovery/index.js +111 -0
  21. package/extensions/@palexplorer/email-notifications/extension.json +23 -0
  22. package/extensions/@palexplorer/email-notifications/index.js +242 -0
  23. package/extensions/@palexplorer/explorer-integration/extension.json +13 -0
  24. package/extensions/@palexplorer/explorer-integration/index.js +122 -0
  25. package/extensions/@palexplorer/groups/extension.json +17 -0
  26. package/extensions/@palexplorer/groups/index.js +2 -0
  27. package/extensions/@palexplorer/networks/extension.json +17 -0
  28. package/extensions/@palexplorer/networks/index.js +2 -0
  29. package/extensions/@palexplorer/share-links/extension.json +17 -0
  30. package/extensions/@palexplorer/share-links/index.js +2 -0
  31. package/extensions/@palexplorer/sync/extension.json +17 -0
  32. package/extensions/@palexplorer/sync/index.js +2 -0
  33. package/extensions/@palexplorer/user-mgmt/extension.json +17 -0
  34. package/extensions/@palexplorer/user-mgmt/index.js +2 -0
  35. package/extensions/@palexplorer/vfs/extension.json +17 -0
  36. package/extensions/@palexplorer/vfs/index.js +167 -0
  37. package/lib/capabilities.js +263 -0
  38. package/lib/commands/analytics.js +175 -0
  39. package/lib/commands/api-keys.js +131 -0
  40. package/lib/commands/audit.js +235 -0
  41. package/lib/commands/auth.js +137 -0
  42. package/lib/commands/backup.js +76 -0
  43. package/lib/commands/billing.js +148 -0
  44. package/lib/commands/chat.js +217 -0
  45. package/lib/commands/cloud-backup.js +231 -0
  46. package/lib/commands/comment.js +99 -0
  47. package/lib/commands/completion.js +203 -0
  48. package/lib/commands/compliance.js +218 -0
  49. package/lib/commands/config.js +136 -0
  50. package/lib/commands/connect.js +44 -0
  51. package/lib/commands/dept.js +294 -0
  52. package/lib/commands/device.js +146 -0
  53. package/lib/commands/download.js +226 -0
  54. package/lib/commands/explorer.js +178 -0
  55. package/lib/commands/extension.js +970 -0
  56. package/lib/commands/favorite.js +90 -0
  57. package/lib/commands/federation.js +270 -0
  58. package/lib/commands/file.js +533 -0
  59. package/lib/commands/group.js +271 -0
  60. package/lib/commands/gui-share.js +29 -0
  61. package/lib/commands/init.js +61 -0
  62. package/lib/commands/invite.js +59 -0
  63. package/lib/commands/list.js +59 -0
  64. package/lib/commands/log.js +116 -0
  65. package/lib/commands/nearby.js +108 -0
  66. package/lib/commands/network.js +251 -0
  67. package/lib/commands/notify.js +198 -0
  68. package/lib/commands/org.js +273 -0
  69. package/lib/commands/pal.js +180 -0
  70. package/lib/commands/permissions.js +216 -0
  71. package/lib/commands/pin.js +97 -0
  72. package/lib/commands/protocol.js +357 -0
  73. package/lib/commands/rbac.js +147 -0
  74. package/lib/commands/recover.js +36 -0
  75. package/lib/commands/register.js +171 -0
  76. package/lib/commands/relay.js +131 -0
  77. package/lib/commands/remote.js +368 -0
  78. package/lib/commands/revoke.js +50 -0
  79. package/lib/commands/scanner.js +280 -0
  80. package/lib/commands/schedule.js +344 -0
  81. package/lib/commands/scim.js +203 -0
  82. package/lib/commands/search.js +181 -0
  83. package/lib/commands/serve.js +438 -0
  84. package/lib/commands/server.js +350 -0
  85. package/lib/commands/share-link.js +199 -0
  86. package/lib/commands/share.js +323 -0
  87. package/lib/commands/sso.js +200 -0
  88. package/lib/commands/status.js +136 -0
  89. package/lib/commands/stream.js +562 -0
  90. package/lib/commands/su.js +187 -0
  91. package/lib/commands/sync.js +827 -0
  92. package/lib/commands/transfers.js +152 -0
  93. package/lib/commands/uninstall.js +188 -0
  94. package/lib/commands/update.js +204 -0
  95. package/lib/commands/user.js +276 -0
  96. package/lib/commands/vfs.js +84 -0
  97. package/lib/commands/web.js +52 -0
  98. package/lib/commands/webhook.js +180 -0
  99. package/lib/commands/whoami.js +59 -0
  100. package/lib/commands/workspace.js +121 -0
  101. package/lib/core/accessLog.js +54 -0
  102. package/lib/core/analytics.js +99 -0
  103. package/lib/core/backup.js +84 -0
  104. package/lib/core/billing.js +336 -0
  105. package/lib/core/bitfieldStore.js +53 -0
  106. package/lib/core/connectionManager.js +182 -0
  107. package/lib/core/dhtDiscovery.js +148 -0
  108. package/lib/core/discoveryClient.js +408 -0
  109. package/lib/core/extensionAnalyzer.js +357 -0
  110. package/lib/core/extensionSandbox.js +250 -0
  111. package/lib/core/extensionWorkerHost.js +166 -0
  112. package/lib/core/extensions.js +1082 -0
  113. package/lib/core/fileDiff.js +69 -0
  114. package/lib/core/groups.js +119 -0
  115. package/lib/core/identity.js +340 -0
  116. package/lib/core/mdnsService.js +126 -0
  117. package/lib/core/networks.js +81 -0
  118. package/lib/core/permissions.js +109 -0
  119. package/lib/core/pro.js +27 -0
  120. package/lib/core/resolver.js +74 -0
  121. package/lib/core/serverList.js +224 -0
  122. package/lib/core/sharePolicy.js +69 -0
  123. package/lib/core/shares.js +325 -0
  124. package/lib/core/signalingServer.js +441 -0
  125. package/lib/core/streamTransport.js +106 -0
  126. package/lib/core/su.js +55 -0
  127. package/lib/core/syncEngine.js +264 -0
  128. package/lib/core/syncState.js +159 -0
  129. package/lib/core/transfers.js +259 -0
  130. package/lib/core/users.js +225 -0
  131. package/lib/core/vfs.js +216 -0
  132. package/lib/core/webServer.js +702 -0
  133. package/lib/core/webrtcStream.js +396 -0
  134. package/lib/crypto/chatEncryption.js +57 -0
  135. package/lib/crypto/shareEncryption.js +195 -0
  136. package/lib/crypto/sharePassword.js +35 -0
  137. package/lib/crypto/streamEncryption.js +189 -0
  138. package/lib/package.json +1 -0
  139. package/lib/protocol/envelope.js +271 -0
  140. package/lib/protocol/handler.js +191 -0
  141. package/lib/protocol/index.js +27 -0
  142. package/lib/protocol/messages.js +247 -0
  143. package/lib/protocol/negotiation.js +127 -0
  144. package/lib/protocol/policy.js +142 -0
  145. package/lib/protocol/router.js +86 -0
  146. package/lib/protocol/sync.js +122 -0
  147. package/lib/utils/cli.js +15 -0
  148. package/lib/utils/config.js +123 -0
  149. package/lib/utils/configIntegrity.js +87 -0
  150. package/lib/utils/downloadDir.js +9 -0
  151. package/lib/utils/explorer.js +83 -0
  152. package/lib/utils/format.js +12 -0
  153. package/lib/utils/help.js +357 -0
  154. package/lib/utils/logger.js +103 -0
  155. package/lib/utils/torrent.js +203 -0
  156. package/package.json +71 -0
package/LICENSE.md ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2015-2026 Palexplorer. All rights reserved.
2
+
3
+ This software is provided under a proprietary license. You may use the
4
+ compiled/installed version of this software for personal and commercial
5
+ purposes. You may NOT:
6
+
7
+ - Redistribute, sublicense, or sell copies of the source code
8
+ - Modify the source code and distribute derivative works
9
+ - Reverse engineer, decompile, or disassemble the software
10
+ - Remove or alter any proprietary notices or labels
11
+
12
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
18
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,314 @@
1
+ # Pal Explorer (`pe`)
2
+
3
+ [![npm](https://img.shields.io/npm/v/pal-explorer-cli)](https://www.npmjs.com/package/pal-explorer-cli)
4
+ [![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
5
+ [![node](https://img.shields.io/badge/node-%3E%3D20-brightgreen)](https://nodejs.org/)
6
+
7
+ > Peer-to-peer file sharing with end-to-end encryption. No cloud. No middleman.
8
+
9
+ Pal Explorer lets you share files directly with friends using P2P protocols. Files are encrypted before leaving your device, transferred via WebTorrent, and only decryptable by intended recipients.
10
+
11
+ ## Features
12
+
13
+ ### File Sharing
14
+ - Share files, folders, or entire drives via WebTorrent
15
+ - Private shares with per-recipient E2EE (XChaCha20-Poly1305)
16
+ - Public shares with global magnet links
17
+ - Non-recursive folder sharing (top-level only)
18
+ - Web share links with expiry and download limits
19
+ - Share invites sent automatically via discovery server inbox
20
+
21
+ ### Groups
22
+ - Create named groups and organize pals
23
+ - Share with entire groups in one command (`--with-group`)
24
+ - Broadcast messages to all group members
25
+ - Pro limits on group count and member count
26
+
27
+ ### Comments
28
+ - Per-share comment threads
29
+ - Add, list, and delete comments on any share
30
+ - Comments linked to author identity
31
+
32
+ ### Sync
33
+ - Push/pull directory sync between pals
34
+ - SHA-256 manifest-based delta sync
35
+ - Watch mode for automatic sync on file changes
36
+ - Conflict detection with `.sync-conflict` copies
37
+ - Sync history tracking
38
+
39
+ ### File Explorer
40
+ - Browse, search, and manage files with permission checks
41
+ - Directory tree visualization
42
+ - File info with share status
43
+ - Copy, move, rename, delete operations
44
+ - Audit log for all file operations
45
+
46
+ ### Remote Browsing
47
+ - Browse pals' shares without downloading
48
+ - List files in remote shares
49
+ - Selective file download from remote shares
50
+ - Server signature verification
51
+
52
+ ### Chat
53
+ - Real-time encrypted messaging between pals
54
+ - Chat history with conversation list
55
+ - Fetch new messages from the server
56
+
57
+ ### Security
58
+ - **Ed25519 identities** with private keys stored in OS credential manager (keytar)
59
+ - **XChaCha20-Poly1305** authenticated encryption for all private shares
60
+ - **Per-recipient key wrapping** -- each recipient gets a uniquely encrypted share key
61
+ - **Key rotation on revocation** -- removing a recipient triggers re-encryption
62
+ - **BIP-39 recovery** -- 24-word mnemonic phrase for identity backup
63
+ - **Challenge-response registration** -- server never sees your private key
64
+ - **Signed server responses** -- clients verify discovery server authenticity
65
+ - **Encrypted messages** -- inbox payloads encrypted end-to-end
66
+ - **Metadata protection** -- opaque filenames and encrypted manifests in torrents
67
+ - **TOFU server key pinning** -- federated servers pinned on first contact
68
+ - **Config integrity** -- HMAC-SHA256 signing for critical config sections
69
+ - **PIN lock** -- protect the GUI with a PIN code
70
+
71
+ ### Pro Features
72
+ - Multi-user profiles with role-based access (owner, admin, member)
73
+ - Encrypted backup and restore
74
+ - API keys for programmatic access
75
+ - Web share links
76
+ - Scheduled tasks (shares, downloads, auto-revokes)
77
+ - Extended inbox and transfer analytics
78
+ - Ad-free experience
79
+
80
+ ### Multi-User
81
+ - Multiple identity profiles on one device
82
+ - Owner/admin/member role hierarchy
83
+ - Login switching between profiles
84
+ - Profile promotion and demotion
85
+
86
+ ### Discovery & Presence
87
+ - LAN peer discovery via mDNS (Bonjour)
88
+ - Federated handle system (`alice@server.com`)
89
+ - DHT-based decentralized fallback
90
+ - Online/offline presence status for pals
91
+ - Multi-device support with per-device registration
92
+
93
+ ## Quick Start
94
+
95
+ ```bash
96
+ # Install CLI globally
97
+ npm install -g pal-explorer-cli
98
+
99
+ # Or clone and link for development
100
+ # git clone https://github.com/hn2/palexplorer && cd palexplorer && npm install && npm link
101
+
102
+ # Create your identity
103
+ pe init "YourName"
104
+ # Save the 24-word recovery phrase!
105
+
106
+ # Share a folder with a friend
107
+ pe share ~/Documents --visibility private --with Alice
108
+
109
+ # Start seeding
110
+ pe serve
111
+
112
+ # Download from a peer
113
+ pe download "magnet:?xt=urn:btih:..."
114
+ ```
115
+
116
+ ### Adding Friends
117
+
118
+ ```bash
119
+ pe invite --qr # Generate invite link + QR code
120
+ pe pal add @alice # Add by handle
121
+ pe pal add pal://eyJ... # Add by invite link
122
+ pe nearby --add # Auto-discover on LAN
123
+ ```
124
+
125
+ ## CLI Reference
126
+
127
+ The `pe` CLI includes 100+ subcommands. See [docs/CLI.md](docs/CLI.md) for the complete reference.
128
+
129
+ ### Key Commands
130
+
131
+ ```bash
132
+ pe init <name> # Create identity
133
+ pe register <handle> # Register on discovery network
134
+ pe share <path> -v private -w bob # Share encrypted with a pal
135
+ pe serve # Start seeding
136
+ pe download <magnet> # Download from magnet link
137
+ pe pal add @alice # Add a pal by handle
138
+ pe sync push ./project alice # Push sync to a pal
139
+ pe status # System health dashboard
140
+ ```
141
+
142
+ ### Global Flags
143
+
144
+ | Flag | Description |
145
+ |:---|:---|
146
+ | `--json` | Output as JSON |
147
+ | `--verbose` | Verbose logging |
148
+ | `--quiet` | Suppress non-essential output |
149
+
150
+ ## GUI
151
+
152
+ The Electron desktop app lives in the `gui/` directory:
153
+
154
+ ```bash
155
+ cd gui
156
+ npm install
157
+ npm run electron
158
+ ```
159
+
160
+ Features: setup wizard, splash screen with startup progress, dark/light themes, system tray, drag-and-drop sharing, P2P chat, PIN lock, media streaming, workspaces, extensions, file explorer with favorites, command palette, and sidebar grouped into Content/Social/System sections.
161
+
162
+ ## Web Dashboard
163
+
164
+ Start the web dashboard alongside the seeder:
165
+
166
+ ```bash
167
+ pe serve --web
168
+ pe web # Opens in browser
169
+ ```
170
+
171
+ REST API + WebSocket for real-time transfer monitoring, chat, and Pro/billing management.
172
+
173
+ ## Configuration
174
+
175
+ | Key | Default | Description |
176
+ |:---|:---|:---|
177
+ | `port` | auto | Local seeder port |
178
+ | `storage_path` | `./downloads` | Default download directory |
179
+ | `max_connections` | `50` | Max P2P connections |
180
+ | `bandwidth_cap` | `0` | Upload cap in KB/s (0 = unlimited) |
181
+ | `discovery_servers` | `http://localhost:3000` | Discovery server URLs (comma-separated) |
182
+ | `announce_interval` | -- | DHT announce interval in seconds |
183
+
184
+ Config file location: `~/.config/palexplorer-cli/config.json`
185
+
186
+ Override the discovery server URL with the `PAL_DISCOVERY_URL` environment variable.
187
+
188
+ ## Free vs Pro
189
+
190
+ | Feature | Free | Pro |
191
+ |:---|:---:|:---:|
192
+ | File sharing, E2EE, chat | Yes | Yes |
193
+ | Groups (max 3, 5 members each) | Yes | Unlimited |
194
+ | Comments (max 10 per share) | Yes | Unlimited |
195
+ | Share recipients (max 5) | Yes | Unlimited |
196
+ | Ad-free experience | No | Yes |
197
+ | Multi-user profiles | No | Yes |
198
+ | Vanity handles | No | Yes |
199
+ | Extended inbox (1000 msgs) | No | Yes |
200
+ | Transfer analytics | No | Yes |
201
+ | Encrypted backup/restore | No | Yes |
202
+ | API keys | No | Yes |
203
+ | Scheduled tasks | No | Yes |
204
+
205
+ **Pricing:** Pro $2.99/mo | Pro Annual $29.99/yr | Enterprise $9.99/seat/mo
206
+
207
+ See [docs/MONETIZATION.md](docs/MONETIZATION.md) for full comparison, enforcement architecture, and details.
208
+
209
+ ## Architecture
210
+
211
+ ```
212
+ +------------------+ +------------------+ +--------------------+
213
+ | CLI (pe) | | Electron GUI | | Web Dashboard |
214
+ | bin/pal.js | | gui/ | | web/ |
215
+ +--------+---------+ +--------+---------+ +--------+-----------+
216
+ | | |
217
+ +------------------------+------------------------+
218
+ | Core Libraries |
219
+ | lib/core/ - identity, shares, groups, sync |
220
+ | lib/crypto/ - E2EE, key exchange |
221
+ | lib/utils/ - config, logging |
222
+ +-------------------------+-----------------------+
223
+ |
224
+ +--------------+--------------+
225
+ | Discovery Server |
226
+ | palexplorer-server (repo) |
227
+ | LevelDB + Express |
228
+ | OAuth + Lemon Squeezy billing |
229
+ +------------------------------+
230
+ ```
231
+
232
+ **CLI** -- 100+ subcommands, shell completion (bash/zsh), JSON output.
233
+
234
+ **GUI** -- Electron desktop app with command palette, setup wizard, splash screen, dark/light themes, media streaming, extensions, workspaces, file explorer, P2P chat, PIN lock.
235
+
236
+ **Web Dashboard** -- Browser-based node management. REST API + WebSocket, OAuth login, chat, billing.
237
+
238
+ **Discovery Server** -- Separate repo: [palexplorer-server](../palexplorer-server). Identity resolution, message relay, OAuth, Lemon Squeezy billing, self-update server. Self-hostable via Docker.
239
+
240
+ **PAL/1.0 Protocol** -- Proprietary control protocol on top of BitTorrent/DHT/mDNS. 27 message types, policy engine, smart routing.
241
+
242
+ ## Ecosystem
243
+
244
+ | Project | Description |
245
+ |:---|:---|
246
+ | [palexplorer](.) | Desktop app (Electron + React + CLI) |
247
+ | [palexplorer-server](../palexplorer-server) | Discovery server (Express 5 + LevelDB) |
248
+ | [palexplorer-www](../palexplorer-www) | Web dashboard (React + Express) |
249
+ | [palexplorer-mobile](../palexplorer-mobile) | Mobile app (React Native) |
250
+ | [palexplorer-sdk](../palexplorer-sdk) | JavaScript SDK for integrations |
251
+
252
+ ## Repo Review
253
+
254
+ A focused architecture and product review was added on 2026-03-23.
255
+
256
+ Executive summary:
257
+ - `palexplorer` is the core desktop product, not just a thin client.
258
+ - Do not rename `palexplorer` to `palexplorer-client`.
259
+ - Keep `palexplorer-mobile` as a separate app, but treat it as a companion surface with shared core packages.
260
+ - Highest-priority fixes are packaged CLI installation, cross-platform packaging parity, startup page loading order, and stale GUI audit documentation.
261
+
262
+ See:
263
+ - [docs/REPO-REVIEW-2026-03-23.md](docs/REPO-REVIEW-2026-03-23.md)
264
+ - [docs/REPO-REVIEW-ACTION-PLAN-2026-03-23.md](docs/REPO-REVIEW-ACTION-PLAN-2026-03-23.md)
265
+
266
+ ## Development
267
+
268
+ ```bash
269
+ cd palexplorer
270
+ npm install
271
+ npm link
272
+
273
+ # Run tests
274
+ npm test
275
+
276
+ # Start the discovery server (separate repo)
277
+ cd ../palexplorer-server
278
+ npm install
279
+ npm start
280
+ ```
281
+
282
+ ### Project Structure
283
+
284
+ ```
285
+ bin/ CLI entry point
286
+ lib/commands/ One file per command group (63 files)
287
+ lib/core/ Core logic (identity, shares, groups, sync, transfers, users, backup, extensions, billing, vfs, streaming)
288
+ lib/crypto/ Encryption (stream cipher, share key wrapping)
289
+ lib/protocol/ PAL/1.0 protocol (envelope, messages, policy, router, negotiation, sync, handler)
290
+ lib/utils/ Config, logging, config integrity
291
+ gui/ Electron desktop app (React + Vite)
292
+ web/ Web dashboard (HTML/JS)
293
+ extensions/ Bundled extensions (@palexplorer/vfs, explorer-integration, transfer-analytics, media-streaming)
294
+ test/ Test suite (node:test, incl. stress tests)
295
+ scripts/ Build and publish scripts
296
+ docs/ Documentation
297
+ ```
298
+
299
+ ## Security
300
+
301
+ For full details, see [SECURITY.md](docs/SECURITY.md).
302
+
303
+ ## Contributing
304
+
305
+ 1. Fork the repository
306
+ 2. Create a feature branch (`git checkout -b feature/my-feature`)
307
+ 3. Write tests for new functionality
308
+ 4. Ensure `npm test` passes
309
+ 5. Submit a pull request
310
+
311
+ ## License
312
+
313
+ MIT
314
+
package/bin/pal.js ADDED
@@ -0,0 +1,230 @@
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
+ ['gui-share', '../lib/commands/gui-share.js'],
85
+ ['vfs', '../lib/commands/vfs.js'],
86
+ ['file', '../lib/commands/file.js'],
87
+ ['remote', '../lib/commands/remote.js'],
88
+ ['user', '../lib/commands/user.js'],
89
+ ['schedule', '../lib/commands/schedule.js'],
90
+ ['chat', '../lib/commands/chat.js'],
91
+ ['backup', '../lib/commands/backup.js'],
92
+ ['api-keys', '../lib/commands/api-keys.js'],
93
+ ['share-link', '../lib/commands/share-link.js'],
94
+ ['comment', '../lib/commands/comment.js'],
95
+ ['update', '../lib/commands/update.js'],
96
+ ['auth', '../lib/commands/auth.js'],
97
+ ['network', '../lib/commands/network.js'],
98
+ ['search', '../lib/commands/search.js'],
99
+ ['connect', '../lib/commands/connect.js'],
100
+ ['protocol', '../lib/commands/protocol.js'],
101
+ ['workspace', '../lib/commands/workspace.js'],
102
+ ['favorite', '../lib/commands/favorite.js'],
103
+ ['pin', '../lib/commands/pin.js'],
104
+ ['stream', '../lib/commands/stream.js'],
105
+ ['uninstall', '../lib/commands/uninstall.js'],
106
+ ['extension', '../lib/commands/extension.js'],
107
+ ['ext', '../lib/commands/extension.js'],
108
+ ['billing', '../lib/commands/billing.js'],
109
+ ['permissions', '../lib/commands/permissions.js'],
110
+ ['org', '../lib/commands/org.js'],
111
+ ['su', '../lib/commands/su.js'],
112
+ ['federation', '../lib/commands/federation.js'],
113
+ ['analytics', '../lib/commands/analytics.js'],
114
+ ['webhook', '../lib/commands/webhook.js'],
115
+ ['relay', '../lib/commands/relay.js'],
116
+ ['compliance', '../lib/commands/compliance.js'],
117
+ ['sso', '../lib/commands/sso.js'],
118
+ ['rbac', '../lib/commands/rbac.js'],
119
+ ['scim', '../lib/commands/scim.js'],
120
+ ['audit', '../lib/commands/audit.js'],
121
+ ['scanner', '../lib/commands/scanner.js'],
122
+ ['notify', '../lib/commands/notify.js'],
123
+ ['cloud-backup', '../lib/commands/cloud-backup.js'],
124
+ ['dept', '../lib/commands/dept.js'],
125
+ ];
126
+
127
+ // To keep it truly fast, we should only register the specific command being run.
128
+ // Commander's parseAsync will handle the matching.
129
+ const currentCmd = process.argv[2];
130
+ const cmdMatch = commands.find(c => c[0] === currentCmd);
131
+
132
+ // Commands that don't modify data — skip migrations for speed
133
+ const readOnlyCommands = new Set([
134
+ 'whoami', 'status', 'list', 'log', 'completion', 'nearby',
135
+ 'protocol', 'pin', 'favorite', 'search', 'workspace',
136
+ ]);
137
+ const isVersionOrHelp = !currentCmd || currentCmd === '--help' || currentCmd === '-h'
138
+ || currentCmd === '--version' || currentCmd === '-V';
139
+ const needsMigrations = cmdMatch && !readOnlyCommands.has(cmdMatch[0]) && !isVersionOrHelp;
140
+
141
+ if (cmdMatch) {
142
+ if (needsMigrations) await runMigrations();
143
+ const register = await lazy(cmdMatch[1]);
144
+ await register(program);
145
+ } else if (isVersionOrHelp) {
146
+ // Help/version — load all commands for full help text, skip migrations
147
+ await Promise.all(commands.map(c => lazy(c[1])(program)));
148
+ } else {
149
+ // Unknown command — load all for Commander to handle
150
+ await runMigrations();
151
+ await Promise.all(commands.map(c => lazy(c[1])(program)));
152
+ }
153
+
154
+ // Grouped help text
155
+ program.addHelpText('after', `
156
+ ${chalk.cyan.bold('Command Groups:')}
157
+
158
+ ${chalk.yellow('Identity:')} init, whoami, register, verify, recover
159
+ ${chalk.yellow('Users:')} user list/add/remove/promote, login
160
+ ${chalk.yellow('Sharing:')} share, list, revoke, serve, download, share-link, share-rename, permissions
161
+ ${chalk.yellow('Sync:')} sync push/pull/status/watch/list/remove/diff
162
+ ${chalk.yellow('Files:')} file ls/tree/info/copy/move/rename/mkdir/delete/search/open/reveal/audit
163
+ ${chalk.yellow('Remote:')} remote browse/files/download
164
+ ${chalk.yellow('Social:')} pal, invite, nearby, group, chat, comment
165
+ ${chalk.yellow('Search:')} search (files, pals, groups)
166
+ ${chalk.yellow('Network:')} network create/list/info/invite/join/members/groups/connect/disconnect
167
+ ${chalk.yellow('Workspace:')} workspace list/create/delete/add/remove
168
+ ${chalk.yellow('Management:')} transfers, config, device, server, explorer, vfs, web, schedule, favorite
169
+ ${chalk.yellow('Protocol:')} protocol info/policy/route/envelope/keys
170
+ ${chalk.yellow('PIN:')} pin set/remove/status
171
+ ${chalk.yellow('Billing:')} billing status/plans/activate/deactivate/checkout
172
+ ${chalk.yellow('Pro:')} backup, api-keys
173
+ ${chalk.yellow('Streaming:')} stream local/remote/stop/status/broadcast/join/transport
174
+ ${chalk.yellow('Extensions:')} ext list/install/remove/enable/disable/info/config/create
175
+ ${chalk.yellow('Orgs:')} org create/list/info/invite/remove/subscribe/unsubscribe/billing
176
+ ${chalk.yellow('Utilities:')} status, log, completion, gui-share, uninstall
177
+
178
+ ${chalk.gray('Aliases: pe connect → pe network connect, pe disconnect → pe network disconnect')}
179
+ `);
180
+
181
+ // Load extensions and emit lifecycle hooks
182
+ let extensionsLoaded = false;
183
+ if (!isVersionOrHelp) {
184
+ try {
185
+ const { loadAllExtensions, hooks, ensureDefaultExtensions } = await import('../lib/core/extensions.js');
186
+ ensureDefaultExtensions();
187
+ await loadAllExtensions();
188
+ extensionsLoaded = true;
189
+
190
+ // Clean stale transfers (0% for >24h)
191
+ const { cleanStaleTransfers } = await import('../lib/core/transfers.js');
192
+ const cleaned = cleanStaleTransfers(24);
193
+ if (cleaned > 0) console.log(`[transfers] Cleaned ${cleaned} stale transfer(s)`);
194
+
195
+ const identity = (await import('../lib/utils/config.js')).default.get('identity');
196
+ await hooks.emit('on:app:ready', {
197
+ version: program.version(),
198
+ identity: identity ? { handle: identity.handle, publicKey: identity.publicKey } : null,
199
+ mode: 'cli',
200
+ });
201
+
202
+ const shutdown = async () => {
203
+ await hooks.emit('on:app:shutdown', {});
204
+ process.exit(process.exitCode || 0);
205
+ };
206
+ process.on('SIGINT', shutdown);
207
+ process.on('SIGTERM', shutdown);
208
+ } catch (err) {
209
+ // Extension loading is non-fatal
210
+ logger.warn?.(`Extension loading failed: ${err.message}`) || console.warn(`[extensions] ${err.message}`);
211
+ }
212
+ }
213
+
214
+ if (!process.argv.slice(2).length) {
215
+ program.outputHelp();
216
+ } else {
217
+ try {
218
+ await program.parseAsync(process.argv);
219
+ } catch (err) {
220
+ if (err.code !== 'commander.helpDisplayed' && err.code !== 'commander.unknownCommand') {
221
+ console.error(chalk.red(`Error: ${err.message}`));
222
+ }
223
+ }
224
+ }
225
+
226
+ // keytar's native D-Bus handles block the event loop on headless Linux
227
+ const longRunning = new Set(['serve', 'nearby', 'stream']);
228
+ if (!longRunning.has(currentCmd)) {
229
+ process.exit(process.exitCode || 0);
230
+ }
@@ -0,0 +1,45 @@
1
+ # Analytics Extension
2
+
3
+ Anonymous, opt-in usage analytics for Palexplorer via PostHog.
4
+
5
+ ## Privacy Guarantees
6
+
7
+ - **Opt-in only** — disabled by default, must be explicitly enabled
8
+ - **No PII** — device ID is a random UUID, not tied to identity
9
+ - **No content** — never tracks file names, share names, peer identities, or message content
10
+ - **No IP logging** — PostHog configured to anonymize IPs
11
+ - **Transparent** — all tracked events listed below
12
+
13
+ ## Events Tracked
14
+
15
+ | Event | Properties | When |
16
+ |-------|-----------|------|
17
+ | `app_open` | platform, version, arch | App starts |
18
+ | `app_close` | sessionDurationSec | App closes |
19
+ | `share_created` | visibility, recipientCount | Share created |
20
+ | `share_revoked` | — | Share revoked |
21
+ | `transfer_complete` | size, duration, speed | Download finishes |
22
+ | `peer_connected` | — | Peer connects |
23
+ | `peer_disconnected` | — | Peer disconnects |
24
+ | `settings_changed` | key (setting name only) | Setting modified |
25
+
26
+ ## Configuration
27
+
28
+ | Key | Type | Default | Description |
29
+ |-----|------|---------|-------------|
30
+ | `enabled` | boolean | `false` | Enable analytics (opt-in) |
31
+ | `posthogKey` | string | (built-in) | PostHog project API key |
32
+ | `posthogHost` | string | `https://us.i.posthog.com` | PostHog host |
33
+ | `sessionTracking` | boolean | `true` | Track session duration |
34
+
35
+ ```bash
36
+ pe ext config analytics enabled true
37
+ pe ext config analytics sessionTracking false
38
+ ```
39
+
40
+ ## How to Opt Out
41
+
42
+ ```bash
43
+ pe ext config analytics enabled false
44
+ ```
45
+ Or toggle "Usage Analytics" in Settings > Privacy.
@@ -0,0 +1,14 @@
1
+ # Analytics Extension — Monetization
2
+
3
+ ## Tier
4
+
5
+ - [x] Free — available to all users
6
+
7
+ ## Rationale
8
+
9
+ Analytics benefits us (product decisions, bug detection, growth tracking), not the user. It must be free and opt-in to maintain trust. Charging for analytics would be counterproductive — we want maximum opt-in rate.
10
+
11
+ ## Revenue Potential
12
+
13
+ - No direct revenue
14
+ - Indirect value: data-driven feature prioritization, retention insights, conversion tracking for Pro upsells
@@ -0,0 +1,23 @@
1
+ # Analytics Extension — Plan
2
+
3
+ ## Goal
4
+
5
+ Move PostHog analytics out of core into an extension. Core should be pure P2P with no server dependencies. Analytics phones home to PostHog, so it must be an extension.
6
+
7
+ ## Design
8
+
9
+ - Bundled extension (`@palexplorer/analytics`) — runs in-process, no sandbox
10
+ - Listens to existing core hooks — no changes to core event emission needed
11
+ - PostHog client initialized only when enabled (opt-in)
12
+ - Device ID stored in extension's own store (not core config)
13
+ - Offline queue with periodic flush — same pattern as before
14
+ - Error reporting (`reportCrash`/`reportError`) stays in core as a safety feature
15
+
16
+ ## What Changed
17
+
18
+ - Moved from: `lib/core/analytics.js` (PostHog + track functions)
19
+ - Moved to: `extensions/@palexplorer/analytics/index.js`
20
+ - Core `analytics.js` slimmed to error reporting only
21
+ - `posthog-node` dependency moved from root to extension
22
+ - Removed direct `track()` imports from `shares.js`, `transfers.js`, `billing.js`
23
+ - Those events now flow through hooks → analytics extension
@@ -0,0 +1,38 @@
1
+ # Analytics Extension — Privacy
2
+
3
+ ## What We Collect
4
+
5
+ - App open/close events with session duration
6
+ - Feature usage counts (shares, transfers, peer connections)
7
+ - Setting change events (key name only, not values)
8
+ - Platform, app version, architecture
9
+
10
+ ## What We Never Collect
11
+
12
+ - File names, paths, or content
13
+ - Share names, IDs, or recipients
14
+ - Peer identities, handles, or public keys
15
+ - IP addresses (PostHog IP anonymization enabled)
16
+ - Messages or chat content
17
+ - Encryption keys or credentials
18
+ - Browsing/usage patterns that could identify individuals
19
+
20
+ ## Device ID
21
+
22
+ A random UUID generated on first use. Not derived from hardware, identity, or any personal data. Stored locally in the extension's store. Can be reset by disabling and re-enabling the extension.
23
+
24
+ ## Data Flow
25
+
26
+ ```
27
+ App Events → Core Hooks → Analytics Extension → PostHog (US Cloud)
28
+ ```
29
+
30
+ No data is sent to Palexplorer servers. Only PostHog receives analytics data.
31
+
32
+ ## User Control
33
+
34
+ - Disabled by default (opt-in required)
35
+ - Toggle in Settings > Privacy
36
+ - First-launch setup wizard asks for consent
37
+ - Can be disabled at any time via GUI or CLI
38
+ - Disabling immediately stops all data collection