pal-explorer-cli 0.4.11 → 0.4.13

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 (99) hide show
  1. package/README.md +149 -149
  2. package/bin/pal.js +63 -2
  3. package/extensions/@palexplorer/analytics/extension.json +20 -1
  4. package/extensions/@palexplorer/analytics/index.js +19 -9
  5. package/extensions/@palexplorer/audit/extension.json +14 -0
  6. package/extensions/@palexplorer/auth-email/extension.json +15 -0
  7. package/extensions/@palexplorer/auth-oauth/extension.json +15 -0
  8. package/extensions/@palexplorer/chat/extension.json +14 -0
  9. package/extensions/@palexplorer/discovery/extension.json +17 -0
  10. package/extensions/@palexplorer/discovery/index.js +1 -1
  11. package/extensions/@palexplorer/email-notifications/extension.json +23 -0
  12. package/extensions/@palexplorer/groups/extension.json +15 -0
  13. package/extensions/@palexplorer/share-links/extension.json +15 -0
  14. package/extensions/@palexplorer/sync/extension.json +16 -0
  15. package/extensions/@palexplorer/user-mgmt/extension.json +15 -0
  16. package/lib/capabilities.js +24 -24
  17. package/lib/commands/analytics.js +175 -175
  18. package/lib/commands/api-keys.js +131 -131
  19. package/lib/commands/audit.js +235 -235
  20. package/lib/commands/auth.js +137 -137
  21. package/lib/commands/backup.js +76 -76
  22. package/lib/commands/billing.js +148 -148
  23. package/lib/commands/chat.js +217 -217
  24. package/lib/commands/cloud-backup.js +231 -231
  25. package/lib/commands/comment.js +99 -99
  26. package/lib/commands/completion.js +203 -203
  27. package/lib/commands/compliance.js +218 -218
  28. package/lib/commands/config.js +136 -136
  29. package/lib/commands/connect.js +44 -44
  30. package/lib/commands/dept.js +294 -294
  31. package/lib/commands/device.js +146 -146
  32. package/lib/commands/download.js +240 -226
  33. package/lib/commands/explorer.js +178 -178
  34. package/lib/commands/extension.js +1060 -970
  35. package/lib/commands/favorite.js +90 -90
  36. package/lib/commands/federation.js +270 -270
  37. package/lib/commands/file.js +533 -533
  38. package/lib/commands/group.js +271 -271
  39. package/lib/commands/gui-share.js +29 -29
  40. package/lib/commands/init.js +61 -61
  41. package/lib/commands/invite.js +59 -59
  42. package/lib/commands/list.js +58 -58
  43. package/lib/commands/log.js +116 -116
  44. package/lib/commands/nearby.js +108 -108
  45. package/lib/commands/network.js +251 -251
  46. package/lib/commands/notify.js +198 -198
  47. package/lib/commands/org.js +273 -273
  48. package/lib/commands/pal.js +403 -180
  49. package/lib/commands/permissions.js +216 -216
  50. package/lib/commands/pin.js +97 -97
  51. package/lib/commands/protocol.js +357 -357
  52. package/lib/commands/rbac.js +147 -147
  53. package/lib/commands/recover.js +36 -36
  54. package/lib/commands/register.js +171 -171
  55. package/lib/commands/relay.js +131 -131
  56. package/lib/commands/remote.js +368 -368
  57. package/lib/commands/revoke.js +50 -50
  58. package/lib/commands/scanner.js +280 -280
  59. package/lib/commands/schedule.js +344 -344
  60. package/lib/commands/scim.js +203 -203
  61. package/lib/commands/search.js +181 -181
  62. package/lib/commands/serve.js +438 -438
  63. package/lib/commands/server.js +350 -350
  64. package/lib/commands/share-link.js +199 -199
  65. package/lib/commands/share.js +336 -323
  66. package/lib/commands/sso.js +200 -200
  67. package/lib/commands/status.js +145 -145
  68. package/lib/commands/stream.js +562 -562
  69. package/lib/commands/su.js +187 -187
  70. package/lib/commands/sync.js +979 -979
  71. package/lib/commands/transfers.js +152 -152
  72. package/lib/commands/uninstall.js +188 -188
  73. package/lib/commands/update.js +204 -204
  74. package/lib/commands/user.js +276 -276
  75. package/lib/commands/vfs.js +84 -84
  76. package/lib/commands/web-login.js +79 -79
  77. package/lib/commands/web.js +52 -52
  78. package/lib/commands/webhook.js +180 -180
  79. package/lib/commands/whoami.js +59 -59
  80. package/lib/commands/workspace.js +121 -121
  81. package/lib/core/billing.js +16 -5
  82. package/lib/core/dhtDiscovery.js +9 -2
  83. package/lib/core/discoveryClient.js +13 -7
  84. package/lib/core/extensions.js +142 -1
  85. package/lib/core/identity.js +33 -2
  86. package/lib/core/imageProcessor.js +109 -0
  87. package/lib/core/imageTorrent.js +167 -0
  88. package/lib/core/permissions.js +1 -1
  89. package/lib/core/pro.js +11 -4
  90. package/lib/core/serverList.js +4 -1
  91. package/lib/core/shares.js +12 -1
  92. package/lib/core/signalingServer.js +14 -2
  93. package/lib/core/su.js +1 -1
  94. package/lib/core/users.js +1 -1
  95. package/lib/protocol/messages.js +12 -3
  96. package/lib/utils/explorer.js +1 -1
  97. package/lib/utils/help.js +357 -357
  98. package/lib/utils/torrent.js +1 -0
  99. package/package.json +4 -3
package/README.md CHANGED
@@ -1,149 +1,149 @@
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-proprietary-red)](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
- **All features free during beta.** Download at [palexplorer.com](https://palexplorer.com).
12
-
13
- ## Quick Start
14
-
15
- ```bash
16
- # Install CLI globally
17
- npm install -g pal-explorer-cli
18
-
19
- # Create your identity
20
- pe init "YourName"
21
- # Save the 24-word recovery phrase!
22
-
23
- # Share a folder with a friend
24
- pe share ~/Documents --visibility private --with Alice
25
-
26
- # Start seeding
27
- pe serve
28
-
29
- # Download from a peer
30
- pe download "magnet:?xt=urn:btih:..."
31
- ```
32
-
33
- ### Adding Friends
34
-
35
- ```bash
36
- pe invite --qr # Generate invite link + QR code
37
- pe pal add @alice # Add by handle
38
- pe pal add pal://eyJ... # Add by invite link
39
- pe nearby --add # Auto-discover on LAN
40
- ```
41
-
42
- ## Features
43
-
44
- ### File Sharing
45
- - Share files, folders, or entire drives via WebTorrent
46
- - Private shares with per-recipient E2EE (XChaCha20-Poly1305)
47
- - Public shares with global magnet links
48
- - Web share links with expiry and download limits
49
-
50
- ### Sync
51
- - Push/pull directory sync between pals
52
- - SHA-256 manifest-based delta sync
53
- - Watch mode for automatic sync on file changes
54
- - Conflict detection
55
-
56
- ### Chat
57
- - Real-time encrypted messaging between pals
58
- - Chat history with conversation list
59
-
60
- ### Groups
61
- - Create named groups and organize pals
62
- - Share with entire groups in one command (`--with-group`)
63
- - Broadcast messages to all group members
64
-
65
- ### Discovery & Presence
66
- - LAN peer discovery via mDNS
67
- - Federated handle system (`@alice@server.com`)
68
- - DHT-based decentralized fallback
69
- - Online/offline presence status
70
-
71
- ### Security
72
- - **Ed25519 identities** with private keys stored in OS credential manager
73
- - **XChaCha20-Poly1305** authenticated encryption for all private shares
74
- - **Per-recipient key wrapping** -- each recipient gets a uniquely encrypted share key
75
- - **Key rotation on revocation** -- removing a recipient triggers re-encryption
76
- - **BIP-39 recovery** -- 24-word mnemonic phrase for identity backup
77
- - **Zero-knowledge server** -- discovery server never sees your private key or files
78
- - **Signed server responses** -- clients verify server authenticity
79
- - **Encrypted inbox** -- message payloads encrypted end-to-end
80
- - **PIN lock** -- protect the desktop app with a PIN code
81
-
82
- ## CLI
83
-
84
- The `pe` CLI includes 100+ subcommands.
85
-
86
- ### Key Commands
87
-
88
- ```bash
89
- pe init <name> # Create identity
90
- pe register <handle> # Register on discovery network
91
- pe share <path> -v private -w bob # Share encrypted with a pal
92
- pe serve # Start seeding
93
- pe download <magnet> # Download from magnet link
94
- pe pal add @alice # Add a pal by handle
95
- pe sync push ./project alice # Push sync to a pal
96
- pe status # System health dashboard
97
- pe ext list # List available extensions
98
- ```
99
-
100
- ### Global Flags
101
-
102
- | Flag | Description |
103
- |:---|:---|
104
- | `--json` | Output as JSON |
105
- | `--verbose` | Verbose logging |
106
- | `--quiet` | Suppress non-essential output |
107
-
108
- Run `pe --help` or `pe <command> --help` for full usage.
109
-
110
- ## Desktop App
111
-
112
- Download the desktop app at [palexplorer.com/download](https://palexplorer.com/download).
113
-
114
- Features: setup wizard, dark/light themes, system tray, drag-and-drop sharing, P2P chat, PIN lock, media streaming, extensions, file explorer, command palette (Ctrl+K), and workspaces.
115
-
116
- ## Extensions
117
-
118
- 14 built-in extensions including virtual drive (WebDAV), folder sync, chat, groups, discovery, OAuth login, email notifications, and more. All free during beta.
119
-
120
- Browse extensions at [palexplorer.com/extensions](https://palexplorer.com/extensions).
121
-
122
- ```bash
123
- pe ext list # List all extensions
124
- pe ext enable @palexplorer/vfs # Enable an extension
125
- pe ext config @palexplorer/vfs port 1900 # Configure
126
- ```
127
-
128
- ## Configuration
129
-
130
- | Key | Default | Description |
131
- |:---|:---|:---|
132
- | `port` | auto | Local seeder port |
133
- | `storage_path` | `./downloads` | Default download directory |
134
- | `max_connections` | `50` | Max P2P connections |
135
- | `bandwidth_cap` | `0` | Upload cap in KB/s (0 = unlimited) |
136
-
137
- Config file: `~/.config/palexplorer-cli/config.json`
138
-
139
- ## Links
140
-
141
- - [Website](https://palexplorer.com)
142
- - [Download](https://palexplorer.com/download)
143
- - [Extensions](https://palexplorer.com/extensions)
144
- - [Marketplace](https://palexplorer.com/marketplace)
145
- - [Discord](https://discord.gg/VrCcGmNJ8Q)
146
-
147
- ## License
148
-
149
- Proprietary. All rights reserved. See [LICENSE.md](LICENSE.md).
1
+ # Pal Explorer (`pal`)
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-proprietary-red)](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
+ **All features free during beta.** Download at [palexplorer.com](https://palexplorer.com).
12
+
13
+ ## Quick Start
14
+
15
+ ```bash
16
+ # Install CLI globally
17
+ npm install -g pal-explorer-cli
18
+
19
+ # Create your identity
20
+ pal init "YourName"
21
+ # Save the 24-word recovery phrase!
22
+
23
+ # Share a folder with a friend
24
+ pal share ~/Documents --visibility private --with Alice
25
+
26
+ # Start seeding
27
+ pal serve
28
+
29
+ # Download from a peer
30
+ pal download "magnet:?xt=urn:btih:..."
31
+ ```
32
+
33
+ ### Adding Friends
34
+
35
+ ```bash
36
+ pal invite --qr # Generate invite link + QR code
37
+ pal pal add @alice # Add by handle
38
+ pal pal add pal://eyJ... # Add by invite link
39
+ pal nearby --add # Auto-discover on LAN
40
+ ```
41
+
42
+ ## Features
43
+
44
+ ### File Sharing
45
+ - Share files, folders, or entire drives via WebTorrent
46
+ - Private shares with per-recipient E2EE (XChaCha20-Poly1305)
47
+ - Public shares with global magnet links
48
+ - Web share links with expiry and download limits
49
+
50
+ ### Sync
51
+ - Push/pull directory sync between pals
52
+ - SHA-256 manifest-based delta sync
53
+ - Watch mode for automatic sync on file changes
54
+ - Conflict detection
55
+
56
+ ### Chat
57
+ - Real-time encrypted messaging between pals
58
+ - Chat history with conversation list
59
+
60
+ ### Groups
61
+ - Create named groups and organize pals
62
+ - Share with entire groups in one command (`--with-group`)
63
+ - Broadcast messages to all group members
64
+
65
+ ### Discovery & Presence
66
+ - LAN peer discovery via mDNS
67
+ - Federated handle system (`@alice@server.com`)
68
+ - DHT-based decentralized fallback
69
+ - Online/offline presence status
70
+
71
+ ### Security
72
+ - **Ed25519 identities** with private keys stored in OS credential manager
73
+ - **XChaCha20-Poly1305** authenticated encryption for all private shares
74
+ - **Per-recipient key wrapping** -- each recipient gets a uniquely encrypted share key
75
+ - **Key rotation on revocation** -- removing a recipient triggers re-encryption
76
+ - **BIP-39 recovery** -- 24-word mnemonic phrase for identity backup
77
+ - **Zero-knowledge server** -- discovery server never sees your private key or files
78
+ - **Signed server responses** -- clients verify server authenticity
79
+ - **Encrypted inbox** -- message payloads encrypted end-to-end
80
+ - **PIN lock** -- protect the desktop app with a PIN code
81
+
82
+ ## CLI
83
+
84
+ The `pal` CLI includes 100+ subcommands.
85
+
86
+ ### Key Commands
87
+
88
+ ```bash
89
+ pal init <name> # Create identity
90
+ pal register <handle> # Register on discovery network
91
+ pal share <path> -v private -w bob # Share encrypted with a pal
92
+ pal serve # Start seeding
93
+ pal download <magnet> # Download from magnet link
94
+ pal pal add @alice # Add a pal by handle
95
+ pal sync push ./project alice # Push sync to a pal
96
+ pal status # System health dashboard
97
+ pal ext list # List available extensions
98
+ ```
99
+
100
+ ### Global Flags
101
+
102
+ | Flag | Description |
103
+ |:---|:---|
104
+ | `--json` | Output as JSON |
105
+ | `--verbose` | Verbose logging |
106
+ | `--quiet` | Suppress non-essential output |
107
+
108
+ Run `pe --help` or `pe <command> --help` for full usage.
109
+
110
+ ## Desktop App
111
+
112
+ Download the desktop app at [palexplorer.com/download](https://palexplorer.com/download).
113
+
114
+ Features: setup wizard, dark/light themes, system tray, drag-and-drop sharing, P2P chat, PIN lock, media streaming, extensions, file explorer, command palette (Ctrl+K), and workspaces.
115
+
116
+ ## Extensions
117
+
118
+ 14 built-in extensions including virtual drive (WebDAV), folder sync, chat, groups, discovery, OAuth login, email notifications, and more. All free during beta.
119
+
120
+ Browse extensions at [palexplorer.com/extensions](https://palexplorer.com/extensions).
121
+
122
+ ```bash
123
+ pal ext list # List all extensions
124
+ pal ext enable @palexplorer/vfs # Enable an extension
125
+ pal ext config @palexplorer/vfs port 1900 # Configure
126
+ ```
127
+
128
+ ## Configuration
129
+
130
+ | Key | Default | Description |
131
+ |:---|:---|:---|
132
+ | `port` | auto | Local seeder port |
133
+ | `storage_path` | `./downloads` | Default download directory |
134
+ | `max_connections` | `50` | Max P2P connections |
135
+ | `bandwidth_cap` | `0` | Upload cap in KB/s (0 = unlimited) |
136
+
137
+ Config file: `~/.config/palexplorer-cli/config.json`
138
+
139
+ ## Links
140
+
141
+ - [Website](https://palexplorer.com)
142
+ - [Download](https://palexplorer.com/download)
143
+ - [Extensions](https://palexplorer.com/extensions)
144
+ - [Marketplace](https://palexplorer.com/marketplace)
145
+ - [Discord](https://discord.gg/VrCcGmNJ8Q)
146
+
147
+ ## License
148
+
149
+ Proprietary. All rights reserved. See [LICENSE.md](LICENSE.md).
package/bin/pal.js CHANGED
@@ -47,7 +47,7 @@ if (logLevelIdx !== -1 && process.argv[logLevelIdx + 1]) {
47
47
  logger.applyGlobalOverride();
48
48
 
49
49
  program
50
- .name('pe')
50
+ .name('pal')
51
51
  .description('p2p file sharing for friends')
52
52
  .version('0.4.0')
53
53
  .option('--json', 'output in JSON format')
@@ -173,10 +173,11 @@ ${chalk.cyan.bold('Command Groups:')}
173
173
  ${chalk.yellow('Pro:')} backup, api-keys
174
174
  ${chalk.yellow('Streaming:')} stream local/remote/stop/status/broadcast/join/transport
175
175
  ${chalk.yellow('Extensions:')} ext list/install/remove/enable/disable/info/config/create
176
+ ${chalk.yellow('Ext Commands:')} <ext-name> <command> (run commands contributed by extensions)
176
177
  ${chalk.yellow('Orgs:')} org create/list/info/invite/remove/subscribe/unsubscribe/billing
177
178
  ${chalk.yellow('Utilities:')} status, log, completion, gui-share, uninstall
178
179
 
179
- ${chalk.gray('Aliases: pe connect → pe network connect, pe disconnect → pe network disconnect')}
180
+ ${chalk.gray('Aliases: pal connect → pal network connect, pal disconnect → pal network disconnect')}
180
181
  `);
181
182
 
182
183
  // Load extensions and emit lifecycle hooks
@@ -193,6 +194,59 @@ if (!isVersionOrHelp) {
193
194
  const cleaned = cleanStaleTransfers(24);
194
195
  if (cleaned > 0) console.log(`[transfers] Cleaned ${cleaned} stale transfer(s)`);
195
196
 
197
+ // Register extension-contributed CLI commands
198
+ const { getContributedCommands, invokeExtensionCommand } = await import('../lib/core/extensions.js');
199
+ const coreCommandNames = new Set(commands.map(c => c[0]));
200
+ const extCommands = getContributedCommands();
201
+ for (const { extension, commands: cmds } of extCommands) {
202
+ // Prevent namespace collision with core commands
203
+ const shortName = extension.replace(/^@palexplorer\//, '');
204
+ if (coreCommandNames.has(shortName) || coreCommandNames.has(extension)) {
205
+ console.warn(`[extensions] Skipping commands from ${extension}: name conflicts with core command`);
206
+ continue;
207
+ }
208
+ // Each extension gets a parent command: pal <ext-name> <subcommand>
209
+ const extCmd = program.command(extension).description(`Commands from ${extension} extension`);
210
+ for (const cmd of cmds) {
211
+ const parts = cmd.name.split(' ');
212
+ let target = extCmd;
213
+ // Support nested subcommands like "scan report"
214
+ if (parts.length > 1) {
215
+ for (let i = 0; i < parts.length - 1; i++) {
216
+ const existing = target.commands.find(c => c.name() === parts[i]);
217
+ target = existing || target.command(parts[i]);
218
+ }
219
+ }
220
+ const leafName = parts[parts.length - 1];
221
+ let cmdDef = leafName;
222
+ for (const arg of cmd.args) {
223
+ cmdDef += arg.required ? ` <${arg.name}>` : ` [${arg.name}]`;
224
+ }
225
+ const sub = target.command(cmdDef).description(cmd.description);
226
+ for (const opt of cmd.options) {
227
+ sub.option(opt.flags, opt.description);
228
+ }
229
+ sub.action(async (...actionArgs) => {
230
+ const opts = actionArgs[actionArgs.length - 1]?.opts?.() || actionArgs[actionArgs.length - 1] || {};
231
+ const positional = {};
232
+ cmd.args.forEach((arg, i) => { positional[arg.name] = actionArgs[i]; });
233
+ try {
234
+ const result = await invokeExtensionCommand(extension, cmd.name, positional, opts);
235
+ if (program.opts().json) {
236
+ console.log(JSON.stringify(result, null, 2));
237
+ } else if (result?.message) {
238
+ console.log(result.message);
239
+ } else if (result && typeof result === 'object') {
240
+ console.log(JSON.stringify(result, null, 2));
241
+ }
242
+ } catch (err) {
243
+ console.error(chalk.red(`[${extension}] ${err.message}`));
244
+ process.exitCode = 1;
245
+ }
246
+ });
247
+ }
248
+ }
249
+
196
250
  const identity = (await import('../lib/utils/config.js')).default.get('identity');
197
251
  await hooks.emit('on:app:ready', {
198
252
  version: program.version(),
@@ -227,5 +281,12 @@ if (!process.argv.slice(2).length) {
227
281
  // keytar's native D-Bus handles block the event loop on headless Linux
228
282
  const longRunning = new Set(['serve', 'nearby', 'stream']);
229
283
  if (!longRunning.has(currentCmd)) {
284
+ // Flush analytics and run shutdown hooks before exiting
285
+ if (extensionsLoaded) {
286
+ try {
287
+ const { hooks } = await import('../lib/core/extensions.js');
288
+ await hooks.emit('on:app:shutdown', {});
289
+ } catch {}
290
+ }
230
291
  process.exit(process.exitCode || 0);
231
292
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "analytics",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Anonymous usage analytics via PostHog — opt-in, no PII, privacy-safe",
5
5
  "author": "Palexplorer Team",
6
6
  "license": "MIT",
@@ -22,6 +22,25 @@
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" }
24
24
  },
25
+ "help": {
26
+ "summary": "Tracks anonymous usage events (shares, downloads, sessions) via PostHog. No personal data is collected — only platform, version, and event counts.",
27
+ "usage": "Enabled by default. Disable with: pal ext config analytics enabled false",
28
+ "examples": [
29
+ "pal ext config analytics enabled false",
30
+ "pal ext config analytics posthogHost https://eu.posthog.com",
31
+ "pal ext config analytics sessionTracking false"
32
+ ],
33
+ "configReference": {
34
+ "enabled": "Enable or disable analytics collection (true/false)",
35
+ "posthogKey": "PostHog project API key. Leave default unless self-hosting PostHog",
36
+ "posthogHost": "PostHog ingestion endpoint URL",
37
+ "sessionTracking": "Track how long the app is open per session (true/false)"
38
+ },
39
+ "links": {
40
+ "PostHog": "https://posthog.com",
41
+ "Privacy Policy": "https://palexplorer.com/privacy"
42
+ }
43
+ },
25
44
  "tier": "free",
26
45
  "minAppVersion": "0.4.0"
27
46
  }
@@ -23,7 +23,8 @@ function getDeviceId() {
23
23
  }
24
24
 
25
25
  function isEnabled() {
26
- return ctx.config.get('enabled') === true;
26
+ const v = ctx.config.get('enabled');
27
+ return v === true || v === undefined;
27
28
  }
28
29
 
29
30
  function baseProperties() {
@@ -37,20 +38,24 @@ function baseProperties() {
37
38
  async function initPostHog() {
38
39
  if (posthog) return;
39
40
  try {
40
- // Resolve posthog-node from the app's node_modules (deployed extensions can't resolve from their own path)
41
41
  let PostHog;
42
- try {
43
- const appRoot = ctx.app.appRoot || process.cwd();
44
- const require = createRequire(appRoot + '/package.json');
45
- PostHog = require('posthog-node').PostHog;
46
- } catch {
42
+ const appRoot = ctx.app.appRoot || process.cwd();
43
+ // Try multiple resolution strategies for posthog-node
44
+ const strategies = [
45
+ () => { const r = createRequire(appRoot + '/package.json'); return r('posthog-node').PostHog; },
46
+ () => { const r = createRequire(process.cwd() + '/package.json'); return r('posthog-node').PostHog; },
47
+ ];
48
+ for (const strategy of strategies) {
49
+ try { PostHog = strategy(); break; } catch {}
50
+ }
51
+ if (!PostHog) {
47
52
  PostHog = (await import('posthog-node')).PostHog;
48
53
  }
49
54
  const key = ctx.config.get('posthogKey') || DEFAULT_POSTHOG_KEY;
50
55
  const host = ctx.config.get('posthogHost') || 'https://us.i.posthog.com';
51
56
  posthog = new PostHog(key, { host, flushAt: 20, flushInterval: FLUSH_INTERVAL });
52
57
  } catch (err) {
53
- ctx.logger.warn('PostHog not available:', err.message);
58
+ ctx.logger.warn('PostHog init failed:', err.message);
54
59
  }
55
60
  }
56
61
 
@@ -98,7 +103,12 @@ async function startTracking() {
98
103
  sessionStart = Date.now();
99
104
  initialized = true;
100
105
  track('app_open');
101
- ctx.logger.info('Analytics enabled — PostHog tracking started');
106
+ if (posthog) {
107
+ await posthog.flush().catch(() => {});
108
+ ctx.logger.info('Analytics enabled — PostHog tracking started');
109
+ } else {
110
+ ctx.logger.warn('Analytics enabled but PostHog failed to initialize — events will not be sent');
111
+ }
102
112
  }
103
113
 
104
114
  export async function activate(context) {
@@ -13,5 +13,19 @@
13
13
  ]
14
14
  },
15
15
  "config": { "enabled": { "type": "boolean", "default": false } },
16
+ "help": {
17
+ "summary": "Records all share, transfer, and access events in a searchable audit trail. View who accessed what and when.",
18
+ "usage": "Enable: pal ext config audit enabled true. View logs: pal audit",
19
+ "examples": [
20
+ "pal ext config audit enabled true",
21
+ "pal audit",
22
+ "pal audit --filter share --after 2026-01-01",
23
+ "pal compliance export --format csv"
24
+ ],
25
+ "configReference": {
26
+ "enabled": "Enable or disable audit trail logging (true/false)"
27
+ },
28
+ "links": {}
29
+ },
16
30
  "tier": "free"
17
31
  }
@@ -12,6 +12,21 @@
12
12
  "verifiedEmail": { "type": "string", "default": "", "description": "Currently verified email" },
13
13
  "verifiedToken": { "type": "string", "default": "", "description": "Verified JWT token" }
14
14
  },
15
+ "help": {
16
+ "summary": "Links your email address to your Palexplorer identity for verification badges and account recovery. Uses the discovery server to send verification codes.",
17
+ "usage": "pal auth link-email <email> — starts the verification flow",
18
+ "examples": [
19
+ "pal auth link-email user@example.com",
20
+ "pal auth verify-email <code>",
21
+ "pal auth status"
22
+ ],
23
+ "configReference": {
24
+ "server": "Discovery server URL used for email verification (default: discovery-1.palexplorer.com)",
25
+ "verifiedEmail": "Your currently verified email address (set automatically after verification)",
26
+ "verifiedToken": "JWT verification token (set automatically, do not edit)"
27
+ },
28
+ "links": {}
29
+ },
15
30
  "tier": "free",
16
31
  "minAppVersion": "0.5.0"
17
32
  }
@@ -11,6 +11,21 @@
11
11
  "providers": { "type": "array", "default": ["google", "github", "microsoft"], "description": "Enabled OAuth providers" },
12
12
  "autoLink": { "type": "boolean", "default": true, "description": "Automatically link OAuth identity to Palexplorer identity" }
13
13
  },
14
+ "help": {
15
+ "summary": "Sign in with Google, GitHub, or Microsoft using OAuth 2.0 PKCE flow. Links your social identity to your Palexplorer handle.",
16
+ "usage": "pal auth oauth <provider> — starts OAuth login flow in your browser",
17
+ "examples": [
18
+ "pal auth oauth google",
19
+ "pal auth oauth github",
20
+ "pal ext config auth-oauth providers '[\"google\",\"github\"]'",
21
+ "pal ext config auth-oauth autoLink false"
22
+ ],
23
+ "configReference": {
24
+ "providers": "List of enabled OAuth providers: google, github, microsoft",
25
+ "autoLink": "Automatically link the OAuth identity to your Palexplorer handle (true/false)"
26
+ },
27
+ "links": {}
28
+ },
14
29
  "tier": "free",
15
30
  "minAppVersion": "0.5.0"
16
31
  }
@@ -13,5 +13,19 @@
13
13
  ]
14
14
  },
15
15
  "config": { "enabled": { "type": "boolean", "default": false } },
16
+ "help": {
17
+ "summary": "End-to-end encrypted 1:1 messaging. Messages transfer directly via WebSocket when both peers are online, or queue through the discovery server inbox when offline.",
18
+ "usage": "pal chat send <handle> <message>",
19
+ "examples": [
20
+ "pal chat send alice 'Hey, check out my new share!'",
21
+ "pal chat history alice",
22
+ "pal chat fetch",
23
+ "pal chat flush"
24
+ ],
25
+ "configReference": {
26
+ "enabled": "Enable or disable the chat feature (true/false)"
27
+ },
28
+ "links": {}
29
+ },
16
30
  "tier": "free"
17
31
  }
@@ -13,6 +13,23 @@
13
13
  "refreshInterval": { "type": "number", "default": 1800000, "description": "Server list refresh interval in ms (default: 30 min)" },
14
14
  "enableGossip": { "type": "boolean", "default": true, "description": "Exchange server lists with connected peers" }
15
15
  },
16
+ "help": {
17
+ "summary": "Connects to discovery servers for @handle registration, online presence, and peer finding. Supports DHT-based gossip to discover additional servers automatically.",
18
+ "usage": "pal ext config discovery enabled true",
19
+ "examples": [
20
+ "pal ext config discovery enabled true",
21
+ "pal server list",
22
+ "pal server add https://my-server.example.com",
23
+ "pal ext config discovery enableGossip false"
24
+ ],
25
+ "configReference": {
26
+ "enabled": "Enable or disable discovery server integration (true/false)",
27
+ "bootstrapServers": "List of discovery server URLs to connect to on startup",
28
+ "refreshInterval": "How often to refresh the server list, in milliseconds (default: 30 min)",
29
+ "enableGossip": "Exchange server lists with connected peers for automatic discovery (true/false)"
30
+ },
31
+ "links": {}
32
+ },
16
33
  "tier": "free",
17
34
  "minAppVersion": "0.4.0"
18
35
  }
@@ -114,7 +114,7 @@ export async function onGossipServers(servers) {
114
114
  }
115
115
  }
116
116
 
117
- // Export for pe server list to show source
117
+ // Export for pal server list to show source
118
118
  export function getPublicKey() {
119
119
  return SERVER_LIST_PUBLIC_KEY;
120
120
  }
@@ -18,6 +18,29 @@
18
18
  "toAddress": { "type": "string", "default": "", "description": "Recipient email address" },
19
19
  "events": { "type": "array", "default": ["share:invite", "transfer:complete", "security:alert"], "description": "Enabled event types" }
20
20
  },
21
+ "help": {
22
+ "summary": "Sends email notifications when shares are created, transfers complete, peers connect/disconnect, or security events occur. Supports SMTP, SendGrid, Mailgun, and Resend.",
23
+ "usage": "Configure your email provider, then enable events you want notifications for.",
24
+ "examples": [
25
+ "pal ext config email-notifications provider smtp",
26
+ "pal ext config email-notifications smtpHost smtp.gmail.com",
27
+ "pal ext config email-notifications smtpUser you@gmail.com",
28
+ "pal ext config email-notifications toAddress alerts@example.com",
29
+ "pal ext config email-notifications events '[\"share:invite\",\"security:alert\"]'"
30
+ ],
31
+ "configReference": {
32
+ "provider": "Email service: smtp, sendgrid, mailgun, or resend",
33
+ "smtpHost": "SMTP server hostname (e.g. smtp.gmail.com)",
34
+ "smtpPort": "SMTP server port (587 for TLS, 465 for SSL)",
35
+ "smtpUser": "SMTP authentication username",
36
+ "smtpPass": "SMTP authentication password",
37
+ "apiKey": "API key when using SendGrid, Mailgun, or Resend",
38
+ "fromAddress": "Sender email address shown in notifications",
39
+ "toAddress": "Email address to receive notifications",
40
+ "events": "Event types to notify on: share:invite, transfer:complete, peer:connect, peer:disconnect, security:alert"
41
+ },
42
+ "links": {}
43
+ },
21
44
  "tier": "free",
22
45
  "minAppVersion": "0.5.0"
23
46
  }
@@ -13,5 +13,20 @@
13
13
  ]
14
14
  },
15
15
  "config": { "enabled": { "type": "boolean", "default": false } },
16
+ "help": {
17
+ "summary": "Organize pals into groups. Share files with entire groups at once — all current and future members get access. Free: 2 groups, Pro: 20, Enterprise: unlimited.",
18
+ "usage": "pal group create <name>",
19
+ "examples": [
20
+ "pal group create 'Team Alpha'",
21
+ "pal group add 'Team Alpha' alice bob charlie",
22
+ "pal group list",
23
+ "pal group broadcast 'Team Alpha' 'Meeting in 10 min'",
24
+ "pal share ./docs --recipients group:Team Alpha"
25
+ ],
26
+ "configReference": {
27
+ "enabled": "Enable or disable the groups feature (true/false)"
28
+ },
29
+ "links": {}
30
+ },
16
31
  "tier": "free"
17
32
  }