wldx-cli 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +15 -20
  2. package/package.json +9 -6
  3. package/src/chat/format.mjs +40 -8
  4. package/src/chat/lib.mjs +71 -23
  5. package/src/chat/watch.mjs +46 -15
  6. package/src/cli.mjs +13 -252
  7. package/src/commands/calls.mjs +135 -32
  8. package/src/commands/channel.mjs +99 -21
  9. package/src/commands/chat.mjs +77 -22
  10. package/src/commands/conversations.mjs +146 -29
  11. package/src/commands/file.mjs +13 -5
  12. package/src/commands/inbox.mjs +20 -13
  13. package/src/commands/index.mjs +5 -29
  14. package/src/commands/live.mjs +82 -24
  15. package/src/commands/meetings.mjs +114 -27
  16. package/src/commands/phone.mjs +145 -33
  17. package/src/commands/reports.mjs +219 -44
  18. package/src/commands/section.mjs +49 -8
  19. package/src/commands/user.mjs +42 -13
  20. package/src/history/text.mjs +30 -4
  21. package/src/history/wda.mjs +20 -10
  22. package/src/live/socket.mjs +25 -7
  23. package/src/live/stats.mjs +41 -9
  24. package/src/live/text.mjs +122 -23
  25. package/src/live/views.mjs +93 -23
  26. package/src/phone/control.mjs +93 -33
  27. package/src/phone/events.mjs +38 -7
  28. package/src/phone/pbx.mjs +29 -11
  29. package/src/phone/sip.mjs +87 -17
  30. package/src/phone/xml.mjs +2 -1
  31. package/src/stream.mjs +10 -5
  32. package/src/version.mjs +0 -1
  33. package/src/auth.mjs +0 -274
  34. package/src/commands/login.mjs +0 -126
  35. package/src/commands/profile.mjs +0 -51
  36. package/src/commands/status.mjs +0 -95
  37. package/src/completion.mjs +0 -132
  38. package/src/errors.mjs +0 -30
  39. package/src/files.mjs +0 -98
  40. package/src/format.mjs +0 -82
  41. package/src/http.mjs +0 -64
  42. package/src/services.mjs +0 -58
@@ -1,16 +1,42 @@
1
1
  // wx channel: channels of the Wildix stream service and their members, as the logged-in user (the agent skill's
2
2
  // channels.sh, members.sh and search.sh channels).
3
- import {UsageError} from '../errors.mjs';
4
- import {localTime, paint, table} from '../format.mjs';
5
- import {apiPath, query} from '../http.mjs';
3
+ import {UsageError} from 'wldx-core/errors';
4
+ import {localTime, paint, table} from 'wldx-core/format';
5
+ import {apiPath, query} from 'wldx-core/http';
6
6
  import {channelId} from '../stream.mjs';
7
7
  import {channelDetails, channelLines, channelUserIds, done, nextPage} from '../chat/format.mjs';
8
- import {CHANNEL_ID, NOT_IMPLEMENTED, api, checkId, checkInt, nameIn, newChannelId, notImplemented, withNames} from '../chat/lib.mjs';
8
+ import {
9
+ CHANNEL_ID,
10
+ NOT_IMPLEMENTED,
11
+ api,
12
+ checkId,
13
+ checkInt,
14
+ nameIn,
15
+ newChannelId,
16
+ notImplemented,
17
+ withNames,
18
+ } from '../chat/lib.mjs';
9
19
 
10
20
  const ACCESS = ['private', 'public'];
11
21
  const UNSET = [
12
- 'subject', 'description', 'picture', 'pictureColor', 'access', 'broadcast', 'context', 'contextPtt', 'contextEvents', 'whatsappStatus',
13
- 'messagingStatus', 'kiteAssign', 'service', 'serviceTitle', 'assignee', 'sms', 'mms', 'whatsapp',
22
+ 'subject',
23
+ 'description',
24
+ 'picture',
25
+ 'pictureColor',
26
+ 'access',
27
+ 'broadcast',
28
+ 'context',
29
+ 'contextPtt',
30
+ 'contextEvents',
31
+ 'whatsappStatus',
32
+ 'messagingStatus',
33
+ 'kiteAssign',
34
+ 'service',
35
+ 'serviceTitle',
36
+ 'assignee',
37
+ 'sms',
38
+ 'mms',
39
+ 'whatsapp',
14
40
  ];
15
41
  const SORT = ['relevance', 'recency', 'recency_asc'];
16
42
  const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
@@ -20,7 +46,8 @@ function email(value, what = '--invite') {
20
46
  return value;
21
47
  }
22
48
 
23
- const channelResult = async (ctx, client, r) => withNames(ctx, r, [...channelUserIds(r?.channel), r?.channel?.createdBy], client);
49
+ const channelResult = async (ctx, client, r) =>
50
+ withNames(ctx, r, [...channelUserIds(r?.channel), r?.channel?.createdBy], client);
24
51
  const channelText = (r, ctx) => (r?.channel ? channelDetails(r.channel, r, ctx) : 'No channel.');
25
52
 
26
53
  const get = {
@@ -52,7 +79,8 @@ const queryCmd = {
52
79
 
53
80
  const search = {
54
81
  summary: 'search your channels by subject or member',
55
- usage: 'wx channel search <query> [--type direct|group] [--member USERID] [--size 1-50] [--cursor C] [--sort relevance|recency|recency_asc]',
82
+ usage:
83
+ 'wx channel search <query> [--type direct|group] [--member USERID] [--size 1-50] [--cursor C] [--sort relevance|recency|recency_asc]',
56
84
  options: {
57
85
  type: {type: 'string', choices: ['direct', 'group'], description: 'only 1:1 or group channels'},
58
86
  member: {type: 'string', placeholder: 'USERID', description: 'only channels with this member'},
@@ -73,7 +101,12 @@ const search = {
73
101
  });
74
102
  const client = api(ctx);
75
103
  const r = await client.call('GET', `/search/channels${q}`);
76
- return withNames(ctx, r, (r?.results ?? []).flatMap((x) => channelUserIds(x.channel)), client);
104
+ return withNames(
105
+ ctx,
106
+ r,
107
+ (r?.results ?? []).flatMap((x) => channelUserIds(x.channel)),
108
+ client,
109
+ );
77
110
  },
78
111
  text(r, ctx) {
79
112
  const list = (r?.results ?? []).map((x) => x.channel).filter(Boolean);
@@ -84,7 +117,8 @@ const search = {
84
117
 
85
118
  const create = {
86
119
  summary: 'create a group channel',
87
- usage: 'wx channel create --subject S [--description D] [--access private|public] [--member USERID]... [--invite EMAIL]... [--id 8-4-4-4-8] [--auto-transcribe | --no-auto-transcribe]',
120
+ usage:
121
+ 'wx channel create --subject S [--description D] [--access private|public] [--member USERID]... [--invite EMAIL]... [--id 8-4-4-4-8] [--auto-transcribe | --no-auto-transcribe]',
88
122
  details: [
89
123
  'An id of 8-4-4-4-8 characters is always sent (one is made when --id is not given): without it the service makes',
90
124
  'a 36-character id that none of its own calls accepts. --auto-transcribe makes a conference in the channel start',
@@ -104,7 +138,8 @@ const create = {
104
138
  async run(ctx) {
105
139
  const {values} = ctx;
106
140
  if (!values.subject) throw new UsageError('--subject is required', ctx.usage());
107
- if (values['auto-transcribe'] && values['no-auto-transcribe']) throw new UsageError('Give --auto-transcribe or --no-auto-transcribe, not both', ctx.usage());
141
+ if (values['auto-transcribe'] && values['no-auto-transcribe'])
142
+ throw new UsageError('Give --auto-transcribe or --no-auto-transcribe, not both', ctx.usage());
108
143
  const id = values.id ?? newChannelId();
109
144
  if (!CHANNEL_ID.test(id)) throw new UsageError(`--id must look like 8-4-4-4-8 characters of [0-9a-z_]: ${id}`);
110
145
  const body = {channelId: id, subject: values.subject};
@@ -138,7 +173,11 @@ const direct = {
138
173
  args: [0, 0],
139
174
  async run(ctx) {
140
175
  const client = api(ctx);
141
- return channelResult(ctx, client, await client.call('POST', '/channels/direct/get-or-create', directBody(ctx.values, ctx.usage())));
176
+ return channelResult(
177
+ ctx,
178
+ client,
179
+ await client.call('POST', '/channels/direct/get-or-create', directBody(ctx.values, ctx.usage())),
180
+ );
142
181
  },
143
182
  text: channelText,
144
183
  };
@@ -171,7 +210,8 @@ const update = {
171
210
  const set = {};
172
211
  for (const k of ['subject', 'description', 'access']) if (values[k] !== undefined) set[k] = values[k];
173
212
  const unset = values.unset ?? [];
174
- if (!Object.keys(set).length && !unset.length) throw new UsageError('Nothing to change: give --subject, --description, --access or --unset', ctx.usage());
213
+ if (!Object.keys(set).length && !unset.length)
214
+ throw new UsageError('Nothing to change: give --subject, --description, --access or --unset', ctx.usage());
175
215
  const body = {set};
176
216
  if (unset.length) body.unset = unset;
177
217
  return api(ctx).call('PATCH', apiPath`/channels/${ch}`, body);
@@ -200,7 +240,11 @@ const del = {
200
240
  args: [1, 1],
201
241
  async run(ctx) {
202
242
  const ch = channelId(ctx.positionals[0]);
203
- if (!ctx.values.yes) throw new UsageError('delete removes the channel for everyone: confirm with the person, then add --yes', ctx.usage());
243
+ if (!ctx.values.yes)
244
+ throw new UsageError(
245
+ 'delete removes the channel for everyone: confirm with the person, then add --yes',
246
+ ctx.usage(),
247
+ );
204
248
  return api(ctx).call('DELETE', apiPath`/channels/${ch}`);
205
249
  },
206
250
  text: done('Deleted'),
@@ -216,16 +260,26 @@ const members = {
216
260
  args: [1, 1],
217
261
  async run(ctx) {
218
262
  const ch = channelId(ctx.positionals[0]);
219
- const q = query({limit: checkInt('limit', ctx.values.limit, 1, 100), offset: checkInt('offset', ctx.values.offset, 0, 1000)});
263
+ const q = query({
264
+ limit: checkInt('limit', ctx.values.limit, 1, 100),
265
+ offset: checkInt('offset', ctx.values.offset, 0, 1000),
266
+ });
220
267
  const client = api(ctx);
221
268
  const r = await client.call('GET', apiPath`/channels/${ch}/members` + q);
222
- return withNames(ctx, r, (r?.members ?? []).map((m) => m.userId), client);
269
+ return withNames(
270
+ ctx,
271
+ r,
272
+ (r?.members ?? []).map((m) => m.userId),
273
+ client,
274
+ );
223
275
  },
224
276
  text(r, ctx) {
225
277
  const c = paint(ctx);
226
278
  const list = r?.members ?? [];
227
279
  if (!list.length) return 'No members.';
228
- return table(list.map((m) => [c.bold(nameIn(r, m.userId)), m.userId, m.role ?? '', c.dim(`joined ${localTime(m.joinedAt)}`)]));
280
+ return table(
281
+ list.map((m) => [c.bold(nameIn(r, m.userId)), m.userId, m.role ?? '', c.dim(`joined ${localTime(m.joinedAt)}`)]),
282
+ );
229
283
  },
230
284
  };
231
285
 
@@ -242,7 +296,17 @@ const member = {
242
296
  const r = await client.call('GET', apiPath`/channels/${ch}/members/${user}`);
243
297
  return withNames(ctx, r, [r?.member?.userId], client);
244
298
  },
245
- text: (r, ctx) => (r?.member ? table([[paint(ctx).bold(nameIn(r, r.member.userId)), r.member.userId, r.member.role ?? '', `joined ${localTime(r.member.joinedAt)}`]]) : 'No member.'),
299
+ text: (r, ctx) =>
300
+ r?.member
301
+ ? table([
302
+ [
303
+ paint(ctx).bold(nameIn(r, r.member.userId)),
304
+ r.member.userId,
305
+ r.member.role ?? '',
306
+ `joined ${localTime(r.member.joinedAt)}`,
307
+ ],
308
+ ])
309
+ : 'No member.',
246
310
  };
247
311
 
248
312
  const add = {
@@ -291,13 +355,24 @@ const readStatus = {
291
355
  const ch = channelId(ctx.positionals[0]);
292
356
  const client = api(ctx);
293
357
  const r = await client.call('GET', apiPath`/channels/${ch}/read-status`);
294
- return withNames(ctx, r, (r?.members ?? []).map((m) => m.userId), client);
358
+ return withNames(
359
+ ctx,
360
+ r,
361
+ (r?.members ?? []).map((m) => m.userId),
362
+ client,
363
+ );
295
364
  },
296
365
  text(r, ctx) {
297
366
  const c = paint(ctx);
298
367
  const list = r?.members ?? [];
299
368
  if (!list.length) return 'No members.';
300
- return table(list.map((m) => [c.bold(nameIn(r, m.userId)), m.lastReadVisibleSeq === undefined ? c.dim('nothing read') : `up to #${m.lastReadVisibleSeq}`, c.dim(localTime(m.lastReadAt))]));
369
+ return table(
370
+ list.map((m) => [
371
+ c.bold(nameIn(r, m.userId)),
372
+ m.lastReadVisibleSeq === undefined ? c.dim('nothing read') : `up to #${m.lastReadVisibleSeq}`,
373
+ c.dim(localTime(m.lastReadAt)),
374
+ ]),
375
+ );
301
376
  },
302
377
  };
303
378
 
@@ -312,7 +387,10 @@ const channel = {
312
387
  direct,
313
388
  'direct-id': directId,
314
389
  update,
315
- join: simple('join', `join a public channel${NOT_IMPLEMENTED}`, 'POST', '/join', {details: notImplemented('JoinChannel'), text: 'Joined'}),
390
+ join: simple('join', `join a public channel${NOT_IMPLEMENTED}`, 'POST', '/join', {
391
+ details: notImplemented('JoinChannel'),
392
+ text: 'Joined',
393
+ }),
316
394
  leave: simple('leave', 'leave a channel', 'POST', '/leave', {text: 'Left'}),
317
395
  delete: del,
318
396
  members,
@@ -1,12 +1,24 @@
1
1
  // wx chat: channels and messages of the Wildix stream service, as the logged-in user (the agent skill's
2
2
  // messages.sh, mentions.sh, search.sh messages and watch.sh). The inbox state is `wx inbox`, channels and their
3
3
  // members `wx channel`, files `wx file`, people `wx user`.
4
- import {AuthError, UsageError} from '../errors.mjs';
5
- import {localTime, oneLine, paint, table} from '../format.mjs';
6
- import {apiPath, query} from '../http.mjs';
4
+ import {AuthError, UsageError} from 'wldx-core/errors';
5
+ import {localTime, oneLine, paint, table} from 'wldx-core/format';
6
+ import {apiPath, query} from 'wldx-core/http';
7
7
  import {channelId, messageId} from '../stream.mjs';
8
8
  import {channelLines, channelUserIds, done, messageLines, messageText, nextPage} from '../chat/format.mjs';
9
- import {api, attachmentOf, checkId, checkInt, checkReaction, meId, messageUserIds, nameIn, readText, uploadFile, withNames} from '../chat/lib.mjs';
9
+ import {
10
+ api,
11
+ attachmentOf,
12
+ checkId,
13
+ checkInt,
14
+ checkReaction,
15
+ meId,
16
+ messageUserIds,
17
+ nameIn,
18
+ readText,
19
+ uploadFile,
20
+ withNames,
21
+ } from '../chat/lib.mjs';
10
22
  import {EVENT_CHOICES, EVENT_GROUPS, eventText, watch} from '../chat/watch.mjs';
11
23
 
12
24
  const SEQ_MAX = 999_999_999_999;
@@ -19,7 +31,8 @@ async function ownMessage(ctx, client, ch, msg) {
19
31
  const r = await client.call('GET', apiPath`/channels/${ch}/messages/${msg}`);
20
32
  const author = r?.message?.userId;
21
33
  const me = await meId(ctx, client);
22
- if (!author || author !== me) throw new AuthError(`The message is not yours (author: ${author ?? 'unknown'}, you: ${me}). Nothing was changed.`);
34
+ if (!author || author !== me)
35
+ throw new AuthError(`The message is not yours (author: ${author ?? 'unknown'}, you: ${me}). Nothing was changed.`);
23
36
  return r.message;
24
37
  }
25
38
 
@@ -36,7 +49,9 @@ export function editBody(message, text, markdown) {
36
49
  forward: ref(message.forward),
37
50
  markdown: markdown ? true : message.markdown,
38
51
  };
39
- return Object.fromEntries(Object.entries(body).filter(([, v]) => v !== undefined && v !== null && !(Array.isArray(v) && !v.length)));
52
+ return Object.fromEntries(
53
+ Object.entries(body).filter(([, v]) => v !== undefined && v !== null && !(Array.isArray(v) && !v.length)),
54
+ );
40
55
  }
41
56
 
42
57
  const sentText = (verb) => (r, ctx) => {
@@ -56,7 +71,10 @@ const list = {
56
71
  async run(ctx) {
57
72
  const {values} = ctx;
58
73
  const client = api(ctx);
59
- const r = await client.call('GET', `/channels${query({filter: values.filter, section: values.section, cursor: values.cursor})}`);
74
+ const r = await client.call(
75
+ 'GET',
76
+ `/channels${query({filter: values.filter, section: values.section, cursor: values.cursor})}`,
77
+ );
60
78
  return withNames(ctx, r, (r?.channels ?? []).flatMap(channelUserIds), client);
61
79
  },
62
80
  text(r, ctx) {
@@ -91,7 +109,8 @@ const read = {
91
109
  if (!messages.length) return 'No messages.';
92
110
  const lines = messageLines(messages, r, ctx);
93
111
  const oldest = Math.min(...messages.map((m) => m.timelineSeq ?? Infinity));
94
- if (messages.length >= 30 && Number.isFinite(oldest) && oldest > 0) lines.push(paint(ctx).dim(`Older: --to ${oldest - 1}`));
112
+ if (messages.length >= 30 && Number.isFinite(oldest) && oldest > 0)
113
+ lines.push(paint(ctx).dim(`Older: --to ${oldest - 1}`));
95
114
  return lines;
96
115
  },
97
116
  };
@@ -99,7 +118,8 @@ const read = {
99
118
  const send = {
100
119
  summary: 'send a message to a channel ("-" reads the text from stdin)',
101
120
  usage: 'wx chat send <channel> <text|-> [--markdown] [--reply-to MESSAGE] [--mention USERID]... [--file PATH]...',
102
- details: 'Sends as the logged-in user. --file uploads the file and attaches it (files in credential folders are refused).',
121
+ details:
122
+ 'Sends as the logged-in user. --file uploads the file and attaches it (files in credential folders are refused).',
103
123
  options: {
104
124
  markdown: {type: 'boolean', description: 'the text is markdown'},
105
125
  'reply-to': {type: 'string', description: 'quote this message (id or inbox link)'},
@@ -151,8 +171,11 @@ const get = {
151
171
  ];
152
172
  if (m.quote) rows.push(['Reply to', `${nameIn(r, m.quote.userId)}: ${oneLine(m.quote.text ?? '', 100)}`]);
153
173
  if (m.mentions?.length) rows.push(['Mentions', m.mentions.map((id) => nameIn(r, id)).join(', ')]);
154
- for (const f of m.attachments ?? []) rows.push(['File', `${f.name} (${f.mime ?? '?'}, ${f.size ?? '?'} bytes) fsId ${f.fsId}`]);
155
- const counts = Object.entries(m.reactionCounts ?? {}).map(([t, n]) => `${t} ${n}`).join(', ');
174
+ for (const f of m.attachments ?? [])
175
+ rows.push(['File', `${f.name} (${f.mime ?? '?'}, ${f.size ?? '?'} bytes) fsId ${f.fsId}`]);
176
+ const counts = Object.entries(m.reactionCounts ?? {})
177
+ .map(([t, n]) => `${t} ${n}`)
178
+ .join(', ');
156
179
  if (counts) rows.push(['Reactions', counts]);
157
180
  if (m.pinned) rows.push(['Pinned', `${localTime(m.pinnedAt)}${m.pinnedBy ? ` by ${nameIn(r, m.pinnedBy)}` : ''}`]);
158
181
  const body = m.messageType === 'deleted' || m.deletedAt ? ['(deleted)'] : String(m.text ?? '').split('\n');
@@ -184,12 +207,17 @@ const edit = {
184
207
  const del = {
185
208
  summary: 'delete your own message, for everyone (needs --yes)',
186
209
  usage: 'wx chat delete <channel> <message> --yes',
187
- details: 'Only your own messages (else exit 2, nothing changed). Confirm with the person first: --yes is not the confirmation.',
210
+ details:
211
+ 'Only your own messages (else exit 2, nothing changed). Confirm with the person first: --yes is not the confirmation.',
188
212
  options: {yes: {type: 'boolean', description: 'yes, delete it for everyone'}},
189
213
  args: [2, 2],
190
214
  async run(ctx) {
191
215
  const {ch, msg} = target(ctx.positionals);
192
- if (!ctx.values.yes) throw new UsageError('delete removes the message for everyone: confirm with the person, then add --yes', ctx.usage());
216
+ if (!ctx.values.yes)
217
+ throw new UsageError(
218
+ 'delete removes the message for everyone: confirm with the person, then add --yes',
219
+ ctx.usage(),
220
+ );
193
221
  const client = api(ctx);
194
222
  await ownMessage(ctx, client, ch, msg);
195
223
  return client.call('DELETE', apiPath`/channels/${ch}/messages/${msg}`);
@@ -226,7 +254,8 @@ const reactCmd = (add) => ({
226
254
  const reactions = {
227
255
  summary: 'who reacted to a message: one entry per person and type',
228
256
  usage: 'wx chat reactions <channel> <message> [--type TYPE] [--users]',
229
- details: 'The JSON gives user ids; --users adds the name, e-mail and bot of each (the text output always shows names).',
257
+ details:
258
+ 'The JSON gives user ids; --users adds the name, e-mail and bot of each (the text output always shows names).',
230
259
  options: {
231
260
  type: {type: 'string', description: 'only this reaction type'},
232
261
  users: {type: 'boolean', description: 'add the name, e-mail and bot of each person (JSON)'},
@@ -269,8 +298,16 @@ const mentions = {
269
298
  args: [0, 0],
270
299
  async run(ctx) {
271
300
  const client = api(ctx);
272
- const r = await client.call('GET', `/mentions${query({filter: ctx.values.unread && 'unread', cursor: ctx.values.cursor})}`);
273
- return withNames(ctx, r, (r?.mentions ?? []).flatMap((x) => [...messageUserIds(x.message), x.channel?.peerId]), client);
301
+ const r = await client.call(
302
+ 'GET',
303
+ `/mentions${query({filter: ctx.values.unread && 'unread', cursor: ctx.values.cursor})}`,
304
+ );
305
+ return withNames(
306
+ ctx,
307
+ r,
308
+ (r?.mentions ?? []).flatMap((x) => [...messageUserIds(x.message), x.channel?.peerId]),
309
+ client,
310
+ );
274
311
  },
275
312
  text(r, ctx) {
276
313
  const c = paint(ctx);
@@ -291,7 +328,8 @@ const SORT = ['relevance', 'recency', 'recency_asc'];
291
328
 
292
329
  const search = {
293
330
  summary: 'search messages in your channels',
294
- usage: 'wx chat search <query> [--channel C] [--sender USERID]... [--attachments] [--links] [--size 1-50] [--cursor C] [--sort relevance|recency|recency_asc]',
331
+ usage:
332
+ 'wx chat search <query> [--channel C] [--sender USERID]... [--attachments] [--links] [--size 1-50] [--cursor C] [--sort relevance|recency|recency_asc]',
295
333
  options: {
296
334
  channel: {type: 'string', description: 'only in this channel (id or inbox link)'},
297
335
  sender: {type: 'string', multiple: true, placeholder: 'USERID', description: 'only messages of this person'},
@@ -316,7 +354,12 @@ const search = {
316
354
  });
317
355
  const client = api(ctx);
318
356
  const r = await client.call('GET', `/search/messages${q}`);
319
- return withNames(ctx, r, (r?.results ?? []).flatMap((x) => messageUserIds(x.message)), client);
357
+ return withNames(
358
+ ctx,
359
+ r,
360
+ (r?.results ?? []).flatMap((x) => messageUserIds(x.message)),
361
+ client,
362
+ );
320
363
  },
321
364
  text(r, ctx) {
322
365
  const c = paint(ctx);
@@ -335,7 +378,8 @@ const search = {
335
378
 
336
379
  const watchCmd = {
337
380
  summary: 'follow your chats live: messages, reactions, reads, members, channel changes',
338
- usage: 'wx chat watch [--channel C]... [--from USERID]... [--type TYPE]... [--mine] [--count N] [--for SECONDS] [--raw]',
381
+ usage:
382
+ 'wx chat watch [--channel C]... [--from USERID]... [--type TYPE]... [--mine] [--count N] [--for SECONDS] [--raw]',
339
383
  details: [
340
384
  'Read only: nothing is sent and nothing is marked read. One line per event (one JSON object per line with',
341
385
  '--output json): READY when listening, then message.new, reaction.new, read.receipt.updated, member.added,',
@@ -348,8 +392,18 @@ const watchCmd = {
348
392
  ].join('\n'),
349
393
  options: {
350
394
  channel: {type: 'string', multiple: true, placeholder: 'C', description: 'only this channel (id or inbox link)'},
351
- from: {type: 'string', multiple: true, placeholder: 'USERID', description: 'only what this person did (messages, reading, typing)'},
352
- type: {type: 'string', multiple: true, choices: EVENT_CHOICES, description: 'only these events: a group or one event'},
395
+ from: {
396
+ type: 'string',
397
+ multiple: true,
398
+ placeholder: 'USERID',
399
+ description: 'only what this person did (messages, reading, typing)',
400
+ },
401
+ type: {
402
+ type: 'string',
403
+ multiple: true,
404
+ choices: EVENT_CHOICES,
405
+ description: 'only these events: a group or one event',
406
+ },
353
407
  mine: {type: 'boolean', description: 'also what you did yourself'},
354
408
  count: {type: 'string', placeholder: 'N', description: 'stop after N events (1-100000)'},
355
409
  for: {type: 'string', placeholder: 'SECONDS', description: 'stop after this long (1-604800)'},
@@ -370,7 +424,8 @@ const watchCmd = {
370
424
  });
371
425
  },
372
426
  textEvent(e, ctx) {
373
- if (ctx.values.raw && !['READY', 'RECONNECTING', 'RECONNECTED', 'END'].includes(e.event)) return `${e.event} ${JSON.stringify(e)}`;
427
+ if (ctx.values.raw && !['READY', 'RECONNECTING', 'RECONNECTED', 'END'].includes(e.event))
428
+ return `${e.event} ${JSON.stringify(e)}`;
374
429
  return eventText(e, ctx);
375
430
  },
376
431
  };