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.
- package/README.md +149 -149
- package/bin/pal.js +63 -2
- package/extensions/@palexplorer/analytics/extension.json +20 -1
- package/extensions/@palexplorer/analytics/index.js +19 -9
- package/extensions/@palexplorer/audit/extension.json +14 -0
- package/extensions/@palexplorer/auth-email/extension.json +15 -0
- package/extensions/@palexplorer/auth-oauth/extension.json +15 -0
- package/extensions/@palexplorer/chat/extension.json +14 -0
- package/extensions/@palexplorer/discovery/extension.json +17 -0
- package/extensions/@palexplorer/discovery/index.js +1 -1
- package/extensions/@palexplorer/email-notifications/extension.json +23 -0
- package/extensions/@palexplorer/groups/extension.json +15 -0
- package/extensions/@palexplorer/share-links/extension.json +15 -0
- package/extensions/@palexplorer/sync/extension.json +16 -0
- package/extensions/@palexplorer/user-mgmt/extension.json +15 -0
- package/lib/capabilities.js +24 -24
- package/lib/commands/analytics.js +175 -175
- package/lib/commands/api-keys.js +131 -131
- package/lib/commands/audit.js +235 -235
- package/lib/commands/auth.js +137 -137
- package/lib/commands/backup.js +76 -76
- package/lib/commands/billing.js +148 -148
- package/lib/commands/chat.js +217 -217
- package/lib/commands/cloud-backup.js +231 -231
- package/lib/commands/comment.js +99 -99
- package/lib/commands/completion.js +203 -203
- package/lib/commands/compliance.js +218 -218
- package/lib/commands/config.js +136 -136
- package/lib/commands/connect.js +44 -44
- package/lib/commands/dept.js +294 -294
- package/lib/commands/device.js +146 -146
- package/lib/commands/download.js +240 -226
- package/lib/commands/explorer.js +178 -178
- package/lib/commands/extension.js +1060 -970
- package/lib/commands/favorite.js +90 -90
- package/lib/commands/federation.js +270 -270
- package/lib/commands/file.js +533 -533
- package/lib/commands/group.js +271 -271
- package/lib/commands/gui-share.js +29 -29
- package/lib/commands/init.js +61 -61
- package/lib/commands/invite.js +59 -59
- package/lib/commands/list.js +58 -58
- package/lib/commands/log.js +116 -116
- package/lib/commands/nearby.js +108 -108
- package/lib/commands/network.js +251 -251
- package/lib/commands/notify.js +198 -198
- package/lib/commands/org.js +273 -273
- package/lib/commands/pal.js +403 -180
- package/lib/commands/permissions.js +216 -216
- package/lib/commands/pin.js +97 -97
- package/lib/commands/protocol.js +357 -357
- package/lib/commands/rbac.js +147 -147
- package/lib/commands/recover.js +36 -36
- package/lib/commands/register.js +171 -171
- package/lib/commands/relay.js +131 -131
- package/lib/commands/remote.js +368 -368
- package/lib/commands/revoke.js +50 -50
- package/lib/commands/scanner.js +280 -280
- package/lib/commands/schedule.js +344 -344
- package/lib/commands/scim.js +203 -203
- package/lib/commands/search.js +181 -181
- package/lib/commands/serve.js +438 -438
- package/lib/commands/server.js +350 -350
- package/lib/commands/share-link.js +199 -199
- package/lib/commands/share.js +336 -323
- package/lib/commands/sso.js +200 -200
- package/lib/commands/status.js +145 -145
- package/lib/commands/stream.js +562 -562
- package/lib/commands/su.js +187 -187
- package/lib/commands/sync.js +979 -979
- package/lib/commands/transfers.js +152 -152
- package/lib/commands/uninstall.js +188 -188
- package/lib/commands/update.js +204 -204
- package/lib/commands/user.js +276 -276
- package/lib/commands/vfs.js +84 -84
- package/lib/commands/web-login.js +79 -79
- package/lib/commands/web.js +52 -52
- package/lib/commands/webhook.js +180 -180
- package/lib/commands/whoami.js +59 -59
- package/lib/commands/workspace.js +121 -121
- package/lib/core/billing.js +16 -5
- package/lib/core/dhtDiscovery.js +9 -2
- package/lib/core/discoveryClient.js +13 -7
- package/lib/core/extensions.js +142 -1
- package/lib/core/identity.js +33 -2
- package/lib/core/imageProcessor.js +109 -0
- package/lib/core/imageTorrent.js +167 -0
- package/lib/core/permissions.js +1 -1
- package/lib/core/pro.js +11 -4
- package/lib/core/serverList.js +4 -1
- package/lib/core/shares.js +12 -1
- package/lib/core/signalingServer.js +14 -2
- package/lib/core/su.js +1 -1
- package/lib/core/users.js +1 -1
- package/lib/protocol/messages.js +12 -3
- package/lib/utils/explorer.js +1 -1
- package/lib/utils/help.js +357 -357
- package/lib/utils/torrent.js +1 -0
- package/package.json +4 -3
package/lib/commands/group.js
CHANGED
|
@@ -1,271 +1,271 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { getFriends } from '../core/users.js';
|
|
3
|
-
import config from '../utils/config.js';
|
|
4
|
-
import { getIdentity } from '../core/identity.js';
|
|
5
|
-
import {
|
|
6
|
-
getGroups,
|
|
7
|
-
getGroup,
|
|
8
|
-
getGroupMembers,
|
|
9
|
-
createGroup,
|
|
10
|
-
updateGroup,
|
|
11
|
-
addMemberToGroup,
|
|
12
|
-
removeMemberFromGroup,
|
|
13
|
-
deleteGroup
|
|
14
|
-
} from '../core/groups.js';
|
|
15
|
-
import { checkLimit } from '../core/pro.js';
|
|
16
|
-
import { announceGroups } from '../core/discoveryClient.js';
|
|
17
|
-
|
|
18
|
-
export default function groupCommand(program) {
|
|
19
|
-
const cmd = program
|
|
20
|
-
.command('group')
|
|
21
|
-
.description('manage sharing groups')
|
|
22
|
-
.addHelpText('after', `
|
|
23
|
-
Examples:
|
|
24
|
-
$
|
|
25
|
-
$
|
|
26
|
-
$
|
|
27
|
-
$
|
|
28
|
-
$
|
|
29
|
-
$
|
|
30
|
-
$
|
|
31
|
-
`);
|
|
32
|
-
|
|
33
|
-
cmd
|
|
34
|
-
.command('create <name>')
|
|
35
|
-
.description('create a new group')
|
|
36
|
-
.option('--public', 'Make group discoverable by others')
|
|
37
|
-
.option('--desc <description>', 'Group description')
|
|
38
|
-
.action((name, opts) => {
|
|
39
|
-
if (!name || !name.trim()) {
|
|
40
|
-
console.log(chalk.red('Group name cannot be empty.'));
|
|
41
|
-
process.exitCode = 1;
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
try {
|
|
45
|
-
const groups = getGroups();
|
|
46
|
-
checkLimit('maxGroups', groups.length);
|
|
47
|
-
const identity = config.get('identity');
|
|
48
|
-
const group = createGroup(name, identity?.publicKey, {
|
|
49
|
-
visibility: opts.public ? 'public' : 'private',
|
|
50
|
-
description: opts.desc || null
|
|
51
|
-
});
|
|
52
|
-
console.log(chalk.green(`Group '${name}' created.`));
|
|
53
|
-
if (opts.public) {
|
|
54
|
-
console.log(chalk.yellow('Note: Public groups are discoverable. Use `
|
|
55
|
-
}
|
|
56
|
-
console.log(chalk.gray(`ID: ${group.id}`));
|
|
57
|
-
} catch (err) {
|
|
58
|
-
console.log(chalk.red(err.message));
|
|
59
|
-
process.exitCode = 1;
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
cmd
|
|
64
|
-
.command('edit <group>')
|
|
65
|
-
.description('edit a group (rename, description, visibility)')
|
|
66
|
-
.option('--name <name>', 'New group name')
|
|
67
|
-
.option('--desc <description>', 'Group description')
|
|
68
|
-
.option('--public', 'Make group public')
|
|
69
|
-
.option('--private', 'Make group private')
|
|
70
|
-
.action((groupName, opts) => {
|
|
71
|
-
try {
|
|
72
|
-
if (!opts.name && !opts.desc && !opts.public && !opts.private) {
|
|
73
|
-
console.log(chalk.yellow('Specify at least one option to update.'));
|
|
74
|
-
process.exitCode = 1;
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
const identity = config.get('identity');
|
|
78
|
-
const updates = {};
|
|
79
|
-
if (opts.name) updates.name = opts.name;
|
|
80
|
-
if (opts.desc) updates.description = opts.desc;
|
|
81
|
-
if (opts.public) updates.visibility = 'public';
|
|
82
|
-
if (opts.private) updates.visibility = 'private';
|
|
83
|
-
const group = updateGroup(groupName, updates, identity?.publicKey);
|
|
84
|
-
console.log(chalk.green(`Group '${group.name}' updated.`));
|
|
85
|
-
if (group.visibility === 'public') {
|
|
86
|
-
console.log(chalk.yellow('Remember to run `
|
|
87
|
-
}
|
|
88
|
-
} catch (err) {
|
|
89
|
-
console.log(chalk.red(err.message));
|
|
90
|
-
process.exitCode = 1;
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
cmd
|
|
95
|
-
.command('announce')
|
|
96
|
-
.description('announce public groups to the primary discovery server')
|
|
97
|
-
.action(async () => {
|
|
98
|
-
try {
|
|
99
|
-
const groups = getGroups();
|
|
100
|
-
const publicGroups = groups.filter(g => g.visibility === 'public');
|
|
101
|
-
if (publicGroups.length === 0) {
|
|
102
|
-
console.log(chalk.yellow('No public groups to announce.'));
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
process.stdout.write(chalk.gray(`Announcing ${publicGroups.length} public group(s)... `));
|
|
106
|
-
const res = await announceGroups(groups);
|
|
107
|
-
if (res && res.success) {
|
|
108
|
-
console.log(chalk.green('done.'));
|
|
109
|
-
} else {
|
|
110
|
-
console.log(chalk.red('failed.'));
|
|
111
|
-
process.exitCode = 1;
|
|
112
|
-
}
|
|
113
|
-
} catch (err) {
|
|
114
|
-
console.log(chalk.red(`\nError: ${err.message}`));
|
|
115
|
-
process.exitCode = 1;
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
cmd
|
|
120
|
-
.command('add <group> <pal>')
|
|
121
|
-
.description('add a pal to a group')
|
|
122
|
-
.action((groupName, palName) => {
|
|
123
|
-
const friends = getFriends();
|
|
124
|
-
const stripped = palName.replace(/^@/, '');
|
|
125
|
-
const pal = friends.find(f =>
|
|
126
|
-
f.name === palName || f.id === palName || f.handle === palName || f.publicKey === palName ||
|
|
127
|
-
f.name === stripped || f.id === stripped || f.handle === stripped || f.publicKey === stripped
|
|
128
|
-
);
|
|
129
|
-
if (!pal) {
|
|
130
|
-
console.log(chalk.red(`Pal '${palName}' not found. Add them first with \`
|
|
131
|
-
process.exitCode = 1;
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
try {
|
|
135
|
-
const members = getGroupMembers(groupName);
|
|
136
|
-
checkLimit('maxGroupMembers', members.length);
|
|
137
|
-
addMemberToGroup(groupName, pal);
|
|
138
|
-
console.log(chalk.green(`Added '${pal.name}' to '${groupName}'.`));
|
|
139
|
-
} catch (err) {
|
|
140
|
-
console.log(chalk.red(err.message));
|
|
141
|
-
process.exitCode = 1;
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
cmd
|
|
146
|
-
.command('remove <group> <pal>')
|
|
147
|
-
.description('remove a pal from a group')
|
|
148
|
-
.action((groupName, palName) => {
|
|
149
|
-
try {
|
|
150
|
-
removeMemberFromGroup(groupName, palName);
|
|
151
|
-
console.log(chalk.green(`Removed '${palName}' from '${groupName}'.`));
|
|
152
|
-
} catch (err) {
|
|
153
|
-
console.log(chalk.red(err.message));
|
|
154
|
-
process.exitCode = 1;
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
cmd
|
|
159
|
-
.command('delete <group>')
|
|
160
|
-
.description('delete a group')
|
|
161
|
-
.action((groupName) => {
|
|
162
|
-
const group = getGroup(groupName);
|
|
163
|
-
if (!group) {
|
|
164
|
-
console.log(chalk.red(`Group '${groupName}' not found.`));
|
|
165
|
-
process.exitCode = 1;
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
const identity = config.get('identity');
|
|
169
|
-
deleteGroup(groupName, identity?.publicKey);
|
|
170
|
-
console.log(chalk.green(`Group '${group.name}' deleted.`));
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
cmd
|
|
174
|
-
.command('list')
|
|
175
|
-
.description('list all groups')
|
|
176
|
-
.action(() => {
|
|
177
|
-
const groups = getGroups();
|
|
178
|
-
if (program.opts().json) {
|
|
179
|
-
console.log(JSON.stringify({ groups }, null, 2));
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
if (groups.length === 0) {
|
|
183
|
-
console.log(chalk.gray('No groups. Create one with `
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
console.log('');
|
|
187
|
-
console.log(chalk.cyan('Groups:'));
|
|
188
|
-
for (const g of groups) {
|
|
189
|
-
console.log(` ${chalk.white(g.name)} ${chalk.gray(`(${g.members.length} members)`)}`);
|
|
190
|
-
for (const m of g.members) {
|
|
191
|
-
console.log(` - ${m.name}${m.handle ? chalk.gray(` @${m.handle}`) : ''}`);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
console.log('');
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
cmd
|
|
198
|
-
.command('broadcast <group> <message>')
|
|
199
|
-
.description('send a message to all group members')
|
|
200
|
-
.action(async (groupName, message) => {
|
|
201
|
-
try {
|
|
202
|
-
const group = getGroup(groupName);
|
|
203
|
-
if (!group) {
|
|
204
|
-
console.log(chalk.red(`Group '${groupName}' not found.`));
|
|
205
|
-
process.exitCode = 1;
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
if (group.members.length === 0) {
|
|
209
|
-
console.log(chalk.yellow(`Group '${group.name}' has no members.`));
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
const identity = await getIdentity();
|
|
213
|
-
if (!identity?.handle) {
|
|
214
|
-
console.log(chalk.red('You must register a handle first:
|
|
215
|
-
process.exitCode = 1;
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
const { getPrimaryServer } = await import('../core/discoveryClient.js');
|
|
219
|
-
const discoveryUrl = config.get('settings')?.discovery_servers?.[0]
|
|
220
|
-
|| config.get('settings')?.discovery_url
|
|
221
|
-
|| process.env.PAL_DISCOVERY_URL
|
|
222
|
-
|| getPrimaryServer();
|
|
223
|
-
|
|
224
|
-
let sent = 0;
|
|
225
|
-
let failed = 0;
|
|
226
|
-
for (const member of group.members) {
|
|
227
|
-
const handle = member.handle;
|
|
228
|
-
if (!handle) {
|
|
229
|
-
console.log(chalk.yellow(` Skipping ${member.name} (no handle)`));
|
|
230
|
-
failed++;
|
|
231
|
-
continue;
|
|
232
|
-
}
|
|
233
|
-
try {
|
|
234
|
-
const res = await fetch(`${discoveryUrl}/api/v1/messages`, {
|
|
235
|
-
method: 'POST',
|
|
236
|
-
headers: { 'Content-Type': 'application/json' },
|
|
237
|
-
body: JSON.stringify({
|
|
238
|
-
toHandle: handle,
|
|
239
|
-
fromHandle: identity.handle,
|
|
240
|
-
fromDeviceId: identity.deviceId || null,
|
|
241
|
-
payload: { type: 'chat', text: message, timestamp: Date.now() },
|
|
242
|
-
}),
|
|
243
|
-
signal: AbortSignal.timeout(10000),
|
|
244
|
-
});
|
|
245
|
-
if (res.ok) {
|
|
246
|
-
sent++;
|
|
247
|
-
} else {
|
|
248
|
-
console.log(chalk.yellow(` Failed to send to @${handle}`));
|
|
249
|
-
failed++;
|
|
250
|
-
}
|
|
251
|
-
} catch {
|
|
252
|
-
console.log(chalk.yellow(` Failed to send to @${handle}`));
|
|
253
|
-
failed++;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
console.log(chalk.green(`Broadcast to '${group.name}': ${sent} sent, ${failed} failed.`));
|
|
257
|
-
} catch (err) {
|
|
258
|
-
console.log(chalk.red(err.message));
|
|
259
|
-
process.exitCode = 1;
|
|
260
|
-
}
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
cmd.action(() => {
|
|
264
|
-
const groups = getGroups();
|
|
265
|
-
if (groups.length === 0) {
|
|
266
|
-
console.log(chalk.gray('No groups. Create one with `
|
|
267
|
-
} else {
|
|
268
|
-
console.log(chalk.cyan(`${groups.length} group(s). Use \`
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
}
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { getFriends } from '../core/users.js';
|
|
3
|
+
import config from '../utils/config.js';
|
|
4
|
+
import { getIdentity } from '../core/identity.js';
|
|
5
|
+
import {
|
|
6
|
+
getGroups,
|
|
7
|
+
getGroup,
|
|
8
|
+
getGroupMembers,
|
|
9
|
+
createGroup,
|
|
10
|
+
updateGroup,
|
|
11
|
+
addMemberToGroup,
|
|
12
|
+
removeMemberFromGroup,
|
|
13
|
+
deleteGroup
|
|
14
|
+
} from '../core/groups.js';
|
|
15
|
+
import { checkLimit } from '../core/pro.js';
|
|
16
|
+
import { announceGroups } from '../core/discoveryClient.js';
|
|
17
|
+
|
|
18
|
+
export default function groupCommand(program) {
|
|
19
|
+
const cmd = program
|
|
20
|
+
.command('group')
|
|
21
|
+
.description('manage sharing groups')
|
|
22
|
+
.addHelpText('after', `
|
|
23
|
+
Examples:
|
|
24
|
+
$ pal group create team --public Create a public group called "team"
|
|
25
|
+
$ pal group edit team --desc "info" Edit group description
|
|
26
|
+
$ pal group add team @alice Add alice to the group
|
|
27
|
+
$ pal group remove team @alice Remove alice from the group
|
|
28
|
+
$ pal group list List all groups
|
|
29
|
+
$ pal group announce Announce public groups to discovery server
|
|
30
|
+
$ pal group delete team Delete a group
|
|
31
|
+
`);
|
|
32
|
+
|
|
33
|
+
cmd
|
|
34
|
+
.command('create <name>')
|
|
35
|
+
.description('create a new group')
|
|
36
|
+
.option('--public', 'Make group discoverable by others')
|
|
37
|
+
.option('--desc <description>', 'Group description')
|
|
38
|
+
.action((name, opts) => {
|
|
39
|
+
if (!name || !name.trim()) {
|
|
40
|
+
console.log(chalk.red('Group name cannot be empty.'));
|
|
41
|
+
process.exitCode = 1;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const groups = getGroups();
|
|
46
|
+
checkLimit('maxGroups', groups.length);
|
|
47
|
+
const identity = config.get('identity');
|
|
48
|
+
const group = createGroup(name, identity?.publicKey, {
|
|
49
|
+
visibility: opts.public ? 'public' : 'private',
|
|
50
|
+
description: opts.desc || null
|
|
51
|
+
});
|
|
52
|
+
console.log(chalk.green(`Group '${name}' created.`));
|
|
53
|
+
if (opts.public) {
|
|
54
|
+
console.log(chalk.yellow('Note: Public groups are discoverable. Use `pal group announce` to publish.'));
|
|
55
|
+
}
|
|
56
|
+
console.log(chalk.gray(`ID: ${group.id}`));
|
|
57
|
+
} catch (err) {
|
|
58
|
+
console.log(chalk.red(err.message));
|
|
59
|
+
process.exitCode = 1;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
cmd
|
|
64
|
+
.command('edit <group>')
|
|
65
|
+
.description('edit a group (rename, description, visibility)')
|
|
66
|
+
.option('--name <name>', 'New group name')
|
|
67
|
+
.option('--desc <description>', 'Group description')
|
|
68
|
+
.option('--public', 'Make group public')
|
|
69
|
+
.option('--private', 'Make group private')
|
|
70
|
+
.action((groupName, opts) => {
|
|
71
|
+
try {
|
|
72
|
+
if (!opts.name && !opts.desc && !opts.public && !opts.private) {
|
|
73
|
+
console.log(chalk.yellow('Specify at least one option to update.'));
|
|
74
|
+
process.exitCode = 1;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const identity = config.get('identity');
|
|
78
|
+
const updates = {};
|
|
79
|
+
if (opts.name) updates.name = opts.name;
|
|
80
|
+
if (opts.desc) updates.description = opts.desc;
|
|
81
|
+
if (opts.public) updates.visibility = 'public';
|
|
82
|
+
if (opts.private) updates.visibility = 'private';
|
|
83
|
+
const group = updateGroup(groupName, updates, identity?.publicKey);
|
|
84
|
+
console.log(chalk.green(`Group '${group.name}' updated.`));
|
|
85
|
+
if (group.visibility === 'public') {
|
|
86
|
+
console.log(chalk.yellow('Remember to run `pal group announce` to sync changes to discovery server.'));
|
|
87
|
+
}
|
|
88
|
+
} catch (err) {
|
|
89
|
+
console.log(chalk.red(err.message));
|
|
90
|
+
process.exitCode = 1;
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
cmd
|
|
95
|
+
.command('announce')
|
|
96
|
+
.description('announce public groups to the primary discovery server')
|
|
97
|
+
.action(async () => {
|
|
98
|
+
try {
|
|
99
|
+
const groups = getGroups();
|
|
100
|
+
const publicGroups = groups.filter(g => g.visibility === 'public');
|
|
101
|
+
if (publicGroups.length === 0) {
|
|
102
|
+
console.log(chalk.yellow('No public groups to announce.'));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
process.stdout.write(chalk.gray(`Announcing ${publicGroups.length} public group(s)... `));
|
|
106
|
+
const res = await announceGroups(groups);
|
|
107
|
+
if (res && res.success) {
|
|
108
|
+
console.log(chalk.green('done.'));
|
|
109
|
+
} else {
|
|
110
|
+
console.log(chalk.red('failed.'));
|
|
111
|
+
process.exitCode = 1;
|
|
112
|
+
}
|
|
113
|
+
} catch (err) {
|
|
114
|
+
console.log(chalk.red(`\nError: ${err.message}`));
|
|
115
|
+
process.exitCode = 1;
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
cmd
|
|
120
|
+
.command('add <group> <pal>')
|
|
121
|
+
.description('add a pal to a group')
|
|
122
|
+
.action((groupName, palName) => {
|
|
123
|
+
const friends = getFriends();
|
|
124
|
+
const stripped = palName.replace(/^@/, '');
|
|
125
|
+
const pal = friends.find(f =>
|
|
126
|
+
f.name === palName || f.id === palName || f.handle === palName || f.publicKey === palName ||
|
|
127
|
+
f.name === stripped || f.id === stripped || f.handle === stripped || f.publicKey === stripped
|
|
128
|
+
);
|
|
129
|
+
if (!pal) {
|
|
130
|
+
console.log(chalk.red(`Pal '${palName}' not found. Add them first with \`pal pal add\`.`));
|
|
131
|
+
process.exitCode = 1;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const members = getGroupMembers(groupName);
|
|
136
|
+
checkLimit('maxGroupMembers', members.length);
|
|
137
|
+
addMemberToGroup(groupName, pal);
|
|
138
|
+
console.log(chalk.green(`Added '${pal.name}' to '${groupName}'.`));
|
|
139
|
+
} catch (err) {
|
|
140
|
+
console.log(chalk.red(err.message));
|
|
141
|
+
process.exitCode = 1;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
cmd
|
|
146
|
+
.command('remove <group> <pal>')
|
|
147
|
+
.description('remove a pal from a group')
|
|
148
|
+
.action((groupName, palName) => {
|
|
149
|
+
try {
|
|
150
|
+
removeMemberFromGroup(groupName, palName);
|
|
151
|
+
console.log(chalk.green(`Removed '${palName}' from '${groupName}'.`));
|
|
152
|
+
} catch (err) {
|
|
153
|
+
console.log(chalk.red(err.message));
|
|
154
|
+
process.exitCode = 1;
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
cmd
|
|
159
|
+
.command('delete <group>')
|
|
160
|
+
.description('delete a group')
|
|
161
|
+
.action((groupName) => {
|
|
162
|
+
const group = getGroup(groupName);
|
|
163
|
+
if (!group) {
|
|
164
|
+
console.log(chalk.red(`Group '${groupName}' not found.`));
|
|
165
|
+
process.exitCode = 1;
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const identity = config.get('identity');
|
|
169
|
+
deleteGroup(groupName, identity?.publicKey);
|
|
170
|
+
console.log(chalk.green(`Group '${group.name}' deleted.`));
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
cmd
|
|
174
|
+
.command('list')
|
|
175
|
+
.description('list all groups')
|
|
176
|
+
.action(() => {
|
|
177
|
+
const groups = getGroups();
|
|
178
|
+
if (program.opts().json) {
|
|
179
|
+
console.log(JSON.stringify({ groups }, null, 2));
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (groups.length === 0) {
|
|
183
|
+
console.log(chalk.gray('No groups. Create one with `pal group create <name>`.'));
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
console.log('');
|
|
187
|
+
console.log(chalk.cyan('Groups:'));
|
|
188
|
+
for (const g of groups) {
|
|
189
|
+
console.log(` ${chalk.white(g.name)} ${chalk.gray(`(${g.members.length} members)`)}`);
|
|
190
|
+
for (const m of g.members) {
|
|
191
|
+
console.log(` - ${m.name}${m.handle ? chalk.gray(` @${m.handle}`) : ''}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
console.log('');
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
cmd
|
|
198
|
+
.command('broadcast <group> <message>')
|
|
199
|
+
.description('send a message to all group members')
|
|
200
|
+
.action(async (groupName, message) => {
|
|
201
|
+
try {
|
|
202
|
+
const group = getGroup(groupName);
|
|
203
|
+
if (!group) {
|
|
204
|
+
console.log(chalk.red(`Group '${groupName}' not found.`));
|
|
205
|
+
process.exitCode = 1;
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (group.members.length === 0) {
|
|
209
|
+
console.log(chalk.yellow(`Group '${group.name}' has no members.`));
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const identity = await getIdentity();
|
|
213
|
+
if (!identity?.handle) {
|
|
214
|
+
console.log(chalk.red('You must register a handle first: pal register'));
|
|
215
|
+
process.exitCode = 1;
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const { getPrimaryServer } = await import('../core/discoveryClient.js');
|
|
219
|
+
const discoveryUrl = config.get('settings')?.discovery_servers?.[0]
|
|
220
|
+
|| config.get('settings')?.discovery_url
|
|
221
|
+
|| process.env.PAL_DISCOVERY_URL
|
|
222
|
+
|| getPrimaryServer();
|
|
223
|
+
|
|
224
|
+
let sent = 0;
|
|
225
|
+
let failed = 0;
|
|
226
|
+
for (const member of group.members) {
|
|
227
|
+
const handle = member.handle;
|
|
228
|
+
if (!handle) {
|
|
229
|
+
console.log(chalk.yellow(` Skipping ${member.name} (no handle)`));
|
|
230
|
+
failed++;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
try {
|
|
234
|
+
const res = await fetch(`${discoveryUrl}/api/v1/messages`, {
|
|
235
|
+
method: 'POST',
|
|
236
|
+
headers: { 'Content-Type': 'application/json' },
|
|
237
|
+
body: JSON.stringify({
|
|
238
|
+
toHandle: handle,
|
|
239
|
+
fromHandle: identity.handle,
|
|
240
|
+
fromDeviceId: identity.deviceId || null,
|
|
241
|
+
payload: { type: 'chat', text: message, timestamp: Date.now() },
|
|
242
|
+
}),
|
|
243
|
+
signal: AbortSignal.timeout(10000),
|
|
244
|
+
});
|
|
245
|
+
if (res.ok) {
|
|
246
|
+
sent++;
|
|
247
|
+
} else {
|
|
248
|
+
console.log(chalk.yellow(` Failed to send to @${handle}`));
|
|
249
|
+
failed++;
|
|
250
|
+
}
|
|
251
|
+
} catch {
|
|
252
|
+
console.log(chalk.yellow(` Failed to send to @${handle}`));
|
|
253
|
+
failed++;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
console.log(chalk.green(`Broadcast to '${group.name}': ${sent} sent, ${failed} failed.`));
|
|
257
|
+
} catch (err) {
|
|
258
|
+
console.log(chalk.red(err.message));
|
|
259
|
+
process.exitCode = 1;
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
cmd.action(() => {
|
|
264
|
+
const groups = getGroups();
|
|
265
|
+
if (groups.length === 0) {
|
|
266
|
+
console.log(chalk.gray('No groups. Create one with `pal group create <name>`.'));
|
|
267
|
+
} else {
|
|
268
|
+
console.log(chalk.cyan(`${groups.length} group(s). Use \`pal group list\` for details.`));
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { spawn } from 'child_process';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
|
-
|
|
6
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
-
|
|
8
|
-
export default function guiShareCommand(program) {
|
|
9
|
-
program
|
|
10
|
-
.command('gui-share <path>')
|
|
11
|
-
.description('internal: Open GUI to share a specific file/folder')
|
|
12
|
-
.action((filePath) => {
|
|
13
|
-
const fullPath = path.resolve(filePath);
|
|
14
|
-
console.log(chalk.cyan(`Opening Palexplorer to share: ${fullPath}`));
|
|
15
|
-
|
|
16
|
-
// In a real app, we'd check if Electron is already running and send an IPC message
|
|
17
|
-
// For now, we'll just launch the GUI with an environment variable or argument
|
|
18
|
-
const guiPath = path.resolve(__dirname, '../../gui');
|
|
19
|
-
|
|
20
|
-
const child = spawn('npm', ['run', 'electron', '--', `--share=${fullPath}`], {
|
|
21
|
-
cwd: guiPath,
|
|
22
|
-
detached: true,
|
|
23
|
-
stdio: 'ignore',
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
child.unref();
|
|
27
|
-
process.exit(0);
|
|
28
|
-
});
|
|
29
|
-
}
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { spawn } from 'child_process';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
|
|
8
|
+
export default function guiShareCommand(program) {
|
|
9
|
+
program
|
|
10
|
+
.command('gui-share <path>')
|
|
11
|
+
.description('internal: Open GUI to share a specific file/folder')
|
|
12
|
+
.action((filePath) => {
|
|
13
|
+
const fullPath = path.resolve(filePath);
|
|
14
|
+
console.log(chalk.cyan(`Opening Palexplorer to share: ${fullPath}`));
|
|
15
|
+
|
|
16
|
+
// In a real app, we'd check if Electron is already running and send an IPC message
|
|
17
|
+
// For now, we'll just launch the GUI with an environment variable or argument
|
|
18
|
+
const guiPath = path.resolve(__dirname, '../../gui');
|
|
19
|
+
|
|
20
|
+
const child = spawn('npm', ['run', 'electron', '--', `--share=${fullPath}`], {
|
|
21
|
+
cwd: guiPath,
|
|
22
|
+
detached: true,
|
|
23
|
+
stdio: 'ignore',
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
child.unref();
|
|
27
|
+
process.exit(0);
|
|
28
|
+
});
|
|
29
|
+
}
|