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/dept.js
CHANGED
|
@@ -1,294 +1,294 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
|
|
3
|
-
export default function deptCommand(program) {
|
|
4
|
-
const cmd = program
|
|
5
|
-
.command('dept')
|
|
6
|
-
.description('department management within organizations (Enterprise)')
|
|
7
|
-
.addHelpText('after', `
|
|
8
|
-
Examples:
|
|
9
|
-
$
|
|
10
|
-
$
|
|
11
|
-
$
|
|
12
|
-
$
|
|
13
|
-
$
|
|
14
|
-
$
|
|
15
|
-
$
|
|
16
|
-
$
|
|
17
|
-
$
|
|
18
|
-
`)
|
|
19
|
-
.action(() => { cmd.outputHelp(); });
|
|
20
|
-
|
|
21
|
-
cmd
|
|
22
|
-
.command('create <orgId> <name>')
|
|
23
|
-
.description('create a department in an organization')
|
|
24
|
-
.action(async (orgId, name) => {
|
|
25
|
-
try {
|
|
26
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
27
|
-
const key = `departments:${orgId}`;
|
|
28
|
-
const departments = extConfig.get(key) || [];
|
|
29
|
-
if (departments.find(d => d.name === name)) {
|
|
30
|
-
console.log(chalk.yellow(`Department already exists: ${name}`));
|
|
31
|
-
process.exitCode = 1;
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
departments.push({
|
|
35
|
-
name,
|
|
36
|
-
createdAt: new Date().toISOString(),
|
|
37
|
-
members: [],
|
|
38
|
-
policies: {},
|
|
39
|
-
});
|
|
40
|
-
extConfig.set(key, departments);
|
|
41
|
-
console.log(chalk.green(`✔ Department created: ${name}`));
|
|
42
|
-
console.log(` Organization: ${chalk.white(orgId)}`);
|
|
43
|
-
} catch (err) {
|
|
44
|
-
console.log(chalk.red(`Create failed: ${err.message}`));
|
|
45
|
-
process.exitCode = 1;
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
cmd
|
|
50
|
-
.command('list <orgId>')
|
|
51
|
-
.description('list all departments in an organization')
|
|
52
|
-
.action(async (orgId) => {
|
|
53
|
-
try {
|
|
54
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
55
|
-
const key = `departments:${orgId}`;
|
|
56
|
-
const departments = extConfig.get(key) || [];
|
|
57
|
-
if (departments.length === 0) {
|
|
58
|
-
console.log(chalk.dim('No departments.'));
|
|
59
|
-
console.log(chalk.dim(`
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
console.log(chalk.bold(`Departments in ${orgId} (${departments.length})\n`));
|
|
63
|
-
for (const d of departments) {
|
|
64
|
-
console.log(` ${chalk.cyan(d.name)}`);
|
|
65
|
-
console.log(` Members: ${chalk.white(d.members.length)} Created: ${chalk.dim(d.createdAt)}`);
|
|
66
|
-
}
|
|
67
|
-
} catch (err) {
|
|
68
|
-
console.log(chalk.red(`List failed: ${err.message}`));
|
|
69
|
-
process.exitCode = 1;
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
cmd
|
|
74
|
-
.command('info <orgId> <deptName>')
|
|
75
|
-
.description('show department details and members')
|
|
76
|
-
.action(async (orgId, deptName) => {
|
|
77
|
-
try {
|
|
78
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
79
|
-
const key = `departments:${orgId}`;
|
|
80
|
-
const departments = extConfig.get(key) || [];
|
|
81
|
-
const dept = departments.find(d => d.name === deptName);
|
|
82
|
-
if (!dept) {
|
|
83
|
-
console.log(chalk.red(`Department not found: ${deptName}`));
|
|
84
|
-
process.exitCode = 1;
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
console.log('');
|
|
88
|
-
console.log(chalk.bold.cyan(dept.name));
|
|
89
|
-
console.log(chalk.gray('─'.repeat(50)));
|
|
90
|
-
console.log(` Organization: ${chalk.white(orgId)}`);
|
|
91
|
-
console.log(` Created: ${chalk.dim(dept.createdAt)}`);
|
|
92
|
-
console.log(` Members: ${chalk.white(dept.members.length)}`);
|
|
93
|
-
|
|
94
|
-
if (dept.members.length > 0) {
|
|
95
|
-
console.log('');
|
|
96
|
-
console.log(chalk.cyan(' Members:'));
|
|
97
|
-
for (const m of dept.members) {
|
|
98
|
-
const roleColor = m.role === 'dept-admin' ? chalk.yellow : chalk.gray;
|
|
99
|
-
const pubShort = m.publicKey.slice(0, 16) + '...';
|
|
100
|
-
console.log(` ${chalk.white(pubShort)} ${roleColor(m.role)} ${chalk.dim('joined ' + m.joinedAt)}`);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const policyKeys = Object.keys(dept.policies || {});
|
|
105
|
-
if (policyKeys.length > 0) {
|
|
106
|
-
console.log('');
|
|
107
|
-
console.log(chalk.cyan(' Policies:'));
|
|
108
|
-
for (const k of policyKeys) {
|
|
109
|
-
console.log(` ${chalk.white(k)}: ${chalk.dim(dept.policies[k])}`);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
console.log('');
|
|
113
|
-
} catch (err) {
|
|
114
|
-
console.log(chalk.red(`Info failed: ${err.message}`));
|
|
115
|
-
process.exitCode = 1;
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
cmd
|
|
120
|
-
.command('add <orgId> <deptName> <publicKey>')
|
|
121
|
-
.description('add a member to a department (requires dept-admin or org-admin)')
|
|
122
|
-
.option('--role <role>', 'member role', 'member')
|
|
123
|
-
.action(async (orgId, deptName, publicKey, opts) => {
|
|
124
|
-
try {
|
|
125
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
126
|
-
const key = `departments:${orgId}`;
|
|
127
|
-
const departments = extConfig.get(key) || [];
|
|
128
|
-
const dept = departments.find(d => d.name === deptName);
|
|
129
|
-
if (!dept) {
|
|
130
|
-
console.log(chalk.red(`Department not found: ${deptName}`));
|
|
131
|
-
process.exitCode = 1;
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
const VALID_ROLES = ['member', 'dept-admin'];
|
|
135
|
-
if (opts.role && !VALID_ROLES.includes(opts.role)) {
|
|
136
|
-
console.error(chalk.red(`Invalid role "${opts.role}". Allowed: ${VALID_ROLES.join(', ')}`));
|
|
137
|
-
process.exitCode = 1;
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
if (dept.members.find(m => m.publicKey === publicKey)) {
|
|
141
|
-
console.log(chalk.yellow('Member already in department.'));
|
|
142
|
-
process.exitCode = 1;
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
dept.members.push({
|
|
146
|
-
publicKey,
|
|
147
|
-
role: opts.role,
|
|
148
|
-
joinedAt: new Date().toISOString(),
|
|
149
|
-
});
|
|
150
|
-
extConfig.set(key, departments);
|
|
151
|
-
console.log(chalk.green(`✔ Member added to ${deptName}`));
|
|
152
|
-
console.log(` Key: ${chalk.dim(publicKey.slice(0, 16) + '...')}`);
|
|
153
|
-
console.log(` Role: ${chalk.white(opts.role)}`);
|
|
154
|
-
} catch (err) {
|
|
155
|
-
console.log(chalk.red(`Add failed: ${err.message}`));
|
|
156
|
-
process.exitCode = 1;
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
cmd
|
|
161
|
-
.command('remove <orgId> <deptName> <publicKey>')
|
|
162
|
-
.description('remove a member from a department')
|
|
163
|
-
.action(async (orgId, deptName, publicKey) => {
|
|
164
|
-
try {
|
|
165
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
166
|
-
const key = `departments:${orgId}`;
|
|
167
|
-
const departments = extConfig.get(key) || [];
|
|
168
|
-
const dept = departments.find(d => d.name === deptName);
|
|
169
|
-
if (!dept) {
|
|
170
|
-
console.log(chalk.red(`Department not found: ${deptName}`));
|
|
171
|
-
process.exitCode = 1;
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
const idx = dept.members.findIndex(m => m.publicKey === publicKey);
|
|
175
|
-
if (idx === -1) {
|
|
176
|
-
console.log(chalk.yellow('Member not found in department.'));
|
|
177
|
-
process.exitCode = 1;
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
dept.members.splice(idx, 1);
|
|
181
|
-
extConfig.set(key, departments);
|
|
182
|
-
console.log(chalk.green(`✔ Member removed from ${deptName}`));
|
|
183
|
-
} catch (err) {
|
|
184
|
-
console.log(chalk.red(`Remove failed: ${err.message}`));
|
|
185
|
-
process.exitCode = 1;
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
cmd
|
|
190
|
-
.command('delete <orgId> <name>')
|
|
191
|
-
.description('delete a department')
|
|
192
|
-
.action(async (orgId, name) => {
|
|
193
|
-
try {
|
|
194
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
195
|
-
const key = `departments:${orgId}`;
|
|
196
|
-
const departments = extConfig.get(key) || [];
|
|
197
|
-
const idx = departments.findIndex(d => d.name === name);
|
|
198
|
-
if (idx === -1) {
|
|
199
|
-
console.log(chalk.red(`Department not found: ${name}`));
|
|
200
|
-
process.exitCode = 1;
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
const removed = departments.splice(idx, 1)[0];
|
|
204
|
-
extConfig.set(key, departments);
|
|
205
|
-
console.log(chalk.green(`✔ Department deleted: ${name}`));
|
|
206
|
-
console.log(` Had ${chalk.white(removed.members.length)} member(s)`);
|
|
207
|
-
} catch (err) {
|
|
208
|
-
console.log(chalk.red(`Delete failed: ${err.message}`));
|
|
209
|
-
process.exitCode = 1;
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
cmd
|
|
214
|
-
.command('shares <orgId> <deptName>')
|
|
215
|
-
.description('list shares scoped to a department')
|
|
216
|
-
.action(async (orgId, deptName) => {
|
|
217
|
-
try {
|
|
218
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
219
|
-
const key = `departments:${orgId}`;
|
|
220
|
-
const departments = extConfig.get(key) || [];
|
|
221
|
-
const dept = departments.find(d => d.name === deptName);
|
|
222
|
-
if (!dept) {
|
|
223
|
-
console.log(chalk.red(`Department not found: ${deptName}`));
|
|
224
|
-
process.exitCode = 1;
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const config = (await import('../utils/config.js')).default;
|
|
229
|
-
const shares = config.get('shares') || [];
|
|
230
|
-
const deptShares = shares.filter(s => s.department === deptName && s.orgId === orgId);
|
|
231
|
-
|
|
232
|
-
if (deptShares.length === 0) {
|
|
233
|
-
console.log(chalk.dim(`No shares scoped to department ${deptName}.`));
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
console.log(chalk.bold(`Shares for ${deptName} (${deptShares.length})\n`));
|
|
237
|
-
for (const s of deptShares) {
|
|
238
|
-
console.log(` ${chalk.cyan(s.path || s.name)}`);
|
|
239
|
-
console.log(` Visibility: ${chalk.dim(s.visibility || 'private')} Created: ${chalk.dim(s.createdAt || 'unknown')}`);
|
|
240
|
-
}
|
|
241
|
-
} catch (err) {
|
|
242
|
-
console.log(chalk.red(`Shares failed: ${err.message}`));
|
|
243
|
-
process.exitCode = 1;
|
|
244
|
-
}
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
cmd
|
|
248
|
-
.command('policy <orgId> <deptName>')
|
|
249
|
-
.description('show/set department policies')
|
|
250
|
-
.option('--set <key>', 'set a policy value (followed by value as argument)')
|
|
251
|
-
.action(async (orgId, deptName, opts, command) => {
|
|
252
|
-
try {
|
|
253
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
254
|
-
const key = `departments:${orgId}`;
|
|
255
|
-
const departments = extConfig.get(key) || [];
|
|
256
|
-
const dept = departments.find(d => d.name === deptName);
|
|
257
|
-
if (!dept) {
|
|
258
|
-
console.log(chalk.red(`Department not found: ${deptName}`));
|
|
259
|
-
process.exitCode = 1;
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if (opts.set) {
|
|
264
|
-
const value = command.args[0];
|
|
265
|
-
if (!value) {
|
|
266
|
-
console.log(chalk.red('Usage:
|
|
267
|
-
process.exitCode = 1;
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
if (!dept.policies) dept.policies = {};
|
|
271
|
-
dept.policies[opts.set] = value;
|
|
272
|
-
extConfig.set(key, departments);
|
|
273
|
-
console.log(chalk.green(`✔ Policy set: ${opts.set} = ${value}`));
|
|
274
|
-
return;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// Show policies
|
|
278
|
-
const policies = dept.policies || {};
|
|
279
|
-
const policyKeys = Object.keys(policies);
|
|
280
|
-
if (policyKeys.length === 0) {
|
|
281
|
-
console.log(chalk.dim(`No policies set for ${deptName}.`));
|
|
282
|
-
console.log(chalk.dim(`
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
console.log(chalk.bold(`Policies for ${deptName}\n`));
|
|
286
|
-
for (const k of policyKeys) {
|
|
287
|
-
console.log(` ${chalk.white(k)}: ${chalk.cyan(policies[k])}`);
|
|
288
|
-
}
|
|
289
|
-
} catch (err) {
|
|
290
|
-
console.log(chalk.red(`Policy failed: ${err.message}`));
|
|
291
|
-
process.exitCode = 1;
|
|
292
|
-
}
|
|
293
|
-
});
|
|
294
|
-
}
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
export default function deptCommand(program) {
|
|
4
|
+
const cmd = program
|
|
5
|
+
.command('dept')
|
|
6
|
+
.description('department management within organizations (Enterprise)')
|
|
7
|
+
.addHelpText('after', `
|
|
8
|
+
Examples:
|
|
9
|
+
$ pal dept create org123 "Engineering" Create a department
|
|
10
|
+
$ pal dept list org123 List departments
|
|
11
|
+
$ pal dept info org123 Engineering Show department details
|
|
12
|
+
$ pal dept add org123 Engineering <pubkey> Add a member
|
|
13
|
+
$ pal dept remove org123 Engineering <pubkey>
|
|
14
|
+
$ pal dept delete org123 Engineering Delete a department
|
|
15
|
+
$ pal dept shares org123 Engineering List department shares
|
|
16
|
+
$ pal dept policy org123 Engineering Show department policies
|
|
17
|
+
$ pal dept policy org123 Engineering --set maxFileSize 100MB
|
|
18
|
+
`)
|
|
19
|
+
.action(() => { cmd.outputHelp(); });
|
|
20
|
+
|
|
21
|
+
cmd
|
|
22
|
+
.command('create <orgId> <name>')
|
|
23
|
+
.description('create a department in an organization')
|
|
24
|
+
.action(async (orgId, name) => {
|
|
25
|
+
try {
|
|
26
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
27
|
+
const key = `departments:${orgId}`;
|
|
28
|
+
const departments = extConfig.get(key) || [];
|
|
29
|
+
if (departments.find(d => d.name === name)) {
|
|
30
|
+
console.log(chalk.yellow(`Department already exists: ${name}`));
|
|
31
|
+
process.exitCode = 1;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
departments.push({
|
|
35
|
+
name,
|
|
36
|
+
createdAt: new Date().toISOString(),
|
|
37
|
+
members: [],
|
|
38
|
+
policies: {},
|
|
39
|
+
});
|
|
40
|
+
extConfig.set(key, departments);
|
|
41
|
+
console.log(chalk.green(`✔ Department created: ${name}`));
|
|
42
|
+
console.log(` Organization: ${chalk.white(orgId)}`);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
console.log(chalk.red(`Create failed: ${err.message}`));
|
|
45
|
+
process.exitCode = 1;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
cmd
|
|
50
|
+
.command('list <orgId>')
|
|
51
|
+
.description('list all departments in an organization')
|
|
52
|
+
.action(async (orgId) => {
|
|
53
|
+
try {
|
|
54
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
55
|
+
const key = `departments:${orgId}`;
|
|
56
|
+
const departments = extConfig.get(key) || [];
|
|
57
|
+
if (departments.length === 0) {
|
|
58
|
+
console.log(chalk.dim('No departments.'));
|
|
59
|
+
console.log(chalk.dim(` pal dept create ${orgId} <name>`));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
console.log(chalk.bold(`Departments in ${orgId} (${departments.length})\n`));
|
|
63
|
+
for (const d of departments) {
|
|
64
|
+
console.log(` ${chalk.cyan(d.name)}`);
|
|
65
|
+
console.log(` Members: ${chalk.white(d.members.length)} Created: ${chalk.dim(d.createdAt)}`);
|
|
66
|
+
}
|
|
67
|
+
} catch (err) {
|
|
68
|
+
console.log(chalk.red(`List failed: ${err.message}`));
|
|
69
|
+
process.exitCode = 1;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
cmd
|
|
74
|
+
.command('info <orgId> <deptName>')
|
|
75
|
+
.description('show department details and members')
|
|
76
|
+
.action(async (orgId, deptName) => {
|
|
77
|
+
try {
|
|
78
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
79
|
+
const key = `departments:${orgId}`;
|
|
80
|
+
const departments = extConfig.get(key) || [];
|
|
81
|
+
const dept = departments.find(d => d.name === deptName);
|
|
82
|
+
if (!dept) {
|
|
83
|
+
console.log(chalk.red(`Department not found: ${deptName}`));
|
|
84
|
+
process.exitCode = 1;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
console.log('');
|
|
88
|
+
console.log(chalk.bold.cyan(dept.name));
|
|
89
|
+
console.log(chalk.gray('─'.repeat(50)));
|
|
90
|
+
console.log(` Organization: ${chalk.white(orgId)}`);
|
|
91
|
+
console.log(` Created: ${chalk.dim(dept.createdAt)}`);
|
|
92
|
+
console.log(` Members: ${chalk.white(dept.members.length)}`);
|
|
93
|
+
|
|
94
|
+
if (dept.members.length > 0) {
|
|
95
|
+
console.log('');
|
|
96
|
+
console.log(chalk.cyan(' Members:'));
|
|
97
|
+
for (const m of dept.members) {
|
|
98
|
+
const roleColor = m.role === 'dept-admin' ? chalk.yellow : chalk.gray;
|
|
99
|
+
const pubShort = m.publicKey.slice(0, 16) + '...';
|
|
100
|
+
console.log(` ${chalk.white(pubShort)} ${roleColor(m.role)} ${chalk.dim('joined ' + m.joinedAt)}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const policyKeys = Object.keys(dept.policies || {});
|
|
105
|
+
if (policyKeys.length > 0) {
|
|
106
|
+
console.log('');
|
|
107
|
+
console.log(chalk.cyan(' Policies:'));
|
|
108
|
+
for (const k of policyKeys) {
|
|
109
|
+
console.log(` ${chalk.white(k)}: ${chalk.dim(dept.policies[k])}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
console.log('');
|
|
113
|
+
} catch (err) {
|
|
114
|
+
console.log(chalk.red(`Info failed: ${err.message}`));
|
|
115
|
+
process.exitCode = 1;
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
cmd
|
|
120
|
+
.command('add <orgId> <deptName> <publicKey>')
|
|
121
|
+
.description('add a member to a department (requires dept-admin or org-admin)')
|
|
122
|
+
.option('--role <role>', 'member role', 'member')
|
|
123
|
+
.action(async (orgId, deptName, publicKey, opts) => {
|
|
124
|
+
try {
|
|
125
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
126
|
+
const key = `departments:${orgId}`;
|
|
127
|
+
const departments = extConfig.get(key) || [];
|
|
128
|
+
const dept = departments.find(d => d.name === deptName);
|
|
129
|
+
if (!dept) {
|
|
130
|
+
console.log(chalk.red(`Department not found: ${deptName}`));
|
|
131
|
+
process.exitCode = 1;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const VALID_ROLES = ['member', 'dept-admin'];
|
|
135
|
+
if (opts.role && !VALID_ROLES.includes(opts.role)) {
|
|
136
|
+
console.error(chalk.red(`Invalid role "${opts.role}". Allowed: ${VALID_ROLES.join(', ')}`));
|
|
137
|
+
process.exitCode = 1;
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (dept.members.find(m => m.publicKey === publicKey)) {
|
|
141
|
+
console.log(chalk.yellow('Member already in department.'));
|
|
142
|
+
process.exitCode = 1;
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
dept.members.push({
|
|
146
|
+
publicKey,
|
|
147
|
+
role: opts.role,
|
|
148
|
+
joinedAt: new Date().toISOString(),
|
|
149
|
+
});
|
|
150
|
+
extConfig.set(key, departments);
|
|
151
|
+
console.log(chalk.green(`✔ Member added to ${deptName}`));
|
|
152
|
+
console.log(` Key: ${chalk.dim(publicKey.slice(0, 16) + '...')}`);
|
|
153
|
+
console.log(` Role: ${chalk.white(opts.role)}`);
|
|
154
|
+
} catch (err) {
|
|
155
|
+
console.log(chalk.red(`Add failed: ${err.message}`));
|
|
156
|
+
process.exitCode = 1;
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
cmd
|
|
161
|
+
.command('remove <orgId> <deptName> <publicKey>')
|
|
162
|
+
.description('remove a member from a department')
|
|
163
|
+
.action(async (orgId, deptName, publicKey) => {
|
|
164
|
+
try {
|
|
165
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
166
|
+
const key = `departments:${orgId}`;
|
|
167
|
+
const departments = extConfig.get(key) || [];
|
|
168
|
+
const dept = departments.find(d => d.name === deptName);
|
|
169
|
+
if (!dept) {
|
|
170
|
+
console.log(chalk.red(`Department not found: ${deptName}`));
|
|
171
|
+
process.exitCode = 1;
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const idx = dept.members.findIndex(m => m.publicKey === publicKey);
|
|
175
|
+
if (idx === -1) {
|
|
176
|
+
console.log(chalk.yellow('Member not found in department.'));
|
|
177
|
+
process.exitCode = 1;
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
dept.members.splice(idx, 1);
|
|
181
|
+
extConfig.set(key, departments);
|
|
182
|
+
console.log(chalk.green(`✔ Member removed from ${deptName}`));
|
|
183
|
+
} catch (err) {
|
|
184
|
+
console.log(chalk.red(`Remove failed: ${err.message}`));
|
|
185
|
+
process.exitCode = 1;
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
cmd
|
|
190
|
+
.command('delete <orgId> <name>')
|
|
191
|
+
.description('delete a department')
|
|
192
|
+
.action(async (orgId, name) => {
|
|
193
|
+
try {
|
|
194
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
195
|
+
const key = `departments:${orgId}`;
|
|
196
|
+
const departments = extConfig.get(key) || [];
|
|
197
|
+
const idx = departments.findIndex(d => d.name === name);
|
|
198
|
+
if (idx === -1) {
|
|
199
|
+
console.log(chalk.red(`Department not found: ${name}`));
|
|
200
|
+
process.exitCode = 1;
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const removed = departments.splice(idx, 1)[0];
|
|
204
|
+
extConfig.set(key, departments);
|
|
205
|
+
console.log(chalk.green(`✔ Department deleted: ${name}`));
|
|
206
|
+
console.log(` Had ${chalk.white(removed.members.length)} member(s)`);
|
|
207
|
+
} catch (err) {
|
|
208
|
+
console.log(chalk.red(`Delete failed: ${err.message}`));
|
|
209
|
+
process.exitCode = 1;
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
cmd
|
|
214
|
+
.command('shares <orgId> <deptName>')
|
|
215
|
+
.description('list shares scoped to a department')
|
|
216
|
+
.action(async (orgId, deptName) => {
|
|
217
|
+
try {
|
|
218
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
219
|
+
const key = `departments:${orgId}`;
|
|
220
|
+
const departments = extConfig.get(key) || [];
|
|
221
|
+
const dept = departments.find(d => d.name === deptName);
|
|
222
|
+
if (!dept) {
|
|
223
|
+
console.log(chalk.red(`Department not found: ${deptName}`));
|
|
224
|
+
process.exitCode = 1;
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const config = (await import('../utils/config.js')).default;
|
|
229
|
+
const shares = config.get('shares') || [];
|
|
230
|
+
const deptShares = shares.filter(s => s.department === deptName && s.orgId === orgId);
|
|
231
|
+
|
|
232
|
+
if (deptShares.length === 0) {
|
|
233
|
+
console.log(chalk.dim(`No shares scoped to department ${deptName}.`));
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
console.log(chalk.bold(`Shares for ${deptName} (${deptShares.length})\n`));
|
|
237
|
+
for (const s of deptShares) {
|
|
238
|
+
console.log(` ${chalk.cyan(s.path || s.name)}`);
|
|
239
|
+
console.log(` Visibility: ${chalk.dim(s.visibility || 'private')} Created: ${chalk.dim(s.createdAt || 'unknown')}`);
|
|
240
|
+
}
|
|
241
|
+
} catch (err) {
|
|
242
|
+
console.log(chalk.red(`Shares failed: ${err.message}`));
|
|
243
|
+
process.exitCode = 1;
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
cmd
|
|
248
|
+
.command('policy <orgId> <deptName>')
|
|
249
|
+
.description('show/set department policies')
|
|
250
|
+
.option('--set <key>', 'set a policy value (followed by value as argument)')
|
|
251
|
+
.action(async (orgId, deptName, opts, command) => {
|
|
252
|
+
try {
|
|
253
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
254
|
+
const key = `departments:${orgId}`;
|
|
255
|
+
const departments = extConfig.get(key) || [];
|
|
256
|
+
const dept = departments.find(d => d.name === deptName);
|
|
257
|
+
if (!dept) {
|
|
258
|
+
console.log(chalk.red(`Department not found: ${deptName}`));
|
|
259
|
+
process.exitCode = 1;
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (opts.set) {
|
|
264
|
+
const value = command.args[0];
|
|
265
|
+
if (!value) {
|
|
266
|
+
console.log(chalk.red('Usage: pal dept policy <orgId> <deptName> --set <key> <value>'));
|
|
267
|
+
process.exitCode = 1;
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
if (!dept.policies) dept.policies = {};
|
|
271
|
+
dept.policies[opts.set] = value;
|
|
272
|
+
extConfig.set(key, departments);
|
|
273
|
+
console.log(chalk.green(`✔ Policy set: ${opts.set} = ${value}`));
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Show policies
|
|
278
|
+
const policies = dept.policies || {};
|
|
279
|
+
const policyKeys = Object.keys(policies);
|
|
280
|
+
if (policyKeys.length === 0) {
|
|
281
|
+
console.log(chalk.dim(`No policies set for ${deptName}.`));
|
|
282
|
+
console.log(chalk.dim(` pal dept policy ${orgId} ${deptName} --set <key> <value>`));
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
console.log(chalk.bold(`Policies for ${deptName}\n`));
|
|
286
|
+
for (const k of policyKeys) {
|
|
287
|
+
console.log(` ${chalk.white(k)}: ${chalk.cyan(policies[k])}`);
|
|
288
|
+
}
|
|
289
|
+
} catch (err) {
|
|
290
|
+
console.log(chalk.red(`Policy failed: ${err.message}`));
|
|
291
|
+
process.exitCode = 1;
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
}
|