pal-explorer-cli 0.4.12 → 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/chat.js
CHANGED
|
@@ -1,217 +1,217 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import config from '../utils/config.js';
|
|
3
|
-
import { getIdentity } from '../core/identity.js';
|
|
4
|
-
|
|
5
|
-
import { getPrimaryServer } from '../core/discoveryClient.js';
|
|
6
|
-
const DISCOVERY_URL = process.env.PAL_DISCOVERY_URL || config.get('discoveryUrl') || getPrimaryServer();
|
|
7
|
-
|
|
8
|
-
export default function chatCommand(program) {
|
|
9
|
-
const cmd = program
|
|
10
|
-
.command('chat')
|
|
11
|
-
.description('send and receive encrypted chat messages')
|
|
12
|
-
.addHelpText('after', `
|
|
13
|
-
Examples:
|
|
14
|
-
$
|
|
15
|
-
$
|
|
16
|
-
$
|
|
17
|
-
`)
|
|
18
|
-
.action(() => {
|
|
19
|
-
const chatStore = config.get('chatHistory') || {};
|
|
20
|
-
const keys = Object.keys(chatStore);
|
|
21
|
-
if (keys.length === 0) {
|
|
22
|
-
console.log(chalk.gray('No conversations yet. Use `
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
console.log('');
|
|
26
|
-
console.log(chalk.cyan('Conversations:'));
|
|
27
|
-
for (const key of keys) {
|
|
28
|
-
const msgs = chatStore[key] || [];
|
|
29
|
-
const last = msgs[msgs.length - 1];
|
|
30
|
-
const preview = last ? last.text.slice(0, 60) : '';
|
|
31
|
-
const time = last ? new Date(last.timestamp).toLocaleString() : '';
|
|
32
|
-
console.log(` ${chalk.white(key)} ${chalk.gray(time)}`);
|
|
33
|
-
if (preview) console.log(` ${chalk.gray(preview)}`);
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
cmd
|
|
38
|
-
.command('send <handle> <message>')
|
|
39
|
-
.description('send a chat message to a pal')
|
|
40
|
-
.action(async (handle, message) => {
|
|
41
|
-
if (!message || !message.trim()) {
|
|
42
|
-
console.log(chalk.red('Error: message cannot be empty.'));
|
|
43
|
-
process.exitCode = 1;
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
try {
|
|
47
|
-
const identity = await getIdentity();
|
|
48
|
-
if (!identity?.handle) {
|
|
49
|
-
console.log(chalk.red('You must register a handle first:
|
|
50
|
-
process.exitCode = 1;
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const timestamp = Date.now();
|
|
55
|
-
|
|
56
|
-
// Save locally first (always persisted regardless of delivery)
|
|
57
|
-
const chatStore = config.get('chatHistory') || {};
|
|
58
|
-
if (!chatStore[handle]) chatStore[handle] = [];
|
|
59
|
-
chatStore[handle].push({ id: timestamp, text: message, sent: true, timestamp, fromHandle: identity.handle, toHandle: handle });
|
|
60
|
-
config.set('chatHistory', chatStore);
|
|
61
|
-
|
|
62
|
-
// Try to deliver via discovery server
|
|
63
|
-
try {
|
|
64
|
-
const res = await fetch(`${DISCOVERY_URL}/messages`, {
|
|
65
|
-
method: 'POST',
|
|
66
|
-
headers: { 'Content-Type': 'application/json' },
|
|
67
|
-
body: JSON.stringify({
|
|
68
|
-
toHandle: handle,
|
|
69
|
-
fromHandle: identity.handle,
|
|
70
|
-
fromDeviceId: identity.deviceId || null,
|
|
71
|
-
payload: JSON.stringify({ type: 'chat', text: message, timestamp }),
|
|
72
|
-
}),
|
|
73
|
-
signal: AbortSignal.timeout(10000),
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
if (!res.ok) {
|
|
77
|
-
const err = await res.json().catch(() => ({}));
|
|
78
|
-
// Queue for later delivery
|
|
79
|
-
const outbox = config.get('chatOutbox') || [];
|
|
80
|
-
outbox.push({ toHandle: handle, fromHandle: identity.handle, text: message, timestamp });
|
|
81
|
-
config.set('chatOutbox', outbox);
|
|
82
|
-
console.log(chalk.yellow(`Message saved locally. Delivery failed: ${err.error || res.statusText}`));
|
|
83
|
-
console.log(chalk.gray(' Will retry delivery when `
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
} catch (deliveryErr) {
|
|
87
|
-
// Queue for later delivery
|
|
88
|
-
const outbox = config.get('chatOutbox') || [];
|
|
89
|
-
outbox.push({ toHandle: handle, fromHandle: identity.handle, text: message, timestamp });
|
|
90
|
-
config.set('chatOutbox', outbox);
|
|
91
|
-
console.log(chalk.yellow(`Message saved locally. Server unreachable: ${deliveryErr.message}`));
|
|
92
|
-
console.log(chalk.gray(' Will retry delivery when `
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
console.log(chalk.green(`✔ Message sent to ${handle}`));
|
|
97
|
-
} catch (err) {
|
|
98
|
-
console.log(chalk.red(`Error: ${err.message}`));
|
|
99
|
-
process.exitCode = 1;
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
cmd
|
|
104
|
-
.command('history <handle>')
|
|
105
|
-
.description('show chat history with a pal')
|
|
106
|
-
.option('-n, --limit <n>', 'Number of messages to show', '20')
|
|
107
|
-
.action((handle, opts) => {
|
|
108
|
-
const chatStore = config.get('chatHistory') || {};
|
|
109
|
-
const msgs = chatStore[handle] || [];
|
|
110
|
-
if (msgs.length === 0) {
|
|
111
|
-
console.log(chalk.gray(`No messages with ${handle}.`));
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
const limit = parseInt(opts.limit) || 20;
|
|
115
|
-
const shown = msgs.slice(-limit);
|
|
116
|
-
console.log('');
|
|
117
|
-
console.log(chalk.cyan(`Chat with ${handle} (${shown.length}/${msgs.length}):`));
|
|
118
|
-
for (const m of shown) {
|
|
119
|
-
const time = new Date(m.timestamp).toLocaleTimeString();
|
|
120
|
-
const who = m.sent ? chalk.blue('You') : chalk.green(m.fromHandle || handle);
|
|
121
|
-
console.log(` ${chalk.gray(time)} ${who}: ${m.text}`);
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
cmd
|
|
126
|
-
.command('fetch')
|
|
127
|
-
.description('fetch new chat messages from the server')
|
|
128
|
-
.action(async () => {
|
|
129
|
-
try {
|
|
130
|
-
const identity = await getIdentity();
|
|
131
|
-
if (!identity?.handle) {
|
|
132
|
-
console.log(chalk.red('You must register a handle first:
|
|
133
|
-
process.exitCode = 1;
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const res = await fetch(`${DISCOVERY_URL}/messages/${encodeURIComponent(identity.handle)}${identity.deviceId ? `?deviceId=${identity.deviceId}` : ''}`, {
|
|
138
|
-
signal: AbortSignal.timeout(10000),
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
if (!res.ok) {
|
|
142
|
-
console.log(chalk.red('Failed to fetch messages.'));
|
|
143
|
-
process.exitCode = 1;
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const msgs = await res.json();
|
|
148
|
-
const chatMsgs = (Array.isArray(msgs) ? msgs : []).filter(m => m?.payload?.type === 'chat');
|
|
149
|
-
|
|
150
|
-
if (chatMsgs.length === 0) {
|
|
151
|
-
console.log(chalk.gray('No new chat messages.'));
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const chatStore = config.get('chatHistory') || {};
|
|
156
|
-
let newCount = 0;
|
|
157
|
-
for (const msg of chatMsgs) {
|
|
158
|
-
const key = msg.fromHandle || msg.payload?.fromHandle || 'unknown';
|
|
159
|
-
if (!chatStore[key]) chatStore[key] = [];
|
|
160
|
-
const id = msg.id || msg.payload?.timestamp;
|
|
161
|
-
if (!chatStore[key].some(m => m.id === id)) {
|
|
162
|
-
chatStore[key].push({
|
|
163
|
-
id, text: msg.payload.text, sent: false,
|
|
164
|
-
timestamp: msg.payload.timestamp || msg.timestamp,
|
|
165
|
-
fromHandle: key, toHandle: identity.handle,
|
|
166
|
-
});
|
|
167
|
-
newCount++;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
config.set('chatHistory', chatStore);
|
|
171
|
-
console.log(chalk.green(`✔ ${newCount} new message${newCount !== 1 ? 's' : ''} fetched.`));
|
|
172
|
-
} catch (err) {
|
|
173
|
-
console.log(chalk.red(`Error: ${err.message}`));
|
|
174
|
-
process.exitCode = 1;
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
cmd
|
|
179
|
-
.command('flush')
|
|
180
|
-
.description('retry delivering queued offline messages')
|
|
181
|
-
.action(async () => {
|
|
182
|
-
const outbox = config.get('chatOutbox') || [];
|
|
183
|
-
if (outbox.length === 0) {
|
|
184
|
-
console.log(chalk.gray('No queued messages.'));
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
console.log(chalk.blue(`Flushing ${outbox.length} queued message(s)...`));
|
|
189
|
-
const remaining = [];
|
|
190
|
-
let sent = 0;
|
|
191
|
-
|
|
192
|
-
for (const msg of outbox) {
|
|
193
|
-
try {
|
|
194
|
-
const res = await fetch(`${DISCOVERY_URL}/messages`, {
|
|
195
|
-
method: 'POST',
|
|
196
|
-
headers: { 'Content-Type': 'application/json' },
|
|
197
|
-
body: JSON.stringify({
|
|
198
|
-
toHandle: msg.toHandle,
|
|
199
|
-
fromHandle: msg.fromHandle,
|
|
200
|
-
payload: JSON.stringify({ type: 'chat', text: msg.text, timestamp: msg.timestamp }),
|
|
201
|
-
}),
|
|
202
|
-
signal: AbortSignal.timeout(10000),
|
|
203
|
-
});
|
|
204
|
-
if (res.ok) {
|
|
205
|
-
sent++;
|
|
206
|
-
} else {
|
|
207
|
-
remaining.push(msg);
|
|
208
|
-
}
|
|
209
|
-
} catch {
|
|
210
|
-
remaining.push(msg);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
config.set('chatOutbox', remaining);
|
|
215
|
-
console.log(chalk.green(`✔ ${sent} delivered, ${remaining.length} still queued.`));
|
|
216
|
-
});
|
|
217
|
-
}
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import config from '../utils/config.js';
|
|
3
|
+
import { getIdentity } from '../core/identity.js';
|
|
4
|
+
|
|
5
|
+
import { getPrimaryServer } from '../core/discoveryClient.js';
|
|
6
|
+
const DISCOVERY_URL = process.env.PAL_DISCOVERY_URL || config.get('discoveryUrl') || getPrimaryServer();
|
|
7
|
+
|
|
8
|
+
export default function chatCommand(program) {
|
|
9
|
+
const cmd = program
|
|
10
|
+
.command('chat')
|
|
11
|
+
.description('send and receive encrypted chat messages')
|
|
12
|
+
.addHelpText('after', `
|
|
13
|
+
Examples:
|
|
14
|
+
$ pal chat List recent conversations
|
|
15
|
+
$ pal chat send <handle> "hello" Send a message
|
|
16
|
+
$ pal chat history <handle> Show chat history with a pal
|
|
17
|
+
`)
|
|
18
|
+
.action(() => {
|
|
19
|
+
const chatStore = config.get('chatHistory') || {};
|
|
20
|
+
const keys = Object.keys(chatStore);
|
|
21
|
+
if (keys.length === 0) {
|
|
22
|
+
console.log(chalk.gray('No conversations yet. Use `pal chat send <handle> "message"` to start one.'));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
console.log('');
|
|
26
|
+
console.log(chalk.cyan('Conversations:'));
|
|
27
|
+
for (const key of keys) {
|
|
28
|
+
const msgs = chatStore[key] || [];
|
|
29
|
+
const last = msgs[msgs.length - 1];
|
|
30
|
+
const preview = last ? last.text.slice(0, 60) : '';
|
|
31
|
+
const time = last ? new Date(last.timestamp).toLocaleString() : '';
|
|
32
|
+
console.log(` ${chalk.white(key)} ${chalk.gray(time)}`);
|
|
33
|
+
if (preview) console.log(` ${chalk.gray(preview)}`);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
cmd
|
|
38
|
+
.command('send <handle> <message>')
|
|
39
|
+
.description('send a chat message to a pal')
|
|
40
|
+
.action(async (handle, message) => {
|
|
41
|
+
if (!message || !message.trim()) {
|
|
42
|
+
console.log(chalk.red('Error: message cannot be empty.'));
|
|
43
|
+
process.exitCode = 1;
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const identity = await getIdentity();
|
|
48
|
+
if (!identity?.handle) {
|
|
49
|
+
console.log(chalk.red('You must register a handle first: pal register'));
|
|
50
|
+
process.exitCode = 1;
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const timestamp = Date.now();
|
|
55
|
+
|
|
56
|
+
// Save locally first (always persisted regardless of delivery)
|
|
57
|
+
const chatStore = config.get('chatHistory') || {};
|
|
58
|
+
if (!chatStore[handle]) chatStore[handle] = [];
|
|
59
|
+
chatStore[handle].push({ id: timestamp, text: message, sent: true, timestamp, fromHandle: identity.handle, toHandle: handle });
|
|
60
|
+
config.set('chatHistory', chatStore);
|
|
61
|
+
|
|
62
|
+
// Try to deliver via discovery server
|
|
63
|
+
try {
|
|
64
|
+
const res = await fetch(`${DISCOVERY_URL}/messages`, {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
headers: { 'Content-Type': 'application/json' },
|
|
67
|
+
body: JSON.stringify({
|
|
68
|
+
toHandle: handle,
|
|
69
|
+
fromHandle: identity.handle,
|
|
70
|
+
fromDeviceId: identity.deviceId || null,
|
|
71
|
+
payload: JSON.stringify({ type: 'chat', text: message, timestamp }),
|
|
72
|
+
}),
|
|
73
|
+
signal: AbortSignal.timeout(10000),
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
if (!res.ok) {
|
|
77
|
+
const err = await res.json().catch(() => ({}));
|
|
78
|
+
// Queue for later delivery
|
|
79
|
+
const outbox = config.get('chatOutbox') || [];
|
|
80
|
+
outbox.push({ toHandle: handle, fromHandle: identity.handle, text: message, timestamp });
|
|
81
|
+
config.set('chatOutbox', outbox);
|
|
82
|
+
console.log(chalk.yellow(`Message saved locally. Delivery failed: ${err.error || res.statusText}`));
|
|
83
|
+
console.log(chalk.gray(' Will retry delivery when `pal serve` starts or `pal chat flush` is run.'));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
} catch (deliveryErr) {
|
|
87
|
+
// Queue for later delivery
|
|
88
|
+
const outbox = config.get('chatOutbox') || [];
|
|
89
|
+
outbox.push({ toHandle: handle, fromHandle: identity.handle, text: message, timestamp });
|
|
90
|
+
config.set('chatOutbox', outbox);
|
|
91
|
+
console.log(chalk.yellow(`Message saved locally. Server unreachable: ${deliveryErr.message}`));
|
|
92
|
+
console.log(chalk.gray(' Will retry delivery when `pal serve` starts or `pal chat flush` is run.'));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log(chalk.green(`✔ Message sent to ${handle}`));
|
|
97
|
+
} catch (err) {
|
|
98
|
+
console.log(chalk.red(`Error: ${err.message}`));
|
|
99
|
+
process.exitCode = 1;
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
cmd
|
|
104
|
+
.command('history <handle>')
|
|
105
|
+
.description('show chat history with a pal')
|
|
106
|
+
.option('-n, --limit <n>', 'Number of messages to show', '20')
|
|
107
|
+
.action((handle, opts) => {
|
|
108
|
+
const chatStore = config.get('chatHistory') || {};
|
|
109
|
+
const msgs = chatStore[handle] || [];
|
|
110
|
+
if (msgs.length === 0) {
|
|
111
|
+
console.log(chalk.gray(`No messages with ${handle}.`));
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const limit = parseInt(opts.limit) || 20;
|
|
115
|
+
const shown = msgs.slice(-limit);
|
|
116
|
+
console.log('');
|
|
117
|
+
console.log(chalk.cyan(`Chat with ${handle} (${shown.length}/${msgs.length}):`));
|
|
118
|
+
for (const m of shown) {
|
|
119
|
+
const time = new Date(m.timestamp).toLocaleTimeString();
|
|
120
|
+
const who = m.sent ? chalk.blue('You') : chalk.green(m.fromHandle || handle);
|
|
121
|
+
console.log(` ${chalk.gray(time)} ${who}: ${m.text}`);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
cmd
|
|
126
|
+
.command('fetch')
|
|
127
|
+
.description('fetch new chat messages from the server')
|
|
128
|
+
.action(async () => {
|
|
129
|
+
try {
|
|
130
|
+
const identity = await getIdentity();
|
|
131
|
+
if (!identity?.handle) {
|
|
132
|
+
console.log(chalk.red('You must register a handle first: pal register'));
|
|
133
|
+
process.exitCode = 1;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const res = await fetch(`${DISCOVERY_URL}/messages/${encodeURIComponent(identity.handle)}${identity.deviceId ? `?deviceId=${identity.deviceId}` : ''}`, {
|
|
138
|
+
signal: AbortSignal.timeout(10000),
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
if (!res.ok) {
|
|
142
|
+
console.log(chalk.red('Failed to fetch messages.'));
|
|
143
|
+
process.exitCode = 1;
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const msgs = await res.json();
|
|
148
|
+
const chatMsgs = (Array.isArray(msgs) ? msgs : []).filter(m => m?.payload?.type === 'chat');
|
|
149
|
+
|
|
150
|
+
if (chatMsgs.length === 0) {
|
|
151
|
+
console.log(chalk.gray('No new chat messages.'));
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const chatStore = config.get('chatHistory') || {};
|
|
156
|
+
let newCount = 0;
|
|
157
|
+
for (const msg of chatMsgs) {
|
|
158
|
+
const key = msg.fromHandle || msg.payload?.fromHandle || 'unknown';
|
|
159
|
+
if (!chatStore[key]) chatStore[key] = [];
|
|
160
|
+
const id = msg.id || msg.payload?.timestamp;
|
|
161
|
+
if (!chatStore[key].some(m => m.id === id)) {
|
|
162
|
+
chatStore[key].push({
|
|
163
|
+
id, text: msg.payload.text, sent: false,
|
|
164
|
+
timestamp: msg.payload.timestamp || msg.timestamp,
|
|
165
|
+
fromHandle: key, toHandle: identity.handle,
|
|
166
|
+
});
|
|
167
|
+
newCount++;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
config.set('chatHistory', chatStore);
|
|
171
|
+
console.log(chalk.green(`✔ ${newCount} new message${newCount !== 1 ? 's' : ''} fetched.`));
|
|
172
|
+
} catch (err) {
|
|
173
|
+
console.log(chalk.red(`Error: ${err.message}`));
|
|
174
|
+
process.exitCode = 1;
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
cmd
|
|
179
|
+
.command('flush')
|
|
180
|
+
.description('retry delivering queued offline messages')
|
|
181
|
+
.action(async () => {
|
|
182
|
+
const outbox = config.get('chatOutbox') || [];
|
|
183
|
+
if (outbox.length === 0) {
|
|
184
|
+
console.log(chalk.gray('No queued messages.'));
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
console.log(chalk.blue(`Flushing ${outbox.length} queued message(s)...`));
|
|
189
|
+
const remaining = [];
|
|
190
|
+
let sent = 0;
|
|
191
|
+
|
|
192
|
+
for (const msg of outbox) {
|
|
193
|
+
try {
|
|
194
|
+
const res = await fetch(`${DISCOVERY_URL}/messages`, {
|
|
195
|
+
method: 'POST',
|
|
196
|
+
headers: { 'Content-Type': 'application/json' },
|
|
197
|
+
body: JSON.stringify({
|
|
198
|
+
toHandle: msg.toHandle,
|
|
199
|
+
fromHandle: msg.fromHandle,
|
|
200
|
+
payload: JSON.stringify({ type: 'chat', text: msg.text, timestamp: msg.timestamp }),
|
|
201
|
+
}),
|
|
202
|
+
signal: AbortSignal.timeout(10000),
|
|
203
|
+
});
|
|
204
|
+
if (res.ok) {
|
|
205
|
+
sent++;
|
|
206
|
+
} else {
|
|
207
|
+
remaining.push(msg);
|
|
208
|
+
}
|
|
209
|
+
} catch {
|
|
210
|
+
remaining.push(msg);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
config.set('chatOutbox', remaining);
|
|
215
|
+
console.log(chalk.green(`✔ ${sent} delivered, ${remaining.length} still queued.`));
|
|
216
|
+
});
|
|
217
|
+
}
|