not-manage 0.1.17

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/src/cli.js ADDED
@@ -0,0 +1,668 @@
1
+ const {
2
+ authLogin,
3
+ authRevoke,
4
+ authSetup,
5
+ authStatus,
6
+ maybeRunSetupOnFirstUse,
7
+ setupWizard,
8
+ whoAmI,
9
+ } = require("./commands-auth");
10
+ const { billsGet, billsList } = require("./commands-bills");
11
+ const {
12
+ activitiesGet,
13
+ activitiesList,
14
+ } = require("./commands-activities");
15
+ const { tasksGet, tasksList } = require("./commands-tasks");
16
+ const {
17
+ billableClientsList,
18
+ } = require("./commands-billable-clients");
19
+ const {
20
+ billableMattersList,
21
+ } = require("./commands-billable-matters");
22
+ const { contactsGet, contactsList } = require("./commands-contacts");
23
+ const { mattersGet, mattersList } = require("./commands-matters");
24
+ const {
25
+ practiceAreasGet,
26
+ practiceAreasList,
27
+ } = require("./commands-practice-areas");
28
+ const { usersGet, usersList } = require("./commands-users");
29
+ const { version } = require("../package.json");
30
+
31
+ const COMMAND_ALIASES = {
32
+ activity: "activities",
33
+ bill: "bills",
34
+ "billable-client": "billable-clients",
35
+ "billable-matter": "billable-matters",
36
+ contact: "contacts",
37
+ invoice: "invoices",
38
+ matter: "matters",
39
+ "practice-area": "practice-areas",
40
+ task: "tasks",
41
+ "time-entry": "time-entries",
42
+ user: "users",
43
+ };
44
+ const DEFAULT_FIELDS_BY_COMMAND = {
45
+ activities: {
46
+ get:
47
+ "id,type,date,quantity,quantity_in_hours,rounded_quantity,rounded_quantity_in_hours,price,total,billed,on_bill,non_billable,no_charge,flat_rate,contingency_fee,note,reference,created_at,updated_at,activity_description{id,name},bill{id,number,state},matter{id,display_number,number,description},user{id,name,first_name,last_name,email}",
48
+ list:
49
+ "id,type,date,quantity,quantity_in_hours,rounded_quantity,rounded_quantity_in_hours,price,total,billed,on_bill,non_billable,no_charge,flat_rate,contingency_fee,note,reference,created_at,updated_at,activity_description{id,name},bill{id,number,state},matter{id,display_number,number,description},user{id,name,first_name,last_name,email}",
50
+ },
51
+ "billable-clients": {
52
+ list: "id,name,unbilled_hours,unbilled_amount,amount_in_trust,billable_matters_count",
53
+ },
54
+ "billable-matters": {
55
+ list:
56
+ "id,display_number,unbilled_hours,unbilled_amount,amount_in_trust,client{id,name,first_name,last_name}",
57
+ },
58
+ bills: {
59
+ get:
60
+ "id,number,state,type,kind,subject,memo,issued_at,due_at,paid,paid_at,pending,due,total,balance,created_at,updated_at,client{id,name,first_name,last_name},matters{id,display_number,number,description}",
61
+ list:
62
+ "id,number,state,type,kind,subject,memo,issued_at,due_at,paid,paid_at,pending,due,total,balance,created_at,updated_at,client{id,name,first_name,last_name},matters{id,display_number,number,description}",
63
+ },
64
+ contacts: {
65
+ get:
66
+ "id,name,first_name,last_name,type,is_client,primary_email_address,secondary_email_address,primary_phone_number,secondary_phone_number,clio_connect_email,title,prefix,created_at,updated_at",
67
+ list:
68
+ "id,name,first_name,last_name,type,is_client,primary_email_address,secondary_email_address,primary_phone_number,secondary_phone_number,clio_connect_email,title,prefix,created_at,updated_at",
69
+ },
70
+ invoices: {
71
+ get:
72
+ "id,number,state,type,kind,subject,memo,issued_at,due_at,paid,paid_at,pending,due,total,balance,created_at,updated_at,client{id,name,first_name,last_name},matters{id,display_number,number,description}",
73
+ list:
74
+ "id,number,state,type,kind,subject,memo,issued_at,due_at,paid,paid_at,pending,due,total,balance,created_at,updated_at,client{id,name,first_name,last_name},matters{id,display_number,number,description}",
75
+ },
76
+ matters: {
77
+ get:
78
+ "id,display_number,number,description,status,billable,open_date,close_date,pending_date,client{id,name,first_name,last_name},practice_area{id,name},responsible_attorney{id,name,email},responsible_staff{id,name,email},originating_attorney{id,name,email},created_at,updated_at",
79
+ list:
80
+ "id,display_number,number,description,status,billable,open_date,close_date,pending_date,client{id,name,first_name,last_name},practice_area{id,name},responsible_attorney{id,name,email},responsible_staff{id,name,email},originating_attorney{id,name,email},created_at,updated_at",
81
+ },
82
+ "practice-areas": {
83
+ get: "id,code,name,category,created_at,updated_at",
84
+ list: "id,code,name,category,created_at,updated_at",
85
+ },
86
+ tasks: {
87
+ get:
88
+ "id,name,description,status,priority,due_at,created_at,updated_at,matter{id,display_number,number,description,client},assignee{id,name},assigner{id,name},task_type{id,name}",
89
+ list:
90
+ "id,name,description,status,priority,due_at,created_at,updated_at,matter{id,display_number,number,description,client},assignee{id,name},assigner{id,name},task_type{id,name}",
91
+ },
92
+ "time-entries": {
93
+ get:
94
+ "id,type,date,quantity,quantity_in_hours,rounded_quantity,rounded_quantity_in_hours,price,total,billed,on_bill,non_billable,no_charge,flat_rate,contingency_fee,note,reference,created_at,updated_at,activity_description{id,name},bill{id,number,state},matter{id,display_number,number,description},user{id,name,first_name,last_name,email}",
95
+ list:
96
+ "id,type,date,quantity,quantity_in_hours,rounded_quantity,rounded_quantity_in_hours,price,total,billed,on_bill,non_billable,no_charge,flat_rate,contingency_fee,note,reference,created_at,updated_at,activity_description{id,name},bill{id,number,state},matter{id,display_number,number,description},user{id,name,first_name,last_name,email}",
97
+ },
98
+ users: {
99
+ get:
100
+ "id,name,first_name,last_name,email,enabled,roles,subscription_type,phone_number,time_zone,rate,account_owner,clio_connect,court_rules_default_attendee,created_at,updated_at",
101
+ list:
102
+ "id,name,first_name,last_name,email,enabled,roles,subscription_type,phone_number,time_zone,rate,account_owner,clio_connect,court_rules_default_attendee,created_at,updated_at",
103
+ },
104
+ };
105
+ const DATA_COMMANDS = new Set([
106
+ "activities",
107
+ "time-entries",
108
+ "tasks",
109
+ "contacts",
110
+ "matters",
111
+ "bills",
112
+ "invoices",
113
+ "users",
114
+ "practice-areas",
115
+ "billable-matters",
116
+ "billable-clients",
117
+ ]);
118
+ const HIGH_RISK_COMMANDS = new Set([
119
+ "activities",
120
+ "time-entries",
121
+ "tasks",
122
+ "contacts",
123
+ "matters",
124
+ "bills",
125
+ "invoices",
126
+ "billable-matters",
127
+ "billable-clients",
128
+ ]);
129
+ const LIMITED_REDACTION_COMMANDS = new Set([
130
+ "activities",
131
+ "time-entries",
132
+ "tasks",
133
+ "matters",
134
+ "bills",
135
+ "invoices",
136
+ "billable-matters",
137
+ ]);
138
+
139
+ function hasFlag(args, ...flags) {
140
+ return flags.some((flag) => args.includes(flag));
141
+ }
142
+
143
+ function parseOptions(args) {
144
+ const parsed = {};
145
+ const positional = [];
146
+
147
+ for (let i = 0; i < args.length; i += 1) {
148
+ const token = args[i];
149
+
150
+ if (!token.startsWith("--")) {
151
+ positional.push(token);
152
+ continue;
153
+ }
154
+
155
+ const inlineSplit = token.slice(2).split("=");
156
+ const key = inlineSplit[0];
157
+ const inlineValue = inlineSplit.length > 1 ? inlineSplit.slice(1).join("=") : null;
158
+
159
+ if (inlineValue !== null) {
160
+ parsed[key] = inlineValue;
161
+ continue;
162
+ }
163
+
164
+ const next = args[i + 1];
165
+ if (next && !next.startsWith("--")) {
166
+ parsed[key] = next;
167
+ i += 1;
168
+ continue;
169
+ }
170
+
171
+ parsed[key] = true;
172
+ }
173
+
174
+ return { parsed, positional };
175
+ }
176
+
177
+ function normalizeCommand(command) {
178
+ return COMMAND_ALIASES[command] || command;
179
+ }
180
+
181
+ function maybePrintDefaultFields(command, sub, optionValues) {
182
+ if (optionValues.fields !== true) {
183
+ return false;
184
+ }
185
+
186
+ const defaults = DEFAULT_FIELDS_BY_COMMAND[command]?.[sub];
187
+ if (!defaults) {
188
+ throw new Error(
189
+ "`--fields` requires a comma-separated value for this command. Example: --fields id,name"
190
+ );
191
+ }
192
+
193
+ console.log(defaults);
194
+ return true;
195
+ }
196
+
197
+ function resolveRedactionPreference(optionValues) {
198
+ if (optionValues.unredacted !== undefined) {
199
+ return false;
200
+ }
201
+
202
+ return true;
203
+ }
204
+
205
+ function warnAboutRedaction(command, sub, optionValues, redacted) {
206
+ if (sub !== "list" && sub !== "get" || !DATA_COMMANDS.has(command)) {
207
+ return;
208
+ }
209
+
210
+ const explicitUnredacted = optionValues.unredacted !== undefined;
211
+ const highRisk = HIGH_RISK_COMMANDS.has(command);
212
+
213
+ if (explicitUnredacted && highRisk) {
214
+ console.error(
215
+ "Warning: showing raw output without redaction. This command can include client-identifying, confidential, or privileged information."
216
+ );
217
+ console.error(
218
+ "Review output carefully before sharing it outside your firm or with any third party."
219
+ );
220
+ return;
221
+ }
222
+
223
+ if (!redacted || !highRisk) {
224
+ return;
225
+ }
226
+
227
+ console.error(
228
+ "Warning: output is redacted by default. Redaction is best-effort and may miss client or matter identifiers."
229
+ );
230
+ console.error(
231
+ "Review output before sharing it outside your firm. Re-run with `--unredacted` to show raw output."
232
+ );
233
+
234
+ if (LIMITED_REDACTION_COMMANDS.has(command)) {
235
+ console.error(
236
+ "Warning: related matter labels, captions, and other non-client fields may still identify a matter."
237
+ );
238
+ }
239
+ }
240
+
241
+ function printHelp() {
242
+ console.log("not-manage");
243
+ console.log("");
244
+ console.log("Usage:");
245
+ console.log(" not-manage <command> [options]");
246
+ console.log("");
247
+ console.log("Commands:");
248
+ console.log(" setup Run guided setup and OAuth login");
249
+ console.log(" auth setup Configure client credentials in OS keychain");
250
+ console.log(" auth login Run local OAuth login flow");
251
+ console.log(" auth status Show auth status and connected user");
252
+ console.log(" auth revoke Revoke token and clear local token storage");
253
+ console.log(" activities list List activities with filters and pagination");
254
+ console.log(" activities get Fetch a single activity by id");
255
+ console.log(" tasks list List tasks with filters and pagination");
256
+ console.log(" tasks get Fetch a single task by id");
257
+ console.log(" contacts list List contacts with filters and pagination");
258
+ console.log(" contacts get Fetch a single contact by id");
259
+ console.log(" time-entries list Alias for activities list filtered to TimeEntry");
260
+ console.log(" time-entries get Alias for activities get");
261
+ console.log(" billable-clients list List clients with unbilled activity");
262
+ console.log(" billable-matters list List matters with unbilled activity");
263
+ console.log(" bills list List bills with filters and pagination");
264
+ console.log(" bills get Fetch a single bill by id");
265
+ console.log(" invoices list Alias for bills list");
266
+ console.log(" invoices get Alias for bills get");
267
+ console.log(" matters list List matters with filters and pagination");
268
+ console.log(" matters get Fetch a single matter by id");
269
+ console.log(" users list List users with filters and pagination");
270
+ console.log(" users get Fetch a single user by id");
271
+ console.log(" practice-areas list List practice areas with filters and pagination");
272
+ console.log(" practice-areas get Fetch a single practice area by id");
273
+ console.log(" whoami Call /api/v4/users/who_am_i");
274
+ console.log("");
275
+ console.log("Aliases:");
276
+ console.log(" Singular aliases are accepted, for example:");
277
+ console.log(" contact get, matter get, bill get, invoice get, task get, user get");
278
+ console.log("");
279
+ console.log("Options:");
280
+ console.log(" --fields <list> Override returned fields; pass `--fields` alone to print defaults");
281
+ console.log(" --json Print machine-readable JSON for supported commands");
282
+ console.log(" --redacted Kept for compatibility; data commands are redacted by default");
283
+ console.log(" --unredacted Show raw output without default redaction");
284
+ console.log(" -h, --help Show help");
285
+ console.log(" -v, --version Show version");
286
+ }
287
+
288
+ async function run(args) {
289
+ if (!args.length) {
290
+ const startedOnboarding = await maybeRunSetupOnFirstUse();
291
+ if (!startedOnboarding) {
292
+ printHelp();
293
+ }
294
+ return;
295
+ }
296
+
297
+ if (hasFlag(args, "-h", "--help")) {
298
+ printHelp();
299
+ return;
300
+ }
301
+
302
+ if (hasFlag(args, "-v", "--version")) {
303
+ console.log(`not-manage v${version}`);
304
+ return;
305
+ }
306
+
307
+ const json = hasFlag(args, "--json");
308
+ const command = normalizeCommand(args[0]);
309
+ const sub = args[1];
310
+ const optionTokens = args.slice(2);
311
+ const parsedOptions = parseOptions(optionTokens);
312
+ const optionValues = parsedOptions.parsed;
313
+ const positional = parsedOptions.positional;
314
+ const redacted = resolveRedactionPreference(optionValues);
315
+
316
+ if (maybePrintDefaultFields(command, sub, optionValues)) {
317
+ return;
318
+ }
319
+
320
+ if (command === "setup") {
321
+ await setupWizard();
322
+ return;
323
+ }
324
+
325
+ if (command === "auth" && sub === "setup") {
326
+ await authSetup();
327
+ return;
328
+ }
329
+
330
+ if (command === "auth" && sub === "login") {
331
+ await authLogin();
332
+ return;
333
+ }
334
+
335
+ if (command === "auth" && sub === "status") {
336
+ await authStatus({ json });
337
+ return;
338
+ }
339
+
340
+ if (command === "auth" && sub === "revoke") {
341
+ await authRevoke();
342
+ return;
343
+ }
344
+
345
+ if (command === "whoami") {
346
+ await whoAmI({ json });
347
+ return;
348
+ }
349
+
350
+ if ((command === "activities" || command === "time-entries") && sub === "list") {
351
+ warnAboutRedaction(command, sub, optionValues, redacted);
352
+ await activitiesList({
353
+ activityDescriptionId:
354
+ optionValues["activity-description-id"] || optionValues.activity_description_id,
355
+ all: Boolean(optionValues.all),
356
+ clientId: optionValues["client-id"] || optionValues.client_id,
357
+ createdSince: optionValues["created-since"] || optionValues.created_since,
358
+ endDate: optionValues["end-date"] || optionValues.end_date,
359
+ fields: optionValues.fields,
360
+ flatRate:
361
+ optionValues["flat-rate"] === undefined && optionValues.flat_rate === undefined
362
+ ? undefined
363
+ : (optionValues["flat-rate"] || optionValues.flat_rate) !== "false",
364
+ json,
365
+ limit: optionValues.limit,
366
+ matterId: optionValues["matter-id"] || optionValues.matter_id,
367
+ onlyUnaccountedFor: Boolean(
368
+ optionValues["only-unaccounted-for"] || optionValues.only_unaccounted_for
369
+ ),
370
+ order: optionValues.order,
371
+ pageToken: optionValues["page-token"] || optionValues.page_token,
372
+ query: optionValues.query,
373
+ redacted,
374
+ startDate: optionValues["start-date"] || optionValues.start_date,
375
+ status: optionValues.status,
376
+ taskId: optionValues["task-id"] || optionValues.task_id,
377
+ type:
378
+ command === "time-entries"
379
+ ? "TimeEntry"
380
+ : optionValues.type,
381
+ updatedSince: optionValues["updated-since"] || optionValues.updated_since,
382
+ userId: optionValues["user-id"] || optionValues.user_id,
383
+ });
384
+ return;
385
+ }
386
+
387
+ if ((command === "activities" || command === "time-entries") && sub === "get") {
388
+ warnAboutRedaction(command, sub, optionValues, redacted);
389
+ await activitiesGet({
390
+ fields: optionValues.fields,
391
+ id: positional[0],
392
+ json,
393
+ redacted,
394
+ });
395
+ return;
396
+ }
397
+
398
+ if (command === "tasks" && sub === "list") {
399
+ warnAboutRedaction(command, sub, optionValues, redacted);
400
+ await tasksList({
401
+ all: Boolean(optionValues.all),
402
+ clientId: optionValues["client-id"] || optionValues.client_id,
403
+ complete:
404
+ optionValues.complete === undefined
405
+ ? undefined
406
+ : optionValues.complete !== "false",
407
+ createdSince: optionValues["created-since"] || optionValues.created_since,
408
+ dueAtFrom: optionValues["due-at-from"] || optionValues.due_at_from,
409
+ dueAtTo: optionValues["due-at-to"] || optionValues.due_at_to,
410
+ fields: optionValues.fields,
411
+ json,
412
+ limit: optionValues.limit,
413
+ matterId: optionValues["matter-id"] || optionValues.matter_id,
414
+ order: optionValues.order,
415
+ pageToken: optionValues["page-token"] || optionValues.page_token,
416
+ priority: optionValues.priority,
417
+ query: optionValues.query,
418
+ redacted,
419
+ responsibleAttorneyId:
420
+ optionValues["responsible-attorney-id"] || optionValues.responsible_attorney_id,
421
+ status: optionValues.status,
422
+ taskTypeId: optionValues["task-type-id"] || optionValues.task_type_id,
423
+ updatedSince: optionValues["updated-since"] || optionValues.updated_since,
424
+ });
425
+ return;
426
+ }
427
+
428
+ if (command === "tasks" && sub === "get") {
429
+ warnAboutRedaction(command, sub, optionValues, redacted);
430
+ await tasksGet({
431
+ fields: optionValues.fields,
432
+ id: positional[0],
433
+ json,
434
+ redacted,
435
+ });
436
+ return;
437
+ }
438
+
439
+ if (command === "contacts" && sub === "list") {
440
+ warnAboutRedaction(command, sub, optionValues, redacted);
441
+ await contactsList({
442
+ all: Boolean(optionValues.all),
443
+ clientOnly: Boolean(optionValues["client-only"] || optionValues.client_only),
444
+ clioConnectOnly: Boolean(
445
+ optionValues["clio-connect-only"] || optionValues.clio_connect_only
446
+ ),
447
+ createdSince: optionValues["created-since"] || optionValues.created_since,
448
+ emailOnly: Boolean(optionValues["email-only"] || optionValues.email_only),
449
+ fields: optionValues.fields,
450
+ initial: optionValues.initial,
451
+ json,
452
+ limit: optionValues.limit,
453
+ order: optionValues.order,
454
+ pageToken: optionValues["page-token"] || optionValues.page_token,
455
+ query: optionValues.query,
456
+ redacted,
457
+ type: optionValues.type,
458
+ updatedSince: optionValues["updated-since"] || optionValues.updated_since,
459
+ });
460
+ return;
461
+ }
462
+
463
+ if (command === "contacts" && sub === "get") {
464
+ warnAboutRedaction(command, sub, optionValues, redacted);
465
+ await contactsGet({
466
+ fields: optionValues.fields,
467
+ id: positional[0],
468
+ json,
469
+ redacted,
470
+ });
471
+ return;
472
+ }
473
+
474
+ if (command === "matters" && sub === "list") {
475
+ warnAboutRedaction(command, sub, optionValues, redacted);
476
+ await mattersList({
477
+ all: Boolean(optionValues.all),
478
+ clientId: optionValues["client-id"] || optionValues.client_id,
479
+ createdSince: optionValues["created-since"] || optionValues.created_since,
480
+ fields: optionValues.fields,
481
+ json,
482
+ limit: optionValues.limit,
483
+ order: optionValues.order,
484
+ originatingAttorneyId:
485
+ optionValues["originating-attorney-id"] || optionValues.originating_attorney_id,
486
+ pageToken: optionValues["page-token"] || optionValues.page_token,
487
+ practiceAreaId: optionValues["practice-area-id"] || optionValues.practice_area_id,
488
+ query: optionValues.query,
489
+ redacted,
490
+ responsibleAttorneyId:
491
+ optionValues["responsible-attorney-id"] || optionValues.responsible_attorney_id,
492
+ responsibleStaffId:
493
+ optionValues["responsible-staff-id"] || optionValues.responsible_staff_id,
494
+ status: optionValues.status,
495
+ updatedSince: optionValues["updated-since"] || optionValues.updated_since,
496
+ });
497
+ return;
498
+ }
499
+
500
+ if (command === "matters" && sub === "get") {
501
+ warnAboutRedaction(command, sub, optionValues, redacted);
502
+ await mattersGet({
503
+ fields: optionValues.fields,
504
+ id: positional[0],
505
+ json,
506
+ redacted,
507
+ });
508
+ return;
509
+ }
510
+
511
+ if ((command === "bills" || command === "invoices") && sub === "list") {
512
+ warnAboutRedaction(command, sub, optionValues, redacted);
513
+ await billsList({
514
+ all: Boolean(optionValues.all),
515
+ clientId: optionValues["client-id"] || optionValues.client_id,
516
+ createdSince: optionValues["created-since"] || optionValues.created_since,
517
+ dueAfter: optionValues["due-after"] || optionValues.due_after,
518
+ dueBefore: optionValues["due-before"] || optionValues.due_before,
519
+ fields: optionValues.fields,
520
+ issuedAfter: optionValues["issued-after"] || optionValues.issued_after,
521
+ issuedBefore: optionValues["issued-before"] || optionValues.issued_before,
522
+ json,
523
+ limit: optionValues.limit,
524
+ matterId: optionValues["matter-id"] || optionValues.matter_id,
525
+ order: optionValues.order,
526
+ overdueOnly: Boolean(optionValues["overdue-only"] || optionValues.overdue_only),
527
+ pageToken: optionValues["page-token"] || optionValues.page_token,
528
+ query: optionValues.query,
529
+ redacted,
530
+ state: optionValues.state,
531
+ status: optionValues.status,
532
+ type: optionValues.type,
533
+ updatedSince: optionValues["updated-since"] || optionValues.updated_since,
534
+ });
535
+ return;
536
+ }
537
+
538
+ if ((command === "bills" || command === "invoices") && sub === "get") {
539
+ warnAboutRedaction(command, sub, optionValues, redacted);
540
+ await billsGet({
541
+ fields: optionValues.fields,
542
+ id: positional[0],
543
+ json,
544
+ redacted,
545
+ });
546
+ return;
547
+ }
548
+
549
+ if (command === "users" && sub === "list") {
550
+ warnAboutRedaction(command, sub, optionValues, redacted);
551
+ await usersList({
552
+ all: Boolean(optionValues.all),
553
+ createdSince: optionValues["created-since"] || optionValues.created_since,
554
+ enabled:
555
+ optionValues.enabled === undefined ? undefined : optionValues.enabled !== "false",
556
+ fields: optionValues.fields,
557
+ includeCoCounsel: Boolean(
558
+ optionValues["include-co-counsel"] || optionValues.include_co_counsel
559
+ ),
560
+ json,
561
+ limit: optionValues.limit,
562
+ name: optionValues.name,
563
+ order: optionValues.order,
564
+ pageToken: optionValues["page-token"] || optionValues.page_token,
565
+ pendingSetup:
566
+ optionValues["pending-setup"] === undefined && optionValues.pending_setup === undefined
567
+ ? undefined
568
+ : (optionValues["pending-setup"] || optionValues.pending_setup) !== "false",
569
+ redacted,
570
+ role: optionValues.role,
571
+ subscriptionType:
572
+ optionValues["subscription-type"] || optionValues.subscription_type,
573
+ updatedSince: optionValues["updated-since"] || optionValues.updated_since,
574
+ });
575
+ return;
576
+ }
577
+
578
+ if (command === "users" && sub === "get") {
579
+ warnAboutRedaction(command, sub, optionValues, redacted);
580
+ await usersGet({
581
+ fields: optionValues.fields,
582
+ id: positional[0],
583
+ json,
584
+ redacted,
585
+ });
586
+ return;
587
+ }
588
+
589
+ if (command === "practice-areas" && sub === "list") {
590
+ warnAboutRedaction(command, sub, optionValues, redacted);
591
+ await practiceAreasList({
592
+ all: Boolean(optionValues.all),
593
+ code: optionValues.code,
594
+ createdSince: optionValues["created-since"] || optionValues.created_since,
595
+ fields: optionValues.fields,
596
+ json,
597
+ limit: optionValues.limit,
598
+ matterId: optionValues["matter-id"] || optionValues.matter_id,
599
+ name: optionValues.name,
600
+ order: optionValues.order,
601
+ pageToken: optionValues["page-token"] || optionValues.page_token,
602
+ redacted,
603
+ updatedSince: optionValues["updated-since"] || optionValues.updated_since,
604
+ });
605
+ return;
606
+ }
607
+
608
+ if (command === "practice-areas" && sub === "get") {
609
+ warnAboutRedaction(command, sub, optionValues, redacted);
610
+ await practiceAreasGet({
611
+ fields: optionValues.fields,
612
+ id: positional[0],
613
+ json,
614
+ redacted,
615
+ });
616
+ return;
617
+ }
618
+
619
+ if (command === "billable-matters" && sub === "list") {
620
+ warnAboutRedaction(command, sub, optionValues, redacted);
621
+ await billableMattersList({
622
+ all: Boolean(optionValues.all),
623
+ clientId: optionValues["client-id"] || optionValues.client_id,
624
+ endDate: optionValues["end-date"] || optionValues.end_date,
625
+ fields: optionValues.fields,
626
+ json,
627
+ limit: optionValues.limit,
628
+ matterId: optionValues["matter-id"] || optionValues.matter_id,
629
+ originatingAttorneyId:
630
+ optionValues["originating-attorney-id"] || optionValues.originating_attorney_id,
631
+ pageToken: optionValues["page-token"] || optionValues.page_token,
632
+ query: optionValues.query,
633
+ redacted,
634
+ responsibleAttorneyId:
635
+ optionValues["responsible-attorney-id"] || optionValues.responsible_attorney_id,
636
+ startDate: optionValues["start-date"] || optionValues.start_date,
637
+ });
638
+ return;
639
+ }
640
+
641
+ if (command === "billable-clients" && sub === "list") {
642
+ warnAboutRedaction(command, sub, optionValues, redacted);
643
+ await billableClientsList({
644
+ all: Boolean(optionValues.all),
645
+ clientId: optionValues["client-id"] || optionValues.client_id,
646
+ endDate: optionValues["end-date"] || optionValues.end_date,
647
+ fields: optionValues.fields,
648
+ json,
649
+ limit: optionValues.limit,
650
+ matterId: optionValues["matter-id"] || optionValues.matter_id,
651
+ originatingAttorneyId:
652
+ optionValues["originating-attorney-id"] || optionValues.originating_attorney_id,
653
+ pageToken: optionValues["page-token"] || optionValues.page_token,
654
+ query: optionValues.query,
655
+ redacted,
656
+ responsibleAttorneyId:
657
+ optionValues["responsible-attorney-id"] || optionValues.responsible_attorney_id,
658
+ startDate: optionValues["start-date"] || optionValues.start_date,
659
+ });
660
+ return;
661
+ }
662
+
663
+ throw new Error(`Unknown command: ${args.join(" ")}`);
664
+ }
665
+
666
+ module.exports = {
667
+ run,
668
+ };