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.
- package/LICENSE.md +18 -0
- package/README.md +314 -0
- package/bin/pal.js +230 -0
- package/extensions/@palexplorer/analytics/README.md +45 -0
- package/extensions/@palexplorer/analytics/docs/MONETIZATION.md +14 -0
- package/extensions/@palexplorer/analytics/docs/PLAN.md +23 -0
- package/extensions/@palexplorer/analytics/docs/PRIVACY.md +38 -0
- package/extensions/@palexplorer/analytics/extension.json +27 -0
- package/extensions/@palexplorer/analytics/index.js +186 -0
- package/extensions/@palexplorer/analytics/test/analytics.test.js +82 -0
- package/extensions/@palexplorer/audit/extension.json +17 -0
- package/extensions/@palexplorer/audit/index.js +2 -0
- package/extensions/@palexplorer/auth-email/extension.json +17 -0
- package/extensions/@palexplorer/auth-email/index.js +102 -0
- package/extensions/@palexplorer/auth-oauth/extension.json +16 -0
- package/extensions/@palexplorer/auth-oauth/index.js +199 -0
- package/extensions/@palexplorer/chat/extension.json +17 -0
- package/extensions/@palexplorer/chat/index.js +2 -0
- package/extensions/@palexplorer/discovery/extension.json +16 -0
- package/extensions/@palexplorer/discovery/index.js +111 -0
- package/extensions/@palexplorer/email-notifications/extension.json +23 -0
- package/extensions/@palexplorer/email-notifications/index.js +242 -0
- package/extensions/@palexplorer/explorer-integration/extension.json +13 -0
- package/extensions/@palexplorer/explorer-integration/index.js +122 -0
- package/extensions/@palexplorer/groups/extension.json +17 -0
- package/extensions/@palexplorer/groups/index.js +2 -0
- package/extensions/@palexplorer/networks/extension.json +17 -0
- package/extensions/@palexplorer/networks/index.js +2 -0
- package/extensions/@palexplorer/share-links/extension.json +17 -0
- package/extensions/@palexplorer/share-links/index.js +2 -0
- package/extensions/@palexplorer/sync/extension.json +17 -0
- package/extensions/@palexplorer/sync/index.js +2 -0
- package/extensions/@palexplorer/user-mgmt/extension.json +17 -0
- package/extensions/@palexplorer/user-mgmt/index.js +2 -0
- package/extensions/@palexplorer/vfs/extension.json +17 -0
- package/extensions/@palexplorer/vfs/index.js +167 -0
- package/lib/capabilities.js +263 -0
- package/lib/commands/analytics.js +175 -0
- package/lib/commands/api-keys.js +131 -0
- package/lib/commands/audit.js +235 -0
- package/lib/commands/auth.js +137 -0
- package/lib/commands/backup.js +76 -0
- package/lib/commands/billing.js +148 -0
- package/lib/commands/chat.js +217 -0
- package/lib/commands/cloud-backup.js +231 -0
- package/lib/commands/comment.js +99 -0
- package/lib/commands/completion.js +203 -0
- package/lib/commands/compliance.js +218 -0
- package/lib/commands/config.js +136 -0
- package/lib/commands/connect.js +44 -0
- package/lib/commands/dept.js +294 -0
- package/lib/commands/device.js +146 -0
- package/lib/commands/download.js +226 -0
- package/lib/commands/explorer.js +178 -0
- package/lib/commands/extension.js +970 -0
- package/lib/commands/favorite.js +90 -0
- package/lib/commands/federation.js +270 -0
- package/lib/commands/file.js +533 -0
- package/lib/commands/group.js +271 -0
- package/lib/commands/gui-share.js +29 -0
- package/lib/commands/init.js +61 -0
- package/lib/commands/invite.js +59 -0
- package/lib/commands/list.js +59 -0
- package/lib/commands/log.js +116 -0
- package/lib/commands/nearby.js +108 -0
- package/lib/commands/network.js +251 -0
- package/lib/commands/notify.js +198 -0
- package/lib/commands/org.js +273 -0
- package/lib/commands/pal.js +180 -0
- package/lib/commands/permissions.js +216 -0
- package/lib/commands/pin.js +97 -0
- package/lib/commands/protocol.js +357 -0
- package/lib/commands/rbac.js +147 -0
- package/lib/commands/recover.js +36 -0
- package/lib/commands/register.js +171 -0
- package/lib/commands/relay.js +131 -0
- package/lib/commands/remote.js +368 -0
- package/lib/commands/revoke.js +50 -0
- package/lib/commands/scanner.js +280 -0
- package/lib/commands/schedule.js +344 -0
- package/lib/commands/scim.js +203 -0
- package/lib/commands/search.js +181 -0
- package/lib/commands/serve.js +438 -0
- package/lib/commands/server.js +350 -0
- package/lib/commands/share-link.js +199 -0
- package/lib/commands/share.js +323 -0
- package/lib/commands/sso.js +200 -0
- package/lib/commands/status.js +136 -0
- package/lib/commands/stream.js +562 -0
- package/lib/commands/su.js +187 -0
- package/lib/commands/sync.js +827 -0
- package/lib/commands/transfers.js +152 -0
- package/lib/commands/uninstall.js +188 -0
- package/lib/commands/update.js +204 -0
- package/lib/commands/user.js +276 -0
- package/lib/commands/vfs.js +84 -0
- package/lib/commands/web.js +52 -0
- package/lib/commands/webhook.js +180 -0
- package/lib/commands/whoami.js +59 -0
- package/lib/commands/workspace.js +121 -0
- package/lib/core/accessLog.js +54 -0
- package/lib/core/analytics.js +99 -0
- package/lib/core/backup.js +84 -0
- package/lib/core/billing.js +336 -0
- package/lib/core/bitfieldStore.js +53 -0
- package/lib/core/connectionManager.js +182 -0
- package/lib/core/dhtDiscovery.js +148 -0
- package/lib/core/discoveryClient.js +408 -0
- package/lib/core/extensionAnalyzer.js +357 -0
- package/lib/core/extensionSandbox.js +250 -0
- package/lib/core/extensionWorkerHost.js +166 -0
- package/lib/core/extensions.js +1082 -0
- package/lib/core/fileDiff.js +69 -0
- package/lib/core/groups.js +119 -0
- package/lib/core/identity.js +340 -0
- package/lib/core/mdnsService.js +126 -0
- package/lib/core/networks.js +81 -0
- package/lib/core/permissions.js +109 -0
- package/lib/core/pro.js +27 -0
- package/lib/core/resolver.js +74 -0
- package/lib/core/serverList.js +224 -0
- package/lib/core/sharePolicy.js +69 -0
- package/lib/core/shares.js +325 -0
- package/lib/core/signalingServer.js +441 -0
- package/lib/core/streamTransport.js +106 -0
- package/lib/core/su.js +55 -0
- package/lib/core/syncEngine.js +264 -0
- package/lib/core/syncState.js +159 -0
- package/lib/core/transfers.js +259 -0
- package/lib/core/users.js +225 -0
- package/lib/core/vfs.js +216 -0
- package/lib/core/webServer.js +702 -0
- package/lib/core/webrtcStream.js +396 -0
- package/lib/crypto/chatEncryption.js +57 -0
- package/lib/crypto/shareEncryption.js +195 -0
- package/lib/crypto/sharePassword.js +35 -0
- package/lib/crypto/streamEncryption.js +189 -0
- package/lib/package.json +1 -0
- package/lib/protocol/envelope.js +271 -0
- package/lib/protocol/handler.js +191 -0
- package/lib/protocol/index.js +27 -0
- package/lib/protocol/messages.js +247 -0
- package/lib/protocol/negotiation.js +127 -0
- package/lib/protocol/policy.js +142 -0
- package/lib/protocol/router.js +86 -0
- package/lib/protocol/sync.js +122 -0
- package/lib/utils/cli.js +15 -0
- package/lib/utils/config.js +123 -0
- package/lib/utils/configIntegrity.js +87 -0
- package/lib/utils/downloadDir.js +9 -0
- package/lib/utils/explorer.js +83 -0
- package/lib/utils/format.js +12 -0
- package/lib/utils/help.js +357 -0
- package/lib/utils/logger.js +103 -0
- package/lib/utils/torrent.js +203 -0
- package/package.json +71 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
const BASH_COMPLETION = `
|
|
4
|
+
# pe bash completion
|
|
5
|
+
_pe_completions() {
|
|
6
|
+
local cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
7
|
+
local prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
8
|
+
|
|
9
|
+
local commands="init whoami register recover share download list revoke serve pal invite nearby group sync transfers config device server explorer status log completion web gui-share vfs file remote user schedule chat backup api-keys share-link comment update auth network search connect protocol workspace favorite pin stream uninstall extension ext billing permissions org su federation analytics webhook relay compliance sso rbac scim audit scanner notify cloud-backup dept"
|
|
10
|
+
|
|
11
|
+
case "\$prev" in
|
|
12
|
+
pe)
|
|
13
|
+
COMPREPLY=( $(compgen -W "\$commands" -- "\$cur") )
|
|
14
|
+
;;
|
|
15
|
+
pal)
|
|
16
|
+
COMPREPLY=( $(compgen -W "add list remove" -- "\$cur") )
|
|
17
|
+
;;
|
|
18
|
+
group)
|
|
19
|
+
COMPREPLY=( $(compgen -W "create add remove delete list broadcast" -- "\$cur") )
|
|
20
|
+
;;
|
|
21
|
+
sync)
|
|
22
|
+
COMPREPLY=( $(compgen -W "push pull status watch list remove diff" -- "\$cur") )
|
|
23
|
+
;;
|
|
24
|
+
file)
|
|
25
|
+
COMPREPLY=( $(compgen -W "ls tree info copy move rename mkdir delete search open reveal audit" -- "\$cur") )
|
|
26
|
+
;;
|
|
27
|
+
remote)
|
|
28
|
+
COMPREPLY=( $(compgen -W "browse files download" -- "\$cur") )
|
|
29
|
+
;;
|
|
30
|
+
user)
|
|
31
|
+
COMPREPLY=( $(compgen -W "list add remove promote" -- "\$cur") )
|
|
32
|
+
;;
|
|
33
|
+
chat)
|
|
34
|
+
COMPREPLY=( $(compgen -W "send history fetch" -- "\$cur") )
|
|
35
|
+
;;
|
|
36
|
+
comment)
|
|
37
|
+
COMPREPLY=( $(compgen -W "list add delete" -- "\$cur") )
|
|
38
|
+
;;
|
|
39
|
+
schedule)
|
|
40
|
+
COMPREPLY=( $(compgen -W "add cancel clear" -- "\$cur") )
|
|
41
|
+
;;
|
|
42
|
+
backup)
|
|
43
|
+
COMPREPLY=( $(compgen -W "create restore" -- "\$cur") )
|
|
44
|
+
;;
|
|
45
|
+
api-keys)
|
|
46
|
+
COMPREPLY=( $(compgen -W "create revoke" -- "\$cur") )
|
|
47
|
+
;;
|
|
48
|
+
share-link)
|
|
49
|
+
COMPREPLY=( $(compgen -W "create list" -- "\$cur") )
|
|
50
|
+
;;
|
|
51
|
+
vfs)
|
|
52
|
+
COMPREPLY=( $(compgen -W "mount unmount status" -- "\$cur") )
|
|
53
|
+
;;
|
|
54
|
+
config)
|
|
55
|
+
COMPREPLY=( $(compgen -W "list get set reset" -- "\$cur") )
|
|
56
|
+
;;
|
|
57
|
+
device)
|
|
58
|
+
COMPREPLY=( $(compgen -W "rename register list" -- "\$cur") )
|
|
59
|
+
;;
|
|
60
|
+
server)
|
|
61
|
+
COMPREPLY=( $(compgen -W "install config register status add remove list set-primary" -- "\$cur") )
|
|
62
|
+
;;
|
|
63
|
+
transfers)
|
|
64
|
+
COMPREPLY=( $(compgen -W "cancel pause resume stats" -- "\$cur") )
|
|
65
|
+
;;
|
|
66
|
+
explorer)
|
|
67
|
+
COMPREPLY=( $(compgen -W "install uninstall" -- "\$cur") )
|
|
68
|
+
;;
|
|
69
|
+
*)
|
|
70
|
+
COMPREPLY=()
|
|
71
|
+
;;
|
|
72
|
+
esac
|
|
73
|
+
}
|
|
74
|
+
complete -F _pe_completions pe
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
const ZSH_COMPLETION = `
|
|
78
|
+
#compdef pe
|
|
79
|
+
|
|
80
|
+
_pe() {
|
|
81
|
+
local -a commands subcommands
|
|
82
|
+
|
|
83
|
+
commands=(
|
|
84
|
+
'init:Initialize your identity'
|
|
85
|
+
'whoami:Display your current identity'
|
|
86
|
+
'register:Register a handle'
|
|
87
|
+
'verify:Verify OTP code'
|
|
88
|
+
'recover:Recover identity from mnemonic'
|
|
89
|
+
'share:Share a file or folder'
|
|
90
|
+
'list:List active shares'
|
|
91
|
+
'revoke:Revoke a share'
|
|
92
|
+
'serve:Start the sharing daemon'
|
|
93
|
+
'download:Download from magnet link'
|
|
94
|
+
'pal:Manage pals'
|
|
95
|
+
'invite:Generate invite link'
|
|
96
|
+
'nearby:Discover nearby peers'
|
|
97
|
+
'group:Manage groups'
|
|
98
|
+
'sync:Sync a directory with a pal'
|
|
99
|
+
'transfers:View and manage transfers'
|
|
100
|
+
'config:View and manage config'
|
|
101
|
+
'device:Manage devices'
|
|
102
|
+
'server:Manage headless server'
|
|
103
|
+
'explorer:Windows Explorer integration'
|
|
104
|
+
'vfs:Manage virtual filesystem drive'
|
|
105
|
+
'web:Open web dashboard'
|
|
106
|
+
'status:System health dashboard'
|
|
107
|
+
'log:View application logs'
|
|
108
|
+
'completion:Generate shell completion script'
|
|
109
|
+
'file:File operations with permissions'
|
|
110
|
+
'remote:Browse and download from pals'
|
|
111
|
+
'user:Manage user profiles'
|
|
112
|
+
'login:Switch active user profile'
|
|
113
|
+
'schedule:Manage scheduled tasks'
|
|
114
|
+
'chat:Encrypted chat messages'
|
|
115
|
+
'backup:Create and restore backups (Pro)'
|
|
116
|
+
'api-keys:Manage API keys (Pro)'
|
|
117
|
+
'share-link:Create web share links'
|
|
118
|
+
'comment:Manage comments on shares'
|
|
119
|
+
'update:Check for updates'
|
|
120
|
+
'auth:Authentication commands'
|
|
121
|
+
'network:Network management'
|
|
122
|
+
'search:Search files, pals, or groups'
|
|
123
|
+
'connect:Connect to a pal'
|
|
124
|
+
'protocol:PAL/1.0 protocol tools'
|
|
125
|
+
'workspace:Manage workspaces'
|
|
126
|
+
'favorite:Manage favorites'
|
|
127
|
+
'pin:Pin shares'
|
|
128
|
+
'stream:Media streaming'
|
|
129
|
+
'uninstall:Uninstall Palexplorer'
|
|
130
|
+
'extension:Manage extensions'
|
|
131
|
+
'ext:Manage extensions (alias)'
|
|
132
|
+
'billing:Billing and subscription'
|
|
133
|
+
'permissions:Manage permissions'
|
|
134
|
+
'org:Organization management'
|
|
135
|
+
'su:Switch user context'
|
|
136
|
+
'federation:Federation settings'
|
|
137
|
+
'analytics:Usage analytics'
|
|
138
|
+
'webhook:Manage webhooks'
|
|
139
|
+
'relay:TURN relay management'
|
|
140
|
+
'compliance:Compliance tools'
|
|
141
|
+
'sso:Single sign-on settings'
|
|
142
|
+
'rbac:Role-based access control'
|
|
143
|
+
'scim:SCIM provisioning'
|
|
144
|
+
'audit:Audit log'
|
|
145
|
+
'scanner:Security scanner'
|
|
146
|
+
'notify:Notification settings'
|
|
147
|
+
'cloud-backup:Cloud backup management'
|
|
148
|
+
'dept:Department management'
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
if (( CURRENT == 2 )); then
|
|
152
|
+
_describe 'command' commands
|
|
153
|
+
elif (( CURRENT == 3 )); then
|
|
154
|
+
case "\$words[2]" in
|
|
155
|
+
pal) subcommands=('add:Add a pal' 'list:List pals' 'remove:Remove a pal'); _describe 'subcommand' subcommands ;;
|
|
156
|
+
group) subcommands=('create:Create group' 'add:Add to group' 'remove:Remove from group' 'delete:Delete group' 'list:List groups' 'broadcast:Broadcast message'); _describe 'subcommand' subcommands ;;
|
|
157
|
+
sync) subcommands=('push:Push local changes' 'pull:Pull remote changes' 'status:Show sync status' 'watch:Watch and auto-push' 'list:List sync pairs' 'remove:Remove sync pair' 'diff:Compare folders'); _describe 'subcommand' subcommands ;;
|
|
158
|
+
vfs) subcommands=('mount:Mount virtual drive' 'unmount:Unmount drive' 'status:Show status'); _describe 'subcommand' subcommands ;;
|
|
159
|
+
config) subcommands=('list:Show settings' 'get:Get value' 'set:Set value' 'reset:Reset all'); _describe 'subcommand' subcommands ;;
|
|
160
|
+
device) subcommands=('rename:Rename device' 'register:Register device' 'list:List devices'); _describe 'subcommand' subcommands ;;
|
|
161
|
+
server) subcommands=('install:Install service' 'config:Configure' 'register:Register handle' 'status:Check health' 'add:Add server' 'remove:Remove server' 'list:List servers' 'set-primary:Set primary server'); _describe 'subcommand' subcommands ;;
|
|
162
|
+
transfers) subcommands=('cancel:Cancel transfer' 'pause:Pause transfer' 'resume:Resume transfer' 'stats:Show statistics'); _describe 'subcommand' subcommands ;;
|
|
163
|
+
explorer) subcommands=('install:Install integration' 'uninstall:Remove integration'); _describe 'subcommand' subcommands ;;
|
|
164
|
+
file) subcommands=('ls:List directory' 'tree:Show tree' 'info:File properties' 'copy:Copy file' 'move:Move file' 'rename:Rename file' 'mkdir:Create directory' 'delete:Delete file' 'search:Search files' 'open:Open file' 'reveal:Show in explorer' 'audit:Audit log'); _describe 'subcommand' subcommands ;;
|
|
165
|
+
remote) subcommands=('browse:List pal shares' 'files:List files in share' 'download:Download share'); _describe 'subcommand' subcommands ;;
|
|
166
|
+
user) subcommands=('list:List profiles' 'add:Add profile' 'remove:Remove profile' 'promote:Promote/demote'); _describe 'subcommand' subcommands ;;
|
|
167
|
+
chat) subcommands=('send:Send message' 'history:Show history' 'fetch:Fetch new messages'); _describe 'subcommand' subcommands ;;
|
|
168
|
+
comment) subcommands=('list:List comments' 'add:Add comment' 'delete:Delete comment'); _describe 'subcommand' subcommands ;;
|
|
169
|
+
schedule) subcommands=('add:Schedule task' 'cancel:Cancel task' 'clear:Clear old tasks'); _describe 'subcommand' subcommands ;;
|
|
170
|
+
backup) subcommands=('create:Create backup' 'restore:Restore backup'); _describe 'subcommand' subcommands ;;
|
|
171
|
+
api-keys) subcommands=('create:Create key' 'revoke:Revoke key'); _describe 'subcommand' subcommands ;;
|
|
172
|
+
share-link) subcommands=('create:Create link' 'list:List links'); _describe 'subcommand' subcommands ;;
|
|
173
|
+
update) subcommands=('check:Check for updates'); _describe 'subcommand' subcommands ;;
|
|
174
|
+
esac
|
|
175
|
+
fi
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
_pe
|
|
179
|
+
`;
|
|
180
|
+
|
|
181
|
+
export default function completionCommand(program) {
|
|
182
|
+
program
|
|
183
|
+
.command('completion')
|
|
184
|
+
.description('generate shell completion script')
|
|
185
|
+
.option('--shell <shell>', 'Shell type: bash or zsh', 'bash')
|
|
186
|
+
.addHelpText('after', `
|
|
187
|
+
Examples:
|
|
188
|
+
$ pe completion Output bash completion script
|
|
189
|
+
$ pe completion --shell zsh Output zsh completion script
|
|
190
|
+
$ source <(pe completion) Activate completion in current shell
|
|
191
|
+
`)
|
|
192
|
+
.action((opts) => {
|
|
193
|
+
if (opts.shell === 'zsh') {
|
|
194
|
+
console.log(ZSH_COMPLETION.trim());
|
|
195
|
+
console.log('');
|
|
196
|
+
console.log(chalk.gray('# Add to ~/.zshrc or save to a file in your fpath'));
|
|
197
|
+
} else {
|
|
198
|
+
console.log(BASH_COMPLETION.trim());
|
|
199
|
+
console.log('');
|
|
200
|
+
console.log(chalk.gray('# Add to ~/.bashrc or run: eval "$(pe completion)"'));
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
const SUPPORTED_MODULES = [
|
|
4
|
+
'gdpr', 'hipaa', 'soc2', 'pci', 'financial', 'fedramp',
|
|
5
|
+
'cmmc', 'iso27001', 'dora', 'itar', 'ccpa', 'ferpa',
|
|
6
|
+
];
|
|
7
|
+
|
|
8
|
+
export default function complianceCommand(program) {
|
|
9
|
+
const cmd = program
|
|
10
|
+
.command('compliance')
|
|
11
|
+
.description('manage compliance modules (GDPR, HIPAA, etc.)')
|
|
12
|
+
.addHelpText('after', `
|
|
13
|
+
Examples:
|
|
14
|
+
$ pe compliance status Show enabled compliance modules
|
|
15
|
+
$ pe compliance enable gdpr Enable GDPR compliance
|
|
16
|
+
$ pe compliance disable hipaa Disable HIPAA compliance
|
|
17
|
+
$ pe compliance consent grant <pubkey> Record GDPR consent
|
|
18
|
+
$ pe compliance consent revoke <pubkey> Revoke GDPR consent
|
|
19
|
+
$ pe compliance erasure <pubkey> GDPR right to erasure
|
|
20
|
+
$ pe compliance export <pubkey> GDPR data subject access request
|
|
21
|
+
|
|
22
|
+
Supported modules: ${SUPPORTED_MODULES.join(', ')}
|
|
23
|
+
`)
|
|
24
|
+
.action(() => {
|
|
25
|
+
cmd.outputHelp();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
cmd
|
|
29
|
+
.command('status')
|
|
30
|
+
.description('show which compliance modules are enabled')
|
|
31
|
+
.action(async () => {
|
|
32
|
+
try {
|
|
33
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
34
|
+
const cfg = extConfig.get('ext.compliance') || {};
|
|
35
|
+
const enabled = cfg.enabledModules || [];
|
|
36
|
+
|
|
37
|
+
console.log('');
|
|
38
|
+
console.log(chalk.cyan.bold('Compliance Modules'));
|
|
39
|
+
console.log('');
|
|
40
|
+
for (const mod of SUPPORTED_MODULES) {
|
|
41
|
+
const active = enabled.includes(mod);
|
|
42
|
+
const icon = active ? chalk.green('●') : chalk.gray('○');
|
|
43
|
+
console.log(` ${icon} ${active ? chalk.white(mod.toUpperCase()) : chalk.gray(mod.toUpperCase())}`);
|
|
44
|
+
}
|
|
45
|
+
console.log('');
|
|
46
|
+
} catch (err) {
|
|
47
|
+
console.log(chalk.red(`Failed to get status: ${err.message}`));
|
|
48
|
+
process.exitCode = 1;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
cmd
|
|
53
|
+
.command('enable <module>')
|
|
54
|
+
.description('enable a compliance module')
|
|
55
|
+
.action(async (mod) => {
|
|
56
|
+
try {
|
|
57
|
+
const module = mod.toLowerCase();
|
|
58
|
+
if (!SUPPORTED_MODULES.includes(module)) {
|
|
59
|
+
console.log(chalk.red(`Unknown module "${mod}". Supported: ${SUPPORTED_MODULES.join(', ')}`));
|
|
60
|
+
process.exitCode = 1;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
65
|
+
const cfg = extConfig.get('ext.compliance') || {};
|
|
66
|
+
const enabled = cfg.enabledModules || [];
|
|
67
|
+
|
|
68
|
+
if (enabled.includes(module)) {
|
|
69
|
+
console.log(chalk.yellow(`${module.toUpperCase()} is already enabled.`));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
enabled.push(module);
|
|
74
|
+
extConfig.set('ext.compliance', { ...cfg, enabledModules: enabled });
|
|
75
|
+
console.log(chalk.green(`✔ Enabled ${module.toUpperCase()} compliance module`));
|
|
76
|
+
} catch (err) {
|
|
77
|
+
console.log(chalk.red(`Failed to enable module: ${err.message}`));
|
|
78
|
+
process.exitCode = 1;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
cmd
|
|
83
|
+
.command('disable <module>')
|
|
84
|
+
.description('disable a compliance module')
|
|
85
|
+
.action(async (mod) => {
|
|
86
|
+
try {
|
|
87
|
+
const module = mod.toLowerCase();
|
|
88
|
+
if (!SUPPORTED_MODULES.includes(module)) {
|
|
89
|
+
console.log(chalk.red(`Unknown module "${mod}". Supported: ${SUPPORTED_MODULES.join(', ')}`));
|
|
90
|
+
process.exitCode = 1;
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
95
|
+
const cfg = extConfig.get('ext.compliance') || {};
|
|
96
|
+
const enabled = cfg.enabledModules || [];
|
|
97
|
+
|
|
98
|
+
if (!enabled.includes(module)) {
|
|
99
|
+
console.log(chalk.yellow(`${module.toUpperCase()} is not enabled.`));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
extConfig.set('ext.compliance', {
|
|
104
|
+
...cfg,
|
|
105
|
+
enabledModules: enabled.filter(m => m !== module),
|
|
106
|
+
});
|
|
107
|
+
console.log(chalk.green(`✔ Disabled ${module.toUpperCase()} compliance module`));
|
|
108
|
+
} catch (err) {
|
|
109
|
+
console.log(chalk.red(`Failed to disable module: ${err.message}`));
|
|
110
|
+
process.exitCode = 1;
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const consent = cmd
|
|
115
|
+
.command('consent')
|
|
116
|
+
.description('manage GDPR consent records')
|
|
117
|
+
.action(() => {
|
|
118
|
+
consent.outputHelp();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
consent
|
|
122
|
+
.command('grant <publicKey>')
|
|
123
|
+
.description('record GDPR consent for a user')
|
|
124
|
+
.action(async (publicKey) => {
|
|
125
|
+
try {
|
|
126
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
127
|
+
const store = extConfig.get('ext_store.compliance-gdpr') || {};
|
|
128
|
+
|
|
129
|
+
store[`consent:${publicKey}`] = {
|
|
130
|
+
status: 'granted',
|
|
131
|
+
timestamp: new Date().toISOString(),
|
|
132
|
+
};
|
|
133
|
+
extConfig.set('ext_store.compliance-gdpr', store);
|
|
134
|
+
|
|
135
|
+
console.log(chalk.green(`✔ Consent granted for ${chalk.white(publicKey.slice(0, 16))}...`));
|
|
136
|
+
} catch (err) {
|
|
137
|
+
console.log(chalk.red(`Failed to grant consent: ${err.message}`));
|
|
138
|
+
process.exitCode = 1;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
consent
|
|
143
|
+
.command('revoke <publicKey>')
|
|
144
|
+
.description('revoke GDPR consent for a user')
|
|
145
|
+
.action(async (publicKey) => {
|
|
146
|
+
try {
|
|
147
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
148
|
+
const store = extConfig.get('ext_store.compliance-gdpr') || {};
|
|
149
|
+
|
|
150
|
+
store[`consent:${publicKey}`] = {
|
|
151
|
+
status: 'revoked',
|
|
152
|
+
timestamp: new Date().toISOString(),
|
|
153
|
+
};
|
|
154
|
+
extConfig.set('ext_store.compliance-gdpr', store);
|
|
155
|
+
|
|
156
|
+
console.log(chalk.green(`✔ Consent revoked for ${chalk.white(publicKey.slice(0, 16))}...`));
|
|
157
|
+
} catch (err) {
|
|
158
|
+
console.log(chalk.red(`Failed to revoke consent: ${err.message}`));
|
|
159
|
+
process.exitCode = 1;
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
cmd
|
|
164
|
+
.command('erasure <publicKey>')
|
|
165
|
+
.description('GDPR right to erasure — mark user data for deletion')
|
|
166
|
+
.action(async (publicKey) => {
|
|
167
|
+
try {
|
|
168
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
169
|
+
const store = extConfig.get('ext_store.compliance-gdpr') || {};
|
|
170
|
+
|
|
171
|
+
store[`erasure:${publicKey}`] = {
|
|
172
|
+
status: 'pending',
|
|
173
|
+
requestedAt: new Date().toISOString(),
|
|
174
|
+
};
|
|
175
|
+
store[`consent:${publicKey}`] = {
|
|
176
|
+
status: 'revoked',
|
|
177
|
+
timestamp: new Date().toISOString(),
|
|
178
|
+
};
|
|
179
|
+
extConfig.set('ext_store.compliance-gdpr', store);
|
|
180
|
+
|
|
181
|
+
console.log(chalk.green(`✔ Erasure request recorded for ${chalk.white(publicKey.slice(0, 16))}...`));
|
|
182
|
+
console.log(chalk.gray(' User data marked for deletion. Consent auto-revoked.'));
|
|
183
|
+
} catch (err) {
|
|
184
|
+
console.log(chalk.red(`Failed to record erasure: ${err.message}`));
|
|
185
|
+
process.exitCode = 1;
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
cmd
|
|
190
|
+
.command('export <publicKey>')
|
|
191
|
+
.description('GDPR data subject access request — export user data')
|
|
192
|
+
.action(async (publicKey) => {
|
|
193
|
+
try {
|
|
194
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
195
|
+
const store = extConfig.get('ext_store.compliance-gdpr') || {};
|
|
196
|
+
|
|
197
|
+
const consentRecord = store[`consent:${publicKey}`];
|
|
198
|
+
const erasureRecord = store[`erasure:${publicKey}`];
|
|
199
|
+
|
|
200
|
+
const exportData = {
|
|
201
|
+
publicKey,
|
|
202
|
+
exportedAt: new Date().toISOString(),
|
|
203
|
+
consent: consentRecord || null,
|
|
204
|
+
erasure: erasureRecord || null,
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const filename = `gdpr-export-${publicKey.slice(0, 8)}-${Date.now()}.json`;
|
|
208
|
+
const fs = await import('fs');
|
|
209
|
+
fs.writeFileSync(filename, JSON.stringify(exportData, null, 2));
|
|
210
|
+
|
|
211
|
+
console.log(chalk.green(`✔ Data exported for ${chalk.white(publicKey.slice(0, 16))}...`));
|
|
212
|
+
console.log(` File: ${chalk.white(filename)}`);
|
|
213
|
+
} catch (err) {
|
|
214
|
+
console.log(chalk.red(`Failed to export data: ${err.message}`));
|
|
215
|
+
process.exitCode = 1;
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import config from '../utils/config.js';
|
|
3
|
+
|
|
4
|
+
const KNOWN_KEYS = {
|
|
5
|
+
port: 'Local seeder port (default: auto)',
|
|
6
|
+
downloadDir: 'Download folder (default: ~/Downloads/Palexplorer)',
|
|
7
|
+
max_connections: 'Max P2P connections',
|
|
8
|
+
bandwidth_cap: 'Upload bandwidth cap in KB/s (0 = unlimited)',
|
|
9
|
+
bandwidth_up: 'Upload bandwidth limit in KB/s',
|
|
10
|
+
bandwidth_down: 'Download bandwidth limit in KB/s',
|
|
11
|
+
discovery_servers: 'Discovery server URLs (comma-separated list)',
|
|
12
|
+
announce_interval: 'DHT announce interval in seconds',
|
|
13
|
+
logLevel: 'Log level: debug, info, warn, error, silent',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default function configCommand(program) {
|
|
17
|
+
const cmd = program
|
|
18
|
+
.command('config')
|
|
19
|
+
.description('view and manage Palexplorer configuration')
|
|
20
|
+
.addHelpText('after', `
|
|
21
|
+
Examples:
|
|
22
|
+
$ pe config list Show all settings
|
|
23
|
+
$ pe config get theme Get a specific setting
|
|
24
|
+
$ pe config set theme dark Set a value
|
|
25
|
+
$ pe config reset theme Reset to default
|
|
26
|
+
`);
|
|
27
|
+
|
|
28
|
+
cmd
|
|
29
|
+
.command('list')
|
|
30
|
+
.description('show all current settings')
|
|
31
|
+
.action(() => {
|
|
32
|
+
const settings = config.get('settings') || {};
|
|
33
|
+
if (program.opts().json) {
|
|
34
|
+
console.log(JSON.stringify({ settings, knownKeys: KNOWN_KEYS }, null, 2));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
console.log('');
|
|
38
|
+
console.log(chalk.cyan('Current Settings:'));
|
|
39
|
+
const SENSITIVE_KEYS = new Set(['apiKey', 'api_key', 'token', 'secret', 'password', 'privateKey']);
|
|
40
|
+
if (Object.keys(settings).length === 0) {
|
|
41
|
+
console.log(chalk.gray(' (no custom settings — all defaults active)'));
|
|
42
|
+
} else {
|
|
43
|
+
for (const [k, v] of Object.entries(settings)) {
|
|
44
|
+
const desc = KNOWN_KEYS[k] ? chalk.gray(` — ${KNOWN_KEYS[k]}`) : '';
|
|
45
|
+
const display = SENSITIVE_KEYS.has(k) ? '***' : v;
|
|
46
|
+
console.log(` ${chalk.white(k)} = ${chalk.yellow(display)}${desc}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
console.log('');
|
|
50
|
+
console.log(chalk.gray('Known keys:'));
|
|
51
|
+
for (const [k, desc] of Object.entries(KNOWN_KEYS)) {
|
|
52
|
+
console.log(` ${chalk.white(k.padEnd(22))} ${chalk.gray(desc)}`);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
cmd
|
|
57
|
+
.command('get <key>')
|
|
58
|
+
.description('get a single config value')
|
|
59
|
+
.action((key) => {
|
|
60
|
+
const settings = config.get('settings') || {};
|
|
61
|
+
if (!(key in settings)) {
|
|
62
|
+
if (program.opts().json) {
|
|
63
|
+
console.log(JSON.stringify({ key, value: null, isDefault: true }, null, 2));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
console.log(chalk.gray(`'${key}' is not set (using default).`));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (program.opts().json) {
|
|
70
|
+
console.log(JSON.stringify({ key, value: settings[key] }, null, 2));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
console.log(`${key} = ${chalk.yellow(settings[key])}`);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
cmd
|
|
77
|
+
.command('set <key> <value>')
|
|
78
|
+
.description('set a config value')
|
|
79
|
+
.action((key, value) => {
|
|
80
|
+
const DANGEROUS_KEYS = ['__proto__', 'constructor', 'prototype'];
|
|
81
|
+
if (!key || !key.trim()) {
|
|
82
|
+
console.log(chalk.red(`Rejected: config key cannot be empty.`));
|
|
83
|
+
process.exitCode = 1;
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const keyParts = key.split('.');
|
|
87
|
+
if (DANGEROUS_KEYS.some(d => keyParts.includes(d)) || key.startsWith('__')) {
|
|
88
|
+
console.log(chalk.red(`Rejected: '${key}' is not a valid config key.`));
|
|
89
|
+
process.exitCode = 1;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (!(key in KNOWN_KEYS)) {
|
|
93
|
+
console.log(chalk.yellow(`Warning: unknown config key '${key}'. Known keys: ${Object.keys(KNOWN_KEYS).join(', ')}`));
|
|
94
|
+
}
|
|
95
|
+
const settings = config.get('settings') || {};
|
|
96
|
+
settings[key] = value;
|
|
97
|
+
config.set('settings', settings);
|
|
98
|
+
console.log(chalk.green(`✔ ${key} = ${value}`));
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
cmd
|
|
102
|
+
.command('reset [key]')
|
|
103
|
+
.description('reset a single key or all settings to defaults')
|
|
104
|
+
.action((key) => {
|
|
105
|
+
if (key && key.trim()) {
|
|
106
|
+
const settings = config.get('settings') || {};
|
|
107
|
+
if (!(key in settings)) {
|
|
108
|
+
console.log(chalk.gray(`'${key}' is not set.`));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
delete settings[key];
|
|
112
|
+
config.set('settings', settings);
|
|
113
|
+
console.log(chalk.green(`✔ '${key}' reset to default.`));
|
|
114
|
+
} else {
|
|
115
|
+
config.set('settings', {});
|
|
116
|
+
console.log(chalk.green('✔ All settings reset to defaults.'));
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Running `pe config` without subcommand shows list
|
|
121
|
+
cmd.action(() => {
|
|
122
|
+
const settings = config.get('settings') || {};
|
|
123
|
+
console.log('');
|
|
124
|
+
console.log(chalk.cyan('Current Settings:'));
|
|
125
|
+
if (Object.keys(settings).length === 0) {
|
|
126
|
+
console.log(chalk.gray(' (no custom settings — all defaults active)'));
|
|
127
|
+
} else {
|
|
128
|
+
for (const [k, v] of Object.entries(settings)) {
|
|
129
|
+
console.log(` ${chalk.white(k)} = ${chalk.yellow(v)}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
console.log('');
|
|
133
|
+
console.log(chalk.gray('Use `pe config set <key> <value>` to change a setting.'));
|
|
134
|
+
console.log(chalk.gray('Use `pe config list` to see all known keys.'));
|
|
135
|
+
});
|
|
136
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { connect, disconnect, getConnectionState } from '../core/connectionManager.js';
|
|
3
|
+
|
|
4
|
+
export default function connectCommand(program) {
|
|
5
|
+
program
|
|
6
|
+
.command('connect')
|
|
7
|
+
.description('connect to the Palexplorer network')
|
|
8
|
+
.action(async () => {
|
|
9
|
+
const current = getConnectionState();
|
|
10
|
+
if (current.status === 'connected') {
|
|
11
|
+
console.log(chalk.yellow('Already connected.'));
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
console.log(chalk.blue('Connecting to Palexplorer network...'));
|
|
16
|
+
const result = await connect({
|
|
17
|
+
onProgress: (step, progress) => {
|
|
18
|
+
console.log(chalk.gray(` [${progress}%] ${step}`));
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
if (result.connectedCount > 0) {
|
|
23
|
+
console.log(chalk.green(`\nConnected to ${result.connectedCount} server(s).`));
|
|
24
|
+
} else {
|
|
25
|
+
console.log(chalk.yellow('\nWarning: No servers reachable.'));
|
|
26
|
+
process.exitCode = 1;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
program
|
|
31
|
+
.command('disconnect')
|
|
32
|
+
.description('disconnect from the Palexplorer network')
|
|
33
|
+
.action(async () => {
|
|
34
|
+
const current = getConnectionState();
|
|
35
|
+
if (current.status === 'disconnected') {
|
|
36
|
+
console.log(chalk.yellow('Already disconnected.'));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
console.log(chalk.blue('Disconnecting...'));
|
|
41
|
+
await disconnect();
|
|
42
|
+
console.log(chalk.green('Disconnected from network.'));
|
|
43
|
+
});
|
|
44
|
+
}
|