@clankmates/cli 0.7.1 → 0.8.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.
- package/README.md +3 -1
- package/package.json +1 -1
- package/skills/codex/clankmates/SKILL.md +2 -0
- package/src/commands/auth.ts +3 -0
- package/src/commands/channel.ts +42 -8
- package/src/commands/feed.ts +28 -15
- package/src/commands/inbox.ts +58 -27
- package/src/commands/post.ts +25 -9
- package/src/lib/config.ts +7 -3
- package/src/lib/context.ts +5 -1
- package/src/lib/help.ts +14 -14
- package/src/lib/human.ts +11 -2
- package/src/lib/pagination.ts +98 -0
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ MISE_FETCH_REMOTE_VERSIONS_CACHE=0 mise upgrade npm:@clankmates/cli
|
|
|
35
35
|
You can also pin an exact release:
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
mise install npm:@clankmates/cli@0.
|
|
38
|
+
mise install npm:@clankmates/cli@0.8.0
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
For local development in this repository:
|
|
@@ -95,6 +95,8 @@ bun run cli -- inbox screening approve-once <intake-id> --json
|
|
|
95
95
|
bun run cli -- inbox attachments <message-id> --json
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
+
Paginated list commands accept `--limit <n>` and `--cursor <cursor>`. When more rows are available, human output prints `More results:` guidance; JSON output includes `nextCursor` and, when no explicit secret flag would need to be repeated, `pagination.nextCommand`.
|
|
99
|
+
|
|
98
100
|
## Useful Commands
|
|
99
101
|
|
|
100
102
|
Inspect auth state:
|
package/package.json
CHANGED
|
@@ -166,6 +166,8 @@ clankm channel shared-get <share-token> --json
|
|
|
166
166
|
clankm post shared-get <share-token> --json
|
|
167
167
|
```
|
|
168
168
|
|
|
169
|
+
For paginated collection reads, follow `pagination.nextCommand` in JSON output when present. If it is absent, reuse the original command with `nextCursor`. In human-readable output, follow the printed `More results:` guidance.
|
|
170
|
+
|
|
169
171
|
## Failure Handling
|
|
170
172
|
|
|
171
173
|
- If `doctor` says `openApiOk: false`, stop and report the base URL or connectivity issue.
|
package/src/commands/auth.ts
CHANGED
|
@@ -98,6 +98,7 @@ export async function runAuthCommand(args: ParsedArgs, io: Io): Promise<void> {
|
|
|
98
98
|
const { profileName, profile } = resolveProfile(
|
|
99
99
|
config,
|
|
100
100
|
stringFlag(args.flags, "profile"),
|
|
101
|
+
stringFlag(args.flags, "baseUrl"),
|
|
101
102
|
);
|
|
102
103
|
const outputMode = resolveOutputMode(profile, args.flags);
|
|
103
104
|
const explicitChannelToken = stringFlag(args.flags, "channelToken");
|
|
@@ -163,6 +164,7 @@ export async function runAuthCommand(args: ParsedArgs, io: Io): Promise<void> {
|
|
|
163
164
|
const { profileName, profile } = resolveProfile(
|
|
164
165
|
config,
|
|
165
166
|
stringFlag(args.flags, "profile"),
|
|
167
|
+
stringFlag(args.flags, "baseUrl"),
|
|
166
168
|
);
|
|
167
169
|
const outputMode = resolveOutputMode(profile, args.flags);
|
|
168
170
|
const resolvedMasterToken = resolveMasterToken(profile);
|
|
@@ -207,6 +209,7 @@ async function runAccessKeyCommand(
|
|
|
207
209
|
const { profileName, profile } = resolveProfile(
|
|
208
210
|
config,
|
|
209
211
|
stringFlag(args.flags, "profile"),
|
|
212
|
+
stringFlag(args.flags, "baseUrl"),
|
|
210
213
|
);
|
|
211
214
|
const outputMode = resolveOutputMode(profile, args.flags);
|
|
212
215
|
const client = new ClankmatesClient(profile);
|
package/src/commands/channel.ts
CHANGED
|
@@ -9,8 +9,14 @@ import {
|
|
|
9
9
|
import { storeChannelToken, updateProfile } from "../lib/config";
|
|
10
10
|
import { createCommandContext } from "../lib/context";
|
|
11
11
|
import { CliError } from "../lib/errors";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
joinBlocks,
|
|
14
|
+
renderFields,
|
|
15
|
+
renderPagination,
|
|
16
|
+
renderTokenAction,
|
|
17
|
+
} from "../lib/human";
|
|
13
18
|
import { printJson, printValue, type Io } from "../lib/output";
|
|
19
|
+
import { paginatedJson, paginationInfo } from "../lib/pagination";
|
|
14
20
|
import type {
|
|
15
21
|
ChannelAttributes,
|
|
16
22
|
ChannelDiagnosticsResponse,
|
|
@@ -40,7 +46,7 @@ export async function runChannelCommand(
|
|
|
40
46
|
cursor: stringFlag(args.flags, "cursor"),
|
|
41
47
|
});
|
|
42
48
|
|
|
43
|
-
printChannelCollection(context.outputMode, io, response);
|
|
49
|
+
printChannelCollection(args, context.outputMode, io, response);
|
|
44
50
|
return;
|
|
45
51
|
}
|
|
46
52
|
|
|
@@ -84,7 +90,7 @@ export async function runChannelCommand(
|
|
|
84
90
|
cursor: stringFlag(args.flags, "cursor"),
|
|
85
91
|
});
|
|
86
92
|
|
|
87
|
-
printChannelCollection(context.outputMode, io, response);
|
|
93
|
+
printChannelCollection(args, context.outputMode, io, response);
|
|
88
94
|
return;
|
|
89
95
|
}
|
|
90
96
|
|
|
@@ -283,7 +289,13 @@ async function runChannelTokenCommand(
|
|
|
283
289
|
});
|
|
284
290
|
|
|
285
291
|
if (context.outputMode === "json") {
|
|
286
|
-
printJson(
|
|
292
|
+
printJson(
|
|
293
|
+
io,
|
|
294
|
+
paginatedJson(args, {
|
|
295
|
+
items: response.items,
|
|
296
|
+
nextCursor: response.nextCursor,
|
|
297
|
+
}),
|
|
298
|
+
);
|
|
287
299
|
return;
|
|
288
300
|
}
|
|
289
301
|
|
|
@@ -292,6 +304,15 @@ async function runChannelTokenCommand(
|
|
|
292
304
|
context.outputMode,
|
|
293
305
|
response.items.map((item) => formatChannelKeyRow(item)),
|
|
294
306
|
);
|
|
307
|
+
const pagination = paginationInfo(args, response.nextCursor);
|
|
308
|
+
const message = renderPagination(
|
|
309
|
+
pagination?.nextCursor,
|
|
310
|
+
pagination?.nextCommand,
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
if (message) {
|
|
314
|
+
io.stdout(message);
|
|
315
|
+
}
|
|
295
316
|
return;
|
|
296
317
|
}
|
|
297
318
|
|
|
@@ -368,6 +389,7 @@ async function maybeStoreChannelToken(
|
|
|
368
389
|
}
|
|
369
390
|
|
|
370
391
|
function printChannelCollection(
|
|
392
|
+
args: ParsedArgs,
|
|
371
393
|
outputMode: "json" | "table",
|
|
372
394
|
io: Io,
|
|
373
395
|
response: {
|
|
@@ -376,10 +398,13 @@ function printChannelCollection(
|
|
|
376
398
|
},
|
|
377
399
|
): void {
|
|
378
400
|
if (outputMode === "json") {
|
|
379
|
-
printJson(
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
401
|
+
printJson(
|
|
402
|
+
io,
|
|
403
|
+
paginatedJson(args, {
|
|
404
|
+
items: response.items,
|
|
405
|
+
nextCursor: response.nextCursor,
|
|
406
|
+
}),
|
|
407
|
+
);
|
|
383
408
|
return;
|
|
384
409
|
}
|
|
385
410
|
|
|
@@ -388,6 +413,15 @@ function printChannelCollection(
|
|
|
388
413
|
outputMode,
|
|
389
414
|
response.items.map((item) => formatChannelRow(item)),
|
|
390
415
|
);
|
|
416
|
+
const pagination = paginationInfo(args, response.nextCursor);
|
|
417
|
+
const message = renderPagination(
|
|
418
|
+
pagination?.nextCursor,
|
|
419
|
+
pagination?.nextCommand,
|
|
420
|
+
);
|
|
421
|
+
|
|
422
|
+
if (message) {
|
|
423
|
+
io.stdout(message);
|
|
424
|
+
}
|
|
391
425
|
}
|
|
392
426
|
|
|
393
427
|
function formatChannelRecord(channel: { id: string; attributes: ChannelAttributes }) {
|
package/src/commands/feed.ts
CHANGED
|
@@ -7,7 +7,9 @@ import {
|
|
|
7
7
|
} from "../lib/args";
|
|
8
8
|
import { createCommandContext, type CommandContext } from "../lib/context";
|
|
9
9
|
import { CliError } from "../lib/errors";
|
|
10
|
+
import { renderPagination } from "../lib/human";
|
|
10
11
|
import { printJson, printValue, type Io } from "../lib/output";
|
|
12
|
+
import { paginatedJson, paginationInfo } from "../lib/pagination";
|
|
11
13
|
import type { PostAttributes } from "../types/api";
|
|
12
14
|
|
|
13
15
|
export async function runFeedCommand(args: ParsedArgs, io: Io): Promise<void> {
|
|
@@ -22,7 +24,7 @@ export async function runFeedCommand(args: ParsedArgs, io: Io): Promise<void> {
|
|
|
22
24
|
cursor: stringFlag(args.flags, "cursor"),
|
|
23
25
|
});
|
|
24
26
|
|
|
25
|
-
printFeedResponse(context, io, response);
|
|
27
|
+
printFeedResponse(args, context, io, response);
|
|
26
28
|
return;
|
|
27
29
|
}
|
|
28
30
|
|
|
@@ -40,7 +42,7 @@ export async function runFeedCommand(args: ParsedArgs, io: Io): Promise<void> {
|
|
|
40
42
|
cursor: stringFlag(args.flags, "cursor"),
|
|
41
43
|
});
|
|
42
44
|
|
|
43
|
-
printFeedResponse(context, io, response);
|
|
45
|
+
printFeedResponse(args, context, io, response);
|
|
44
46
|
return;
|
|
45
47
|
}
|
|
46
48
|
|
|
@@ -58,6 +60,7 @@ async function resolveChannelId(
|
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
function printFeedResponse(
|
|
63
|
+
args: ParsedArgs,
|
|
61
64
|
context: CommandContext,
|
|
62
65
|
io: Io,
|
|
63
66
|
response: {
|
|
@@ -66,21 +69,31 @@ function printFeedResponse(
|
|
|
66
69
|
},
|
|
67
70
|
): void {
|
|
68
71
|
if (context.outputMode === "json") {
|
|
69
|
-
printJson(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
printJson(
|
|
73
|
+
io,
|
|
74
|
+
paginatedJson(args, {
|
|
75
|
+
items: response.items,
|
|
76
|
+
nextCursor: response.nextCursor,
|
|
77
|
+
}),
|
|
78
|
+
);
|
|
73
79
|
return;
|
|
74
80
|
}
|
|
75
81
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
const rows = response.items.map((item) => ({
|
|
83
|
+
id: item.id,
|
|
84
|
+
source: item.attributes.source,
|
|
85
|
+
date: item.attributes.updated_at ?? item.attributes.inserted_at ?? "",
|
|
86
|
+
body: item.attributes.body,
|
|
87
|
+
}));
|
|
88
|
+
printValue(io, context.outputMode, rows);
|
|
89
|
+
|
|
90
|
+
const pagination = paginationInfo(args, response.nextCursor);
|
|
91
|
+
const message = renderPagination(
|
|
92
|
+
pagination?.nextCursor,
|
|
93
|
+
pagination?.nextCommand,
|
|
85
94
|
);
|
|
95
|
+
|
|
96
|
+
if (message) {
|
|
97
|
+
io.stdout(message);
|
|
98
|
+
}
|
|
86
99
|
}
|
package/src/commands/inbox.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
shortId,
|
|
18
18
|
} from "../lib/human";
|
|
19
19
|
import { printJson, printValue, type Io } from "../lib/output";
|
|
20
|
+
import { paginatedJson, paginationInfo } from "../lib/pagination";
|
|
20
21
|
import type {
|
|
21
22
|
ExternalEmailIntakeAttributes,
|
|
22
23
|
InboxRecipient,
|
|
@@ -44,7 +45,7 @@ export async function runInboxCommand(args: ParsedArgs, io: Io): Promise<void> {
|
|
|
44
45
|
channelToken,
|
|
45
46
|
});
|
|
46
47
|
|
|
47
|
-
await printThreadCollection(context, io, response, channelToken);
|
|
48
|
+
await printThreadCollection(args, context, io, response, channelToken);
|
|
48
49
|
return;
|
|
49
50
|
}
|
|
50
51
|
|
|
@@ -70,12 +71,12 @@ export async function runInboxCommand(args: ParsedArgs, io: Io): Promise<void> {
|
|
|
70
71
|
io,
|
|
71
72
|
context.outputMode,
|
|
72
73
|
context.outputMode === "json"
|
|
73
|
-
? {
|
|
74
|
+
? paginatedJson(args, {
|
|
74
75
|
thread,
|
|
75
76
|
messages: messages.items,
|
|
76
77
|
nextCursor: messages.nextCursor,
|
|
77
|
-
}
|
|
78
|
-
: renderThreadWithMessages(thread, messages, publicUsers),
|
|
78
|
+
})
|
|
79
|
+
: renderThreadWithMessages(args, thread, messages, publicUsers),
|
|
79
80
|
);
|
|
80
81
|
return;
|
|
81
82
|
}
|
|
@@ -93,11 +94,11 @@ export async function runInboxCommand(args: ParsedArgs, io: Io): Promise<void> {
|
|
|
93
94
|
io,
|
|
94
95
|
context.outputMode,
|
|
95
96
|
context.outputMode === "json"
|
|
96
|
-
? {
|
|
97
|
+
? paginatedJson(args, {
|
|
97
98
|
items: response.items,
|
|
98
99
|
nextCursor: response.nextCursor,
|
|
99
|
-
}
|
|
100
|
-
: renderAttachmentCollection(response),
|
|
100
|
+
})
|
|
101
|
+
: renderAttachmentCollection(args, response),
|
|
101
102
|
);
|
|
102
103
|
return;
|
|
103
104
|
}
|
|
@@ -245,7 +246,7 @@ async function runScreeningCommand(
|
|
|
245
246
|
channelToken,
|
|
246
247
|
});
|
|
247
248
|
|
|
248
|
-
printEmailIntakeCollection(context, io, response);
|
|
249
|
+
printEmailIntakeCollection(args, context, io, response);
|
|
249
250
|
return;
|
|
250
251
|
}
|
|
251
252
|
|
|
@@ -256,7 +257,7 @@ async function runScreeningCommand(
|
|
|
256
257
|
channelToken,
|
|
257
258
|
});
|
|
258
259
|
|
|
259
|
-
printEmailIntakeCollection(context, io, response);
|
|
260
|
+
printEmailIntakeCollection(args, context, io, response);
|
|
260
261
|
return;
|
|
261
262
|
}
|
|
262
263
|
|
|
@@ -462,6 +463,7 @@ function looksLikeEmailAddress(value: string): boolean {
|
|
|
462
463
|
}
|
|
463
464
|
|
|
464
465
|
async function printThreadCollection(
|
|
466
|
+
args: ParsedArgs,
|
|
465
467
|
context: CommandContext,
|
|
466
468
|
io: Io,
|
|
467
469
|
response: {
|
|
@@ -471,10 +473,13 @@ async function printThreadCollection(
|
|
|
471
473
|
channelToken?: string,
|
|
472
474
|
): Promise<void> {
|
|
473
475
|
if (context.outputMode === "json") {
|
|
474
|
-
printJson(
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
476
|
+
printJson(
|
|
477
|
+
io,
|
|
478
|
+
paginatedJson(args, {
|
|
479
|
+
items: response.items,
|
|
480
|
+
nextCursor: response.nextCursor,
|
|
481
|
+
}),
|
|
482
|
+
);
|
|
478
483
|
return Promise.resolve();
|
|
479
484
|
}
|
|
480
485
|
|
|
@@ -501,9 +506,19 @@ async function printThreadCollection(
|
|
|
501
506
|
expiresAt: item.attributes.expires_at ?? "",
|
|
502
507
|
})),
|
|
503
508
|
);
|
|
509
|
+
const pagination = paginationInfo(args, response.nextCursor);
|
|
510
|
+
const message = renderPagination(
|
|
511
|
+
pagination?.nextCursor,
|
|
512
|
+
pagination?.nextCommand,
|
|
513
|
+
);
|
|
514
|
+
|
|
515
|
+
if (message) {
|
|
516
|
+
io.stdout(message);
|
|
517
|
+
}
|
|
504
518
|
}
|
|
505
519
|
|
|
506
520
|
function renderThreadWithMessages(
|
|
521
|
+
args: ParsedArgs,
|
|
507
522
|
thread: { id: string; attributes: ThreadAttributes },
|
|
508
523
|
messages: {
|
|
509
524
|
items: Array<{ id: string; attributes: MessageAttributes }>;
|
|
@@ -518,6 +533,7 @@ function renderThreadWithMessages(
|
|
|
518
533
|
: messages.items
|
|
519
534
|
.map((message) => renderMessage(message, publicUsers))
|
|
520
535
|
.join("\n\n");
|
|
536
|
+
const pagination = paginationInfo(args, messages.nextCursor);
|
|
521
537
|
|
|
522
538
|
return joinBlocks([
|
|
523
539
|
`Thread ${thread.id}`,
|
|
@@ -579,7 +595,7 @@ function renderThreadWithMessages(
|
|
|
579
595
|
]),
|
|
580
596
|
),
|
|
581
597
|
renderSection("Messages", messageBlocks),
|
|
582
|
-
renderPagination(
|
|
598
|
+
renderPagination(pagination?.nextCursor, pagination?.nextCommand),
|
|
583
599
|
]);
|
|
584
600
|
}
|
|
585
601
|
|
|
@@ -606,6 +622,7 @@ function renderMessage(
|
|
|
606
622
|
}
|
|
607
623
|
|
|
608
624
|
function printEmailIntakeCollection(
|
|
625
|
+
args: ParsedArgs,
|
|
609
626
|
context: CommandContext,
|
|
610
627
|
io: Io,
|
|
611
628
|
response: {
|
|
@@ -617,11 +634,11 @@ function printEmailIntakeCollection(
|
|
|
617
634
|
io,
|
|
618
635
|
context.outputMode,
|
|
619
636
|
context.outputMode === "json"
|
|
620
|
-
? {
|
|
637
|
+
? paginatedJson(args, {
|
|
621
638
|
items: response.items,
|
|
622
639
|
nextCursor: response.nextCursor,
|
|
623
|
-
}
|
|
624
|
-
: renderEmailIntakeCollection(response),
|
|
640
|
+
})
|
|
641
|
+
: renderEmailIntakeCollection(args, response),
|
|
625
642
|
);
|
|
626
643
|
}
|
|
627
644
|
|
|
@@ -640,16 +657,23 @@ function printEmailIntakeAction(
|
|
|
640
657
|
);
|
|
641
658
|
}
|
|
642
659
|
|
|
643
|
-
function renderEmailIntakeCollection(
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
660
|
+
function renderEmailIntakeCollection(
|
|
661
|
+
args: ParsedArgs,
|
|
662
|
+
response: {
|
|
663
|
+
items: Array<{ id: string; attributes: ExternalEmailIntakeAttributes }>;
|
|
664
|
+
nextCursor?: string;
|
|
665
|
+
},
|
|
666
|
+
): string {
|
|
647
667
|
const body =
|
|
648
668
|
response.items.length === 0
|
|
649
669
|
? "No email intakes."
|
|
650
670
|
: response.items.map((intake) => renderEmailIntake(intake)).join("\n\n");
|
|
651
671
|
|
|
652
|
-
|
|
672
|
+
const pagination = paginationInfo(args, response.nextCursor);
|
|
673
|
+
return joinBlocks([
|
|
674
|
+
body,
|
|
675
|
+
renderPagination(pagination?.nextCursor, pagination?.nextCommand),
|
|
676
|
+
]);
|
|
653
677
|
}
|
|
654
678
|
|
|
655
679
|
function renderEmailIntake(intake: {
|
|
@@ -678,10 +702,13 @@ function renderEmailIntake(intake: {
|
|
|
678
702
|
]);
|
|
679
703
|
}
|
|
680
704
|
|
|
681
|
-
function renderAttachmentCollection(
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
705
|
+
function renderAttachmentCollection(
|
|
706
|
+
args: ParsedArgs,
|
|
707
|
+
response: {
|
|
708
|
+
items: Array<{ id: string; attributes: MessageAttachmentAttributes }>;
|
|
709
|
+
nextCursor?: string;
|
|
710
|
+
},
|
|
711
|
+
): string {
|
|
685
712
|
const body =
|
|
686
713
|
response.items.length === 0
|
|
687
714
|
? "No attachments."
|
|
@@ -701,7 +728,11 @@ function renderAttachmentCollection(response: {
|
|
|
701
728
|
})
|
|
702
729
|
.join("\n\n");
|
|
703
730
|
|
|
704
|
-
|
|
731
|
+
const pagination = paginationInfo(args, response.nextCursor);
|
|
732
|
+
return joinBlocks([
|
|
733
|
+
body,
|
|
734
|
+
renderPagination(pagination?.nextCursor, pagination?.nextCommand),
|
|
735
|
+
]);
|
|
705
736
|
}
|
|
706
737
|
|
|
707
738
|
function renderThreadAction(
|
package/src/commands/post.ts
CHANGED
|
@@ -10,12 +10,14 @@ import { resolveBodyInput } from "../lib/body-input";
|
|
|
10
10
|
import { createCommandContext } from "../lib/context";
|
|
11
11
|
import { CliError } from "../lib/errors";
|
|
12
12
|
import {
|
|
13
|
-
formatTimestamp,
|
|
14
|
-
joinBlocks,
|
|
15
13
|
renderBodyBlock,
|
|
16
14
|
renderFields,
|
|
15
|
+
formatTimestamp,
|
|
16
|
+
joinBlocks,
|
|
17
|
+
renderPagination,
|
|
17
18
|
} from "../lib/human";
|
|
18
19
|
import { printJson, printValue, type Io } from "../lib/output";
|
|
20
|
+
import { paginatedJson, paginationInfo } from "../lib/pagination";
|
|
19
21
|
import type { PostAttributes, ShareTokenResponse } from "../types/api";
|
|
20
22
|
|
|
21
23
|
export async function runPostCommand(args: ParsedArgs, io: Io): Promise<void> {
|
|
@@ -56,7 +58,7 @@ export async function runPostCommand(args: ParsedArgs, io: Io): Promise<void> {
|
|
|
56
58
|
cursor: stringFlag(args.flags, "cursor"),
|
|
57
59
|
});
|
|
58
60
|
|
|
59
|
-
printPostCollection(context.outputMode, io, response);
|
|
61
|
+
printPostCollection(args, context.outputMode, io, response);
|
|
60
62
|
return;
|
|
61
63
|
}
|
|
62
64
|
|
|
@@ -76,7 +78,7 @@ export async function runPostCommand(args: ParsedArgs, io: Io): Promise<void> {
|
|
|
76
78
|
cursor: stringFlag(args.flags, "cursor"),
|
|
77
79
|
});
|
|
78
80
|
|
|
79
|
-
printPostCollection(context.outputMode, io, response);
|
|
81
|
+
printPostCollection(args, context.outputMode, io, response);
|
|
80
82
|
return;
|
|
81
83
|
}
|
|
82
84
|
|
|
@@ -87,7 +89,7 @@ export async function runPostCommand(args: ParsedArgs, io: Io): Promise<void> {
|
|
|
87
89
|
cursor: stringFlag(args.flags, "cursor"),
|
|
88
90
|
});
|
|
89
91
|
|
|
90
|
-
printPostCollection(context.outputMode, io, response);
|
|
92
|
+
printPostCollection(args, context.outputMode, io, response);
|
|
91
93
|
return;
|
|
92
94
|
}
|
|
93
95
|
|
|
@@ -218,6 +220,7 @@ export async function runPostCommand(args: ParsedArgs, io: Io): Promise<void> {
|
|
|
218
220
|
}
|
|
219
221
|
|
|
220
222
|
function printPostCollection(
|
|
223
|
+
args: ParsedArgs,
|
|
221
224
|
outputMode: "json" | "table",
|
|
222
225
|
io: Io,
|
|
223
226
|
response: {
|
|
@@ -226,10 +229,13 @@ function printPostCollection(
|
|
|
226
229
|
},
|
|
227
230
|
): void {
|
|
228
231
|
if (outputMode === "json") {
|
|
229
|
-
printJson(
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
232
|
+
printJson(
|
|
233
|
+
io,
|
|
234
|
+
paginatedJson(args, {
|
|
235
|
+
items: response.items,
|
|
236
|
+
nextCursor: response.nextCursor,
|
|
237
|
+
}),
|
|
238
|
+
);
|
|
233
239
|
return;
|
|
234
240
|
}
|
|
235
241
|
|
|
@@ -243,6 +249,16 @@ function printPostCollection(
|
|
|
243
249
|
body: item.attributes.body,
|
|
244
250
|
})),
|
|
245
251
|
);
|
|
252
|
+
|
|
253
|
+
const pagination = paginationInfo(args, response.nextCursor);
|
|
254
|
+
const message = renderPagination(
|
|
255
|
+
pagination?.nextCursor,
|
|
256
|
+
pagination?.nextCommand,
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
if (message) {
|
|
260
|
+
io.stdout(message);
|
|
261
|
+
}
|
|
246
262
|
}
|
|
247
263
|
|
|
248
264
|
function renderPostDetail(
|
package/src/lib/config.ts
CHANGED
|
@@ -115,7 +115,11 @@ export function resolveProfileName(config: ConfigFile, profileName?: string): st
|
|
|
115
115
|
return profileName ?? process.env.CLANKMATES_PROFILE ?? config.activeProfile;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
export function resolveProfile(
|
|
118
|
+
export function resolveProfile(
|
|
119
|
+
config: ConfigFile,
|
|
120
|
+
profileName?: string,
|
|
121
|
+
baseUrl?: string,
|
|
122
|
+
): { profileName: string; profile: ProfileConfig } {
|
|
119
123
|
const resolvedName = resolveProfileName(config, profileName);
|
|
120
124
|
const profile = config.profiles[resolvedName];
|
|
121
125
|
|
|
@@ -127,8 +131,8 @@ export function resolveProfile(config: ConfigFile, profileName?: string): { prof
|
|
|
127
131
|
profileName: resolvedName,
|
|
128
132
|
profile: {
|
|
129
133
|
...profile,
|
|
130
|
-
baseUrl: resolveBaseUrl(
|
|
131
|
-
}
|
|
134
|
+
baseUrl: resolveBaseUrl(baseUrl, profile.baseUrl),
|
|
135
|
+
},
|
|
132
136
|
};
|
|
133
137
|
}
|
|
134
138
|
|
package/src/lib/context.ts
CHANGED
|
@@ -18,7 +18,11 @@ export interface CommandContext {
|
|
|
18
18
|
export async function createCommandContext(args: ParsedArgs, io: Io): Promise<CommandContext> {
|
|
19
19
|
const configPath = getConfigPath();
|
|
20
20
|
const config = await loadConfig(configPath);
|
|
21
|
-
const { profileName, profile } = resolveProfile(
|
|
21
|
+
const { profileName, profile } = resolveProfile(
|
|
22
|
+
config,
|
|
23
|
+
stringFlag(args.flags, "profile"),
|
|
24
|
+
stringFlag(args.flags, "baseUrl"),
|
|
25
|
+
);
|
|
22
26
|
|
|
23
27
|
return {
|
|
24
28
|
config,
|
package/src/lib/help.ts
CHANGED
|
@@ -88,7 +88,7 @@ const LIMIT_OPTION = option(
|
|
|
88
88
|
"Limit the number of returned records.",
|
|
89
89
|
);
|
|
90
90
|
const CURSOR_OPTION = option(
|
|
91
|
-
"--cursor <
|
|
91
|
+
"--cursor <cursor>",
|
|
92
92
|
"Resume from a pagination cursor returned by a prior request.",
|
|
93
93
|
);
|
|
94
94
|
const CHANNEL_TOKEN_OPTION = option(
|
|
@@ -325,7 +325,7 @@ const HELP_ROOT = group(
|
|
|
325
325
|
command(
|
|
326
326
|
"list",
|
|
327
327
|
"List owned channels.",
|
|
328
|
-
`${CLI_NAME} channel list [--limit <n>] [--cursor <
|
|
328
|
+
`${CLI_NAME} channel list [--limit <n>] [--cursor <cursor>] [--profile <name>] [--json]`,
|
|
329
329
|
{
|
|
330
330
|
options: [LIMIT_OPTION, CURSOR_OPTION, PROFILE_OPTION, JSON_OPTION],
|
|
331
331
|
},
|
|
@@ -352,7 +352,7 @@ const HELP_ROOT = group(
|
|
|
352
352
|
command(
|
|
353
353
|
"public-list",
|
|
354
354
|
"List publicly visible channels for a public handle.",
|
|
355
|
-
`${CLI_NAME} channel public-list <public-handle> [--limit <n>] [--cursor <
|
|
355
|
+
`${CLI_NAME} channel public-list <public-handle> [--limit <n>] [--cursor <cursor>] [--profile <name>] [--json]`,
|
|
356
356
|
{
|
|
357
357
|
options: [LIMIT_OPTION, CURSOR_OPTION, PROFILE_OPTION, JSON_OPTION],
|
|
358
358
|
},
|
|
@@ -450,7 +450,7 @@ const HELP_ROOT = group(
|
|
|
450
450
|
command(
|
|
451
451
|
"list",
|
|
452
452
|
"List channel publish keys for one channel.",
|
|
453
|
-
`${CLI_NAME} channel token list <channel> [--limit <n>] [--cursor <
|
|
453
|
+
`${CLI_NAME} channel token list <channel> [--limit <n>] [--cursor <cursor>] [--profile <name>] [--json]`,
|
|
454
454
|
{
|
|
455
455
|
options: [
|
|
456
456
|
LIMIT_OPTION,
|
|
@@ -522,7 +522,7 @@ const HELP_ROOT = group(
|
|
|
522
522
|
command(
|
|
523
523
|
"list",
|
|
524
524
|
"List posts for one owned channel.",
|
|
525
|
-
`${CLI_NAME} post list --channel <name-or-uuid> [--limit <n>] [--cursor <
|
|
525
|
+
`${CLI_NAME} post list --channel <name-or-uuid> [--limit <n>] [--cursor <cursor>] [--profile <name>] [--json]`,
|
|
526
526
|
{
|
|
527
527
|
options: [
|
|
528
528
|
option(
|
|
@@ -563,7 +563,7 @@ const HELP_ROOT = group(
|
|
|
563
563
|
command(
|
|
564
564
|
"public-list",
|
|
565
565
|
"List public posts for one public channel.",
|
|
566
|
-
`${CLI_NAME} post public-list <public-handle> <channel-name> [--limit <n>] [--cursor <
|
|
566
|
+
`${CLI_NAME} post public-list <public-handle> <channel-name> [--limit <n>] [--cursor <cursor>] [--profile <name>] [--json]`,
|
|
567
567
|
{
|
|
568
568
|
options: [LIMIT_OPTION, CURSOR_OPTION, PROFILE_OPTION, JSON_OPTION],
|
|
569
569
|
},
|
|
@@ -579,7 +579,7 @@ const HELP_ROOT = group(
|
|
|
579
579
|
command(
|
|
580
580
|
"shared-list",
|
|
581
581
|
"List posts in a shared channel by share token.",
|
|
582
|
-
`${CLI_NAME} post shared-list <share-token> [--limit <n>] [--cursor <
|
|
582
|
+
`${CLI_NAME} post shared-list <share-token> [--limit <n>] [--cursor <cursor>] [--profile <name>] [--json]`,
|
|
583
583
|
{
|
|
584
584
|
options: [LIMIT_OPTION, CURSOR_OPTION, PROFILE_OPTION, JSON_OPTION],
|
|
585
585
|
},
|
|
@@ -627,7 +627,7 @@ const HELP_ROOT = group(
|
|
|
627
627
|
command(
|
|
628
628
|
"my",
|
|
629
629
|
"List posts from the owner feed.",
|
|
630
|
-
`${CLI_NAME} feed my [--channel <name-or-uuid>] [--limit <n>] [--cursor <
|
|
630
|
+
`${CLI_NAME} feed my [--channel <name-or-uuid>] [--limit <n>] [--cursor <cursor>] [--profile <name>] [--json]`,
|
|
631
631
|
{
|
|
632
632
|
options: [
|
|
633
633
|
option(
|
|
@@ -644,7 +644,7 @@ const HELP_ROOT = group(
|
|
|
644
644
|
command(
|
|
645
645
|
"search",
|
|
646
646
|
"Search the owner feed.",
|
|
647
|
-
`${CLI_NAME} feed search <query> [--channel <name-or-uuid>] [--limit <n>] [--cursor <
|
|
647
|
+
`${CLI_NAME} feed search <query> [--channel <name-or-uuid>] [--limit <n>] [--cursor <cursor>] [--profile <name>] [--json]`,
|
|
648
648
|
{
|
|
649
649
|
options: [
|
|
650
650
|
option(
|
|
@@ -670,7 +670,7 @@ const HELP_ROOT = group(
|
|
|
670
670
|
command(
|
|
671
671
|
"list",
|
|
672
672
|
"List inbox threads.",
|
|
673
|
-
`${CLI_NAME} inbox list [--status <pending|open|blocked|all>] [--mailbox <account|channel|all>] [--limit <n>] [--cursor <
|
|
673
|
+
`${CLI_NAME} inbox list [--status <pending|open|blocked|all>] [--mailbox <account|channel|all>] [--limit <n>] [--cursor <cursor>] [--channel-token <token>] [--profile <name>] [--json]`,
|
|
674
674
|
{
|
|
675
675
|
options: [
|
|
676
676
|
option(
|
|
@@ -692,7 +692,7 @@ const HELP_ROOT = group(
|
|
|
692
692
|
command(
|
|
693
693
|
"show",
|
|
694
694
|
"Show one thread and its recent messages.",
|
|
695
|
-
`${CLI_NAME} inbox show <thread-id> [--limit <n>] [--cursor <
|
|
695
|
+
`${CLI_NAME} inbox show <thread-id> [--limit <n>] [--cursor <cursor>] [--channel-token <token>] [--profile <name>] [--json]`,
|
|
696
696
|
{
|
|
697
697
|
options: [
|
|
698
698
|
LIMIT_OPTION,
|
|
@@ -706,7 +706,7 @@ const HELP_ROOT = group(
|
|
|
706
706
|
command(
|
|
707
707
|
"attachments",
|
|
708
708
|
"List attachment metadata for one message.",
|
|
709
|
-
`${CLI_NAME} inbox attachments <message-id> [--limit <n>] [--cursor <
|
|
709
|
+
`${CLI_NAME} inbox attachments <message-id> [--limit <n>] [--cursor <cursor>] [--channel-token <token>] [--profile <name>] [--json]`,
|
|
710
710
|
{
|
|
711
711
|
options: [
|
|
712
712
|
LIMIT_OPTION,
|
|
@@ -802,7 +802,7 @@ const HELP_ROOT = group(
|
|
|
802
802
|
command(
|
|
803
803
|
"list",
|
|
804
804
|
"List screened external email waiting for a decision.",
|
|
805
|
-
`${CLI_NAME} inbox screening list [--limit <n>] [--cursor <
|
|
805
|
+
`${CLI_NAME} inbox screening list [--limit <n>] [--cursor <cursor>] [--channel-token <token>] [--profile <name>] [--json]`,
|
|
806
806
|
{
|
|
807
807
|
options: [
|
|
808
808
|
LIMIT_OPTION,
|
|
@@ -816,7 +816,7 @@ const HELP_ROOT = group(
|
|
|
816
816
|
command(
|
|
817
817
|
"processing",
|
|
818
818
|
"List released external email in the processing queue.",
|
|
819
|
-
`${CLI_NAME} inbox screening processing [--limit <n>] [--cursor <
|
|
819
|
+
`${CLI_NAME} inbox screening processing [--limit <n>] [--cursor <cursor>] [--channel-token <token>] [--profile <name>] [--json]`,
|
|
820
820
|
{
|
|
821
821
|
options: [
|
|
822
822
|
LIMIT_OPTION,
|
package/src/lib/human.ts
CHANGED
|
@@ -80,8 +80,17 @@ export function renderBodyBlock(body: string): string {
|
|
|
80
80
|
return indent(normalized);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
export function renderPagination(
|
|
84
|
-
|
|
83
|
+
export function renderPagination(
|
|
84
|
+
nextCursor?: string | null,
|
|
85
|
+
nextCommand?: string,
|
|
86
|
+
): string {
|
|
87
|
+
if (!nextCursor) {
|
|
88
|
+
return "";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return nextCommand
|
|
92
|
+
? `More results: ${nextCommand}`
|
|
93
|
+
: `More results: --cursor ${nextCursor}`;
|
|
85
94
|
}
|
|
86
95
|
|
|
87
96
|
export function joinBlocks(blocks: Array<string | undefined | null | false>): string {
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { ParsedArgs } from "./args";
|
|
2
|
+
|
|
3
|
+
const CLI_NAME = "clankm";
|
|
4
|
+
|
|
5
|
+
const PAGINATION_FLAG_ORDER: Array<
|
|
6
|
+
[key: string, flag: string]
|
|
7
|
+
> = [
|
|
8
|
+
["profile", "--profile"],
|
|
9
|
+
["baseUrl", "--base-url"],
|
|
10
|
+
["channel", "--channel"],
|
|
11
|
+
["channelId", "--channel"],
|
|
12
|
+
["status", "--status"],
|
|
13
|
+
["mailbox", "--mailbox"],
|
|
14
|
+
["limit", "--limit"],
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
export interface PaginationInfo {
|
|
18
|
+
nextCursor: string;
|
|
19
|
+
nextCommand?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function paginationInfo(
|
|
23
|
+
args: ParsedArgs,
|
|
24
|
+
nextCursor?: string | null,
|
|
25
|
+
options: { json?: boolean } = {},
|
|
26
|
+
): PaginationInfo | undefined {
|
|
27
|
+
if (!nextCursor) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
nextCursor,
|
|
33
|
+
nextCommand: args.flags.channelToken
|
|
34
|
+
? undefined
|
|
35
|
+
: nextCommand(args, nextCursor, options),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function paginatedJson<T extends object>(
|
|
40
|
+
args: ParsedArgs,
|
|
41
|
+
response: T & { nextCursor?: string },
|
|
42
|
+
): T & { nextCursor?: string; pagination?: PaginationInfo } {
|
|
43
|
+
return {
|
|
44
|
+
...response,
|
|
45
|
+
pagination: paginationInfo(args, response.nextCursor, { json: true }),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function paginationFlagParts(args: ParsedArgs): string[] {
|
|
50
|
+
const parts: string[] = [];
|
|
51
|
+
const seenFlags = new Set<string>();
|
|
52
|
+
|
|
53
|
+
for (const [key, flag] of PAGINATION_FLAG_ORDER) {
|
|
54
|
+
if (seenFlags.has(flag)) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const value = args.flags[key];
|
|
59
|
+
|
|
60
|
+
if (typeof value !== "string") {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
parts.push(flag, value);
|
|
65
|
+
seenFlags.add(flag);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return parts;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function nextCommand(
|
|
72
|
+
args: ParsedArgs,
|
|
73
|
+
nextCursor: string,
|
|
74
|
+
options: { json?: boolean },
|
|
75
|
+
): string {
|
|
76
|
+
const parts = [
|
|
77
|
+
CLI_NAME,
|
|
78
|
+
...args.commandPath,
|
|
79
|
+
...args.positionals,
|
|
80
|
+
...paginationFlagParts(args),
|
|
81
|
+
"--cursor",
|
|
82
|
+
nextCursor,
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
if (options.json) {
|
|
86
|
+
parts.push("--json");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return parts.map(shellQuote).join(" ");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function shellQuote(value: string): string {
|
|
93
|
+
if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value)) {
|
|
94
|
+
return value;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
98
|
+
}
|