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
|
@@ -1,270 +1,270 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
|
|
3
|
-
export default function federationCommand(program) {
|
|
4
|
-
const cmd = program
|
|
5
|
-
.command('federation')
|
|
6
|
-
.description('manage federation between discovery servers (Enterprise)')
|
|
7
|
-
.addHelpText('after', `
|
|
8
|
-
Examples:
|
|
9
|
-
$
|
|
10
|
-
$
|
|
11
|
-
$
|
|
12
|
-
$
|
|
13
|
-
$
|
|
14
|
-
`)
|
|
15
|
-
.action(() => { cmd.outputHelp(); });
|
|
16
|
-
|
|
17
|
-
// --- Registry subcommands ---
|
|
18
|
-
const registry = cmd
|
|
19
|
-
.command('registry')
|
|
20
|
-
.description('manage federated discovery server registry')
|
|
21
|
-
.action(() => { registry.outputHelp(); });
|
|
22
|
-
|
|
23
|
-
registry
|
|
24
|
-
.command('add <url>')
|
|
25
|
-
.description('add a discovery server to federation registry')
|
|
26
|
-
.option('--trust <level>', 'trust level: trusted, verified, unknown', 'unknown')
|
|
27
|
-
.action(async (url, opts) => {
|
|
28
|
-
try {
|
|
29
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
30
|
-
const servers = extConfig.get('ext_store.federation-registry')?.registry || {};
|
|
31
|
-
const id = url.replace(/[^a-z0-9]/gi, '_');
|
|
32
|
-
servers[id] = {
|
|
33
|
-
url,
|
|
34
|
-
trust: opts.trust,
|
|
35
|
-
category: 'public',
|
|
36
|
-
trustScore: opts.trust === 'trusted' ? 100 : 0,
|
|
37
|
-
lastSeen: null,
|
|
38
|
-
healthy: null,
|
|
39
|
-
addedAt: new Date().toISOString(),
|
|
40
|
-
};
|
|
41
|
-
const store = extConfig.get('ext_store.federation-registry') || {};
|
|
42
|
-
store.registry = servers;
|
|
43
|
-
extConfig.set('ext_store.federation-registry', store);
|
|
44
|
-
console.log(chalk.green(`✔ Server added to federation registry: ${url}`));
|
|
45
|
-
console.log(` Trust: ${chalk.white(opts.trust)}`);
|
|
46
|
-
} catch (err) {
|
|
47
|
-
console.log(chalk.red(`Failed to add server: ${err.message}`));
|
|
48
|
-
process.exitCode = 1;
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
registry
|
|
53
|
-
.command('remove <url>')
|
|
54
|
-
.description('remove a server from federation registry')
|
|
55
|
-
.action(async (url) => {
|
|
56
|
-
try {
|
|
57
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
58
|
-
const store = extConfig.get('ext_store.federation-registry') || {};
|
|
59
|
-
const registry = store.registry || {};
|
|
60
|
-
const id = url.replace(/[^a-z0-9]/gi, '_');
|
|
61
|
-
if (!registry[id]) {
|
|
62
|
-
console.log(chalk.yellow(`Server not found in registry: ${url}`));
|
|
63
|
-
process.exitCode = 1;
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
delete registry[id];
|
|
67
|
-
store.registry = registry;
|
|
68
|
-
extConfig.set('ext_store.federation-registry', store);
|
|
69
|
-
console.log(chalk.green(`✔ Server removed from registry: ${url}`));
|
|
70
|
-
} catch (err) {
|
|
71
|
-
console.log(chalk.red(`Failed to remove server: ${err.message}`));
|
|
72
|
-
process.exitCode = 1;
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
registry
|
|
77
|
-
.command('list')
|
|
78
|
-
.description('list all servers in federation registry')
|
|
79
|
-
.action(async () => {
|
|
80
|
-
try {
|
|
81
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
82
|
-
const store = extConfig.get('ext_store.federation-registry') || {};
|
|
83
|
-
const registry = store.registry || {};
|
|
84
|
-
const entries = Object.values(registry);
|
|
85
|
-
if (entries.length === 0) {
|
|
86
|
-
console.log(chalk.dim('No servers in federation registry.'));
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
console.log(chalk.bold(`Federation Registry (${entries.length} servers)\n`));
|
|
90
|
-
for (const s of entries) {
|
|
91
|
-
const health = s.healthy === true ? chalk.green('●') : s.healthy === false ? chalk.red('●') : chalk.dim('○');
|
|
92
|
-
console.log(` ${health} ${chalk.cyan(s.url)}`);
|
|
93
|
-
console.log(` Trust: ${chalk.white(s.trust)} Score: ${s.trustScore} Added: ${s.addedAt || 'unknown'}`);
|
|
94
|
-
}
|
|
95
|
-
} catch (err) {
|
|
96
|
-
console.log(chalk.red(`Failed to list registry: ${err.message}`));
|
|
97
|
-
process.exitCode = 1;
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
registry
|
|
102
|
-
.command('peer <url>')
|
|
103
|
-
.description('initiate peering handshake with another server')
|
|
104
|
-
.action(async (url) => {
|
|
105
|
-
try {
|
|
106
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
107
|
-
const store = extConfig.get('ext_store.federation-registry') || {};
|
|
108
|
-
const registry = store.registry || {};
|
|
109
|
-
const id = url.replace(/[^a-z0-9]/gi, '_');
|
|
110
|
-
if (!registry[id]) {
|
|
111
|
-
console.log(chalk.yellow(`Server not in registry. Add it first with:
|
|
112
|
-
process.exitCode = 1;
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
registry[id].trust = 'verified';
|
|
116
|
-
registry[id].trustScore = Math.max(registry[id].trustScore || 0, 50);
|
|
117
|
-
registry[id].lastSeen = new Date().toISOString();
|
|
118
|
-
store.registry = registry;
|
|
119
|
-
extConfig.set('ext_store.federation-registry', store);
|
|
120
|
-
console.log(chalk.green(`✔ Peering initiated with: ${url}`));
|
|
121
|
-
console.log(` Trust level upgraded to: ${chalk.white('verified')}`);
|
|
122
|
-
} catch (err) {
|
|
123
|
-
console.log(chalk.red(`Peering failed: ${err.message}`));
|
|
124
|
-
process.exitCode = 1;
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
registry
|
|
129
|
-
.command('resolve <handle>')
|
|
130
|
-
.description('resolve a handle across federated servers (e.g. @user@server.com)')
|
|
131
|
-
.action(async (handle) => {
|
|
132
|
-
try {
|
|
133
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
134
|
-
const store = extConfig.get('ext_store.federation-registry') || {};
|
|
135
|
-
const registry = store.registry || {};
|
|
136
|
-
const entries = Object.values(registry).filter(s => s.healthy !== false);
|
|
137
|
-
|
|
138
|
-
if (entries.length === 0) {
|
|
139
|
-
console.log(chalk.yellow('No healthy servers in federation registry.'));
|
|
140
|
-
process.exitCode = 1;
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
let parts = handle.replace(/^@/, '').split('@');
|
|
145
|
-
let username = parts[0];
|
|
146
|
-
let server = parts[1] || null;
|
|
147
|
-
|
|
148
|
-
console.log(chalk.dim(`Resolving ${handle} across ${entries.length} server(s)...`));
|
|
149
|
-
|
|
150
|
-
if (server) {
|
|
151
|
-
const match = entries.find(s => s.url.includes(server));
|
|
152
|
-
if (match) {
|
|
153
|
-
console.log(chalk.green(`✔ Resolved via ${match.url}`));
|
|
154
|
-
console.log(` Handle: ${chalk.cyan('@' + username)}`);
|
|
155
|
-
console.log(` Server: ${chalk.white(match.url)}`);
|
|
156
|
-
} else {
|
|
157
|
-
console.log(chalk.yellow(`Server ${server} not in registry.`));
|
|
158
|
-
process.exitCode = 1;
|
|
159
|
-
}
|
|
160
|
-
} else {
|
|
161
|
-
console.log(chalk.dim(`Querying all ${entries.length} servers for @${username}...`));
|
|
162
|
-
for (const s of entries) {
|
|
163
|
-
console.log(` ${chalk.dim('→')} ${s.url}: ${chalk.dim('queried')}`);
|
|
164
|
-
}
|
|
165
|
-
console.log(chalk.green(`✔ Resolution complete.`));
|
|
166
|
-
}
|
|
167
|
-
} catch (err) {
|
|
168
|
-
console.log(chalk.red(`Resolution failed: ${err.message}`));
|
|
169
|
-
process.exitCode = 1;
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
// --- Bridge subcommands ---
|
|
174
|
-
const bridge = cmd
|
|
175
|
-
.command('bridge')
|
|
176
|
-
.description('manage federation bridge connections')
|
|
177
|
-
.action(() => { bridge.outputHelp(); });
|
|
178
|
-
|
|
179
|
-
bridge
|
|
180
|
-
.command('add <url>')
|
|
181
|
-
.description('connect to a federated network')
|
|
182
|
-
.option('--relay-policy <policy>', 'relay policy: none, all', 'none')
|
|
183
|
-
.action(async (url, opts) => {
|
|
184
|
-
try {
|
|
185
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
186
|
-
const store = extConfig.get('ext_store.federation-bridge') || {};
|
|
187
|
-
const networks = store.bridgedNetworks || [];
|
|
188
|
-
if (networks.includes(url)) {
|
|
189
|
-
console.log(chalk.yellow(`Already bridged to: ${url}`));
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
networks.push(url);
|
|
193
|
-
store.bridgedNetworks = networks;
|
|
194
|
-
store.relayPolicy = opts.relayPolicy;
|
|
195
|
-
extConfig.set('ext_store.federation-bridge', store);
|
|
196
|
-
console.log(chalk.green(`✔ Bridge connected to: ${url}`));
|
|
197
|
-
console.log(` Relay policy: ${chalk.white(opts.relayPolicy)}`);
|
|
198
|
-
} catch (err) {
|
|
199
|
-
console.log(chalk.red(`Bridge failed: ${err.message}`));
|
|
200
|
-
process.exitCode = 1;
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
bridge
|
|
205
|
-
.command('remove <url>')
|
|
206
|
-
.description('disconnect from a federated network')
|
|
207
|
-
.action(async (url) => {
|
|
208
|
-
try {
|
|
209
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
210
|
-
const store = extConfig.get('ext_store.federation-bridge') || {};
|
|
211
|
-
const networks = store.bridgedNetworks || [];
|
|
212
|
-
const idx = networks.indexOf(url);
|
|
213
|
-
if (idx === -1) {
|
|
214
|
-
console.log(chalk.yellow(`Not bridged to: ${url}`));
|
|
215
|
-
process.exitCode = 1;
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
networks.splice(idx, 1);
|
|
219
|
-
store.bridgedNetworks = networks;
|
|
220
|
-
extConfig.set('ext_store.federation-bridge', store);
|
|
221
|
-
console.log(chalk.green(`✔ Bridge disconnected from: ${url}`));
|
|
222
|
-
} catch (err) {
|
|
223
|
-
console.log(chalk.red(`Failed: ${err.message}`));
|
|
224
|
-
process.exitCode = 1;
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
bridge
|
|
229
|
-
.command('list')
|
|
230
|
-
.description('list all bridge connections')
|
|
231
|
-
.action(async () => {
|
|
232
|
-
try {
|
|
233
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
234
|
-
const store = extConfig.get('ext_store.federation-bridge') || {};
|
|
235
|
-
const networks = store.bridgedNetworks || [];
|
|
236
|
-
if (networks.length === 0) {
|
|
237
|
-
console.log(chalk.dim('No bridge connections.'));
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
console.log(chalk.bold(`Federation Bridges (${networks.length})\n`));
|
|
241
|
-
for (const url of networks) {
|
|
242
|
-
console.log(` ${chalk.green('●')} ${chalk.cyan(url)}`);
|
|
243
|
-
}
|
|
244
|
-
console.log(`\n Relay policy: ${chalk.white(store.relayPolicy || 'none')}`);
|
|
245
|
-
} catch (err) {
|
|
246
|
-
console.log(chalk.red(`Failed: ${err.message}`));
|
|
247
|
-
process.exitCode = 1;
|
|
248
|
-
}
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
bridge
|
|
252
|
-
.command('stats')
|
|
253
|
-
.description('show bridge relay statistics')
|
|
254
|
-
.action(async () => {
|
|
255
|
-
try {
|
|
256
|
-
const extConfig = (await import('../utils/config.js')).default;
|
|
257
|
-
const store = extConfig.get('ext_store.federation-bridge') || {};
|
|
258
|
-
const networks = store.bridgedNetworks || [];
|
|
259
|
-
const messageLog = store.messageLog || [];
|
|
260
|
-
console.log(chalk.bold('Bridge Statistics\n'));
|
|
261
|
-
console.log(` Connected networks: ${chalk.white(networks.length)}`);
|
|
262
|
-
console.log(` Messages relayed: ${chalk.white(messageLog.length)}`);
|
|
263
|
-
console.log(` Relay policy: ${chalk.white(store.relayPolicy || 'none')}`);
|
|
264
|
-
console.log(` Last activity: ${chalk.white(store.lastActivity || 'never')}`);
|
|
265
|
-
} catch (err) {
|
|
266
|
-
console.log(chalk.red(`Failed: ${err.message}`));
|
|
267
|
-
process.exitCode = 1;
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
|
-
}
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
export default function federationCommand(program) {
|
|
4
|
+
const cmd = program
|
|
5
|
+
.command('federation')
|
|
6
|
+
.description('manage federation between discovery servers (Enterprise)')
|
|
7
|
+
.addHelpText('after', `
|
|
8
|
+
Examples:
|
|
9
|
+
$ pal federation registry add https://discovery2.example.com
|
|
10
|
+
$ pal federation registry list
|
|
11
|
+
$ pal federation registry resolve @user@server.com
|
|
12
|
+
$ pal federation bridge add https://network2.example.com
|
|
13
|
+
$ pal federation bridge stats
|
|
14
|
+
`)
|
|
15
|
+
.action(() => { cmd.outputHelp(); });
|
|
16
|
+
|
|
17
|
+
// --- Registry subcommands ---
|
|
18
|
+
const registry = cmd
|
|
19
|
+
.command('registry')
|
|
20
|
+
.description('manage federated discovery server registry')
|
|
21
|
+
.action(() => { registry.outputHelp(); });
|
|
22
|
+
|
|
23
|
+
registry
|
|
24
|
+
.command('add <url>')
|
|
25
|
+
.description('add a discovery server to federation registry')
|
|
26
|
+
.option('--trust <level>', 'trust level: trusted, verified, unknown', 'unknown')
|
|
27
|
+
.action(async (url, opts) => {
|
|
28
|
+
try {
|
|
29
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
30
|
+
const servers = extConfig.get('ext_store.federation-registry')?.registry || {};
|
|
31
|
+
const id = url.replace(/[^a-z0-9]/gi, '_');
|
|
32
|
+
servers[id] = {
|
|
33
|
+
url,
|
|
34
|
+
trust: opts.trust,
|
|
35
|
+
category: 'public',
|
|
36
|
+
trustScore: opts.trust === 'trusted' ? 100 : 0,
|
|
37
|
+
lastSeen: null,
|
|
38
|
+
healthy: null,
|
|
39
|
+
addedAt: new Date().toISOString(),
|
|
40
|
+
};
|
|
41
|
+
const store = extConfig.get('ext_store.federation-registry') || {};
|
|
42
|
+
store.registry = servers;
|
|
43
|
+
extConfig.set('ext_store.federation-registry', store);
|
|
44
|
+
console.log(chalk.green(`✔ Server added to federation registry: ${url}`));
|
|
45
|
+
console.log(` Trust: ${chalk.white(opts.trust)}`);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
console.log(chalk.red(`Failed to add server: ${err.message}`));
|
|
48
|
+
process.exitCode = 1;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
registry
|
|
53
|
+
.command('remove <url>')
|
|
54
|
+
.description('remove a server from federation registry')
|
|
55
|
+
.action(async (url) => {
|
|
56
|
+
try {
|
|
57
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
58
|
+
const store = extConfig.get('ext_store.federation-registry') || {};
|
|
59
|
+
const registry = store.registry || {};
|
|
60
|
+
const id = url.replace(/[^a-z0-9]/gi, '_');
|
|
61
|
+
if (!registry[id]) {
|
|
62
|
+
console.log(chalk.yellow(`Server not found in registry: ${url}`));
|
|
63
|
+
process.exitCode = 1;
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
delete registry[id];
|
|
67
|
+
store.registry = registry;
|
|
68
|
+
extConfig.set('ext_store.federation-registry', store);
|
|
69
|
+
console.log(chalk.green(`✔ Server removed from registry: ${url}`));
|
|
70
|
+
} catch (err) {
|
|
71
|
+
console.log(chalk.red(`Failed to remove server: ${err.message}`));
|
|
72
|
+
process.exitCode = 1;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
registry
|
|
77
|
+
.command('list')
|
|
78
|
+
.description('list all servers in federation registry')
|
|
79
|
+
.action(async () => {
|
|
80
|
+
try {
|
|
81
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
82
|
+
const store = extConfig.get('ext_store.federation-registry') || {};
|
|
83
|
+
const registry = store.registry || {};
|
|
84
|
+
const entries = Object.values(registry);
|
|
85
|
+
if (entries.length === 0) {
|
|
86
|
+
console.log(chalk.dim('No servers in federation registry.'));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
console.log(chalk.bold(`Federation Registry (${entries.length} servers)\n`));
|
|
90
|
+
for (const s of entries) {
|
|
91
|
+
const health = s.healthy === true ? chalk.green('●') : s.healthy === false ? chalk.red('●') : chalk.dim('○');
|
|
92
|
+
console.log(` ${health} ${chalk.cyan(s.url)}`);
|
|
93
|
+
console.log(` Trust: ${chalk.white(s.trust)} Score: ${s.trustScore} Added: ${s.addedAt || 'unknown'}`);
|
|
94
|
+
}
|
|
95
|
+
} catch (err) {
|
|
96
|
+
console.log(chalk.red(`Failed to list registry: ${err.message}`));
|
|
97
|
+
process.exitCode = 1;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
registry
|
|
102
|
+
.command('peer <url>')
|
|
103
|
+
.description('initiate peering handshake with another server')
|
|
104
|
+
.action(async (url) => {
|
|
105
|
+
try {
|
|
106
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
107
|
+
const store = extConfig.get('ext_store.federation-registry') || {};
|
|
108
|
+
const registry = store.registry || {};
|
|
109
|
+
const id = url.replace(/[^a-z0-9]/gi, '_');
|
|
110
|
+
if (!registry[id]) {
|
|
111
|
+
console.log(chalk.yellow(`Server not in registry. Add it first with: pal federation registry add ${url}`));
|
|
112
|
+
process.exitCode = 1;
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
registry[id].trust = 'verified';
|
|
116
|
+
registry[id].trustScore = Math.max(registry[id].trustScore || 0, 50);
|
|
117
|
+
registry[id].lastSeen = new Date().toISOString();
|
|
118
|
+
store.registry = registry;
|
|
119
|
+
extConfig.set('ext_store.federation-registry', store);
|
|
120
|
+
console.log(chalk.green(`✔ Peering initiated with: ${url}`));
|
|
121
|
+
console.log(` Trust level upgraded to: ${chalk.white('verified')}`);
|
|
122
|
+
} catch (err) {
|
|
123
|
+
console.log(chalk.red(`Peering failed: ${err.message}`));
|
|
124
|
+
process.exitCode = 1;
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
registry
|
|
129
|
+
.command('resolve <handle>')
|
|
130
|
+
.description('resolve a handle across federated servers (e.g. @user@server.com)')
|
|
131
|
+
.action(async (handle) => {
|
|
132
|
+
try {
|
|
133
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
134
|
+
const store = extConfig.get('ext_store.federation-registry') || {};
|
|
135
|
+
const registry = store.registry || {};
|
|
136
|
+
const entries = Object.values(registry).filter(s => s.healthy !== false);
|
|
137
|
+
|
|
138
|
+
if (entries.length === 0) {
|
|
139
|
+
console.log(chalk.yellow('No healthy servers in federation registry.'));
|
|
140
|
+
process.exitCode = 1;
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
let parts = handle.replace(/^@/, '').split('@');
|
|
145
|
+
let username = parts[0];
|
|
146
|
+
let server = parts[1] || null;
|
|
147
|
+
|
|
148
|
+
console.log(chalk.dim(`Resolving ${handle} across ${entries.length} server(s)...`));
|
|
149
|
+
|
|
150
|
+
if (server) {
|
|
151
|
+
const match = entries.find(s => s.url.includes(server));
|
|
152
|
+
if (match) {
|
|
153
|
+
console.log(chalk.green(`✔ Resolved via ${match.url}`));
|
|
154
|
+
console.log(` Handle: ${chalk.cyan('@' + username)}`);
|
|
155
|
+
console.log(` Server: ${chalk.white(match.url)}`);
|
|
156
|
+
} else {
|
|
157
|
+
console.log(chalk.yellow(`Server ${server} not in registry.`));
|
|
158
|
+
process.exitCode = 1;
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
console.log(chalk.dim(`Querying all ${entries.length} servers for @${username}...`));
|
|
162
|
+
for (const s of entries) {
|
|
163
|
+
console.log(` ${chalk.dim('→')} ${s.url}: ${chalk.dim('queried')}`);
|
|
164
|
+
}
|
|
165
|
+
console.log(chalk.green(`✔ Resolution complete.`));
|
|
166
|
+
}
|
|
167
|
+
} catch (err) {
|
|
168
|
+
console.log(chalk.red(`Resolution failed: ${err.message}`));
|
|
169
|
+
process.exitCode = 1;
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// --- Bridge subcommands ---
|
|
174
|
+
const bridge = cmd
|
|
175
|
+
.command('bridge')
|
|
176
|
+
.description('manage federation bridge connections')
|
|
177
|
+
.action(() => { bridge.outputHelp(); });
|
|
178
|
+
|
|
179
|
+
bridge
|
|
180
|
+
.command('add <url>')
|
|
181
|
+
.description('connect to a federated network')
|
|
182
|
+
.option('--relay-policy <policy>', 'relay policy: none, all', 'none')
|
|
183
|
+
.action(async (url, opts) => {
|
|
184
|
+
try {
|
|
185
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
186
|
+
const store = extConfig.get('ext_store.federation-bridge') || {};
|
|
187
|
+
const networks = store.bridgedNetworks || [];
|
|
188
|
+
if (networks.includes(url)) {
|
|
189
|
+
console.log(chalk.yellow(`Already bridged to: ${url}`));
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
networks.push(url);
|
|
193
|
+
store.bridgedNetworks = networks;
|
|
194
|
+
store.relayPolicy = opts.relayPolicy;
|
|
195
|
+
extConfig.set('ext_store.federation-bridge', store);
|
|
196
|
+
console.log(chalk.green(`✔ Bridge connected to: ${url}`));
|
|
197
|
+
console.log(` Relay policy: ${chalk.white(opts.relayPolicy)}`);
|
|
198
|
+
} catch (err) {
|
|
199
|
+
console.log(chalk.red(`Bridge failed: ${err.message}`));
|
|
200
|
+
process.exitCode = 1;
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
bridge
|
|
205
|
+
.command('remove <url>')
|
|
206
|
+
.description('disconnect from a federated network')
|
|
207
|
+
.action(async (url) => {
|
|
208
|
+
try {
|
|
209
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
210
|
+
const store = extConfig.get('ext_store.federation-bridge') || {};
|
|
211
|
+
const networks = store.bridgedNetworks || [];
|
|
212
|
+
const idx = networks.indexOf(url);
|
|
213
|
+
if (idx === -1) {
|
|
214
|
+
console.log(chalk.yellow(`Not bridged to: ${url}`));
|
|
215
|
+
process.exitCode = 1;
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
networks.splice(idx, 1);
|
|
219
|
+
store.bridgedNetworks = networks;
|
|
220
|
+
extConfig.set('ext_store.federation-bridge', store);
|
|
221
|
+
console.log(chalk.green(`✔ Bridge disconnected from: ${url}`));
|
|
222
|
+
} catch (err) {
|
|
223
|
+
console.log(chalk.red(`Failed: ${err.message}`));
|
|
224
|
+
process.exitCode = 1;
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
bridge
|
|
229
|
+
.command('list')
|
|
230
|
+
.description('list all bridge connections')
|
|
231
|
+
.action(async () => {
|
|
232
|
+
try {
|
|
233
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
234
|
+
const store = extConfig.get('ext_store.federation-bridge') || {};
|
|
235
|
+
const networks = store.bridgedNetworks || [];
|
|
236
|
+
if (networks.length === 0) {
|
|
237
|
+
console.log(chalk.dim('No bridge connections.'));
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
console.log(chalk.bold(`Federation Bridges (${networks.length})\n`));
|
|
241
|
+
for (const url of networks) {
|
|
242
|
+
console.log(` ${chalk.green('●')} ${chalk.cyan(url)}`);
|
|
243
|
+
}
|
|
244
|
+
console.log(`\n Relay policy: ${chalk.white(store.relayPolicy || 'none')}`);
|
|
245
|
+
} catch (err) {
|
|
246
|
+
console.log(chalk.red(`Failed: ${err.message}`));
|
|
247
|
+
process.exitCode = 1;
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
bridge
|
|
252
|
+
.command('stats')
|
|
253
|
+
.description('show bridge relay statistics')
|
|
254
|
+
.action(async () => {
|
|
255
|
+
try {
|
|
256
|
+
const extConfig = (await import('../utils/config.js')).default;
|
|
257
|
+
const store = extConfig.get('ext_store.federation-bridge') || {};
|
|
258
|
+
const networks = store.bridgedNetworks || [];
|
|
259
|
+
const messageLog = store.messageLog || [];
|
|
260
|
+
console.log(chalk.bold('Bridge Statistics\n'));
|
|
261
|
+
console.log(` Connected networks: ${chalk.white(networks.length)}`);
|
|
262
|
+
console.log(` Messages relayed: ${chalk.white(messageLog.length)}`);
|
|
263
|
+
console.log(` Relay policy: ${chalk.white(store.relayPolicy || 'none')}`);
|
|
264
|
+
console.log(` Last activity: ${chalk.white(store.lastActivity || 'never')}`);
|
|
265
|
+
} catch (err) {
|
|
266
|
+
console.log(chalk.red(`Failed: ${err.message}`));
|
|
267
|
+
process.exitCode = 1;
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}
|